/* ==========================================================
   ALICE Design System — alice.css
   Decision 011: No CSS framework. Single handwritten file.
   Decision 010: Okabe-Ito aligned palette. Triple-signal status.
   ========================================================== */

/* 1. CSS Reset */
*, *::before, *::after {
    box-sizing: border-box;
}

/* 2. CSS Custom Properties */
:root {
    /* ── Legend TEXT colours ──────────────────────────────────────
       The bar-segment hues double as label text, and as text they fail
       4.5:1 badly on a light background: submitted was 1.99:1 in the
       default theme and 1.18:1 in high contrast — a legend nobody with
       low vision can read, explaining a chart. Darkening the shared
       --alice-* tokens is not an option: they are also fills behind
       dark text, where darkening removes the contrast it adds here.
       So text gets its own tokens, derived by lowering LIGHTNESS while
       holding hue and saturation. Holding the hue is what protects the
       colour-blind overlays — in tritanopia "submitted" stays at 326°
       and "read" at 26°, so the two columns remain separable, which is
       the whole reason --alice-amber (not --alice-warning) is used for
       submitted in the first place (see the note by .progress-label--*).
       Every value below is measured, not guessed: ≥4.5:1 against that
       theme's own --alice-bg. */
    --alice-teal-text:  #167758;   /* 4.62:1 on the badge tint, 5.04:1 on the page */
    --alice-amber-text: #9C620C;   /* 1.99 -> 4.61:1 */
    --alice-blue-text:  #185FA5;   /* already 5.97:1 — unchanged */
    --alice-gray-text:  #706F69;   /* 3.30 -> 4.61:1 */
    /* Referenced by the FERPA badge and the inbox "communicatio" badge as
       var(--alice-danger, #c0392b) / var(--alice-teal-soft, #d8f0ee) — and
       defined nowhere, so the LITERAL fallback won in every theme while the
       foreground followed the theme. Pale teal on pale mint measured 1.68:1
       in dark. A var() fallback is not a theme value; it is what you get when
       the theme forgot. */
    --alice-danger:     #B03026;   /* 4.5:1 on #F5F5F2 and on #FFFFFF */
    --alice-teal-soft:  #D8F0EE;
    --alice-blue: #185FA5;
    --alice-amber: #EF9F27;
    --alice-teal: #1D9E75;
    /* 3.72:1 on white. Now 4.61:1, same hue. Also used as a fill behind
       white text, where darkening only adds contrast. */
    --alice-warning: #9C6213;
    --alice-gray: #888780;
    --alice-surface: #FFFFFF;
    --alice-bg: #F5F5F2;
    --alice-text: #1A1A1A;
    --alice-text-secondary: #555550;
    /* 3.57:1 on white; used for body-sized muted prose. Now 4.50:1. */
    --alice-text-tertiary: #71716A;
    --alice-border: #E0DFD8;
    --alice-border-strong: #C0BFB8;
    --alice-focus: #185FA5;

    /* Tints for status badges */
    --alice-teal-tint: #E6F5EF;
    --alice-amber-tint: #FFF5E0;
    --alice-gray-tint: #F0EFEC;
    --alice-blue-tint: #E8F0FA;

    --alice-border-secondary: #D0CFc8;
    --alice-border-tertiary: #E8E7E0;

    /* ── --color-* aliases ─────────────────────────────────────────
     * A handful of templates were written with `var(--color-surface)`,
     * `var(--color-border)`, etc. — variables that were never defined
     * anywhere, so their literal-fallback values (#fff / #ccc / #1A1A1A)
     * always won. That meant asset cards, reader cards, and a few other
     * surfaces stayed white in dark mode.
     *
     * Rather than rewrite every template, alias the missing names back
     * to the canonical `--alice-*` tokens. CSS custom properties resolve
     * lazily, so each `var(--color-surface)` site now follows the active
     * theme automatically. New templates should still use `--alice-*`
     * directly — keep these aliases only for backwards compatibility. */
    --color-surface: var(--alice-surface);
    --color-bg:      var(--alice-bg);
    --color-text:    var(--alice-text);
    --color-text-secondary: var(--alice-text-secondary);
    --color-border:  var(--alice-border);
    --color-border-strong: var(--alice-border-strong);

    /* ── Chart tokens (performance plots) ──────────────────────────
     * COMPUTED, NOT CHOSEN. Every value below was produced by snapping a
     * hue to a step that passes the six-check validator (OKLab ΔE, all
     * pairs, against THIS theme's surface) — not picked by eye. Re-run the
     * validator before editing any of them.
     *
     * --alice-series-N: categorical identity, for the ONE plot whose
     *   subject is a set of peers (section vs section). Assigned in fixed
     *   order and never cycled: a 5th section folds into "others" rather
     *   than reusing series 1's hue, because a repeated colour reads as
     *   "the same section" and is worse than an unplotted one.
     *   Light: worst all-pairs ΔE 13.8 (CVD) / 20.2 (normal vision).
     *
     * --alice-ramp-N: sequential magnitude for the course heatmap, six
     *   steps light→dark. Six because the scores are rubric-band midpoints
     *   binned at 70/80/85/90/95 — boundaries fixed by the rubric, never
     *   fitted to the data, so a colour means the same thing on every
     *   course. The palest step keeps >= 2:1 contrast against the surface
     *   so a barely-passing cell is still visibly a CELL: on a grid that is
     *   91% empty, "faint" must never be confusable with "ungraded".
     *
     * No ink token for text ON a cell, deliberately: the value lives in the
     * hover tooltip and the table view, never printed inside 1650 cells.
     * That keeps colour from being the only channel (the table is the
     * non-colour reading) without turning the grid into a wall of numbers.
     */
    --alice-series-1: #025a8d;
    --alice-series-2: #853e14;
    --alice-series-3: #009267;
    --alice-series-4: #b36fcc;
    --alice-ramp-1: #92b1d3;
    --alice-ramp-2: #7190b0;
    --alice-ramp-3: #52708f;
    --alice-ramp-4: #35516e;
    --alice-ramp-5: #183450;
    --alice-ramp-6: #001932;

    /* Radii */
    --radius-md: 6px;
    --radius-lg: 10px;

    /* Map path / inactive elements */
    --alice-map-path:  #888880;
    --alice-map-muted: #C0BFB8;

    /* Hyperlink color */
    /* WCAG 1.4.3: #7ab8f5 measured 1.92:1 on the page background and 2.10:1
       on white — the two surfaces it is actually used on (the "readable on
       navy" note was stale; nothing navy uses it). Same hue, 4.55:1. */
    --link-color: #0F70CE;

    /* Font stacks */
    --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Courier New", monospace;
    --font-serif: Georgia, "Times New Roman", serif;

    /* Legacy accent colors referenced by .tessera-notice / .pill-* —
       defined here so the CSS doesn't fall back to "invisible" (per
       06_CSS_AUDIT §2.6 + cross-cutting audit Axis D). Okabe-Ito-safe. */
    --gold:   #FFB800;
    --orange: #FF8C00;

    /* ── Semantic color aliases (Tier-2, per architecture/UX.md §2.2)
       Components SHOULD reference --color-* tokens. Tenant theming
       overrides --color-brand* in base.html. Other semantic tokens
       are platform-locked. */
    --color-success:        var(--alice-teal);
    --color-success-bg:     var(--alice-teal-tint);
    --color-warning:        var(--alice-amber);
    --color-warning-bg:     var(--alice-amber-tint);
    --color-danger:         var(--alice-warning);   /* brownish-orange per DECISIONS O1 */
    --color-danger-bg:      #FFF0F0;
    --color-info:           var(--alice-blue);
    --color-info-bg:        var(--alice-blue-tint);
    --color-muted:          var(--alice-gray);
    --color-muted-bg:       var(--alice-gray-tint);
    --color-text-tertiary:  var(--alice-text-tertiary);
    --color-focus:          var(--alice-focus);
    --color-brand:          var(--alice-blue);       /* platform default; tenant overrides in base.html */
    --color-brand-tint:     var(--alice-blue-tint);  /* platform default; tenant overrides */
    --color-brand-fg:       #FFFFFF;
}

/* ── STANDARD (default) ─── already in :root ─────────── */
/* --alice-blue:    #185FA5  */
/* --alice-amber:   #EF9F27  */
/* --alice-teal:    #1D9E75  */
/* --alice-warning: #BA7517  */
/* --alice-gray:    #888780  */

/* ── DARK MODE ───────────────────────────────────── */
/* html.th-X selectors mirror body.th-X for the FOUC-prevention
   inline script in base.html, which sets the class on
   <html> before <body> is parsed. CSS variables cascade from
   the root, so all `body.th-X .child` compound rules elsewhere
   keep working unchanged. */
html.th-dark,
body.th-dark {
  --alice-danger:       #F1948A;
  --alice-teal-soft:    #16302B;
  /* Dark already passes at 8.6-9.3:1 — the text tokens are the hues. */
  --alice-teal-text:    #5DCAA5;
  --alice-amber-text:   #EF9F27;
  --alice-blue-text:    #85B7EB;
  --alice-gray-text:    #B4B2A9;
  --alice-blue:         #85B7EB;
  --alice-amber:        #EF9F27;
  --alice-teal:         #5DCAA5;
  --alice-warning:      #FAC775;
  --alice-gray:         #B4B2A9;
  --alice-surface:      #1E1E1E;
  --alice-bg:           #121212;
  --alice-text:         #E8E6E0;
  --alice-text-secondary:#A8A6A0;
  --alice-text-tertiary: #888880;
  --alice-border:       #3A3A38;
  --alice-border-strong:#555550;
  --alice-teal-tint:    #1A2E26;
  --alice-amber-tint:   #2E2618;
  --alice-gray-tint:    #2A2A28;
  --alice-blue-tint:    #1A2436;
  --alice-map-path:     #888880;
  --alice-map-muted:    #555550;
  --link-color:         #85B7EB;
  /* --color-danger-bg is a hardcoded light pink (#FFF0F0) in :root that
     never flips, so danger pills (Failed/Revoked) rendered light-amber text
     on light-pink in dark mode — illegible. Give it a dark warm tint here so
     the amber text/border reads (matches the other dark *-tint backgrounds). */
  --color-danger-bg:    #3A2018;

  /* Chart tokens, RE-STEPPED for the #1E1E1E surface — not an automatic
     flip of the light values. The dark lightness band is narrower (OKLab L
     0.48-0.67 against 0.43-0.77), so the same four HUES get their own steps;
     hue family is preserved, which is what keeps a section's identity stable
     when a reader switches theme mid-session. Validated all-pairs: worst
     CVD ΔE 10.4, normal vision 18.6. The ramp reverses direction — on a dark
     surface, MORE is LIGHTER — and its palest end is the high-score end. */
  --alice-series-1: #1b93e0;
  --alice-series-2: #c95b0d;
  --alice-series-3: #009267;
  --alice-series-4: #885999;
  --alice-ramp-1: #365270;
  --alice-ramp-2: #4e6c8b;
  --alice-ramp-3: #6886a6;
  --alice-ramp-4: #82a2c3;
  --alice-ramp-5: #9ebee0;
  --alice-ramp-6: #badbfe;
}

/* ── LIGHT MODE ──────────────────────────────────── */
html.th-light,
body.th-light {
  --alice-blue:          #185FA5;
  --alice-amber:         #EF9F27;
  --alice-teal:          #1D9E75;
  --alice-surface:       #FFFFFF;
  --alice-bg:            #F5F5F2;
  --alice-text:          #1A1A1A;
  --alice-text-secondary:#555550;
  --alice-border:        #E0DFD8;
  --alice-border-strong: #C0BFB8;
  --alice-teal-tint:     #E6F5EF;
  --alice-amber-tint:    #FFF5E0;
  --alice-gray-tint:     #F0EFEC;
  --alice-blue-tint:     #E8F0FA;
  --alice-map-path:      #444440;
  --alice-map-muted:     #C0BFB8;
  --link-color:          #185FA5;
}

/* ── HIGH CONTRAST (theme) ──────────────────────────── */
html.th-hico,
body.th-hico {
  --alice-danger:        #B30000;
  --alice-teal-soft:     #E6F5EF;
  --alice-teal-text:     #006600;   /* 7.24:1 — unchanged */
  --alice-amber-text:    #7C6C00;   /* 1.35 -> 4.60:1 on #F0F0F0 */
  --alice-blue-text:     #0000EE;   /* 9.40:1 — unchanged */
  --alice-gray-text:     #555555;   /* 7.46:1 — unchanged */
  --alice-blue:          #0000EE;
  --alice-amber:         #FFDD00;
  --alice-teal:          #006600;
  --alice-warning:       #CC4400;
  --alice-gray:          #555555;
  --alice-surface:       #FFFFFF;
  --alice-bg:            #F0F0F0;
  --alice-text:          #000000;
  --alice-text-secondary:#111111;
  --alice-text-tertiary: #333333;
  --alice-border:        #000000;
  --alice-border-strong: #000000;
  --alice-border-secondary: #000000;
  --alice-border-tertiary:  #444444;
  --alice-map-path:      #000000;
  --alice-map-muted:     #888888;
  --alice-focus:         #0000EE;
  --link-color:          #0000EE;
  /* Light tints — explicit so they do NOT leak from the
     prefers-color-scheme: dark @media block, which would otherwise
     override them with very dark values (#1A2436 etc.) leaving
     black text unreadable on hover/active states. */
  --alice-blue-tint:     #DDEEFF;
  --alice-teal-tint:     #DDFFDD;
  --alice-amber-tint:    #FFF7CC;
  --alice-gray-tint:     #EEEEEE;
}

/* High contrast: heavier borders everywhere */
body.th-hico .card,
body.th-hico .nav,
body.th-hico .form-input,
body.th-hico .status-badge {
  border-width: 2px;
  border-color: var(--alice-border);
}

/* ── DEUTERANOPIA (accessibility overlay) ───────────── */
html.th-deut,
body.th-deut {
  --alice-teal-text:  #007858;   /* 4.60:1 on the badge tint, 5.02:1 on the page */
  --alice-amber-text: #79710A;   /* 1.21 -> 4.60:1 */
  --alice-blue-text:  #0070B0;   /* 4.63:1 on the badge tint, 4.87:1 on the page */
  --alice-gray-text:  #706F69;   /* 3.30 -> 4.61:1 */
  --alice-blue:    #0072B2;   /* sky blue — unambiguous   */
  --alice-amber:   #F0E442;   /* yellow — distinct from blue */
  --alice-teal:    #009E73;   /* bluish green              */
  --alice-warning: #E69F00;   /* orange-gold               */
  --alice-focus:   #0072B2;
  --link-color:    #56B4E9;
}

/* ── PROTANOPIA (accessibility overlay) ─────────────── */
html.th-prot,
body.th-prot {
  --alice-teal-text:  #007858;   /* 4.60:1 on the badge tint, 5.02:1 on the page */
  --alice-amber-text: #79710A;   /* 1.21 -> 4.60:1 */
  --alice-blue-text:  #0070B0;   /* 4.63:1 on the badge tint, 4.87:1 on the page */
  --alice-gray-text:  #706F69;   /* 3.30 -> 4.61:1 */
  --alice-blue:    #0072B2;
  --alice-amber:   #F0E442;
  --alice-teal:    #009E73;
  --alice-warning: #E69F00;
  --alice-focus:   #0072B2;
  --link-color:    #56B4E9;
}

/* ── TRITANOPIA (accessibility overlay) ─────────────── */
html.th-trit,
body.th-trit {
  --alice-teal-text:  #007858;   /* 4.60:1 on the badge tint, 5.02:1 on the page */
  --alice-amber-text: #B64584;   /* 2.80 -> 4.61:1, hue 326 held */
  --alice-blue-text:  #B14E00;   /* 4.62:1 on the badge tint, 4.86:1 on the page */
  --alice-gray-text:  #706F69;   /* 3.30 -> 4.61:1 */
  --alice-blue:    #D55E00;   /* vermilion — distinct for trit */
  --alice-amber:   #CC79A7;   /* reddish purple                */
  --alice-teal:    #009E73;   /* green — unaffected            */
  --alice-warning: #D55E00;
  --alice-focus:   #009E73;
  --link-color:    #CC79A7;
}

/* ── DARK MODE + COLOR-BLIND COMBINATIONS ──────────────
   Per architecture/UX.md §4 + cross-cutting audit Axis C.
   Without these rules, a color-blind user who switches to
   dark mode sees the LIGHT-mode color-blind palette on a
   dark background — unreadable. These rules layer the
   dark-mode-aware color-blind palette so contrast stays
   accessible. WCAG fix. */
html.th-dark.th-deut,
body.th-dark.th-deut {
  --alice-blue:    #56B4E9;   /* light blue for dark + deut */
  --alice-warning: #E6B800;   /* gold for dark + deut */
  --alice-teal:    #009E73;   /* green-cyan; readable on dark */
  --alice-focus:   #56B4E9;
  /* On #121212 these clear 4.5:1 as they stand (5.48 / 14.17 / 8.12 / 8.82),
     so the text tokens are the hues — but they must be RESTATED, or the
     light-theme values from html.th-deut win by source order. */
  --alice-teal-text:  #009E73;
  --alice-amber-text: #F0E442;
  --alice-blue-text:  #56B4E9;
  --alice-gray-text:  #B4B2A9;
}
html.th-dark.th-prot,
body.th-dark.th-prot {
  /* Protanopia palette mirrors deuteranopia per Okabe-Ito */
  --alice-blue:    #56B4E9;
  --alice-warning: #E6B800;
  --alice-teal:    #009E73;
  --alice-focus:   #56B4E9;
  --alice-teal-text:  #009E73;
  --alice-amber-text: #F0E442;
  --alice-blue-text:  #56B4E9;
  --alice-gray-text:  #B4B2A9;
}
html.th-dark.th-trit,
body.th-dark.th-trit {
  --alice-blue:    #CC79A7;   /* reddish-purple — distinguishable for trit on dark */
  --alice-warning: #E6B800;
  --alice-teal:    #009E73;
  --alice-focus:   #CC79A7;
  /* --alice-blue and --alice-amber are BOTH #CC79A7 here, so on this theme
     the "read" and "submitted" labels were the same colour — the exact
     collapse the note beside .progress-label--* works to avoid, arrived at
     from the other direction. The text tokens keep them apart: vermilion at
     26 degrees against reddish-purple at 327, both above 4.5:1 on #121212
     (4.84 and 6.12) and both separable under tritanopia, which spares
     red-green while confusing blue-yellow. */
  --alice-teal-text:  #009E73;
  --alice-amber-text: #CC79A7;
  --alice-blue-text:  #D55E00;
  --alice-gray-text:  #B4B2A9;
}

/* ── HYPERLINK COLORS ───────────────────────────────── */
a {
  color: var(--link-color, #7ab8f5);
  text-decoration: none;
}
a:hover { text-decoration: underline; }

/* 1.4.1 Use of Color — a link inside a paragraph must not be identified by
 * colour ALONE. Hover adds an underline and :focus-visible draws an outline,
 * but a reader who is merely LOOKING at the page gets neither, and the link
 * colour does not reach 3:1 against the surrounding body text in the dark and
 * high-contrast themes — so in prose the link was, in practice, invisible.
 *
 * Scoped to running text rather than applied to every anchor. The success
 * criterion is about links "within a block of text"; a nav item, a tab, a card
 * or a button-styled anchor is identified by its position and shape, and
 * underlining all of them would make the interface worse without making it
 * more accessible. `:not()` keeps the button-styled anchors out of the prose
 * rule for the cases where one is genuinely embedded in a sentence. */
.pagina-content a:not([class*="btn"]):not([class*="tab"]),
.pagina-assess-content a:not([class*="btn"]):not([class*="tab"]),
.grade-section-content a:not([class*="btn"]):not([class*="tab"]),
.asset-fs-content a:not([class*="btn"]):not([class*="tab"]),
.maxcell-prose a:not([class*="btn"]):not([class*="tab"]),
.markdown-body a:not([class*="btn"]):not([class*="tab"]),
.nuntius-body a:not([class*="btn"]):not([class*="tab"]),
p > a:not([class*="btn"]):not([class*="tab"]):not([class*="nav"]),
li > a:not([class*="btn"]):not([class*="tab"]):not([class*="nav"]):not([class*="menu"]) {
  text-decoration: underline;
  text-underline-offset: 2px;
}

/*
 * WCAG AA contrast verification (4.5:1 body text, 3:1 large/UI):
 *
 * Standard:      #1A1A1A on #F5F5F2 = 14.5:1 ✓   Nav: #555550 on #FFFFFF = 7.2:1 ✓
 *                Btn: #FFFFFF on #185FA5 = 5.7:1 ✓  Error: #C13030 on #FFF0F0 = 5.1:1 ✓
 * Deuteranopia:  #1A1A1A on #F5F5F2 = 14.5:1 ✓   Btn: #FFFFFF on #0072B2 = 4.9:1 ✓
 * Protanopia:    #1A1A1A on #F5F5F2 = 14.5:1 ✓   Btn: #FFFFFF on #0072B2 = 4.9:1 ✓
 * Tritanopia:    #1A1A1A on #F5F5F2 = 14.5:1 ✓   Btn: #FFFFFF on #D55E00 = 4.6:1 ✓
 * High Contrast: #000000 on #F0F0F0 = 18.1:1 ✓   Btn: #FFFFFF on #0000EE = 8.6:1 ✓
 *                Focus: #0000EE outline = 8.6:1 ✓
 *
 * Badge text on badge tint backgrounds all exceed 4.5:1 in all themes.
 */

/* 3. Dark mode overrides — applied only when the user has not
   chosen an explicit theme. The :not() guard prevents OS=dark
   from briefly flashing dark colors on a user who has stored
   "th-light" in localStorage (the FOUC-prevention head script
   in base.html sets the class on <html> before first paint). */
@media (prefers-color-scheme: dark) {
    html:not(.th-dark):not(.th-light):not(.th-hico):not(.th-deut):not(.th-prot):not(.th-trit) {
        --alice-blue: #85B7EB;
        --alice-amber: #EF9F27;
        --alice-teal: #5DCAA5;
        --alice-warning: #FAC775;
        --alice-gray: #B4B2A9;
        --alice-surface: #1E1E1E;
        --alice-bg: #121212;
        --alice-text: #E8E6E0;
        --alice-text-secondary: #A8A6A0;
        --alice-text-tertiary: #888880;
        --alice-border: #3A3A38;
        --alice-border-strong: #555550;

        --alice-teal-tint: #1A2E26;
        --alice-amber-tint: #2E2618;
        --alice-gray-tint: #2A2A28;
        --alice-blue-tint: #1A2436;

        /* Legend text tokens must flip here too. Defining them only in
           :root and html.th-dark leaves the DEFAULT user — the one who
           never opened the theme menu and is on OS dark — reading the
           light-theme values on a #121212 background, which is the
           inverse of the problem they were introduced to fix. */
        --alice-danger: #F1948A;
        --alice-teal-soft: #16302B;
        /* And the link colour. :root's #0F70CE was picked against a light
           background (4.5:1 there); on #121212 it is 3.77:1 and on the #1E1E1E
           surface 3.36:1 — so EVERY link in the application failed AA for the
           default user, the one who never opened the theme menu and whose OS
           is dark. Same value html.th-dark uses: 8.89:1. */
        --link-color: #85B7EB;
        --alice-teal-text: #5DCAA5;
        --alice-amber-text: #EF9F27;
        --alice-blue-text: #85B7EB;
        --alice-gray-text: #B4B2A9;

        /* Chart tokens must flip with OS dark too, or a chart drawn from the
           light steps sits on a #1E1E1E surface and the palest ramp step
           drops under the contrast floor. Same values as html.th-dark. */
        --alice-series-1: #1b93e0;
        --alice-series-2: #c95b0d;
        --alice-series-3: #009267;
        --alice-series-4: #885999;
        --alice-ramp-1: #365270;
        --alice-ramp-2: #4e6c8b;
        --alice-ramp-3: #6886a6;
        --alice-ramp-4: #82a2c3;
        --alice-ramp-5: #9ebee0;
        --alice-ramp-6: #badbfe;
    }
}

/* ── Performance plots ───────────────────────────────────────────────
 * The CVD themes (.th-deut/.th-prot/.th-trit) deliberately do NOT override
 * --alice-series-*: those themes exist to rescue palettes that were chosen
 * by eye, and the series steps above already pass all-pairs CVD separation
 * for deuteranopia, protanopia and tritanopia at the surface they sit on.
 * Substituting the theme's three-colour set would REDUCE separation and cap
 * the plot at three sections. Anything added here that encodes meaning in
 * colour must be validated the same way before it ships.
 */
.perf-figure {
    background: var(--alice-surface);
    border: 1px solid var(--alice-border);
    border-radius: var(--radius-lg);
    padding: 16px;
    margin-bottom: 20px;
}
.perf-figure__head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 4px;
}
.perf-figure__title { margin: 0; font-size: 1.05rem; font-weight: 600; }
/* The caption carries the "these are rubric-band summaries, not grades"
   disclaimer. It is part of the figure, not a tooltip: a reader who never
   hovers must still see it. */
.perf-figure__caption {
    margin: 0 0 12px;
    font-size: 0.85rem;
    color: var(--alice-text-secondary);
    max-width: 78ch;
}
.perf-figure__canvas-wrap { position: relative; height: 340px; }
.perf-figure__canvas-wrap--tall { height: 420px; }

/* Coverage line: states how much of the grid is actually assessed, so
   sparseness is read as progress rather than as a broken chart. */
.perf-coverage {
    font-size: 0.85rem;
    color: var(--alice-text-secondary);
    margin: 0 0 12px;
}
.perf-coverage strong { color: var(--alice-text); }

.perf-toolbar {
    display: flex;
    gap: 12px;
    align-items: center;
    flex-wrap: wrap;
    margin-bottom: 12px;
}
.perf-toolbar label { font-size: 0.85rem; margin: 0; }

/* ── Heatmap ─────────────────────────────────────────────────────────
 * An HTML table, not a canvas: it is keyboard-reachable, screen-readable,
 * and prints. The colour is a background on a <td> whose title/aria carries
 * the number, so colour is never the only channel.
 */
.perf-heatmap-scroll { overflow: auto; max-height: 70vh; }
table.perf-heatmap { border-collapse: separate; border-spacing: 2px; }
table.perf-heatmap th {
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--alice-text-secondary);
    text-align: left;
    padding: 2px 6px;
    background: var(--alice-surface);
}
table.perf-heatmap thead th {
    position: sticky; top: 0; z-index: 2;
    white-space: nowrap;
    /* Long lexia titles in a 22-column grid: rotate rather than wrap, so the
       header stays one line and the columns stay narrow. */
    height: 132px;
    vertical-align: bottom;
    padding: 0;
}
table.perf-heatmap thead th > span {
    display: inline-block;
    transform: rotate(180deg);
    writing-mode: vertical-rl;
    max-height: 124px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
table.perf-heatmap tbody th {
    position: sticky; left: 0; z-index: 1;
    white-space: nowrap;
    max-width: 220px;
    overflow: hidden;
    text-overflow: ellipsis;
}
table.perf-heatmap td {
    width: 22px; min-width: 22px; height: 22px;
    padding: 0;
    border-radius: 3px;
    /* The 2px border-spacing above IS the surface gap between fills. */
}
/* An ungraded pair is an empty outline, never a pale fill — the one
   distinction the whole grid depends on. */
td.perf-cell--empty {
    background: transparent;
    box-shadow: inset 0 0 0 1px var(--alice-border);
}
/* Unqualified: the heat-map uses <td>, but the LEGEND that explains the
   scale uses <span> (opus/performance.html:117), so a `td.` prefix meant
   every legend swatch painted nothing. The legend rendered as five
   invisible boxes beside one outlined "no data" box — the only swatch
   that is SUPPOSED to be transparent was the only one that showed. */
.perf-cell--b1 { background: var(--alice-ramp-1); }
.perf-cell--b2 { background: var(--alice-ramp-2); }
.perf-cell--b3 { background: var(--alice-ramp-3); }
.perf-cell--b4 { background: var(--alice-ramp-4); }
.perf-cell--b5 { background: var(--alice-ramp-5); }
.perf-cell--b6 { background: var(--alice-ramp-6); }
table.perf-heatmap td:focus-visible {
    outline: 2px solid var(--alice-focus);
    outline-offset: 1px;
}
/* The graded sub-grid — the DEFAULT view. Rows and columns with no assessment
   at all are removed rather than drawn empty; the coverage line above the
   table says how many were removed, so nothing disappears silently. Hiding a
   whole column needs both its header and its cells, hence two selectors. */
table.perf-heatmap--graded-only th[data-col-graded="0"],
table.perf-heatmap--graded-only td[data-col-graded="0"],
table.perf-heatmap--graded-only tr[data-row-graded="0"] {
    display: none;
}

.perf-legend {
    display: flex; align-items: center; gap: 10px;
    flex-wrap: wrap;
    font-size: 0.8rem; color: var(--alice-text-secondary);
    margin-top: 12px;
}
.perf-legend__scale { display: flex; align-items: center; gap: 2px; }
.perf-legend__swatch {
    width: 26px; height: 14px; border-radius: 3px;
    display: inline-block;
    /* The pale end of the ramp is close to the page colour by design (it
       encodes "low"), so the swatch's own EDGE carries the 1.4.11 boundary
       rather than its fill. --alice-border-strong is only 1.69:1 against the
       page and 1.20:1 against the palest step — a boundary you cannot see is
       not a boundary, so this uses the tertiary TEXT token, which is held at
       4.5:1 against the page in every theme. */
    box-shadow: inset 0 0 0 1px var(--alice-text-tertiary);
}
.perf-legend__swatch--empty {
    background: transparent;
    box-shadow: inset 0 0 0 1px var(--alice-border);
}

/* Awaiting-assessment strip on the lexia figure: how much of the backlog
   already has an AI draft ready to release. */
.perf-backlog { margin: 0 0 12px; font-size: 0.85rem; }
.perf-backlog__bar {
    display: flex; height: 10px; border-radius: 5px; overflow: hidden;
    background: var(--alice-gray-tint); margin-top: 6px;
}
.perf-backlog__seg { height: 100%; }
.perf-backlog__seg--graded { background: var(--alice-series-3); }
.perf-backlog__seg--ready { background: var(--alice-series-2); }
.perf-backlog__seg--await { background: var(--alice-border-strong); }

/* The metrics affordance, styled to sit beside Export-LaTeX on the course
   header and in the same slot on a lexia header. */
.opus-metrics-btn {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 5px 10px;
    /* 0.75rem = the section dropdown's own 12px, with a shared min-height so
       the worded member (Go to section), the icon members and the select all
       sit at exactly the same height on the strip (architect, 2026-08-29). */
    font-size: 0.75rem;
    line-height: 1;
    min-height: 26px;
    box-sizing: border-box;
    border: 1px solid var(--alice-border-strong);
    border-radius: var(--radius-md);
    background: var(--alice-surface);
    color: var(--alice-text-secondary);
    text-decoration: none;
    white-space: nowrap;
}
.opus-metrics-btn:hover {
    color: var(--alice-text);
    border-color: var(--alice-focus);
}

/* ── Verifier notes on an AI grading draft (migration 402) ───────────
 * Inside the green AI box but visually separated, because these notes are for
 * the instructor and the draft above them is for the student. Deliberately
 * quiet: a finding is a prompt to look again, not an alarm — the loud amber
 * treatment is reserved for `.grade-ai-partial`, which says the model did not
 * read everything.
 */
.grade-ai-checks {
    margin-top: 10px;
    padding-top: 8px;
    border-top: 1px dashed var(--alice-border-strong);
    font-size: 0.85rem;
}
.grade-ai-checks__head { margin: 0 0 6px; }
.grade-ai-checks__head span {
    display: block;
    color: var(--alice-text-secondary);
    font-size: 0.8rem;
}
.grade-ai-checks ul { margin: 0; padding-left: 18px; }
.grade-ai-checks li { margin-bottom: 5px; }
.grade-ai-checks em { color: var(--alice-text-secondary); font-style: italic; }
.grade-ai-checks__kind {
    display: inline-block;
    padding: 1px 7px;
    margin-right: 6px;
    border-radius: 10px;
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .02em;
    border: 1px solid currentColor;
}
/* A band mismatch and an unreleasable draft are the two that change what the
   instructor does next, so they carry the warning ink; the other two are
   "look again" rather than "do not send". */
.grade-ai-checks__kind--band,
.grade-ai-checks__kind--release { color: var(--alice-warning); }
.grade-ai-checks__kind--missed,
.grade-ai-checks__kind--unsupported { color: var(--alice-text-secondary); }

/* ── Remove-attachment × ─────────────────────────────────────────────
 * A red × immediately right of the filename it removes (architect, 2026-08-15),
 * replacing a full-width "Remove this file" link on its own line below — which
 * read as another action on the answer rather than on that file.
 *
 * A borderless glyph still has to be a usable target: 22px square with the ×
 * centred, so it clears the 24px-with-padding rule of thumb on touch, and it
 * keeps a visible focus ring because it is the destructive control on this
 * surface and must be reachable by keyboard.
 */
.assess-att-delete {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    margin: 0 2px 0 4px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    /* The same red as .btn-danger, deliberately not var(--color-danger) —
       that token maps to --alice-warning (a brownish-orange, per DECISIONS O1)
       and would not read as the destructive red the Delete buttons use. */
    color: #c0392b;
    font-size: 17px;
    line-height: 1;
    cursor: pointer;
    vertical-align: -4px;
}
.assess-att-delete:hover {
    background: var(--color-danger-bg, #FFF0F0);
    color: #a5281b;
}
.assess-att-delete:focus-visible {
    outline: 2px solid var(--alice-focus);
    outline-offset: 1px;
}

/* Image-attachment thumbnail (2026-09-03): the same preview whether the
 * file is freshly picked (client object URL) or already stored (the
 * FERPA-checked /attachment route). Constrained, so a full-page phone
 * photograph reads as a card, not a wall; the full-size view is one click
 * away on the link around it. */
.assess-att-preview {
    display: block;
    max-width: 280px;
    max-height: 220px;
    width: auto;
    height: auto;
    margin-top: 6px;
    border: 1px solid var(--alice-border);
    border-radius: 6px;
    object-fit: contain;
    background: var(--alice-bg);
}
.assess-att-preview-link { display: inline-block; line-height: 0; }
.assess-att-preview-link:hover .assess-att-preview,
.assess-att-preview-link:focus-visible .assess-att-preview {
    border-color: var(--alice-blue, #1d4ed8);
}

/* Rotate + Re-ingest (grade page only — the macro renders these tools only
 * when its caller asks). The modal mirrors the student verify modal's
 * skeleton, wider, with the photo and the transcription side by side. */
.att-preview-tools {
    display: flex;
    gap: 6px;
    margin-top: 4px;
}
.att-ri-modal { position: fixed; inset: 0; z-index: 9998; display: flex;
                align-items: center; justify-content: center; }
.att-ri-modal[hidden] { display: none; }
.att-ri-modal__backdrop { position: absolute; inset: 0;
                          background: rgba(0,0,0,.4); }
.att-ri-modal__box { position: relative; background: var(--alice-surface,#fff);
                     color: var(--alice-text,#1a1a1a);
                     border: 1px solid var(--alice-border,#ddd);
                     border-radius: 10px; padding: 18px 20px;
                     width: min(1100px, calc(100vw - 32px));
                     box-shadow: 0 8px 40px rgba(0,0,0,.25); }
.att-ri-cols { display: flex; gap: 16px; align-items: flex-start; }
.att-ri-imgcol { flex: 1 1 45%; min-width: 0; }
.att-ri-imgcol img { max-width: 100%; max-height: 62vh; height: auto;
                     border: 1px solid var(--alice-border);
                     border-radius: 6px; background: var(--alice-bg); }
.att-ri-txcol { flex: 1 1 55%; min-width: 0;
                max-height: 62vh; overflow-y: auto; }
/* Page-cap notice for a scanned PDF (2026-09-10): both the grade-page
 * viewer and the student verify modal show it above the transcript. Same
 * amber recipe as the student modal's own .img-tx-warn. */
.att-ri-warn { margin: 0 0 10px; padding: 8px 11px; border-radius: 6px;
               font-size: .82rem; line-height: 1.45;
               color: var(--alice-warning,#9C6213);
               background: var(--alice-amber-tint,#FFF5E0);
               border: 1px solid var(--alice-amber,#EF9F27); }
.att-ri-warn[hidden] { display: none; }
@media (max-width: 760px) {
    .att-ri-cols { flex-direction: column; }
    .att-ri-txcol { max-height: 40vh; }
}

/* ── EMBED asset (Desmos, GeoGebra) ──────────────────────────────────
 * A class rather than the inline styles the older frames in asset_list use,
 * because the sizing is the one part of the instructor's pasted snippet we
 * discard: their 500x500 would be a fixed box that overflows a phone. `clamp`
 * keeps a graph tall enough to be usable and short enough to leave the lesson's
 * own text on screen; the fullscreen button handles "I want the whole page".
 */
.asset-embed-frame {
    width: 100%;
    height: clamp(320px, 58vh, 560px);
    border: 1px solid var(--alice-border);
    border-radius: var(--radius-md);
    margin-top: 0.5rem;
    background: var(--alice-surface);
    display: block;
}
/* In fullscreen the frame should take the room it has been given. */
.asset-fs-content:fullscreen .asset-embed-frame,
.asset-fs-content.is-fullscreen .asset-embed-frame {
    height: 88vh;
}
.asset-embed-note {
    display: flex;
    gap: 8px;
    align-items: baseline;
    flex-wrap: wrap;
    margin-top: 6px;
    font-size: 0.8rem;
    color: var(--alice-text-secondary);
}

/* A card whose whole body is a link, plus one corner action. The wrapper is
   the grid item, so the card must still fill it or the row heights break. */
.card-with-action { position: relative; display: flex; }
.card-with-action > .card { flex: 1 1 auto; }
.card-action {
    position: absolute; top: 10px; right: 10px;
    display: inline-flex; align-items: center; justify-content: center;
    width: 28px; height: 28px;
    border: 1px solid var(--alice-border);
    border-radius: var(--radius-md);
    background: var(--alice-surface);
    color: var(--alice-text-secondary);
    text-decoration: none;
}
.card-action:hover {
    color: var(--alice-text);
    border-color: var(--alice-focus);
}

/* Modal for one student against their section, opened from a roster row.
   Sized so the chart is legible without leaving the roster. */
.perf-modal__box { max-width: 900px; width: 92vw; }
/* `.hidden` is NOT a global utility in this stylesheet — it is declared per
   component (.modal.hidden, .burger-panel.hidden, ...). The modal's inner
   loading/chart swap needs its own rule or the "Loading..." line stays on
   screen underneath a chart that drew correctly. */
#perf-student-modal .hidden { display: none; }
.perf-modal__box .perf-figure { border: 0; padding: 0; margin: 0; }
.perf-empty {
    padding: 24px; text-align: center;
    color: var(--alice-text-secondary); font-size: 0.9rem;
}

/* 4. Typography */
body {
    font-family: var(--font-sans);
    background-color: var(--alice-bg);
    color: var(--alice-text);
    margin: 0;
    padding: 0;
    line-height: 1.5;
}

h1, h2, h3 {
    color: var(--alice-text);
    margin-top: 1.5rem;
    margin-bottom: 0.75rem;
}

h1 { font-size: 1.75rem; }
h2 { font-size: 1.35rem; }
h3 { font-size: 1.1rem; }

.wordmark,
.nav__wordmark,
.login-wordmark {
    font-family: Georgia, "Times New Roman", serif;
    font-weight: normal;
    font-style: normal;
    color: var(--alice-text);
    letter-spacing: 0.05em;
}

/* 5. Layout utilities */
.container {
    max-width: 960px;
    margin: 0 auto;
    padding: 1.5rem 1rem;
}

.grid-2col {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1.25rem;
}

/* 6. Navigation */
.nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.75rem 1.5rem;
    background-color: var(--alice-surface);
    border-bottom: 1px solid var(--alice-border);
}

















/* 7. Forms */
.form-group {
    margin-bottom: 1rem;
}

.form-label {
    display: block;
    margin-bottom: 0.35rem;
    font-size: 0.9rem;
    font-weight: 500;
    color: var(--alice-text);
}

.form-input {
    width: 100%;
    padding: 0.6rem 0.75rem;
    font-size: 1rem;
    border: 1px solid var(--alice-border);
    border-radius: 4px;
    background-color: var(--alice-surface);
    color: var(--alice-text);
    font-family: inherit;
}

.form-input:focus {
    outline: none;
    border-color: var(--color-brand, var(--alice-blue));
    box-shadow: 0 0 0 2px var(--color-brand-tint, var(--alice-blue-tint));
}

/* Mirror .form-input so every <textarea class="form-textarea"> respects
   the active theme. Without this baseline, browsers render textareas
   with the OS default (typically white-on-black or white-on-white),
   which fights dark mode. Selects also need it because some forms use
   <select class="form-select"> assuming the theme bleeds through. */
.form-textarea,
.form-select {
    width: 100%;
    padding: 0.6rem 0.75rem;
    font-size: 1rem;
    border: 1px solid var(--alice-border);
    border-radius: 4px;
    background-color: var(--alice-surface);
    color: var(--alice-text);
    font-family: inherit;
}
.form-textarea:focus,
.form-select:focus {
    outline: none;
    border-color: var(--color-brand, var(--alice-blue));
    box-shadow: 0 0 0 2px var(--color-brand-tint, var(--alice-blue-tint));
}

/* The one required-field marker (UX audit lead 53). components.html already
   prescribed the class; it existed nowhere until now. */
.form-required {
    color: var(--alice-danger);
    font-weight: normal;
    margin-left: 2px;
}

.form-error {
    color: #C13030;
    background-color: #FFF0F0;
    border: 1px solid #C13030;
    border-radius: 4px;
    padding: 0.5rem 0.75rem;
    font-size: 0.9rem;
    margin-bottom: 1rem;
}

@media (prefers-color-scheme: dark) {
    html:not(.th-dark):not(.th-light):not(.th-hico):not(.th-deut):not(.th-prot):not(.th-trit) .form-error {
        color: #FF8A8A;
        background-color: #2E1A1A;
        border-color: #FF8A8A;
    }
}

.btn-primary {
    display: inline-block;
    width: auto;
    max-width: var(--btn-max-width, 300px);
    padding: 0.65rem 1.25rem;
    font-size: 1rem;
    font-weight: 600;
    color: var(--color-brand-fg, var(--alice-surface));
    background-color: var(--color-brand, var(--alice-blue));
    border: none;
    border-radius: 4px;
    cursor: pointer;
    text-align: center;
    text-decoration: none;
    font-family: inherit;
}

.btn-primary:hover {
    opacity: 0.9;
}

.btn-secondary {
    display: inline-block;
    padding: 0.4rem 0.85rem;
    font-size: 0.85rem;
    color: var(--alice-text-secondary);
    background-color: var(--alice-surface);
    border: 1px solid var(--alice-border);
    border-radius: 4px;
    cursor: pointer;
    text-align: center;
    text-decoration: none;
    font-family: inherit;
}

.btn-secondary:hover {
    border-color: var(--color-brand, var(--alice-blue));
    color: var(--color-brand, var(--alice-blue));
}

/* ── Admin tab pane ─────────────────────────────────────
   Used by the admin collection / record pages. White surface the
   active .pagina-tab-btn merges into, with side + bottom borders
   that complete the lexia-style card edge (the tab bar's
   border-bottom supplies the top edge of the card). The .hidden
   rule mirrors .pagina-panel.hidden so the `hidden` attribute
   correctly suppresses the pane. */
.admin-tab-pane {
    background: var(--alice-surface);
    border-left: 1px solid var(--alice-border);
    border-right: 1px solid var(--alice-border);
    border-bottom: 1px solid var(--alice-border);
    border-radius: 0 0 8px 8px;
    padding: 20px 22px;
    min-height: 200px;
}
.admin-tab-pane.hidden { display: none; }

/* On admin pages: pin inactive tab background to the page colour
   (--alice-bg). The active tab is var(--alice-surface) (white)
   from .pagina-tab-btn.active and merges with the .admin-tab-pane
   below it; inactive tabs should fade back into the page so the
   visual hierarchy "active = card edge, inactive = page" reads
   cleanly. Scoped via body.admin-page so the lexia tab bar is
   untouched. */
body.admin-page .pagina-tab-btn {
    background: var(--alice-bg);
}
body.admin-page .pagina-tab-btn.active {
    /* Override the rule above for the active state; the active tab
       must remain the same colour as the .admin-tab-pane (white)
       so the active-tab → pane junction reads as a single card. */
    background: var(--alice-surface);
}

/* ── Compact pill (links + small actions) ────────────────
   Used for cross-page nav (Dashboards / Collections), Edit
   toggles, and "back to X" affordances on admin pages. Stays
   visually distinct from primary CTAs (.btn-primary) and from
   plain inline links. Pair --accent for the active / call-to-
   action variant. */
.alice-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.3rem 0.85rem;
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--alice-text-secondary);
    background-color: var(--alice-surface);
    border: 1px solid var(--alice-border);
    border-radius: 999px;
    cursor: pointer;
    text-decoration: none;
    font-family: inherit;
    line-height: 1.2;
    transition: color .15s, border-color .15s, background-color .15s;
}
.alice-pill:hover {
    border-color: var(--alice-blue);
    color: var(--alice-blue);
}
.alice-pill--accent {
    color: var(--alice-blue);
    border-color: var(--alice-blue);
}
.alice-pill--accent:hover {
    background-color: var(--alice-blue);
    color: var(--alice-surface);
}

/* Language pill variant — per specs/frontend/015.051_FRONTEND_2026-05-25_ux_decisions.md O6 resolution.
   Used to indicate a language code (e.g., "EN", "ES") in lists like
   /admin/email-templates. Visually distinct from .nav-role-badge
   (which carries role/inbox-state semantics). Compact, monospace,
   muted neutral fill. */
.alice-pill--lang {
    font-family: var(--font-mono, ui-monospace, Menlo, Consolas, monospace);
    font-size: 0.75rem;
    padding: 2px 6px;
    border: 1px solid var(--alice-border);
    border-radius: 3px;
    background: var(--alice-gray-tint);
    color: var(--alice-text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    cursor: default;
}
.alice-pill--lang:hover {
    /* No-op — language pill is read-only display, not interactive. */
    border-color: var(--alice-border);
    color: var(--alice-text-secondary);
}

/* ── Canonical pill variants (DECISIONS A18 + 07_COHERENCE_AUDIT)
   Five canonical variants compose with intent modifiers below to
   replace the legacy proliferation of ~17 pill / badge classes
   (.pill-ok, .grading-badge--*, .collab-status-pill, .cs-status-pill,
   .originality-badge, .template-badge, .nav-role-badge as status,
   .status-badge/.status-complete/.status-next/.status-skipped/etc.).
   Apply BOTH the variant class AND an intent modifier:
       .alice-pill .alice-pill--status .alice-pill--success
   Read-only by default; never use .alice-pill as a button. */

/* --status: semantic state at-rest (Active, Inactive, Draft, Failed, …) */
.alice-pill--status {
    font-weight: 600;
    cursor: default;
    user-select: none;
}
.alice-pill--status:hover {
    /* status pills are read-only — no hover affordance */
    border-color: var(--alice-border);
    color: inherit;
    background-color: inherit;
}

/* --count: notification count, monospace digit (e.g., "3 new", "12") */
.alice-pill--count {
    font-family: var(--font-mono);
    font-weight: 600;
    padding: 1px 8px;
    font-size: 0.78rem;
    background: var(--alice-gray-tint);
    color: var(--alice-text-secondary);
    border: 1px solid var(--alice-border);
    cursor: default;
}
.alice-pill--count:hover {
    border-color: var(--alice-border);
    color: var(--alice-text-secondary);
}

/* --tag: categorization (e.g., asset type, kind label) — neutral by default */
.alice-pill--tag {
    font-weight: 500;
    background: var(--alice-bg);
    border: 1px solid var(--alice-border);
    cursor: default;
}
.alice-pill--tag:hover {
    border-color: var(--alice-border);
    color: var(--alice-text-secondary);
}

/* --filter: toggleable active-filter chip (carries × to remove) */
.alice-pill--filter {
    font-weight: 500;
    background: var(--color-brand-tint, var(--alice-blue-tint));
    color: var(--color-brand, var(--alice-blue));
    border: 1px solid var(--color-brand, var(--alice-blue));
    cursor: pointer;
}
.alice-pill--filter:hover {
    background: var(--color-brand, var(--alice-blue));
    color: var(--color-brand-fg, #fff);
}

/* ── Intent modifiers ─────────────────────────────────────
   Add ONE of these alongside .alice-pill--status (or --tag where
   meaningful) to set the semantic color. Maps to platform-locked
   semantic tokens; tenants cannot change these. */
.alice-pill--success {
    color: var(--color-success);
    background-color: var(--color-success-bg);
    border-color: var(--color-success);
}
.alice-pill--info {
    color: var(--color-info);
    background-color: var(--color-info-bg);
    border-color: var(--color-info);
}
.alice-pill--warning {
    color: var(--color-warning);
    background-color: var(--color-warning-bg);
    border-color: var(--color-warning);
}
.alice-pill--danger {
    color: var(--color-danger);
    background-color: var(--color-danger-bg);
    border-color: var(--color-danger);
}
.alice-pill--muted {
    color: var(--color-text-secondary);
    background-color: var(--color-muted-bg);
    border-color: var(--alice-border);
}

/* 8. Cards */
.card {
    background-color: var(--alice-surface);
    border: 1px solid var(--alice-border);
    border-radius: 8px;
    padding: 1.25rem;
}



.card__title {
    margin: 0.5rem 0 0.35rem;
    font-size: 1.05rem;
}

.card__meta {
    font-size: 0.85rem;
    color: var(--alice-text-secondary);
    margin: 0 0 0.75rem;
}

.card__actions {
    display: flex;
    gap: 0.5rem;
    margin-top: 1rem;
}

.card__actions .btn-primary {
    width: auto;
    padding: 0.4rem 1rem;
    font-size: 0.85rem;
}

/* ── Hub cards ─────────────────────────────────────
   Tighter sibling of .card for *section landings* one
   level below the main /admin grid. The size delta is
   intentional: it teaches "depth = density" so users
   feel they have descended into a subsystem (e.g.
   /admin/max, /admin/system) without reading the URL.
   Use .hub-grid as the container and .hub-card for
   each routing tile; .hub-card__summary and
   .hub-card__cta style the body and the trailing
   "Manage →" / "Edit →" link. */
.hub-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 16px;
    max-width: 1100px;
}

.hub-card {
    display: block;
    border: 1px solid var(--alice-border);
    border-radius: 8px;
    padding: 18px 20px;
    background: var(--alice-surface);
    color: inherit;
    text-decoration: none;
    transition: border-color 0.15s ease, transform 0.15s ease;
}

.hub-card:hover {
    border-color: var(--alice-teal);
    transform: translateY(-1px);
    text-decoration: none;
}

.hub-card h2 {
    margin: 0 0 8px;
    font-size: 1.05rem;
}

.hub-card__summary {
    margin: 0 0 12px;
    color: var(--alice-text-secondary);
    font-size: 0.9rem;
    line-height: 1.4;
}

.hub-card__cta {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--alice-teal);
}

/* Warning-tinted hub card — color-only modifier, mirrors
   .card--warn for the destructive/special action on a sub-hub
   (e.g. Calculus on /admin/opus/{id}). */
.hub-card--warn {
    border-color: var(--alice-warning);
    background: var(--alice-amber-tint);
}
.hub-card--warn:hover {
    border-color: var(--alice-warning);
    filter: brightness(0.97);
}

/* 9. Progress bar */
.progress-track {
    height: 6px;
    background-color: var(--alice-gray-tint);
    border-radius: 3px;
    overflow: hidden;
    margin-top: 0.5rem;
}

.progress-fill {
    height: 100%;
    background-color: var(--alice-teal);
    border-radius: 3px;
    transition: width 0.3s ease;
}

.progress-label {
    font-size: 0.8rem;
    color: var(--alice-text-secondary);
    margin-top: 0.25rem;
    display: inline-block;
}

/* 10. Status badges */
.status-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.2rem 0.6rem;
    border-radius: 12px;
    font-size: 0.8rem;
    font-weight: 500;
    white-space: nowrap;
}

.status-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
}

.status-complete {
    background-color: var(--alice-teal-tint);
    color: var(--alice-teal);
}

.status-complete .status-dot {
    background-color: var(--alice-teal);
}

.status-next {
    background-color: var(--alice-amber-tint);
    color: var(--alice-warning);
}

.status-next .status-dot {
    background-color: var(--alice-amber);
}

/* Syllabus "Read" status dropdown — styled to look like the
   status-badge pill at rest, with a dropdown on click. */
select.syllabus-status-select {
    appearance: none;
    -webkit-appearance: none;
    background-color: var(--alice-amber-tint);
    color: var(--alice-warning);
    border: none;
    border-radius: 12px;
    padding: 0.2rem 0.6rem;
    font-size: 0.8rem;
    font-weight: 500;
    font-family: inherit;
    cursor: pointer;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23BA7517'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 6px center;
    padding-right: 20px;
}
select.syllabus-status-select:focus {
    outline: 2px solid var(--alice-focus);
    outline-offset: 1px;
}

.status-skipped {
    background-color: var(--alice-gray-tint);
    color: var(--alice-gray);
}

.status-skipped .status-dot {
    background-color: var(--alice-gray);
}



.status-inprogress .status-dot {
    background-color: var(--alice-blue);
}

/* 11. Activity table */
.activity-table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 0.75rem;
}

.activity-table th,
.activity-table td {
    text-align: left;
    padding: 0.6rem 0.75rem;
    border-bottom: 1px solid var(--alice-border);
    font-size: 0.9rem;
}

.activity-table th {
    font-weight: 600;
    color: var(--alice-text-secondary);
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.activity-table tbody tr:hover {
    background-color: var(--alice-blue-tint);
}

/* 12. Login page */
.login-page {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    background-color: var(--alice-bg);
}

.login-card {
    background-color: var(--alice-surface);
    border: 1px solid var(--alice-border);
    border-radius: 10px;
    padding: 2rem 1.5rem;
    width: 100%;
    max-width: 340px;
    text-align: center;
}

.login-wordmark {
    font-size: 2rem;
    margin-bottom: 0.2rem;
}

.login-tagline {
    font-size: 0.8rem;
    color: var(--alice-text-secondary);
    font-style: italic;
    margin: 0 0 0.75rem;
}

.login-divider {
    height: 2px;
    background-color: var(--alice-amber);
    margin: 0 auto 1rem;
    width: 40%;
    border-radius: 1px;
}

.login-card .form-group {
    text-align: left;
}

.login-card .btn-primary {
    width: 100%;
    max-width: none;
    margin-top: 0.5rem;
}

/* 13. Language selector */


.lang-btn {
    background: none;
    border: 1px solid var(--alice-border);
    border-radius: 4px;
    padding: 0.25rem 0.75rem;
    font-size: 0.8rem;
    color: var(--alice-text-secondary);
    cursor: pointer;
    font-family: inherit;
}

.lang-btn:hover,
.lang-btn.active,
.lang-btn.lang-btn--active {
    border-color: var(--alice-blue);
    color: var(--alice-blue);
}

/* Accessibility notice */
.a11y-notice {
    margin-top: 2rem;
    font-size: 0.78rem;
    color: var(--alice-text-secondary);
    text-align: center;
    font-style: italic;
}

.subtitle {
    color: var(--alice-text-secondary);
    margin-top: -0.5rem;
}

/* ── Accessibility: Skip link ─────────────────────── */
.skip-link {
  position: absolute;
  top: -100%;
  left: 1rem;
  z-index: 9999;
  padding: 0.5rem 1rem;
  background: var(--color-brand, var(--alice-blue));
  color: var(--color-brand-fg, #fff);
  font-size: 14px;
  border-radius: 0 0 4px 4px;
  text-decoration: none;
  transition: top 0.1s;
}
.skip-link:focus {
  top: 0;
}

/* ── Accessibility: Focus indicators ──────────────── */
/* Visible focus indicator — applies to all interactive elements */
:focus-visible {
  outline: 3px solid var(--alice-focus, var(--alice-blue));
  outline-offset: 2px;
}

/* Never suppress focus outlines */
:focus:not(:focus-visible) {
  outline: none;
}

/* Screen-reader-only utility class */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ── Gear button ───────────────────────────────── */




/* ── Theme panel ───────────────────────────────── */
.theme-panel {
  position: fixed;
  top: 60px;
  right: 1rem;
  z-index: 500;
  width: 220px;
  background: var(--alice-surface);
  border: 0.5px solid var(--alice-border-strong);
  border-radius: var(--radius-lg);
  box-shadow: 0 4px 16px rgba(0,0,0,0.12);
  padding: 1rem;
}
.theme-panel--hidden {
  display: none;
}




.theme-option {
  width: 100%;
  padding: 7px 10px;
  text-align: left;
  font-size: 13px;
  font-family: var(--font-sans);
  color: var(--alice-text);
  background: none;
  border: 0.5px solid transparent;
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background 0.1s;
}
.theme-option:hover {
  background: var(--alice-bg);
}

.theme-option:focus-visible {
  outline: 3px solid var(--alice-focus, var(--alice-blue));
  outline-offset: 2px;
}


/* ── Site shell and top nav ─────────────────────────── */


/* (topnav rules moved to SPECS12 block below) */




.nav-link {
  display: flex; align-items: center;
  font-size: 11px; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--alice-text-secondary); text-decoration: none;
  padding: 0 12px;
  border-bottom: 2px solid transparent;
  transition: color .18s, border-color .18s;
  cursor: pointer; background: none; border-top: none;
  border-left: none; border-right: none;
}
.nav-link:hover, .nav-link.active {
  color: var(--alice-text); border-bottom-color: var(--alice-amber);
}


.nav-role-badge {
  font-size: 10px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase;
  /* The INK uses the text token, not the fill hue. --alice-blue-tint is a
     fixed pale blue in every theme, but --alice-blue is re-hued per overlay:
     in tritanopia it becomes vermilion, which on that unchanged pale-blue
     tint measures 3.37:1. The text tokens are chosen against the theme's own
     background, so the pair stays legible in all of them. */
  color: var(--alice-blue-text);
  background: var(--alice-blue-tint);
  border: 1px solid var(--alice-blue-text);
  border-radius: 10px; padding: 2px 8px;
}
html.th-dark .nav-role-badge,
body.th-dark .nav-role-badge {
  /* The previous values here were #0F3D6B on var(--color-muted-bg): a deep
     navy on #2A2A28, which is 1.30:1 — the badge was effectively invisible
     for anyone who explicitly CHOSE dark, while users left on system dark got
     the perfectly readable base rule. The stated worry, that the pale brand
     blue "would wash out", does not survive measurement: #85B7EB on the same
     muted background is 8.6:1. Keeping the muted background preserves the
     chip's intended weight; only the ink and the hairline change. */
  background: var(--color-muted-bg);
  color: var(--alice-blue-text);
  border-color: var(--alice-blue-text);
}

/* ── SPECS27: Impersonation banner + UI ─────────────── */
.impersonation-banner {
    background: #ffe082;
    color: #5d4037;
    padding: 8px 16px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    border-bottom: 2px solid #ffb300;
    font-size: 13px;
    /* Locked at the top so the warning never scrolls out of view.
       Sits just below the topnav when the topnav itself is locked. */
    position: sticky;
    top: 0;
    z-index: 499;
    box-shadow: 0 1px 8px rgba(0,0,0,0.08);
}
body.nav-locked .impersonation-banner {
    top: 44px;
}
body.th-dark .impersonation-banner {
    background: #ff9800;
    color: #1a1a1a;
}
body.th-hico .impersonation-banner {
    background: #ff00ff;
    color: #ffffff;
}
.impersonation-banner__form {
    margin: 0;
    flex-shrink: 0;
}

/* ── System Banner — global announcement strip ──────── */
/* Mirrors the impersonation banner mechanics (sticky at top under
   the topnav) but uses a reddish palette so it reads as a critical
   announcement. When both banners are visible the system banner
   stacks above the impersonation banner via normal document order
   plus a slightly higher z-index. */
.system-banner {
    background: #fde2e2;
    color: #7a1a1a;
    padding: 8px 16px;
    display: flex;
    align-items: center;
    gap: 10px;
    border-bottom: 2px solid #c0392b;
    font-size: 13px;
    font-weight: 500;
    position: sticky;
    top: 0;
    z-index: 500;
    box-shadow: 0 1px 8px rgba(192, 57, 43, 0.18);
}
body.nav-locked .system-banner {
    top: 44px;
}
.system-banner__icon {
    flex-shrink: 0;
    font-size: 16px;
    color: #c0392b;
}
.system-banner__msg {
    flex: 1 1 auto;
    white-space: pre-wrap;
    word-break: break-word;
}
body.th-dark .system-banner {
    background: #5a1818;
    color: #ffe6e6;
    border-bottom-color: #ff6b6b;
}
body.th-hico .system-banner {
    background: #c0392b;
    color: #ffffff;
    border-bottom-color: #ffffff;
}
/* When BOTH banners are visible, push the impersonation banner down
   below the system banner. The two stack natively because both are
   `position: sticky; top: 0` — the second sticky element in flow
   parks itself below the first. */

/* Brighter top menu while impersonating, per theme */
body.impersonating .topnav {
    background: #fff8e1;
    border-bottom: 2px solid #ffb300;
}
body.th-dark.impersonating .topnav {
    background: #4a3010;
}
body.th-hico.impersonating .topnav {
    background: #1a001a;
}

/* Impersonation start icon and popup */
.nav-imp-wrap {
    position: relative;
    display: inline-flex;
    align-items: center;
}
.nav-imp-toggle {
    background: none;
    border: none;
    cursor: pointer;
    padding: 6px 8px;
    color: var(--alice-blue);
    display: inline-flex;
    align-items: center;
}
.nav-imp-toggle:hover { color: var(--alice-text); }
.nav-imp-popup {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    z-index: 1000;
    min-width: 320px;
    max-width: 420px;
    background: var(--alice-bg);
    border: 1px solid var(--alice-border);
    border-radius: 6px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    padding: 8px;
}
.nav-imp-popup.hidden { display: none; }
.nav-imp-popup input[type="text"] {
    width: 100%;
    padding: 6px 8px;
    margin-bottom: 6px;
    border: 1px solid var(--alice-border);
    border-radius: 4px;
    box-sizing: border-box;
}
.nav-imp-result-form {
    margin: 0;
}
.nav-imp-result {
    display: block;
    width: 100%;
    text-align: left;
    background: none;
    border: none;
    padding: 6px 8px;
    cursor: pointer;
    font-size: 12px;
    border-radius: 4px;
    color: var(--alice-text);
}
.nav-imp-result:hover {
    background: var(--alice-blue-tint);
}

/* Return arrow next to role badge while impersonating */
.nav-imp-return-form {
    display: inline;
    margin: 0 0 0 4px;
}
.nav-imp-return {
    background: none;
    border: none;
    cursor: pointer;
    color: var(--alice-blue);
    padding: 2px 6px;
    font-size: 16px;
    line-height: 1;
}
.nav-imp-return:hover {
    color: var(--alice-text);
}

/* ── Bookmark nav icon (jump to last visited lesson) ── */
.nav-bookmark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 6px 8px;
}
.nav-bookmark svg {
    display: block;
}
.nav-bookmark--disabled {
    opacity: 0.35;
    cursor: not-allowed;
}

/* ── Role switcher popup ───────────────────────────── */
.nav-role-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.nav-role-badge--switchable {
  cursor: pointer;
  font-family: inherit;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.nav-role-badge--switchable:hover {
  background: var(--alice-blue);
  color: var(--alice-bg);
}
.nav-role-arrow {
  font-size: 9px;
  line-height: 1;
}
.nav-role-popup {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 1000;
  min-width: 220px;
  background: var(--alice-bg);
  border: 1px solid var(--alice-border);
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.12);
  padding: 4px 0;
}
.nav-role-popup.hidden { display: none; }
.nav-role-popup form { margin: 0; }
.nav-role-popup-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: 6px 12px;
  font-size: 12px;
  color: var(--alice-text);
  cursor: pointer;
}
.nav-role-popup-item:hover {
  background: var(--alice-blue-tint);
}
.nav-role-popup-item.active {
  font-weight: 700;
  color: var(--alice-blue);
}
body.th-hico .nav-role-badge {
  background: #FFFFFF;
}
body.th-hico .capstone-card {
  background: #EEEEEE;
}
body.th-hico .capstone-card--active {
  background: #FFFFFF;
}
.nav-email {
  font-size: 12px; color: var(--alice-text-secondary);
}

/* ── Burger button ─────────────────────────────────── */
.burger-btn {
  display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  background: none; border: 1px solid var(--alice-border);
  border-radius: 6px; cursor: pointer; color: var(--alice-text-secondary);
  transition: color .18s, border-color .18s;
}
.burger-btn:hover { color: var(--alice-text); border-color: var(--alice-border-strong); }
.burger-btn svg { pointer-events: none; }

/* ── Burger panel ──────────────────────────────────── */
.burger-panel {
  position: absolute; top: 44px; right: 0; z-index: 300;
  background: var(--alice-surface);
  border: 1px solid var(--alice-border);
  border-radius: 0 0 8px 8px;
  min-width: 220px; padding: 12px 0;
  box-shadow: -4px 4px 24px rgba(0,0,0,.15);
}
.burger-panel.hidden { display: none; }

.burger-item {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 16px; font-size: 13px; color: var(--alice-text-secondary);
  cursor: pointer; background: none; border: none; width: 100%;
  text-align: left; text-decoration: none;
  transition: background .12s;
  font-family: inherit;
}
.burger-item:hover { background: var(--alice-bg); }


/* ── Content container ─────────────────────────────── */
.content-container {
  max-width: var(--content-max-width, 960px);
  margin: 0 auto; padding: 0 16px;
}

/* ── Login shell ───────────────────────────────────── */
.login-shell {
  min-height: calc(100vh - 44px);
  display: flex; align-items: center; justify-content: center;
  padding: 24px 16px;
}

/* ── Cookie notice ─────────────────────────────────── */
.cookie-notice {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 400;
  background: var(--alice-surface);
  border-top: 1px solid var(--alice-border);
  padding: 12px 24px;
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; font-size: 13px; color: var(--alice-text-secondary);
}
.cookie-notice__text { margin: 0; }
.cookie-notice__ok {
  flex-shrink: 0; padding: 6px 18px;
  background: var(--color-brand, var(--alice-blue)); border: none; border-radius: 6px;
  color: var(--color-brand-fg, #fff); font-size: 13px; cursor: pointer;
}

/* ── Responsive: collapse nav links into burger ────── */
@media (max-width: 640px) {
  
  .nav-user .nav-role-badge { display: none; }
}

/* ── Breadcrumb ────────────────────────────────────── */
.breadcrumb {
  font-size: 12px;
  color: var(--alice-text-secondary);
  margin-bottom: 1.25rem;
  display: flex;
  gap: 6px;
  align-items: center;
  flex-wrap: wrap;
}
/* Crumbs that link LOOK like links (2026-09-16): the site's link colour and
   hover underline, not the muted grey that made the path read as a label.
   No rule here — the page-wide `a` rules apply — so the breadcrumb follows
   the site's link style wherever that goes. Only the current crumb and the
   separators keep their own colours. */
.breadcrumb__sep { color: var(--alice-gray); }
.breadcrumb__current { color: var(--alice-text); }
body.nav-locked .breadcrumb {
    position: sticky;
    top: 44px;
    z-index: 490;
    background: var(--alice-bg);
}
/* The Tabula drill-path chrome on /n/ node pages (n/_tabula_chrome.html)
   replaces .breadcrumb there; under a locked top bar it pins the same way,
   so the path stays in view while a long record or dashboard scrolls. */
body.nav-locked .tabula-chrome {
    position: sticky;
    top: 44px;
    z-index: 490;
    background: var(--alice-bg);
    padding-top: 0.35rem;
}
/* The path inside the chrome bar carries the .breadcrumb class for its
   look; the BAR is what pins, so the path must not pin on its own — it
   did, and slid out of the bar over the page title under a locked top bar
   (2026-09-16). */
body.nav-locked .tabula-chrome .breadcrumb {
    top: auto;              /* the pin's offset would shift a relative box down by 44px */
    z-index: auto;
    background: transparent;
}

/* ── Category selector ─────────────────────────────── */
.category-selector { margin-bottom: 1.5rem; }
.category-selector__label {
  font-size: 12px; font-weight: 500;
  color: var(--alice-text-secondary);
  letter-spacing: 0.06em; text-transform: uppercase;
  margin-bottom: 0.6rem;
}
.category-selector__options {
  display: flex; gap: 8px; flex-wrap: wrap;
}
.category-btn {
  padding: 4px 10px; font-size: 11px;
  font-family: var(--font-sans);
  color: var(--alice-text); background: none;
  border: 0.5px solid var(--alice-border-secondary);
  border-radius: var(--radius-md); cursor: pointer;
  transition: background 0.1s, border-color 0.1s;
}
.category-btn:hover { background: var(--alice-bg); }
.category-btn--active {
  background: var(--alice-bg);
  border: 1.5px solid var(--alice-blue);
  color: var(--alice-blue); font-weight: 500;
}
.category-btn:focus-visible {
  outline: 3px solid var(--alice-focus, var(--alice-blue));
  outline-offset: 2px;
}

/* ── Map container ─────────────────────────────────── */
.map-container {
  border: 0.5px solid var(--alice-border-tertiary);
  border-radius: var(--radius-lg);
  overflow: hidden; margin-bottom: 1rem;
  background: var(--alice-surface);
}
.map-controls { display: flex; gap: 8px;
                margin-bottom: 0.75rem; }

/* ── Map legend (moved to SPECS13 block below) ────── */

/* ── Syllabus table ────────────────────────────────── */
.syllabus-table {
  width: 100%; border-collapse: collapse; font-size: 13px;
}
.syllabus-table th {
  font-size: 11px; font-weight: 500;
  color: var(--alice-text-tertiary);
  text-transform: uppercase; letter-spacing: 0.06em;
  padding: 8px 12px; text-align: left;
  border-bottom: 0.5px solid var(--alice-border-tertiary);
}
.syllabus-table td {
  padding: 10px 12px;
  border-bottom: 0.5px solid var(--alice-border-tertiary);
  color: var(--alice-text); vertical-align: middle;
}
.syllabus-table tr:last-child td { border-bottom: none; }
.syllabus-table tr:hover td { background: var(--alice-bg); }

/* ── Admin table ────────────────────────────────── */
/* The framed variant (UX audit lead 57): the collections feature drew its
   own frame inline on a second class (.data-table) inside one deployment.
   Same tokens the inline styles used, so both themes carry over. */
.admin-table--framed {
    background: var(--alice-surface);
    border: 1px solid var(--alice-border);
    border-radius: 6px;
    overflow: hidden;
}

.admin-table {
  width: 100%; border-collapse: collapse; font-size: 13px;
}
.admin-table th {
  font-size: 11px; font-weight: 500;
  color: var(--alice-text-tertiary);
  text-transform: uppercase; letter-spacing: .06em;
  padding: 8px 12px; text-align: left;
  border-bottom: 1px solid var(--alice-border);
}
.admin-table td {
  padding: 9px 12px;
  border-bottom: 1px solid var(--alice-border-tertiary, var(--alice-border));
  vertical-align: middle;
}
.admin-table tr:hover td { background: var(--alice-bg); }

/* ── Admin form ─────────────────────────────────── */
.admin-form { max-width: 900px; }
.form-row {
  display: grid; grid-template-columns: 180px 1fr;
  gap: 8px; align-items: start; margin-bottom: 14px;
}

/* Search bar: input fills space, button fixed width, right-aligned */
.search-bar {
  display: flex; gap: 8px; align-items: center;
  margin-bottom: 14px;
}
.search-bar .form-input { flex: 1; }
.search-bar .btn-primary,
.search-bar .btn-secondary { flex-shrink: 0; }

/* Action bar: right-aligned row of buttons below tables */
.action-bar {
  display: flex; gap: 8px; justify-content: flex-end;
  margin-top: 12px;
}

/* ── Split columns (HISPANIA/BRITANNIA pattern) ─── */



/* ── Permission matrix ──────────────────────────── */
.matrix-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.matrix-table th { padding: 6px 10px; text-align: left;
                   background: var(--alice-bg); }
.matrix-table .fasciculus-row td {
  background: var(--alice-blue-tint);
  font-weight: 600; font-size: 11px; letter-spacing: .08em;
  text-transform: uppercase; padding: 4px 10px;
  color: var(--alice-blue);
}
.matrix-table td { padding: 5px 10px;
  border-bottom: 1px solid var(--alice-border-tertiary, var(--alice-border)); }

/* ── Badge ──────────────────────────────────────── */
.badge {
  display: inline-block; padding: 2px 8px;
  border-radius: 10px; font-size: 10px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
}





/* ── Flash message ──────────────────────────────── */
.flash-msg,
.flash {
  padding: 10px 16px; border-radius: 6px; font-size: 13px;
  margin-bottom: 16px;
}
/* --alice-teal-text, not --alice-teal: the hue is the same and the CONTRAST is
   not. #1D9E75 on the teal tint measures 3.01:1, under the 4.5 a 13px sentence
   needs, and every theme defines a -text token for exactly this reason (the
   tokens carry their measured ratios beside them). The border keeps the vivid
   teal — a 3:1 hairline is a non-text element and passes as one. Measured after
   the change: 4.90:1 light, 7.15:1 dark, 6.70:1 high-contrast, 4.88:1 in each
   of the three colour-vision themes. */
.flash-msg.ok,
.flash-ok { background: var(--alice-teal-tint);
               border: 1px solid var(--alice-teal);
               color: var(--alice-teal-text); }
.flash-msg.err,
.flash-err { background: #FFF0F0;
               border: 1px solid #C13030;
               color: #C13030; }
/* --alice-amber-text for the TEXT, --alice-warning for the hairline — the same
   split .flash-msg.ok makes above, and for the same measured reason. The old
   rule used --alice-warning for both: #BA7517 on the amber tint is 3.43:1 in
   the light theme and 2.08:1 in the deuteranopia and protanopia themes, where
   --alice-warning is a gold (#E69F00) chosen to be DISTINGUISHABLE rather than
   readable. Measured after the change: 4.65:1 light, 6.87:1 dark, 4.85:1
   high-contrast, 4.64:1 deut/prot, 4.65:1 trit. The border keeps the vivid
   token — a hairline is a non-text element and 3:1 passes as one.

   `.flash-msg.warn` is the modifier spelling the flash chain in the templates
   uses (.ok / .err); `.flash-warn` is the standalone spelling older call sites
   use. One declaration, so the amber advisory cannot mean two colours. */
.flash-msg.warn,
.flash-warn { background: var(--alice-amber-tint);
               border: 1px solid var(--alice-warning);
               color: var(--alice-amber-text); }

/* ── Untranslated labels (legacy, kept for non-`t()` link callers) ── */


/* ── ALT+click label editing (EDIT_LINGUA mode) ──────
   The t() helper wraps every body-context label in
   <span class="alice-label" data-nuntius="CODE"> when edit_lingua is
   active. alice.js toggles the body.alt-down class while Alt is held
   so affordances appear only then, and on click with Alt held it
   navigates to /admin/nuntius/CODE without firing the underlying
   hyperlink/htmx action. */
.alice-label[data-nuntius] { cursor: inherit; }
.alice-label--missing[data-nuntius] {
  color: var(--alice-warning);
  text-decoration: underline dashed;
}
body.alt-down .alice-label[data-nuntius] {
  outline: 1px dashed var(--alice-warning);
  outline-offset: 2px;
  cursor: alias;
  background: rgba(186, 117, 23, 0.08);
}

/* Em-dash absence marker — per specs/frontend/015.051_FRONTEND_2026-05-25_ux_decisions.md A8 generalization.
   Used for any "null / absence / no decision" cell or inline display
   (untranslated, no category, not protected, etc.). The aria-label
   carries the semantic ("not set", "not translated", "no category", …)
   so screen readers don't announce the literal em-dash glyph. */
.absence-mark {
  color: var(--alice-text-tertiary);
  font-weight: 400;
  font-style: normal;
}
/* Clickable em-dash — keep the at-rest visual identical to a static
   absence-mark; the action affordance shows only on hover/focus.
   Per specs/frontend/015.051_FRONTEND_2026-05-25_ux_decisions.md "coherence fix Option B" — the user must
   not see two different glyphs for the same "absence" semantic, even
   when one happens to be inside an <a>. */
a.absence-mark,
button.absence-mark {
  text-decoration: none;
  cursor: pointer;
}
a.absence-mark:hover,
a.absence-mark:focus-visible,
button.absence-mark:hover,
button.absence-mark:focus-visible {
  background: rgba(24, 95, 165, 0.07);
  outline: none;
  border-radius: 3px;
}

/* Green check affirm marker — the parallel of .absence-mark for
   "yes / present / set" semantics. Replaces ad-hoc unstyled &#10003;
   spans and the legacy .protected-check class so a Yes always looks
   identical regardless of which column it lives in. */
.affirm-mark {
  color: var(--alice-teal);
  font-weight: 700;
  font-style: normal;
}

/* Edit-lingua toggle in topnav: small pencil icon + iOS-style switch.
   Visible only to EDIT_LINGUA holders; aria-pressed mirrors cookie state. */
.nav-edit-lingua {
  background: transparent;
  border: none;
  padding: 0;
  margin: 0 4px;
  cursor: pointer;
  color: inherit;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.nav-edit-lingua svg { width: 14px; height: 14px; }
.nav-edit-lingua:hover svg { color: var(--alice-warning); }
.nav-edit-lingua-switch {
  display: inline-flex;
  align-items: center;
  width: 26px;
  height: 14px;
  background: var(--alice-border);
  border-radius: 7px;
  position: relative;
  transition: background 0.2s;
  flex-shrink: 0;
}
.nav-edit-lingua-switch::before {
  content: "";
  position: absolute;
  left: 2px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #fff;
  transition: left 0.2s;
  box-shadow: 0 1px 2px rgba(0,0,0,0.25);
}
.nav-edit-lingua[aria-pressed="true"] .nav-edit-lingua-switch {
  background: var(--alice-warning);
}
.nav-edit-lingua[aria-pressed="true"] .nav-edit-lingua-switch::before {
  left: 14px;
}
.nav-edit-lingua[aria-pressed="true"] svg { color: var(--alice-warning); }

/* ── Record card (partials/record_views.html) ───────────────────
   Shared read-only record display used by lexia LIST/RECORD modes and
   the dashboard INDIVIDUAL/BOTH record card. Single source of truth so
   look-&-feel refinements propagate to every read-only record surface.
   Forms use partials/schema_form.html (.schema-grid) — the two share
   the same two-column rhythm and label weight on purpose. */
.record-card {
  border: 1px solid var(--alice-border);
  border-radius: 6px;
  overflow: hidden;
  background: var(--alice-surface);
  margin: 0.5rem 0;
}
.record-card__head {
  background: var(--alice-gray-tint, #f3f4f6);
  padding: 0.5rem 0.8rem;
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--alice-text);
  border-bottom: 1px solid var(--alice-border);
}
.record-card__fields {
  display: grid;
  grid-template-columns: minmax(120px, max-content) minmax(0, 1fr);
  column-gap: 1rem;
  row-gap: 0.45rem;
  align-items: start;
  margin: 0;
  padding: 0.7rem 0.85rem;
  font-size: 0.9rem;
}
.record-card__fields dt {
  font-weight: 600;
  color: var(--alice-text-secondary);
  text-align: right;
}
.record-card__fields dd {
  margin: 0;
  min-width: 0;
  word-break: break-word;
  overflow-wrap: anywhere;
}
.record-card__fields--nested {
  padding: 0.3rem 0;
  row-gap: 0.3rem;
  font-size: 0.86rem;
}
.record-card__group-label {
  font-weight: 600;
  color: var(--alice-text);
  grid-column: 1 / -1;
  border-top: 1px solid var(--alice-border-subtle, var(--alice-border));
  padding-top: 0.4rem;
  text-align: left;
}
.record-card__group { grid-column: 1 / -1; }
.record-card__empty { color: var(--alice-text-tertiary, #999); }
.record-card__md > :first-child { margin-top: 0; }
.record-card__md > :last-child  { margin-bottom: 0; }

/* Record list (lexia LIST mode browser) — matching visual accents:
   a light-gray header strip and zebra-free rows with a subtle hover. */
.record-list {
  list-style: none;
  margin: 0.7rem 0 0 0;
  padding: 0;
  max-height: 280px;
  overflow: auto;
  border: 1px solid var(--alice-border);
  border-radius: 6px;
}
.record-list__item {
  border-bottom: 1px solid var(--alice-border-subtle, var(--alice-border));
  padding: 0;
}
.record-list__item:last-child { border-bottom: none; }
.record-list__item a {
  display: block;
  padding: 0.45rem 0.7rem;
  text-decoration: none;
  font-size: 0.9rem;
  color: var(--alice-text);
}
.record-list__item a:hover { background: var(--alice-gray-tint, #f3f4f6); }

/* ── Markdown preview ──────────────────────────── */
.md-preview {
  margin-top: 6px; padding: 10px 14px;
  background: var(--alice-bg);
  border: 1px solid var(--alice-border);
  border-radius: 6px;
  font-size: 13px; color: var(--alice-text);
  line-height: 1.6; min-height: 40px;
  /* Prevent wide markdown (large images, long unbreakable URLs,
     long inline code) from inflating the parent .lingua-panel and
     pushing the save button off-screen. ALICE_ABOUT regression
     reproduced on /admin/nuntius/ALICE_ABOUT — see SPECS36+ admin
     fixes. */
  max-width: 100%;
  min-width: 0;
  box-sizing: border-box;
  overflow-wrap: anywhere;
  word-break: break-word;
}
.md-preview h1,.md-preview h2,.md-preview h3 {
  color: var(--alice-text); margin-bottom: 8px;
}
.md-preview code {
  background: var(--alice-bg);
  padding: 1px 5px; border-radius: 3px;
}
/* Cap inline images and long pre/code blocks to the preview width so
   page-scale assets (NSF / NIH logos at native pixel size in
   ALICE_ABOUT) never widen the column. */
.md-preview img {
  max-width: 100%;
  height: auto;
}
.md-preview pre {
  max-width: 100%;
  overflow-x: auto;
  white-space: pre;
}
.md-preview table {
  display: block;
  max-width: 100%;
  overflow-x: auto;
}

/* ── Calculus card ─────────────────────────────── */


/* Warning-tinted variant of the standard .card (used by the
   Calculus action on the admin opus actions page). Inherits all
   .card padding/border-radius/sizing — only the colors change. */
.card--warn {
  border-color: var(--alice-warning);
  background: var(--alice-amber-tint);
}
.card--warn:hover {
  filter: brightness(0.97);
}

.btn-calculus {
  background: var(--alice-warning); color: #fff;
  border: none; padding: 8px 20px; border-radius: 6px;
  font-weight: 600; cursor: pointer;
}


/* ── Modal ─────────────────────────────────────── */
.modal {
  position: fixed; top: 0; left: 0; right: 0; bottom: 0;
  z-index: 1000; display: flex;
  align-items: center; justify-content: center;
  background: rgba(0,0,0,.5);
}
.modal.hidden { display: none; }
.modal-box {
  background: var(--alice-surface);
  border: 1px solid var(--alice-border);
  border-radius: 10px; padding: 2rem;
  max-width: 480px; width: 90%;
}
.modal-actions {
  display: flex; gap: 8px; margin-top: 1rem;
}

/* ── NUNTIUS body ──────────────────────────────── */
.nuntius-body {
  line-height: 1.7; font-size: 15px;
}
.nuntius-body h1,.nuntius-body h2,.nuntius-body h3 {
  margin-top: 1.5rem; margin-bottom: 0.5rem;
}

/* ── Edge editor ───────────────────────────────── */
.edge-editor {
  margin-top: 1rem;
  border: 1px solid var(--alice-border);
  border-radius: 8px; padding: 1rem;
}
.edge-editor__hint {
  color: var(--alice-text-secondary);
  font-size: 13px; font-style: italic;
}

/* 14. Responsive overrides */
@media (max-width: 480px) {
    

    .grid-2col {
        grid-template-columns: 1fr;
    }

    .col-course {
        display: none;
    }

    .login-card {
        margin: 1rem;
    }
}

/* ── Submission text display ─────────────────── */
.submission-text {
  background: rgba(255,255,255,.04);
  border: 1px solid rgba(255,255,255,.1);
  border-radius: 6px; padding: 10px 14px;
  font-size: 13px; color: rgba(255,255,255,.8);
  line-height: 1.6; white-space: pre-wrap;
  max-height: 200px; overflow-y: auto;
  margin-bottom: 10px;
}

/* ── Tessera share view ──────────────────────── */
.tessera-notice {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 12px; border-radius: 10px;
  background: rgba(212,168,67,.15);
  border: 1px solid rgba(212,168,67,.3);
  font-size: 11px; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--gold); margin-bottom: 16px;
}

/* ── Status pill ─────────────────────────────── */
.pill {
  display: inline-block; padding: 2px 8px;
  border-radius: 10px; font-size: 11px;
  font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase;
}
.pill-ok { background:rgba(0,120,100,.25); color:#4dd; }
.pill-warn { background:rgba(212,168,67,.2);  color:var(--gold); }
.pill-err { background:rgba(232,75,31,.2);   color:var(--orange); }

/* ── Lingua tabs ─────────────────────────────────── */
.lingua-tabs { margin-top: 1.5rem; }

.lingua-tab-bar {
  display: flex; gap: 4px; flex-wrap: wrap;
  border-bottom: 1px solid var(--alice-border);
  margin-bottom: 0;
  position: sticky;
  top: 0;
  background: var(--alice-bg);
  z-index: 50;
  padding-top: 4px;
}
body.nav-locked .lingua-tab-bar {
  /* --lingua-tab-top is computed by alice.js (updateNavOffsets);
     falls back to 44px if no breadcrumb is present on the page. */
  top: var(--lingua-tab-top, 44px);
}
.lingua-tab-btn {
  padding: 6px 14px; font-size: 12px; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase;
  background: none; border: 1px solid transparent;
  border-bottom: none; border-radius: 4px 4px 0 0;
  cursor: pointer; color: var(--alice-text-secondary);
  transition: color .15s, background .15s;
  margin-bottom: -1px;
}
.lingua-tab-btn:hover { color: var(--alice-text); }
.lingua-tab-btn.active {
  background: var(--alice-surface);
  border-color: var(--alice-border);
  border-bottom-color: var(--alice-surface);
  color: var(--alice-blue); font-weight: 700;
}
.lingua-panel {
  background: var(--alice-surface);
  border: 1px solid var(--alice-border);
  border-top: none;
  border-radius: 0 0 6px 6px;
  padding: 20px;
  /* Same overflow defense as .md-preview — prevents an inner runaway
     element (textarea content, rendered markdown) from inflating the
     panel and hiding controls at the right edge.
     `overflow-x: clip` (not hidden) because `hidden` makes this a
     scroll container, which breaks `position: sticky` for the
     EasyMDE editor toolbar inside. `clip` clips wide content the
     same way without creating a scroll container, so the
     focus-gated sticky toolbar rule (alice.css §EasyMDEContainer)
     keeps working on /admin/opus/{id}/lexia/{id}. */
  min-width: 0;
  max-width: 100%;
  box-sizing: border-box;
  overflow-x: clip;
}
.lingua-panel.hidden { display: none; }

.lingua-add-section {
  margin-top: 1.5rem;
  padding: 16px;
  border: 1px dashed var(--alice-border);
  border-radius: 6px;
}
.lingua-add-section h3 {
  margin-top: 0; font-size: 13px;
  text-transform: uppercase; letter-spacing: .08em;
  color: var(--alice-text-secondary);
}

/* ── Chip input ──────────────────────────────────── */
.chip-input { display: flex; flex-direction: column; gap: 8px; }
.chip-list {
  display: flex; flex-wrap: wrap; gap: 6px; min-height: 32px;
}
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 8px;
  background: rgba(0,85,164,.1);
  border: 1px solid var(--alice-blue);
  border-radius: 4px; font-size: 12px;
}
.chip-label {
  color: var(--alice-blue); text-decoration: none; max-width: 300px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chip-label:hover { text-decoration: underline; }
.chip-remove {
  background: none; border: none; cursor: pointer;
  color: var(--alice-text-secondary); font-size: 12px;
  padding: 0 2px; line-height: 1;
}
.chip-remove:hover { color: var(--alice-text); }
.chip-entry { max-width: 480px; }

/* ── Button alignment (admin forms) ─────────────── */
.form-actions {
  display: flex; justify-content: flex-end;
  margin-top: 16px;
}

.form-actions .btn-primary,
.form-actions .btn-secondary,
.form-actions .btn-danger,
.admin-btn,
.admin-table .btn-primary,
.admin-table .btn-secondary,
.admin-table .btn-danger {
  max-width: var(--btn-max-width, 300px);
  width: auto;
  display: inline-block;
  margin-left: 8px;
}

/* ── Tall textarea ─────────────────────────── */
.form-textarea.tall { min-height: 200px; }

/* ── Lesson page progress chips ────────────── */






/* ── Lesson page tab bar ──────────────────── */
.pagina-tab-bar {
  display: flex; gap: 2px; flex-wrap: wrap;
  margin-bottom: 0;
  /* Bottom border lives on .tab-bar-row so it spans the full width
     including the progress widget on the right. */
}
.pagina-tab-btn {
  padding: 8px 18px; font-size: 13px; font-weight: 400;
  background: none; border: 1px solid transparent;
  border-bottom: none; border-radius: 4px 4px 0 0;
  cursor: pointer; color: var(--alice-text-secondary);
  transition: color .15s; margin-bottom: -1px;
  display: flex; align-items: center; gap: 6px;
  text-decoration: none;
}
.pagina-tab-btn:hover { color: var(--alice-text); }
.pagina-tab-btn.active {
  background: var(--alice-surface);
  border-color: var(--alice-border);
  border-bottom-color: var(--alice-surface);
  color: var(--alice-blue); font-weight: 700;
}
.tab-count {
  background: var(--alice-amber);
  color: var(--alice-surface);
  border-radius: 10px; font-size: 10px;
  padding: 1px 6px; font-weight: 700;
}
.tab-count-quiet {
  background: var(--alice-teal);
  color: var(--alice-surface);
  border-radius: 10px; font-size: 10px;
  padding: 1px 6px; font-weight: 700;
}
/* The reading counter overlaid on the roster's History button. Modelled on
   .tab-count above, but positioned over the control rather than beside a label,
   and coloured by the ONE question it answers: has this person opened anything?
   Teal for yes, danger for no — both are palette variables, so the dark-theme
   block at the top of this file recolours them without a second rule here. */
.roster-hist { position: relative; overflow: visible; }
.roster-badge {
  position: absolute; top: -7px; right: -7px;
  min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: 9px;
  font-size: 10px; line-height: 17px; font-weight: 700;
  text-align: center; color: #FFFFFF;
  /* A ring in the page colour so the bubble reads as sitting ON the button
     rather than as part of its border. */
  box-shadow: 0 0 0 2px var(--alice-surface);
  /* The bubble is decoration over a link; a click on it is a click on the
     button underneath, not a dead spot in the corner. */
  pointer-events: none;
}
.roster-badge.ok   { background: var(--alice-teal); }
.roster-badge.zero { background: var(--alice-danger); }

.ctx-pill {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  margin-right: 8px;
  border-radius: 4px;
  vertical-align: middle;
  user-select: none;
}
.ctx-pill svg { display: block; width: 22px; height: 22px; }
.ctx-pill--opus { background: var(--alice-teal-tint);  color: var(--alice-teal); }
.ctx-pill--lexia { background: var(--alice-amber-tint); color: var(--alice-warning); }
.asset-row.conflict {
  border-color: var(--alice-amber) !important;
  box-shadow: 0 0 0 2px var(--alice-amber-tint);
}
.asset-conflict-banner {
  background: var(--alice-amber-tint);
  color: var(--alice-warning);
  padding: 0.4rem 0.75rem;
  border-radius: 4px;
  font-size: 0.85em;
  margin-top: 0.5rem;
  display: flex; gap: 0.5rem; align-items: center;
}
.asset-reader-row > summary { list-style: none; }
.asset-reader-row > summary::-webkit-details-marker { display: none; }
.asset-reader-row > summary:hover { background: var(--alice-bg, #f5f5f2); }
.asset-reader-row[open] > summary { border-bottom: 1px solid var(--alice-border, #e0dfd8); }
/* Assets-tab sort bar. Plain links, so the ordering is in the URL and
   survives a reload, a bookmark and a shared link. The active one is the
   only one that reads as text rather than as something to click. */
.asset-sort-bar {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 0.15rem 0.65rem;
  margin-bottom: 0.75rem;
  font-size: 0.85em;
}
.asset-sort-label { margin-right: 0.1rem; }
.asset-sort-link {
  color: var(--alice-blue);
  text-decoration: none;
  padding: 0.15rem 0;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
}
.asset-sort-link:hover { border-bottom-color: var(--alice-border, #e0dfd8); }
.asset-sort-link.is-active {
  color: var(--alice-text);
  font-weight: 600;
  border-bottom-color: var(--alice-blue);
  cursor: default;
}
.asset-add-details { margin-bottom: 0.75rem; }
.asset-add-toggle {
  display: flex; align-items: center;
  padding: 0.5rem 0.75rem;
  background: var(--alice-bg, #f5f5f2);
  border: 1px solid var(--alice-border, #e0dfd8);
  border-radius: 6px;
  font-weight: 600; font-size: 0.9em;
  color: var(--alice-blue);
  cursor: pointer; list-style: none;
}
.asset-add-toggle::-webkit-details-marker { display: none; }
.asset-add-details[open] > .asset-add-toggle {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
  margin-bottom: 0;
}
.asset-add-details[open] > form {
  border: 1px solid var(--alice-border, #e0dfd8);
  border-top: none;
  border-radius: 0 0 6px 6px;
  padding: 0.75rem;
}

/* ── Lesson page panels (SPECS28: card treatment) ── */
.pagina-panel {
  background: var(--alice-surface);
  border: 1px solid var(--alice-border);
  border-top: none;          /* the active tab supplies the top edge */
  border-radius: 0 0 8px 8px;
  padding: 20px 22px;
  min-height: 200px;
}
.pagina-panel.hidden { display: none; }
.pagina-tag {
  font-size: 14px; color: var(--alice-text-secondary);
  font-style: italic; margin-bottom: 1rem;
}
.pagina-content { font-size: 15px; line-height: 1.7; }
.pagina-content h1,
.pagina-content h2,
.pagina-content h3 { margin-top: 1.5rem; }
/* Responsive images — never overflow the content container */
.pagina-content img {
    max-width: 100%;
    height: auto;
}

/* Manual-course screenshots: border only for alice_manual images */
.pagina-content img[src*="alice_manual"] {
    border: 1px solid var(--alice-border);
    border-radius: 6px;
    /* margin-BLOCK, not the `margin: 12px 0` this used to be. The shorthand
       also set the INLINE margins to zero, and this selector is more specific
       than `.md-img--center`, so every centred or right-placed screenshot in
       the manual silently sat flush left — the keyword parsed, the class was
       emitted, and the cascade threw it away. */
    margin-block: 12px;
    box-shadow: 0 1px 4px rgba(0,0,0,0.06);
}
/* NEVER UPSCALE A SCREENSHOT. `max-width:100%` alone stretched every picture to
   the column width, so MENU_EN.png — 596px wide by nature — was blown up to 928
   and rendered nearly three screens tall AND blurry, being enlarged past its own
   resolution. `width:auto` caps each image at its natural size; the max-width
   above still shrinks anything wider than the column. An author who wants a
   specific size says so with {width=…}, which is an inline style and wins — the
   :not() is what lets it. */
.pagina-content img[src*="alice_manual"]:not([style*="width"]) {
    width: auto;
}

/* Author placement, from `![alt](/x.png){width=420 center}` — see
   app/services/md_image_attrs.py. The size itself arrives as an inline style
   the plugin built; these only say WHERE the picture sits, because
   `margin:auto` needs `display:block` to do anything at all. They sit AFTER
   the alice_manual block deliberately: a tie on specificity goes to whichever
   rule comes last, and placement is the author's explicit instruction. */
.md-img--center { display: block; margin-inline: auto; }
.md-img--left   { display: block; margin-inline: 0 auto; }
.md-img--right  { display: block; margin-inline: auto 0; }

/* `{wrap}` — the text flows BESIDE the picture instead of below it. The 20px
   gutter is a margin and not padding: padding would fall inside the border of
   a manual screenshot and read as part of the picture. */
.md-img--wrap-right { float: right; margin: 4px 0 20px 20px; }
.md-img--wrap-left  { float: left;  margin: 4px 20px 20px 0; }
@media (max-width: 640px) {
  /* A 300px picture floated in a phone-width column leaves a two-word ravine
     down the side. Narrow enough, and it goes back to being a block. */
  .md-img--wrap-right,
  .md-img--wrap-left { float: none; display: block; margin: 12px 0; }
}
/* A float is out of flow, so when the text beside it is shorter than the
   picture is tall the picture hangs out of the bottom of the panel. This is
   the clearfix that stops it — `.pagina-content` is the container every
   authored lexia renders into. */
.pagina-content::after { content: ""; display: block; clear: both; }

/* ── Collapsible SLO/Assessment ───────────── */
.pagina-collapsible {
  margin-top: 1.5rem;
  border: 1px solid var(--alice-border);
  border-radius: 6px; overflow: hidden;
}
.pagina-collapsible summary {
  padding: 10px 14px; cursor: pointer;
  font-weight: 600; font-size: 14px;
  background: var(--alice-bg);
  list-style: none; user-select: none;
}
.pagina-collapsible summary::-webkit-details-marker { display: none; }
.pagina-collapsible summary::before {
  content: '+ '; color: var(--alice-blue);
}
.pagina-collapsible[open] summary::before { content: '- '; }
.pagina-collapsible-body {
  padding: 14px; font-size: 14px; line-height: 1.6;
}
/* A grading rubric is a five-column table of paragraphs. Let it scroll
   inside its own section instead of widening the page under it. */
.pagina-assess-rubric { overflow-x: auto; }
.pagina-assess-rubric table { min-width: 720px; }

/* ── Asset notice ─────────────────────────── */


/* ── PDF embed ────────────────────────────── */



/* ── Video embed ──────────────────────────── */



/* ── Map inline ───────────────────────────── */


/* ── Layer 2: proposed-plan Apply/Discard card (agent panel) ──────────────
   The act route proposes instead of applying, so this card is the last step
   before anything mutates. Amber, not the neutral surface: it must not read as
   another chat bubble the user can scroll past. */
.agent-plan-proposal {
  margin: 8px 0 12px;
  padding: 10px 12px;
  border: 1px solid var(--alice-amber, #b45309);
  border-left-width: 4px;
  border-radius: 6px;
  background: var(--alice-surface);
  color: var(--alice-text);
}
.agent-plan-proposal__note {
  font-size: 13px; font-weight: 600; margin-bottom: 8px;
}
.agent-plan-proposal__actions {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}
.agent-plan-proposal__status {
  font-size: 13px; color: var(--alice-text-secondary);
}

/* ── Survey embed ─────────────────────────── */
.survey-embed {
  width: 100%; height: 700px;
  border: 1px solid var(--alice-border); border-radius: 4px;
}
/* Fallback link above the embed, for browsers (Edge) that block the
   cross-origin survey iframe and leave the student staring at a blank box. */
.survey-fallback {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 6px 12px; margin: 0 0 10px 0;
}
.survey-fallback-link {
  display: inline-flex; align-items: center; gap: 6px;
  font-weight: 600; text-decoration: underline;
}
.survey-fallback-hint {
  color: var(--alice-text-secondary); font-size: 13px;
}

/* ── Submission form ──────────────────────── */
.pagina-submission {
  margin-top: 2rem; padding-top: 1.5rem;
  border-top: 1px solid var(--alice-border);
}
.pagina-existing-submission {
  background: var(--alice-bg);
  border: 1px solid var(--alice-border);
  border-radius: 6px; padding: 12px;
  margin-bottom: 12px; font-size: 13px;
}
.pagina-existing-submission pre {
  margin: 0; white-space: pre-wrap; word-break: break-word;
}

/* ── Next lessons ─────────────────────────── */
.pagina-next-section {
  margin-top: 0.75rem;
  padding-top: 0;
}
.pagina-next-list {
  display: flex; flex-direction: column; gap: 8px; margin-top: 8px;
}
.pagina-next-link {
  display: block; padding: 10px 14px;
  background: var(--alice-surface);
  border: 1px solid var(--alice-border);
  border-radius: 6px; text-decoration: none;
  color: var(--alice-text); font-size: 14px;
  transition: border-color .15s, background .15s;
}
.pagina-next-link:hover {
  border-color: var(--alice-blue);
  background: rgba(0,85,164,.06);
  color: var(--alice-blue);
}
.pagina-empty {
  color: var(--alice-text-secondary); font-style: italic;
  font-size: 14px;
}

/* ── Capstone list ────────────────────────── */












/* ── Course page tab bar ──────────────────── */
.crs-tab-bar {
  display: flex; gap: 2px;
  margin-bottom: 0;       /* card attaches directly */
  /* Bottom border lives on .tab-bar-row so it spans the full row width. */

  /* The tab strip absorbs the pressure, not the progress bar (2026-08-25).
     .content-container caps the row at 960px on EVERY viewport, so a course
     with many tabs — MDC 4153 has seven — overflowed by a constant amount no
     media query could see: the tabs claimed their min-content width, the
     progress bar is flex-shrink:0, and the sum simply exceeded 928px. A flex
     item will not shrink below min-content unless min-width says it may, so
     without the line below the excess had nowhere to go but past the right
     edge, where it was clipped mid-word. */
  min-width: 0;
  overflow-x: auto;
  /* A visible scrollbar here would sit on the border the active tab joins,
     so keep it out of the layout; the row still scrolls by wheel/touch/keys. */
  scrollbar-width: none;
}
.crs-tab-bar::-webkit-scrollbar { display: none; }
.crs-tab-btn {
  padding: 8px 20px; font-size: 13px; font-weight: 400;
  background: none; border: 1px solid transparent;
  border-bottom: none; border-radius: 4px 4px 0 0;
  cursor: pointer; color: var(--alice-text-secondary);
  margin-bottom: -1px; transition: color .15s;
  text-decoration: none; display: inline-flex; align-items: center; gap: 6px;
}
.crs-tab-btn:hover { color: var(--alice-text); }
.crs-tab-btn.active {
  background: var(--alice-surface);
  border-color: var(--alice-border);
  border-bottom-color: var(--alice-surface);
  color: var(--alice-blue); font-weight: 700;
}
/* SPECS28: course panels are cards attached under the active tab */
.crs-panel {
  background: var(--alice-surface);
  border: 1px solid var(--alice-border);
  border-top: none;        /* the active tab supplies the top edge */
  border-radius: 0 0 8px 8px;
  padding: 20px 22px;
  min-height: 200px;
}
.crs-panel.hidden { display: none; }

/* ── Public home page grid ───────────────────────── */
.home-public-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  margin-top: 1.5rem;
}
@media (max-width: 640px) {
  .home-public-grid { grid-template-columns: 1fr; }
}

.home-panel {
  background: var(--alice-surface);
  border: 1px solid var(--alice-border);
  border-radius: 8px;
  padding: 20px 22px;
  display: flex; flex-direction: column; gap: 10px;
}
.home-panel__title {
  font-size: 13px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--alice-text-secondary);
  margin: 0;
}
.home-panel__body {
  flex: 1; font-size: 14px; line-height: 1.6;
  color: var(--alice-text);
}
.home-panel__body p { margin: 6px 0 0; }
.home-panel__body h1,
.home-panel__body h2,
.home-panel__body h3 {
  font-size: 15px; font-weight: 700;
  color: var(--alice-text); margin: 0 0 6px;
}
.home-panel__more {
  display: inline-block; font-size: 13px;
  color: var(--alice-blue); text-decoration: none;
  align-self: flex-start;
}
.home-panel__more:hover { text-decoration: underline; }


/* ── Textarea and preview fixes ──────────────────── */
.lingua-panel textarea,
.lingua-panel .form-textarea {
  width: 100%;
  box-sizing: border-box;
}
.md-preview:empty { display: none; }

/* ── Nuntius body content ───────────────────────────────────────────── */

.nuntius-body img {
    height: 60px;
    width: auto;
    vertical-align: middle;
    margin-right: 0.6rem;
}

.nuntius-body p {
    margin-block: 0.75rem;
}

.nuntius-body p:has(img) {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.nuntius-body a {
    color: var(--alice-blue);
}

.nuntius-body a:hover {
    text-decoration: underline;
}

.nuntius-body hr {
    border: none;
    border-top: 1px solid var(--alice-border);
    margin-block: 1.5rem;
}

/* ── Asset list (PDF / Video tab) ───────────────────────────── */

.asset-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-bottom: 16px;
}

.asset-list-btn {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    width: 100%;
    background: var(--alice-surface);
    border: 1px solid var(--alice-border);
    border-radius: 6px;
    padding: 10px 14px;
    cursor: pointer;
    text-align: left;
    transition: border-color 0.15s, background 0.15s;
}

.asset-list-btn:hover,
.asset-list-btn.active {
    border-color: var(--alice-blue);
    background: var(--alice-blue-tint);
}

.asset-list-btn[aria-expanded="true"] {
    border-color: var(--alice-blue);
}







/* ── Asset viewer ────────────────────────────────────────────── */











/* ── AI Agent sidebar ────────────────────────────────────────── */

.agent-toggle-btn {
    position: fixed;
    bottom: 24px;
    right: 24px;
    z-index: 300;
    display: flex;
    align-items: center;
    gap: 8px;
    /* Brand-blue (primary-button) scheme — legible in dark mode, where
       --alice-blue flips light. */
    background: var(--color-brand, var(--alice-blue));
    color: var(--color-brand-fg, #fff);
    border: none;
    border-radius: 24px;
    padding: 10px 18px;
    cursor: pointer;
    font-size: 0.85rem;
    font-weight: 600;
    box-shadow: 0 2px 8px rgba(0,0,0,0.18);
    transition: background 0.15s, box-shadow 0.15s;
}

.agent-toggle-btn:hover {
    opacity: 0.9;
    box-shadow: 0 4px 14px rgba(0,0,0,0.22);
}

.agent-toggle-label {
    white-space: nowrap;
}

.agent-panel {
    position: fixed;
    top: 0;
    /* 10 px clear of the viewport so the panel's internal scrollbar
       never gets shoved under the browser scrollbar. */
    right: 10px;
    width: 420px;
    min-width: 320px;
    max-width: 1200px;
    height: 100vh;
    background: var(--alice-surface);
    /* Hard border on every side — the panel now floats inside the
       viewport so the right border is visible too. */
    border: 2px solid var(--alice-border);
    box-shadow: -6px 0 24px rgba(0,0,0,0.18);
    z-index: 400;
    display: flex;
    flex-direction: column;
    /* No width transition — it would fight the drag-to-resize handler. */
}

/* When nav-lock is active the topnav (44px) + breadcrumb become sticky.
   Push the panel down by the combined height so the tab bar / close X
   aren't occluded. alice.js publishes the sum on <body> as
   --lingua-tab-top; we default to 0 when the JS hasn't run yet. */
body.nav-locked .agent-panel {
    top: var(--lingua-tab-top, 0px);
    height: calc(100vh - var(--lingua-tab-top, 0px));
}

/* When the panel is open, reserve its footprint on the right so the
   centered .content-container reflows LEFT instead of being occluded —
   shrinking to the left margin only when it can't fit otherwise.
   --agent-panel-occupied is published by _layoutAgentPanel() and tracks
   the live (resizable) panel width. Gated above the mobile breakpoint
   (the panel goes full-width <=600px, where reflow would push content
   off-screen). _layoutAgentPanel() also sets the panel's top/height
   inline so it clears the impersonation / system banners. */
@media (min-width: 601px) {
    body.agent-panel-open {
        padding-right: var(--agent-panel-occupied, 440px);
        transition: padding-right .18s ease;
    }
    /* The full-width top chrome must NOT reflow with the content — the
       panel sits below it, so a shrunk topnav/banner would leave a broken
       empty strip at the top-right. Cancel the body padding for them with
       a matching negative right margin so they stay edge-to-edge. */
    body.agent-panel-open .topnav,
    body.agent-panel-open .system-banner,
    body.agent-panel-open .impersonation-banner {
        margin-right: calc(-1 * var(--agent-panel-occupied, 440px));
    }
}

/* Left-edge drag handle for live resize. */
.agent-panel-resizer {
    position: absolute;
    top: 0;
    left: -4px;
    width: 8px;
    height: 100%;
    cursor: col-resize;
    background: transparent;
    z-index: 401;
}
.agent-panel-resizer:hover,
.agent-panel-resizer:active {
    background: linear-gradient(
        to right, transparent, var(--alice-blue) 45%,
        var(--alice-blue) 55%, transparent);
    opacity: 0.35;
}

.agent-panel.hidden {
    display: none;
}

.agent-panel-header {
    display: flex;
    align-items: center;
    /* Title only; close button moved to the tab bar. Tighter padding
       so the header doesn't steal vertical space from the chat area. */
    padding: 8px 12px 6px;
    flex-shrink: 0;
}

.agent-panel-title {
    font-weight: 700;
    font-size: 0.95rem;
    color: var(--alice-text);
}

.agent-panel-newchat,
.agent-panel-close {
    background: var(--alice-bg);
    border: 1px solid var(--alice-border);
    cursor: pointer;
    line-height: 1;
    color: var(--alice-text);
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 6px;
    flex-shrink: 0;
    transition: background 0.12s ease, color 0.12s ease,
                border-color 0.12s ease;
}

/* New-chat sits next to the close X; push the pair to the right edge
   of the tab bar via margin-left:auto on the first of the two. */
.agent-panel-newchat {
    margin-left: auto;
    margin-right: 4px;
}

.agent-panel-close {
    font-size: 1.15rem;
    font-weight: 700;
}

.agent-panel-newchat:hover {
    background: var(--color-brand, var(--alice-blue));
    color: var(--color-brand-fg, #fff);
    border-color: var(--color-brand, var(--alice-blue));
}

.agent-panel-close:hover {
    background: #b00;
    color: #fff;
    border-color: #b00;
}

.agent-panel-newchat:focus-visible,
.agent-panel-close:focus-visible {
    outline: 2px solid var(--alice-blue);
    outline-offset: 2px;
}

.agent-messages {
    flex: 1;
    overflow-y: auto;
    /* Match .agent-form padding so the right edge of agent bubbles
       lines up with the right edge of the input textarea below. */
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.agent-msg {
    padding: 10px 12px;
    border-radius: 8px;
    font-size: 0.85rem;
    line-height: 1.5;
    word-break: break-word;
}

.agent-msg--user {
    /* Use the primary-button scheme (fixed saturated brand blue + its
       foreground) rather than --alice-blue, which flips to a LIGHT blue in
       dark mode and left white text low-contrast. Brand blue is the same
       legible blue in both themes. */
    background: var(--color-brand, var(--alice-blue));
    color: var(--color-brand-fg, #fff);
    align-self: flex-end;
    max-width: 88%;           /* questions stay compact + right-aligned */
    border-bottom-right-radius: 2px;
    /* Positioning context for the .agent-msg-copy button (same
       affordance as on assistant bubbles — admin can grab their
       own prompt back to the clipboard). */
    position: relative;
}

.agent-msg--agent {
    background: var(--alice-bg);
    color: var(--alice-text);
    border: 1px solid var(--alice-border);
    /* Stretch full column width so the right edge lines up with the
       input textarea's right edge below. */
    align-self: stretch;
    max-width: none;
    border-bottom-left-radius: 2px;
    /* Establish a positioning context for the .agent-msg-copy button
       so it can pin to the top-right corner of the bubble. */
    position: relative;
}

.agent-msg--thinking {
    background: var(--alice-bg);
    color: var(--alice-text-secondary);
    border: 1px solid var(--alice-border);
    align-self: flex-start;
    font-style: italic;
}

.agent-msg--error {
    background: rgba(200,0,0,0.08);
    color: #b00;
    border: 1px solid rgba(200,0,0,0.2);
    align-self: flex-start;
}

/* "Use as edit plan" button — small primary chip below the bubble's
   top-right controls. Visible only on assistant bubbles when the
   panel data-can-edit-lexia="true". Click fires a single-call
   direct-edit run and streams events into the Flow tab. */
.agent-msg-useplan {
    float: right;
    margin: 6px 0 0 8px;
    padding: 3px 8px;
    border: 1px solid var(--alice-blue, #2563eb);
    border-radius: 12px;
    background: transparent;
    color: var(--alice-blue, #2563eb);
    font-size: 0.72rem;
    font-weight: 500;
    line-height: 1.2;
    cursor: pointer;
    position: sticky;
    top: 4px;
    z-index: 2;
}
.agent-msg-useplan:hover {
    background: var(--color-brand, var(--alice-blue));
    color: var(--color-brand-fg, #fff);
}

/* Tiny timestamp pinned to the bubble's top-left corner. 6pt is
   intentional — informative when wanted, invisible at a glance. The
   .agent-msg-time element is positioned absolutely so it doesn't
   push the bubble text and doesn't fight the sticky float of the
   .agent-msg-copy / .agent-msg-scrolltop controls on the right. */
.agent-msg-time {
    position: absolute;
    top: 3px;
    left: 6px;
    font-size: 8px;        /* ≈6pt, readable but unobtrusive */
    line-height: 1;
    color: rgba(0, 0, 0, 0.45);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    letter-spacing: 0.02em;
    pointer-events: auto;  /* allow hover for the title tooltip */
    user-select: none;
}

/* ── SPECS47 branch UI: per-message fork actions + switcher ───── */
/* One action bar pinned top-right; children flex-ordered (Top → actions →
   Copy → Delete). Children drop their individual float/sticky — the bar
   owns positioning. */
.agent-msg-actions {
    float: right;
    position: sticky;
    top: 6px;
    margin-left: 8px;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    z-index: 2;
    /* Opaque backdrop so the sticky cluster stays legible when it rides over
       message text while scrolling (the buttons themselves are translucent at
       rest — opacity 0.55 — so without this the icons blend into the prose). */
    background: var(--alice-surface);
    padding: 3px 5px;
    border-radius: 8px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12),
                0 0 0 1px var(--alice-border-tertiary);
}
.agent-msg-actions > button {
    position: static;
    float: none;
    margin: 0;
    top: auto;
}
/* Delete sits apart from the cluster and reads destructive. */
.agent-msg-actions > .agent-msg-action--delete {
    margin-left: 10px;
    color: var(--alice-danger, #c0392b);
    border-color: color-mix(in srgb, var(--alice-danger, #c0392b) 35%, var(--alice-border));
}
.agent-msg-actions > .agent-msg-action--delete:hover {
    color: #fff;
    background: var(--alice-danger, #c0392b);
    border-color: var(--alice-danger, #c0392b);
    opacity: 1;
}
/* Regenerate / Edit / Delete — canonical icon buttons in the top-right
   cluster, same affordance as .agent-msg-copy. */
.agent-msg-iconbtn {
    position: sticky;
    top: 6px;
    float: right;
    margin-left: 8px;
    width: 26px;
    height: 26px;
    padding: 0;
    background: var(--alice-surface);
    border: 1px solid var(--alice-border-tertiary);
    border-radius: 4px;
    color: var(--alice-text-secondary);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    opacity: 0.55;
    z-index: 2;
    transition: opacity .15s, color .15s, border-color .15s, background .15s;
}
.agent-msg-iconbtn svg { width: 14px; height: 14px; }
/* Re-analyze · FOO — a labeled value action on the data agent's answers. */
.agent-msg-foo {
    border: 1px solid var(--color-brand, var(--alice-blue));
    border-radius: 12px;
    background: transparent;
    color: var(--color-brand, var(--alice-blue));
    font-size: 0.72rem;
    font-weight: 600;
    line-height: 1.2;
    padding: 2px 9px;
    cursor: pointer;
    white-space: nowrap;
    align-self: center;
}
.agent-msg-foo:hover {
    background: var(--color-brand, var(--alice-blue));
    color: var(--color-brand-fg, #fff);
}
.agent-msg-iconbtn:hover {
    opacity: 1;
    color: var(--alice-blue);
    border-color: var(--alice-blue);
}
/* Delete is destructive — hint red on hover (X/trash reserved for delete). */
.agent-msg-iconbtn[aria-label^="Delete"]:hover {
    color: var(--alice-danger, #c0392b);
    border-color: var(--alice-danger, #c0392b);
}
/* While editing, the user bubble stretches full width for the editor. */
.agent-msg--user.agent-msg--editing {
    align-self: stretch;
    max-width: none;
    width: 100%;
}
/* ‹ k/n › branch switcher — small, inline at the bubble's bottom-left. */
.agent-branch-switch {
    clear: both;
    display: inline-flex;
    align-items: center;
    gap: 2px;
    margin-top: 6px;
    font-size: 0.72rem;
    color: var(--alice-text-secondary);
    user-select: none;
}
.agent-branch-nav {
    border: 1px solid var(--alice-border);
    background: var(--alice-surface);
    border-radius: 4px;
    min-width: 18px;
    height: 18px;
    line-height: 1;
    cursor: pointer;
    /* Explicit dark text: the button has a white background on every bubble
       (incl. the blue user bubble), so it must NOT inherit the bubble color
       or the ‹ › arrows go white-on-white (invisible until hover). */
    color: var(--alice-text-secondary);
    padding: 0 4px;
    font-weight: 700;
}
.agent-branch-nav:disabled { opacity: 0.35; cursor: default; }
.agent-branch-nav:not(:disabled):hover {
    border-color: var(--alice-blue, #2563eb);
    color: var(--alice-blue, #2563eb);
}
.agent-branch-count { padding: 0 4px; font-variant-numeric: tabular-nums; }
/* On the blue user bubble, the switcher count + timestamp must stay
   legible (the default gray is invisible over blue). The nav BUTTONS keep
   their own dark-on-white styling (above) — don't whiten them here. */
.agent-msg--user .agent-branch-count { color: rgba(255, 255, 255, 0.9); }
.agent-msg--user .agent-msg-time { color: rgba(255, 255, 255, 0.75); }
/* Inline edit-and-resubmit box — full-width, ≥4 lines, auto-grows
   (height managed in JS up to the space above the composer). */
.agent-edit-box { clear: both; margin-top: 8px; width: 100%; }
.agent-edit-input {
    width: 100%;
    box-sizing: border-box;
    min-height: 5.5em;            /* ≈4 lines */
    border: 1px solid var(--alice-border);
    border-radius: 6px;
    padding: 8px 10px;
    font: inherit;
    line-height: 1.4;
    resize: none;                 /* JS auto-grows it */
    overflow-y: hidden;
}
.agent-edit-actions { display: flex; gap: 8px; margin-top: 8px; }
.agent-msg--user .agent-msg-time {
    color: rgba(255, 255, 255, 0.65);
}

.agent-form {
    padding: 12px;
    border-top: 1px solid var(--alice-border);
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    /* Anchored at bottom of tab panel via flex parent (.agent-tab-panel
       is column flex, .agent-messages has flex:1). */
}

/* ── Per-round flaw cards (Phase A live FOO visibility) ─────────
   Each round opens with a header band and stacks one collapsible
   <details> per critic / reviewer / verifier / harmonizer that
   returns. End-of-round summary card appears under the stack. */
/* "See flow" link in an answer bubble's timestamp row — same small,
   secondary treatment as the date next to it. */
.agent-msg-seeflow {
    color: var(--color-brand, var(--alice-blue));
    text-decoration: none;
    font-weight: 600;
}
.agent-msg-seeflow:hover { text-decoration: underline; }
.agent-msg--user .agent-msg-seeflow { color: rgba(255, 255, 255, 0.9); }

/* Per-message accordion grouping the Flow rounds. Title links to chat. */
.agent-flow-msg-group { margin: 8px 0; }
.agent-flow-msg-head {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 5px 8px;
    background: var(--alice-bg);
    border: 1px solid var(--alice-border);
    border-radius: 6px;
    cursor: pointer;
    list-style: none;            /* hide native disclosure marker */
    font-size: 0.78rem;
}
.agent-flow-msg-head::-webkit-details-marker { display: none; }
.agent-flow-msg-chevron {
    flex-shrink: 0;
    color: var(--alice-text-secondary);
    transition: transform 0.15s ease;
}
.agent-flow-msg-group:not([open]) .agent-flow-msg-chevron {
    transform: rotate(-90deg);   /* points right when collapsed */
}
.agent-flow-msg-title {
    color: var(--color-brand, var(--alice-blue));
    font-weight: 700;
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.agent-flow-msg-title:hover { text-decoration: underline; }
/* Round boxes nested in a group sit slightly inset from the header. */
.agent-flow-msg-group > .agent-round-box { margin: 8px 0 8px 6px; }

/* Top-level "All messages: Collapse all · Expand all" control. */
.agent-flow-allmsgs-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 4px 8px 8px;
    font-size: 0.74rem;
    font-weight: 700;
    letter-spacing: .03em;
    text-transform: uppercase;
    color: var(--alice-text-secondary);
}
.agent-flow-allmsgs-actions { text-transform: none; font-weight: 500; }
.agent-flow-allmsgs-actions a {
    color: var(--color-brand, var(--alice-blue));
    text-decoration: none;
}
.agent-flow-allmsgs-actions a:hover { text-decoration: underline; }

/* Brief flash when jumping to a message (chat) or its flow group. */
@keyframes agentFlashFade {
    0%   { background-color: var(--color-brand, #185FA5); }
    100% { background-color: transparent; }
}
.agent-flow-flash > .agent-flow-msg-head,
.agent-flow-flash.agent-round-box,
.agent-msg-flash {
    animation: agentFlashFade 1.3s ease-out;
}

.agent-round-box {
    border: 1px solid var(--alice-border);
    border-radius: 6px;
    margin: 8px 0;
    background: var(--alice-bg);
}
.agent-round-head {
    padding: 6px 10px;
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--alice-text-secondary);
    border-bottom: 1px solid var(--alice-border);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}
.agent-round-actions {
    text-transform: none;
    font-weight: 500;
    letter-spacing: 0;
    font-size: 0.72rem;
}
.agent-round-actions a {
    color: var(--alice-blue);
    text-decoration: none;
}
.agent-round-actions a:hover { text-decoration: underline; }
.agent-flaws-card {
    border-bottom: 1px solid var(--alice-border);
    padding: 6px 10px;
}
.agent-flaws-card:last-child { border-bottom: none; }
.agent-flaws-card[open] { background: rgba(0,0,0,0.015); }
.agent-flaws-head {
    cursor: pointer;
    font-size: 0.85rem;
    list-style: none;
}
.agent-flaws-head::-webkit-details-marker { display: none; }
.agent-flaws-badge {
    display: inline-block;
    padding: 1px 6px;
    margin-left: 4px;
    border-radius: 3px;
    font-size: 0.7rem;
    font-weight: 600;
}
.agent-flaws-badge--substantive {
    background: rgba(200, 0, 0, 0.10); color: #b00;
}
.agent-flaws-badge--minor {
    background: rgba(245, 158, 11, 0.12); color: #b45309;
}
.agent-flaws-badge--ok {
    background: rgba(22, 163, 74, 0.10); color: #16a34a;
}
.agent-flaws-body {
    margin-top: 6px;
    font-size: 0.82rem;
    line-height: 1.45;
}
.agent-flaws-summary { margin: 4px 0 8px 0; }
.agent-flaws-section { margin: 4px 0; }
.agent-flaws-section-head {
    font-size: 0.72rem;
    font-weight: 700;
    color: var(--alice-text-secondary);
    text-transform: uppercase;
    letter-spacing: .04em;
}
.agent-flaws-section ul { margin: 4px 0 4px 16px; padding: 0; }
/* Prose card body — reasoner / writer / planner / etc. The
   server has already truncated; we just need to wrap nicely. */
.agent-prose {
    margin: 4px 0 0 0;
    padding: 6px 8px;
    font-family: inherit;
    font-size: 0.82rem;
    line-height: 1.45;
    white-space: pre-wrap;
    word-break: break-word;
    background: var(--alice-bg);
    border: 1px solid var(--alice-border);
    border-radius: 4px;
    max-height: 320px;
    overflow-y: auto;
}
/* Rendered-Markdown variant of a Flow prose card (reasoner output):
   formatted like the final answer instead of raw **bold**/## text. */
.agent-prose--md { white-space: normal; }
.agent-prose--md > :first-child { margin-top: 0; }
.agent-prose--md > :last-child  { margin-bottom: 0; }
.agent-prose--md p { margin: 0 0 0.5em 0; }
.agent-prose--md h1,
.agent-prose--md h2,
.agent-prose--md h3,
.agent-prose--md h4 {
    margin: 0.6em 0 0.3em 0;
    line-height: 1.25;
    font-weight: 700;
}
.agent-prose--md h1 { font-size: 1.05rem; }
.agent-prose--md h2 { font-size: 0.98rem; }
.agent-prose--md h3 { font-size: 0.9rem; }
.agent-prose--md h4 { font-size: 0.84rem; }
.agent-prose--md ul,
.agent-prose--md ol { margin: 0.3em 0 0.5em 0; padding-left: 1.3em; }
.agent-prose--md li { margin: 0.15em 0; }
.agent-prose--md code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.9em;
    background: var(--alice-surface);
    border: 1px solid var(--alice-border);
    border-radius: 3px;
    padding: 0 3px;
}
.agent-prose--md pre {
    background: var(--alice-surface);
    border: 1px solid var(--alice-border);
    border-radius: 4px;
    padding: 6px 8px;
    overflow-x: auto;
    margin: 0.4em 0;
}
.agent-prose--md pre code { background: none; border: none; padding: 0; }
.agent-prose--md table { border-collapse: collapse; margin: 0.4em 0; font-size: 0.95em; }
.agent-prose--md th,
.agent-prose--md td {
    border: 1px solid var(--alice-border);
    padding: 3px 7px;
    text-align: left;
}
.agent-prose--md blockquote {
    margin: 0.4em 0;
    padding: 0 0 0 0.7em;
    border-left: 3px solid var(--alice-border);
    color: var(--alice-text-secondary);
}
.agent-prose--md a { color: var(--color-brand, var(--alice-blue)); }

.agent-flaws-flag {
    margin: 6px 0 0 0;
    color: var(--alice-warning);
    font-weight: 600;
    font-size: 0.78rem;
}
.agent-round-summary {
    padding: 6px 10px;
    font-size: 0.82rem;
    background: var(--alice-surface);
    border-top: 1px solid var(--alice-border);
}

/* Cancel button shares the send-btn slot but signals destructive
   action with a red tint. Visible only while a /agent/stream call
   is in flight (toggled by JS on submit/finish). */
.agent-cancel-btn {
    background: rgba(176, 0, 0, 0.08);
    border-color: rgba(176, 0, 0, 0.35);
    color: #b00;
}
.agent-cancel-btn:hover {
    background: rgba(176, 0, 0, 0.18);
}

/* Opus-level AI Agent shortcut on /opus/{id}. Sits next to the
   chorus selector in the title row. Links to /opus/{id}/chat. */



/* ── Per-call modifiers strip (single/multi + reasoning slider) ── */
/* HUDO disclosure strip — sits under the per-call modifiers.
   The checkbox controls DISCLOSURE of the conversation's published id and
   record download, never whether the chain is recorded. */
.agent-hudo__toggle {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    font-size: .82rem;
    cursor: pointer;
    color: var(--alice-text-secondary);
    margin-left: auto;          /* the right end of the modifiers row */
    white-space: nowrap;
}
.agent-hudo__detail {
    margin-top: .4rem;
    font-size: .78rem;
    color: var(--alice-text-secondary);
}
.agent-hudo__detail.hidden { display: none; }
.agent-hudo__id code { word-break: break-all; font-size: .72rem; }
.agent-hudo__dl { display: inline-block; margin-top: .3rem; }

.agent-modifiers {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-top: 6px;
    flex-wrap: wrap;
    font-size: 0.78rem;
    color: var(--alice-text-secondary);
}
.agent-mod-toggle {
    display: inline-flex;
    border: 1px solid var(--alice-border);
    border-radius: 999px;
    overflow: hidden;
}
.agent-mod-btn {
    background: transparent;
    border: none;
    padding: 3px 12px;
    font-size: 0.78rem;
    color: var(--alice-text-secondary);
    cursor: pointer;
}
.agent-mod-btn.active {
    /* Primary-button scheme — legible in dark mode (see .agent-msg--user). */
    background: var(--color-brand, var(--alice-blue));
    color: var(--color-brand-fg, #fff);
}
.agent-mod-slider {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}
.agent-mod-label {
    font-weight: 600;
}
.agent-mod-slider input[type="range"] {
    width: 110px;
}
.agent-mod-level {
    display: inline-block;
    min-width: 48px;
    font-variant: small-caps;
    letter-spacing: .04em;
}

/* Wrapper establishes a positioning context for the floating send
   button. The wrapper's height grows with the textarea as the learner
   drags the resize grip, so the button tracks the textarea's bottom
   edge automatically. */
.agent-input-wrap {
    position: relative;
    display: block;
    width: 100%;
}

.agent-input {
    width: 100%;
    font-size: 0.9rem;
    /* Native resize grip would conflict with the floating send button
       hugging the right edge; panel height is driven by the side-panel
       drag handle instead. */
    resize: none;
    min-height: 96px;
    max-height: 40vh;
    line-height: 1.45;
    box-sizing: border-box;
    /* Right gutter reserves space for the floating send button so the
       last line of text never collides with it. */
    padding-right: 44px;
}

.agent-send-btn {
    position: absolute;
    /* 10 px from the textarea's right edge, tucked against the bottom. */
    right: 10px;
    bottom: 8px;
    width: 30px;
    height: 30px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    /* Primary-button scheme so the send button stays legible in dark mode
       (--alice-blue flips light there). */
    background: var(--color-brand, var(--alice-blue));
    color: var(--color-brand-fg, #fff);
    border: none;
    border-radius: 50%;
    cursor: pointer;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
    transition: background 0.12s ease, transform 0.12s ease;
}
.agent-send-btn:hover {
    filter: brightness(1.08);
    transform: translateY(-1px);
}
.agent-send-btn:active {
    transform: translateY(0);
}
.agent-send-btn:focus-visible {
    outline: 2px solid var(--alice-blue);
    outline-offset: 2px;
}
.agent-send-btn svg {
    margin-left: -1px;  /* optical nudge so the arrow looks centered */
}

@media (max-width: 600px) {
    .agent-panel { width: 100vw; }
    .agent-toggle-label { display: none; }
}

/* ── MAX agent panel: tabs, status line, history ────────────── */

.agent-tab-bar {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 0 8px 0 12px;
    border-bottom: 1px solid var(--alice-border);
    flex-shrink: 0;
}
/* The tab buttons scroll horizontally when the panel is too narrow to
   show them all; the +/X actions stay pinned and never get clipped. */
.agent-tab-list {
    display: flex;
    align-items: center;
    gap: 4px;
    flex: 1 1 auto;
    min-width: 0;            /* allow the flex item to shrink below content */
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: thin;   /* a slim scrollbar only when it overflows */
}
.agent-tab-list::-webkit-scrollbar { height: 4px; }
.agent-tab-actions {
    display: flex;
    align-items: center;
    gap: 4px;
    flex: 0 0 auto;          /* never shrink — always reachable */
    margin-left: 4px;
}
/* Capability-driven tab hiding (single-agent → no Flow; no verbs → no
   Lexicon). Distinct from the panel-section .hidden toggle. */
.agent-tab-list .pagina-tab-btn.agent-tab-hidden { display: none; }
.agent-tab-panel {
    display: flex;
    flex-direction: column;
    flex: 1;
    overflow: hidden;
}
.agent-tab-panel.hidden { display: none; }

/* ── Conversation tree (branch map) ─────────────────────────────── */
.agent-tree-toolbar {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 10px;
    border-bottom: 1px solid var(--alice-border);
    flex-shrink: 0;
}
.agent-tree-hint {
    font-size: 0.74rem;
    color: var(--alice-text-secondary);
}
.agent-tree-expand-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px; height: 24px;
    border: 1px solid var(--alice-border);
    border-radius: 5px;
    background: var(--alice-surface);
    color: var(--alice-text-secondary);
    cursor: pointer;
    flex-shrink: 0;
}
.agent-tree-expand-btn:hover {
    color: var(--color-brand, var(--alice-blue));
    border-color: var(--color-brand, var(--alice-blue));
}
.agent-tree-cy {
    flex: 1;
    min-height: 320px;
    width: 100%;
    background: var(--alice-bg);
}
/* Expand-to-full-screen: lift the whole Tree section out as an overlay. */
.agent-tab-tree.agent-tree-fullscreen,
#agent-tab-tree.agent-tree-fullscreen {
    position: fixed;
    inset: 0;
    z-index: 4000;
    background: var(--alice-surface);
    box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.35);
}
#agent-tab-tree.agent-tree-fullscreen .agent-tree-cy { min-height: 0; }

/* ── Generic asset full-screen overlay (SPECS49 Part 06, D6) ──────────
   Reusable across every asset type incl. cross-origin/sandboxed iframes:
   the card's content subtree is MOVED into .asset-fs-body and restored on
   exit (no native Fullscreen API → works for sandboxed embeds the parent
   cannot put fullscreen). Same z-index band as .agent-tree-fullscreen
   (above) for coherent stacking. Theme-aware (CSS vars), reduced-motion. */
.asset-fs-overlay {
    position: fixed;
    inset: 0;
    z-index: 4000;
    display: flex;
    flex-direction: column;
    background: var(--alice-surface, var(--color-surface, #fff));
    box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.35);
}
.asset-fs-bar {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.5rem;
    padding: 0.4rem 0.6rem;
    border-bottom: 1px solid var(--alice-border, var(--color-border, #ddd));
    background: var(--alice-bg, var(--color-bg, #fafafa));
    flex: 0 0 auto;
}
.asset-fs-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
    display: flex;            /* let a single iframe/img fill the area */
}
/* A moved iframe/img/canvas (or the wrapper) fills the body when sole child. */
.asset-fs-body > iframe,
.asset-fs-body > img,
.asset-fs-body > canvas,
.asset-fs-body > .asset-fs-content {
    flex: 1 1 auto;
    width: 100%;
    height: 100%;
    max-width: 100%;
    border: none;
    object-fit: contain;      /* images letterbox, not crop */
}
/* A moved markdown/text wrapper scrolls within the body. */
.asset-fs-body > .asset-fs-content {
    overflow: auto;
    object-fit: unset;
}
/* The REPO embed target is a <div class="repo-embed"> wrapping the browser
   iframe — when the overlay fallback is used (native fullscreen unavailable),
   size both so the editor fills the whole area instead of its inline 420px. */
.asset-fs-body > .repo-embed {
    flex: 1 1 auto;
    width: 100%;
    height: 100%;
    display: flex;
}
.asset-fs-body > .repo-embed > iframe {
    flex: 1 1 auto;
    width: 100% !important;
    height: 100% !important;
    border: none !important;
    border-radius: 0 !important;
}
.asset-fs-close {
    cursor: pointer;        /* reuses .btn-secondary look; explicit fallback */
}
/* ── True OS fullscreen (presentation mode, AliceFullscreen.openNative) ──
   When a same-origin embed (PDF, REPO browser) is put into native fullscreen
   via element.requestFullscreen(), the element keeps its in-page sizing (e.g.
   an iframe pinned to height:420px). These rules make the fullscreened element
   and any iframe/canvas/img/video it holds fill the whole screen, with a
   themed backdrop and no leftover border/radius. Generic — no per-type CSS. */
:fullscreen,
:-webkit-full-screen {
    /* Pin the fullscreen element to the screen explicitly. Relying on the UA's
       implicit sizing + height:100% was resolving against a non-fixed ancestor
       and collapsing to the top portion (black below); position:fixed + inset:0
       fills the fullscreen viewport regardless. The single embedded child is a
       flex column item so it stretches to the full height. */
    position: fixed !important;
    inset: 0 !important;
    width: 100% !important;
    height: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
    display: flex !important;
    flex-direction: column !important;
    background: var(--alice-bg, #fff);
}
:fullscreen iframe,
:fullscreen canvas,
:fullscreen img,
:fullscreen video,
:-webkit-full-screen iframe,
:-webkit-full-screen canvas,
:-webkit-full-screen img,
:-webkit-full-screen video {
    flex: 1 1 auto !important;
    width: 100% !important;
    height: 100% !important;
    min-height: 0 !important;
    border: 0 !important;
    border-radius: 0 !important;
    object-fit: contain;
}

/* Body scroll lock while an overlay is open. */
body.asset-fs-lock {
    overflow: hidden !important;
}
@media (prefers-reduced-motion: no-preference) {
    .asset-fs-overlay { transition: opacity 120ms ease; }
}

/* ── Opus map: node-gesture key chip (used in the palette's gesture row) ── */
.legend-kbd {
    font-family: inherit;
    font-size: 10px;
    font-weight: 600;
    color: var(--alice-text);
    background: var(--alice-bg);
    border: 1px solid var(--alice-border);
    border-bottom-width: 2px;
    border-radius: 4px;
    padding: 0 5px;
    margin-right: 1px;
}

/* ── Opus map: right-click rename modal ─────────────────────────── */
.lexia-rename-overlay {
    position: fixed;
    inset: 0;
    z-index: 5000;
    background: rgba(0, 0, 0, 0.4);
    display: flex;
    align-items: center;
    justify-content: center;
}
.lexia-rename-overlay[hidden] { display: none; }
.lexia-rename-modal {
    width: min(440px, 92vw);
    background: var(--alice-surface);
    border: 1px solid var(--alice-border);
    border-radius: 10px;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
    padding: 18px 20px;
}
.lexia-rename-modal h3 { margin: 0 0 2px; font-size: 1.05rem; }
.lexia-rename-sub {
    margin: 0 0 12px;
    font-size: 0.78rem;
    color: var(--alice-text-secondary);
}
.lexia-rename-modal input {
    width: 100%;
    box-sizing: border-box;
    padding: 7px 10px;
    font-size: 0.92rem;
    border: 1px solid var(--alice-border);
    border-radius: 6px;
    background: var(--alice-surface);
    color: var(--alice-text);
}
.lexia-rename-modal input:focus {
    outline: none;
    border-color: var(--color-brand, var(--alice-blue));
    box-shadow: 0 0 0 2px rgba(24, 95, 165, 0.2);
}
.lexia-rename-err {
    margin-top: 8px;
    font-size: 0.8rem;
    color: var(--color-danger, #c0392b);
}
.lexia-rename-err[hidden] { display: none; }
.lexia-rename-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 16px;
}

.agent-status {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
}
.agent-status-head {
    display: flex;
    align-items: center;
    gap: 8px;
}
/* Ephemeral per-agent activity log inside the "thinking" bubble: one line per
   agent as it runs (time · name · verb · round), mutating running → done /
   failed. Removed with the whole bubble when the final answer lands. */
.agent-activity {
    list-style: none;
    margin: 0;
    padding: 0 0 0 20px;   /* align under the header spinner */
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.agent-activity-item {
    display: flex;
    align-items: baseline;
    gap: 6px;
    font-size: 0.78rem;
    line-height: 1.35;
    color: var(--alice-text-secondary);
}
.agent-activity-time {
    font-variant-numeric: tabular-nums;
    opacity: 0.7;
    flex-shrink: 0;
}
.agent-activity-icon {
    flex-shrink: 0;
    width: 1em;
    text-align: center;
    font-weight: 700;
}
.agent-activity-item.is-running .agent-activity-icon::before {
    content: '';
    display: inline-block;
    width: 9px; height: 9px;
    border: 2px solid var(--alice-border);
    border-top-color: var(--alice-blue);
    border-radius: 50%;
    animation: agent-spin 0.9s linear infinite;
    vertical-align: middle;
}
.agent-activity-item.is-done   { color: var(--alice-text); }
.agent-activity-item.is-done   .agent-activity-icon { color: var(--alice-teal); }
.agent-activity-item.is-failed { color: var(--alice-danger); }
.agent-activity-item.is-failed .agent-activity-icon { color: var(--alice-danger); }
.agent-activity-name { font-weight: 600; color: var(--alice-text); }
.agent-activity-item.is-failed .agent-activity-name { color: inherit; }
.agent-activity-verb, .agent-activity-round { opacity: 0.85; }
.agent-activity-detail { opacity: 0.9; }
.agent-spinner {
    width: 12px;
    height: 12px;
    border: 2px solid var(--alice-border);
    border-top-color: var(--alice-blue);
    border-radius: 50%;
    animation: agent-spin 0.9s linear infinite;
    display: inline-block;
    flex-shrink: 0;
}
@keyframes agent-spin { to { transform: rotate(360deg); } }
.agent-status-round {
    color: var(--alice-text-secondary);
    font-size: 0.78rem;
}

/* Auto-apply chip: a small badge attached to an assistant bubble
   after the orchestrator's "ops" proposal has been auto-applied via
   /agent/apply. Colour-coded by outcome: ok=teal, err=red,
   no_purpose / no_ops=neutral. The bubble itself still shows the
   raw proposal JSON for audit + copy. */
.agent-msg-apply-chip {
    display: inline-block;
    margin-top: 0.5rem;
    padding: 0.2rem 0.55rem;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 600;
    line-height: 1.3;
    border: 1px solid var(--alice-border-tertiary);
    background: var(--alice-gray-tint);
    color: var(--alice-text-secondary);
}



/* Editable proposal card — appended to assistant bubbles that
   carry a proposal_kind="ops" payload. Holds a textarea with the
   raw JSON so the admin can tweak titles / body / type before
   firing /agent/apply. Apply / Discard buttons sit below. */
.agent-msg-proposal-card {
    margin-top: 0.6rem;
    padding: 0.55rem 0.65rem;
    border: 1px solid var(--alice-border-tertiary, #ccc);
    border-left: 3px solid var(--alice-blue, #2563eb);
    border-radius: 6px;
    background: var(--alice-bg, #fafafa);
}
.agent-msg-proposal-head {
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--alice-text, #333);
    margin-bottom: 0.35rem;
}
.agent-msg-proposal-title {
    display: inline-block;
}
.agent-msg-proposal-json {
    width: 100%;
    box-sizing: border-box;
    font-family: ui-monospace, SFMono-Regular, 'Consolas', monospace;
    font-size: 0.75rem;
    line-height: 1.4;
    padding: 6px 8px;
    border: 1px solid var(--alice-border-tertiary, #ccc);
    border-radius: 4px;
    background: #fff;
    color: var(--alice-text, #333);
    resize: vertical;
    min-height: 90px;
    max-height: 480px;
}
.agent-msg-proposal-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 0.45rem;
}
.agent-msg-proposal-btn {
    padding: 4px 12px;
    border-radius: 4px;
    font-size: 0.78rem;
    font-weight: 500;
    cursor: pointer;
    border: 1px solid transparent;
}
.agent-msg-proposal-btn--apply {
    background: var(--color-brand, var(--alice-blue));
    color: var(--color-brand-fg, #fff);
    border-color: var(--color-brand, var(--alice-blue));
}
.agent-msg-proposal-btn--apply:hover:not(:disabled) {
    filter: brightness(1.05);
}
.agent-msg-proposal-btn--apply:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}
.agent-msg-proposal-btn--discard {
    background: transparent;
    color: var(--alice-text-secondary, #777);
    border-color: var(--alice-border, #ddd);
}
.agent-msg-proposal-btn--discard:hover:not(:disabled) {
    background: var(--alice-bg-hover, rgba(0,0,0,0.04));
}
.agent-msg-proposal-status {
    font-size: 0.75rem;
    color: var(--alice-text-secondary, #777);
}
.agent-msg-proposal-status--ok {
    color: var(--alice-teal, #14b8a6);
    font-weight: 500;
}
.agent-msg-proposal-status--err {
    color: #922;
    font-weight: 500;
}

/* Copy-to-clipboard button on every assistant bubble. Pinned to the
   top-right corner of .agent-msg--agent. Quiet by default, hover
   brightens; flashes blue+check briefly on successful copy.
   `position: sticky` (with float:right) keeps the button visible at
   the top of the scrolling area while a tall bubble is being read.
   The button only leaves view when the bubble's bottom scrolls past
   the top of .agent-messages. */
.agent-msg-copy {
    position: sticky;
    top: 6px;
    float: right;
    margin-left: 8px;
    width: 26px;
    height: 26px;
    padding: 0;
    background: var(--alice-surface);
    border: 1px solid var(--alice-border-tertiary);
    border-radius: 4px;
    color: var(--alice-text-secondary);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    opacity: 0.55;
    z-index: 2;
    transition: opacity .15s, color .15s, border-color .15s, background .15s;
}
.agent-msg-copy:hover {
    opacity: 1;
    color: var(--alice-blue);
    border-color: var(--alice-blue);
}
.agent-msg-copy--ok {
    opacity: 1;
    color: var(--alice-surface);
    background: var(--alice-teal);
    border-color: var(--alice-teal);
}
.agent-msg-copy svg { display: block; }

.agent-msg-chip {
    margin-top: 6px;
    padding: 2px 8px;
    font-size: 0.72rem;
    color: var(--alice-text-secondary);
    background: transparent;
    border-top: 1px dashed var(--alice-border);
    padding-top: 6px;
}

.agent-msg .lexia-link {
    color: var(--alice-blue);
    text-decoration: underline;
}

.agent-config-body {
    flex: 1;
    overflow-y: auto;
    padding: 12px;
}
.agent-config-row {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 10px;
}
.agent-config-label {
    width: 110px;
    font-size: 0.8rem;
    color: var(--alice-text-secondary);
    flex-shrink: 0;
}
.agent-config-label--link {
    color: var(--link-color);
    text-decoration: none;
    cursor: pointer;
}
.agent-config-label--link:hover { text-decoration: underline; }

/* Friendly-redirect banner shown on the home page when /opus/{id}
   bounces a user back from an inaccessible/missing course. Tinted
   callout — themes via existing CSS variables. */
.home-notice {
    margin: 0 0 1rem;
    padding: 12px 16px;
    border: 1px solid var(--alice-border);
    border-left: 4px solid var(--alice-warning);
    border-radius: 6px;
    background: var(--alice-amber-tint);
    color: var(--alice-text);
    font-size: 0.92rem;
    line-height: 1.5;
}
.home-notice[data-kind="opus_missing"] {
    border-left-color: var(--alice-gray);
    background: var(--alice-gray-tint);
}

/* Friendly error page — used by main.py's global HTTPException
   handler when a browser navigation hits a 4xx/5xx that wasn't
   redirected. Big status code as a visual cue, soft message,
   two-button action row. Themes via existing variables. */
.error-page {
    max-width: 640px;
    margin: 4rem auto;
    padding: 2.5rem 2rem;
    text-align: center;
}
.error-page__code {
    font-size: 4rem;
    font-weight: 700;
    letter-spacing: -0.04em;
    color: var(--alice-text-tertiary);
    line-height: 1;
    margin-bottom: 0.75rem;
}
.error-page__title {
    margin: 0 0 0.5rem;
    font-size: 1.6rem;
}
.error-page__message {
    margin: 0 auto 0.5rem;
    max-width: 480px;
    color: var(--alice-text-secondary);
    line-height: 1.55;
}
.error-page__hint {
    margin: 0 auto 1.5rem;
    max-width: 480px;
    font-size: 0.88rem;
    color: var(--alice-text-tertiary);
    font-style: italic;
}
.error-page__actions {
    display: flex;
    gap: 0.5rem;
    justify-content: center;
    flex-wrap: wrap;
    margin-top: 1.25rem;
}

/* Grade page heading row — title on the left, nav pills on the
   right. Pills are small rounded outlined links that don't compete
   with the H1 visually but stay one click away from the two places
   an assessor typically wants next: the roster (to pick a different
   student) or the inbox (to pick a different submission). */
.grade-heading-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}
.grade-heading {
    margin: 0;
}
.grade-heading-pills {
    display: inline-flex;
    gap: 8px;
    flex-wrap: wrap;
}
.grade-pill {
    display: inline-block;
    padding: 4px 12px;
    border: 1px solid var(--alice-border-strong, var(--alice-border));
    border-radius: 999px;
    background: var(--alice-surface);
    color: var(--link-color);
    font-size: 0.82rem;
    line-height: 1.4;
    text-decoration: none;
    transition: border-color 0.15s ease, background 0.15s ease;
}
.grade-pill:hover {
    border-color: var(--alice-blue);
    background: var(--alice-blue-tint);
    text-decoration: none;
}
.grade-pill:focus-visible {
    outline: 2px solid var(--alice-focus, var(--alice-blue));
    outline-offset: 2px;
}

.agent-config-row .form-select,
.agent-config-row .form-input {
    flex: 1;
    min-width: 0;
}
.agent-config-orphan {
    margin: 10px 0;
    padding: 8px 12px;
    background: rgba(217,119,6,0.12);
    color: #b45309;
    border: 1px solid rgba(217,119,6,0.25);
    border-radius: 6px;
    font-size: 0.78rem;
}
.agent-config-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px solid var(--alice-border);
}
.agent-config-header {
    padding: 8px 10px;
    margin-bottom: 10px;
    background: var(--alice-bg);
    border: 1px solid var(--alice-border);
    border-radius: 6px;
    font-size: 0.82rem;
    color: var(--alice-text);
}
.agent-config-status { font-size: 0.78rem; color: var(--alice-text-secondary); }
.agent-config-status.ok { color: #16a34a; }
.agent-config-status.err { color: #b00; }








/* ── Flow tab — agent execution graph + per-step cards ─────────
   :not(.hidden) qualifier is essential: an ID selector outranks
   the .hidden class on specificity, so without it the Flow tab
   would render its toolbar/graph/steps in EVERY tab. With it,
   `display: flex` only applies when the tab is the active one
   (no .hidden class), letting the default .hidden { display:none }
   actually hide it. */
#agent-tab-flow:not(.hidden) {
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.agent-flow-toolbar {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    border-bottom: 1px solid var(--alice-border);
    flex-shrink: 0;
    font-size: 0.78rem;
}
.agent-flow-mode-pill {
    padding: 3px 10px;
    border-radius: 10px;
    font-weight: 600;
    font-size: 0.72rem;
    letter-spacing: 0.3px;
    background: var(--alice-surface);
    border: 1px solid var(--alice-border);
    color: var(--alice-text-secondary);
}
.agent-flow-mode-pill[data-mode="live"] {
    background: var(--alice-amber-tint);
    border-color: var(--alice-amber);
    color: var(--alice-warning);
}
.agent-flow-mode-pill[data-mode="history"] {
    background: var(--alice-teal-tint);
    border-color: var(--alice-teal);
    color: var(--alice-teal);
}
.agent-flow-follow {
    display: flex;
    align-items: center;
    gap: 4px;
    color: var(--alice-text-secondary);
    cursor: pointer;
}
.agent-flow-replay-controls {
    display: flex;
    align-items: center;
    gap: 4px;
}
.agent-flow-replay-controls.hidden { display: none; }
.agent-flow-ctrl-btn {
    width: 28px; height: 24px;
    padding: 0;
    background: var(--alice-surface);
    border: 1px solid var(--alice-border-tertiary);
    border-radius: 4px;
    color: var(--alice-text-secondary);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: color .12s, border-color .12s, background .12s;
}
.agent-flow-ctrl-btn:hover {
    color: var(--alice-blue);
    border-color: var(--alice-blue);
}
.agent-flow-ctrl-btn:active {
    background: var(--alice-bg);
}
.agent-flow-follow--disabled {
    opacity: 0.5;
    cursor: not-allowed;
}
.agent-flow-follow--disabled input { pointer-events: none; }
.agent-flow-step-counter {
    font-variant-numeric: tabular-nums;
    color: var(--alice-text-secondary);
    font-size: 0.72rem;
}
/* Accordion header that shows / hides the template graph, freeing
   vertical room for the per-agent answer cards when collapsed. */
.agent-flow-graph-toggle {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 6px;
    width: 100%;
    padding: 5px 12px;
    border: none;
    border-bottom: 1px solid var(--alice-border);
    background: var(--alice-bg);
    color: var(--alice-text-secondary);
    font-size: 0.74rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    cursor: pointer;
    text-align: left;
}
.agent-flow-graph-toggle:hover { color: var(--alice-text); }
.agent-flow-graph-chevron {
    transition: transform 0.15s ease;
    flex-shrink: 0;
}
.agent-flow-graph-toggle--collapsed .agent-flow-graph-chevron {
    transform: rotate(-90deg);   /* points right when the graph is hidden */
}
.agent-flow-graph {
    flex-shrink: 0;
    border-bottom: 1px solid var(--alice-border);
    background: var(--alice-bg);
}
.agent-flow-graph--collapsed { display: none; }
.agent-flow-cy {
    width: 100%;
    height: 240px;
}
.agent-flow-steps {
    flex: 1;
    overflow-y: auto;
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
/* Per-step highlight while scrubbing in history mode. */
.agent-flow-current-step {
    outline: 2px solid var(--alice-blue);
    outline-offset: 2px;
    background: rgba(59,130,246,0.04);
}
.agent-flow-dim-step { opacity: 0.55; }

/* Cards in the Flow tab use the native <details> element with `open`
   set by default (see _agentBuildCardElement). Click the card summary
   to collapse any individual card; per-round Collapse All / Expand
   All links toggle every card in that round. No custom CSS clamp
   needed — native details handles expand/collapse. */

/* Scroll-to-top button on long assistant bubbles. Mirrors the
   sticky-positioning on .agent-msg-copy so both ride the top of the
   bubble during scroll. */
.agent-msg-scrolltop {
    position: sticky;
    top: 6px;
    float: right;
    margin-left: 4px;
    width: 26px;
    height: 26px;
    padding: 0;
    background: var(--alice-surface);
    border: 1px solid var(--alice-border-tertiary);
    border-radius: 4px;
    color: var(--alice-text-secondary);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    opacity: 0.55;
    z-index: 2;
    transition: opacity .15s, color .15s, border-color .15s;
}
.agent-msg-scrolltop:hover {
    opacity: 1;
    color: var(--alice-blue);
    border-color: var(--alice-blue);
}

/* ── Structured Raw tab + cross-tab jump highlight ──────────── */
.agent-raw-section {
    padding: 8px 12px;
    border-bottom: 1px solid var(--alice-border);
}
.agent-raw-section h4 {
    margin: 0 0 6px 0;
    font-size: 0.78rem;
    color: var(--alice-text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}
.agent-raw-entry {
    margin: 4px 0 4px 12px;
    border-left: 2px solid var(--alice-border);
    padding-left: 8px;
    transition: background-color .25s linear;
}
.agent-raw-entry summary {
    cursor: pointer;
    font-size: 0.78rem;
    color: var(--alice-text);
    padding: 2px 0;
}
.agent-raw-entry pre {
    margin: 4px 0 8px 0;
    padding: 6px 8px;
    background: var(--alice-bg);
    border: 1px solid var(--alice-border);
    border-radius: 4px;
    font-size: 0.72rem;
    overflow-x: auto;
    white-space: pre-wrap;
}
.agent-raw-entry--card { border-left-color: var(--alice-amber); }
.agent-raw-entry--summary { border-left-color: var(--alice-teal); }
.agent-raw-entry--proposal { border-left-color: var(--alice-blue); }
.agent-raw-highlight {
    background-color: var(--alice-amber-tint);
}
.agent-card-raw-link {
    margin-left: 8px;
    font-size: 0.7rem;
    color: var(--alice-blue);
    text-decoration: none;
}
.agent-card-raw-link:hover { text-decoration: underline; }

/* Per-step timestamp in the Flow card header. Floats right so it
   never crowds the agent name + badge cluster on the left. Same
   muted treatment as the bubble timestamps. */
.agent-flow-step-time {
    float: right;
    margin-left: 8px;
    font-size: 0.7rem;
    color: var(--alice-text-secondary);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    letter-spacing: 0.02em;
    user-select: none;
}

/* PLATFORM 017 — per-node token usage (↑ sent / ↓ received) beside the step time;
   same unobtrusive monospace treatment as .agent-flow-step-time. */
.agent-flow-step-tokens {
    float: right;
    margin-left: 8px;
    font-size: 0.7rem;
    color: var(--alice-text-secondary);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    letter-spacing: 0.02em;
    user-select: none;
    opacity: 0.85;
}
/* Per-user model substitution indicator (grants): 'ran <model>' with a tooltip. */
.agent-flow-step-sub {
    float: right;
    margin-left: 8px;
    font-size: 0.7rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    color: var(--alice-amber, #d97706);
    user-select: none;
    cursor: help;
}

.agent-raw-json {
    flex: 1;
    overflow: auto;
    margin: 0;
    padding: 12px;
    font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
    font-size: 0.75rem;
    line-height: 1.5;
    white-space: pre-wrap;
    word-break: break-all;
    color: var(--alice-text);
    background: var(--alice-bg);
    border: none;
}

/* Sticky toolbar at the top of the Raw tab — mirrors the Flow tab's
   replay controls in shape (small text buttons, separator dot) so
   the two panes feel like a pair. */
.agent-raw-toolbar {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    background: var(--alice-bg);
    border-bottom: 1px solid var(--alice-border);
    font-size: 0.78rem;
}
.agent-raw-toolbtn {
    background: transparent;
    border: 1px solid var(--alice-border);
    border-radius: 4px;
    padding: 3px 8px;
    font-size: 0.78rem;
    color: var(--alice-text);
    cursor: pointer;
    line-height: 1.2;
}
.agent-raw-toolbtn:hover {
    background: var(--alice-bg-hover, rgba(0,0,0,0.04));
}
.agent-raw-toolsep {
    color: var(--alice-text-secondary);
    margin: 0 2px;
}

.agent-history-list {
    flex: 1;
    overflow-y: auto;
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.agent-history-empty {
    color: var(--alice-text-secondary);
    font-style: italic;
    padding: 12px;
    text-align: center;
}

/* ── Thread attachments: + button, pending pills, drop overlay,
   Files tab, author chip, message attachments list ──────────── */

/* + button to the left of the textarea */
.agent-attach-btn {
    flex-shrink: 0;
    width: 28px; height: 28px;
    border: 1px solid var(--alice-border);
    border-radius: 6px;
    background: var(--alice-surface);
    color: var(--alice-text-secondary);
    display: inline-flex; align-items: center; justify-content: center;
    cursor: pointer; align-self: flex-end;
    margin-right: 4px; margin-bottom: 6px;
}
.agent-attach-btn:hover {
    border-color: var(--alice-blue, #2563eb);
    color: var(--alice-blue, #2563eb);
    background: var(--alice-blue-soft, #e7f0ff);
}
.agent-attach-btn:disabled {
    opacity: 0.4; cursor: not-allowed;
}

/* Primary (blue) variant — used on the modifiers row next to the
   Multi/Single toggle. Reads as the affordance for adding files
   without competing visually with the Send button. */
.agent-attach-btn--primary {
    /* Brand-blue (primary-button) scheme so it matches Send / Multi and
       stays legible in dark mode. */
    background: var(--color-brand, var(--alice-blue));
    border-color: var(--color-brand, var(--alice-blue));
    color: var(--color-brand-fg, #fff);
    align-self: center;
    margin-right: 4px; margin-bottom: 0;
}
.agent-attach-btn--primary:hover {
    background: #1d4ed8;
    border-color: #1d4ed8;
    color: #fff;
}
.agent-attach-btn--primary:focus-visible {
    outline: 2px solid var(--alice-blue, #2563eb);
    outline-offset: 2px;
}

/* Pending attachment pills above the textarea */
.agent-pending-attachments {
    display: flex; flex-wrap: wrap; gap: 6px;
    padding: 6px 8px; margin-bottom: 6px;
    background: var(--alice-bg);
    border: 1px dashed var(--alice-border);
    border-radius: 6px;
}
.agent-pending-attachments[hidden] { display: none; }
.agent-pending-pill {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 3px 6px 3px 8px;
    background: var(--alice-surface);
    border: 1px solid var(--alice-border);
    border-radius: 12px;
    font-size: 0.78rem;
    max-width: 220px;
}
.agent-pending-pill__name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    /* Clip the FRONT of a long filename, not the back. The end is where the
       distinguishing part lives — "pasted-20260813-114030-4.png" differs from
       -5 only in its last characters, and five pills all reading
       "pasted-20260813-11403…" are five pills nobody can tell apart. rtl moves
       the ellipsis to the left; the bidi isolate keeps the text itself in
       reading order so the name is not reversed. */
    direction: rtl;
    unicode-bidi: isolate;
    text-align: left;
}
.agent-pending-pill__meta {
    color: var(--alice-text-secondary);
    font-size: 0.72rem;
}
.agent-pending-pill__remove {
    border: none; background: transparent;
    color: var(--alice-text-secondary);
    font-size: 14px; line-height: 1;
    cursor: pointer; padding: 0 2px;
}
.agent-pending-pill__remove:hover {
    color: #cf1322;
}
.agent-pending-pill--uploading {
    opacity: 0.6;
}


/* Drag-and-drop overlay (covers #agent-messages while dragging) */
.agent-drop-overlay {
    position: absolute; inset: 0;
    background: rgba(231, 240, 255, 0.92);
    border: 2px dashed var(--alice-blue, #2563eb);
    border-radius: 6px;
    display: flex; align-items: center; justify-content: center;
    color: var(--alice-blue, #2563eb);
    font-weight: 500; font-size: 0.9rem;
    pointer-events: none;
    z-index: 80;
}
.agent-drop-overlay[hidden] { display: none; }
.agent-drop-overlay__inner {
    display: flex; flex-direction: column; align-items: center;
    gap: 8px; text-align: center;
}

/* The chat panel needs position:relative for the overlay to anchor. */
#agent-tab-chat { position: relative; }

/* Author chip above a USER message bubble */
.agent-msg__author {
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--alice-text-secondary);
    margin-bottom: 3px;
    text-align: right;
}
/* Legible on the blue user bubble (another participant's message). */
.agent-msg--user .agent-msg__author { color: rgba(255, 255, 255, 0.92); }


/* SPECS47 message number (branch.ordinal) — emphasized inside the tiny
   timestamp so the branch address reads at a glance. */
.agent-msg-num {
    font-weight: 700;
    font-size: 10px;
    color: var(--alice-blue, #2563eb);
    letter-spacing: 0;
}
.agent-msg--user .agent-msg-num { color: rgba(255, 255, 255, 0.95); }

/* Jump-to-latest affordance — semi-transparent circle floating bottom-right
   of the chat, above the composer. Shown only when scrolled up (e.g. after a
   branch switch lands on the branch point with content below). */
#agent-tab-chat { position: relative; }
.agent-scroll-down {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    /* `bottom` is set in JS to the composer's current height + a gap, so the
       button always floats just ABOVE the input regardless of how tall the
       textarea has grown. */
    bottom: 132px;
    z-index: 5;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    border: 1px solid var(--alice-border);
    background: var(--alice-surface);
    color: var(--alice-text-secondary);
    opacity: 0.6;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
    transition: opacity .15s, color .15s;
}
.agent-scroll-down:hover { opacity: 1; color: var(--alice-blue, #2563eb); }
.agent-scroll-down.hidden { display: none; }

/* Brief highlight when jumping to a message from a Files-tab link. */
.agent-msg--highlight {
    outline: 2px solid var(--alice-blue, #2563eb);
    outline-offset: 2px;
    transition: outline-color .3s;
}

/* Attachments rendered under a USER message bubble */
.agent-msg__attachments {
    display: flex; flex-wrap: wrap; gap: 6px;
    margin-top: 4px;
    justify-content: flex-end;
}
.agent-msg__attachment-link {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 2px 6px;
    background: var(--alice-surface);
    border: 1px solid var(--alice-border);
    border-radius: 10px;
    font-size: 0.74rem;
    text-decoration: none;
    color: var(--alice-text, #1a1a1a);
    max-width: 200px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.agent-msg__attachment-link:hover {
    border-color: var(--alice-blue, #2563eb);
    color: var(--alice-blue, #2563eb);
}






/* Files tab rows */
.agent-files-list {
    display: flex; flex-direction: column; gap: 6px;
    padding: 8px;
}
.agent-files-row {
    display: flex; align-items: center; gap: 8px;
    padding: 8px;
    border: 1px solid var(--alice-border);
    border-radius: 6px;
    background: var(--alice-surface);
}
.agent-files-row__name {
    flex: 1 1 auto;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-weight: 500;
}
.agent-files-row__meta {
    font-size: 0.74rem;
    color: var(--alice-text-secondary);
}
.agent-files-row__actions {
    display: flex; gap: 4px; align-items: center;
}
.agent-files-row__btn {
    border: 1px solid var(--alice-border);
    background: transparent;
    border-radius: 4px;
    padding: 2px 8px;
    font-size: 0.78rem;
    cursor: pointer;
    color: var(--alice-text, #1a1a1a);
    text-decoration: none;
}
.agent-files-row__btn:hover {
    border-color: var(--alice-blue, #2563eb);
    color: var(--alice-blue, #2563eb);
}
.agent-files-row__btn--del:hover {
    border-color: #cf1322;
    color: #cf1322;
}
/* The search box sits INLINE in the first section header — panel width is
   scarce, but vertical space is scarcer, so it shares the row with the
   "History in this Surface (n)" label instead of taking one of its own. */
.agent-history-accordion-summary {
    display: flex;
    align-items: center;
    gap: 8px;
}
.agent-history-accordion-label { flex: 0 0 auto; }
.agent-history-search {
    display: flex;
    align-items: center;
    gap: 6px;
    height: 24px;
    padding: 0 8px;
    /* Takes the rest of the header row, right of the label. */
    flex: 1 1 auto;
    min-width: 90px;
    /* Normal weight: it is a control sharing a bold header, not a heading. */
    font-weight: 400;
    border: 1px solid var(--alice-border);
    border-radius: 6px;
    background: var(--alice-surface);
    color: var(--alice-text-secondary);
}
/* Same box when there is no header to sit in (empty list / no match). */
.agent-history-searchrow {
    display: flex;
    align-items: center;
    padding: 6px 4px;
}
.agent-history-search input {
    flex: 1 1 auto;
    min-width: 0;
    height: 100%;
    border: 0;
    outline: none;
    background: transparent;
    color: var(--alice-text);
    font-size: 0.8rem;
}
.agent-history-search:focus-within {
    border-color: var(--alice-blue);
    color: var(--alice-blue);
}
.agent-history-row {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 10px 12px;
    border: 1px solid var(--alice-border);
    border-radius: 6px;
    background: var(--alice-bg);
}
/* Files brought to a chat, as chips on its History row. */
.agent-history-atts {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}
.agent-history-att {
    display: inline-block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding: 1px 7px;
    font-size: 0.7rem;
    border: 1px solid var(--alice-border);
    border-radius: 10px;
    background: var(--alice-surface);
    color: var(--alice-text-secondary);
    text-decoration: none;
}
.agent-history-att::before { content: '\1F4CE'; margin-right: 4px; }
.agent-history-att:hover { border-color: var(--alice-blue); color: var(--alice-blue); }
.agent-history-att-more { border-style: dashed; }
.agent-history-att-more::before { content: none; }
.agent-history-title {
    font-weight: 600;
    color: var(--alice-text);
    text-decoration: none;
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}
.agent-history-pill {
    display: inline-block;
    padding: 1px 7px;
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    border-radius: 10px;
}
.agent-history-pill--opus {
    background: rgba(147, 51, 234, 0.12);
    color: #9333ea;
}
.agent-history-title:hover { text-decoration: underline; }
.agent-history-preview {
    color: var(--alice-text-secondary);
    font-size: 0.8rem;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}
.agent-history-meta {
    color: var(--alice-text-secondary);
    font-size: 0.72rem;
}
.agent-history-delete {
    align-self: flex-end;
    padding: 4px 10px;
    font-size: 0.78rem;
    background: transparent;
    color: var(--alice-text-secondary);
    border: 1px solid var(--alice-border);
    border-radius: 4px;
    cursor: pointer;
}
.agent-history-delete:hover {
    background: var(--alice-bg);
    color: var(--alice-text);
}
/* Admin-only hard-delete variant. Tinted red so it never gets
   confused with the soft-delete (Hide) button next to it. */
.agent-history-hard-delete {
    margin-left: 6px;
    color: #b00;
    border-color: rgba(176, 0, 0, 0.35);
}
.agent-history-hard-delete:hover {
    background: rgba(176, 0, 0, 0.08);
    color: #b00;
}

/* Hidden threads: collapsed <details> bands below active threads. */
.agent-history-hidden {
    border: 1px solid var(--alice-border);
    border-radius: 4px;
    background: transparent;
    font-size: 0.8rem;
    color: var(--alice-text-secondary);
}
.agent-history-hidden-summary {
    cursor: pointer;
    padding: 6px 10px;
    user-select: none;
    list-style: none;
    display: flex;
    align-items: center;
    gap: 6px;
}
.agent-history-hidden-summary::-webkit-details-marker { display: none; }
.agent-history-hidden-summary::before {
    content: '\25B6';
    font-size: 0.6rem;
    transition: transform 0.15s ease;
    display: inline-block;
    flex-shrink: 0;
}
.agent-history-hidden[open] > .agent-history-hidden-summary::before {
    transform: rotate(90deg);
}
.agent-history-hidden-body {
    padding: 6px 10px 10px;
    border-top: 1px dashed var(--alice-border);
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.agent-history-unhide {
    align-self: flex-end;
    padding: 4px 10px;
    font-size: 0.78rem;
    background: transparent;
    color: var(--alice-blue);
    border: 1px solid var(--alice-blue);
    border-radius: 4px;
    cursor: pointer;
}
.agent-history-unhide:hover {
    background: var(--color-brand, var(--alice-blue));
    color: var(--color-brand-fg, #fff);
}

/* ── Capstone cards (SPECS11) ───────────────────────────────── */

.capstone-card {
    border: 1px solid var(--alice-border);
    border-radius: 8px;
    padding: 16px;
    margin-bottom: 12px;
    background: var(--alice-surface);
    transition: border-color 0.15s;
}

.capstone-card--active {
    border-color: var(--alice-blue);
    background: var(--alice-blue-tint);
}

.capstone-card-header {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 6px;
}

.capstone-star {
    font-size: 1.3rem;
    color: var(--alice-amber);
    flex-shrink: 0;
}

.capstone-star--empty {
    color: var(--alice-text-secondary);
}

.capstone-card-title {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
}

.capstone-card-tag {
    font-size: 0.82rem;
    color: var(--alice-text-secondary);
    margin: 0 0 10px 0;
}

.capstone-card-actions {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 10px;
}

.capstone-active-label {
    font-size: 0.8rem;
    font-weight: 700;
    color: var(--alice-blue);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.btn-ghost {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 0.82rem;
    color: var(--alice-text-secondary);
    padding: 4px 8px;
    border-radius: 4px;
    text-decoration: underline;
}

.btn-ghost:hover { color: var(--alice-text); }

/* ── Syllabus (SPECS11) ─────────────────────────────────────── */

.syllabus-no-capstone {
    text-align: center;
    padding: 2rem 1rem;
    color: var(--alice-text-secondary);
}

.syllabus-tag {
    display: block;
    font-size: 0.78rem;
    color: var(--alice-text-secondary);
    margin-top: 2px;
}

/* ── Map inline container (SPECS11) ─────────────────────────── */

.map-container-inline {
    width: 100%;
    height: 540px;
    border: 1px solid var(--alice-border);
    border-radius: 6px;
    margin-top: 12px;
}

/* ── Bulk-action bar (admin lists) ───────────────────────────────
   One line: label, action dropdown, the action's value control, Apply.
   It used to carry its layout inline and wrapped onto four lines, because
   .form-input sets width:100% and the <select> therefore ate the whole row
   and pushed Apply below it. The controls now size to their content and the
   bar does not wrap — except below the mobile breakpoint, where wrapping is
   the correct answer rather than a horizontal scrollbar. */
.bulk-actions {
  display: flex; align-items: center;
  gap: 8px; flex-wrap: nowrap;
  margin-bottom: 12px; padding: 8px;
  background: var(--alice-surface, #fafafa);
  border: 1px solid var(--alice-border, #e0dfd8);
  border-radius: 6px;
}
.bulk-actions .form-label { margin: 0; white-space: nowrap; }
/* Content-sized, and allowed to shrink before the button does. */
.bulk-actions select,
.bulk-actions .bulk-value select {
  width: auto; flex: 0 1 auto; min-width: 0;
}
.bulk-actions .bulk-value { display: inline-flex; align-items: center; min-width: 0; }
.bulk-actions button { flex: 0 0 auto; }
/* The selection count sits at the far end, away from the controls. */
.bulk-actions .bulk-count { margin-left: auto; white-space: nowrap; }
@media (max-width: 640px) {
  .bulk-actions { flex-wrap: wrap; }
  .bulk-actions .bulk-count { margin-left: 0; }
}

/* The roster's copy of the bar sits inside the members column, which is a
   third of the page wide — `width:auto` on the shared select collapsed it to
   two letters there ("Ch…"), and an action menu nobody can read the options in
   is not a menu. Wrapping is the right answer in a narrow column: label, then
   the select on its own line, then the button. */
.bulk-actions--roster { flex-wrap: wrap; }
.bulk-actions--roster select {
  flex: 1 1 14rem;
  min-width: 12rem;
  width: auto;
}
.bulk-actions--roster .bulk-count { margin-left: auto; }

/* ── Category selector (SPECS11) ────────────────────────────── */

.category-selector {
    margin-bottom: 12px;
}

.category-selector__label {
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--alice-text-secondary);
    margin-bottom: 6px;
}

.category-selector__options {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
}

/* ── Cytoscape map cursor (SPECS11) ─────────────────────────── */

#cy canvas {
    cursor: default;
}

/* ── Map controls bar ────────────────────────────────────────── */

.map-controls {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 8px 0;
    margin-bottom: 6px;
    flex-wrap: wrap;
}

.map-legend {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}

.legend-item {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 11px;
    color: var(--alice-text-secondary);
}
.legend-sep {
    color: var(--alice-border);
    font-size: 14px;
    margin: 0 2px;
}
.legend-icon {
    font-size: 16px;
    font-weight: bold;
    color: var(--alice-text);
    line-height: 1;
}

.map-ctrl-btns {
    display: flex;
    align-items: center;
    gap: 6px;
}

.map-ctrl-btn {
    padding: 4px 10px;
    font-size: 11px;
    font-weight: 600;
    background: var(--alice-surface);
    border: 1px solid var(--alice-border);
    border-radius: 4px;
    cursor: pointer;
    color: var(--alice-text-secondary);
    transition: border-color 0.12s, color 0.12s;
}

.map-ctrl-btn:hover {
    border-color: var(--alice-blue);
    color: var(--alice-text);
}



.map-toggle {
    display: inline-flex; align-items: center; gap: 6px;
    background: none; border: none; cursor: pointer; padding: 0;
}
.map-toggle-label {
    font-size: 11px; font-weight: 600;
    color: var(--alice-text-secondary);
}
.map-toggle-track {
    display: inline-flex; align-items: center;
    width: 32px; height: 18px;
    background: var(--alice-border); border-radius: 9px;
    flex-shrink: 0; transition: background 0.2s;
    position: relative;
}
.map-toggle--on .map-toggle-track { background: var(--alice-blue); }
.map-toggle-thumb {
    position: absolute; left: 2px;
    width: 14px; height: 14px; border-radius: 50%;
    background: #fff; transition: left 0.2s;
    box-shadow: 0 1px 3px rgba(0,0,0,0.2);
}
.map-toggle--on .map-toggle-thumb { left: 16px; }

.map-font-ctrl {
    display: inline-flex;
    border: 1px solid var(--alice-border);
    border-radius: 12px;
    overflow: hidden;
    background: var(--alice-surface);
}
.map-font-btn {
    width: 26px; height: 26px;
    border: none; background: none;
    cursor: pointer;
    font-size: 15px; font-weight: 600; line-height: 1;
    color: var(--alice-text-secondary);
    display: flex; align-items: center; justify-content: center;
}
.map-font-btn:hover { color: var(--alice-text); background: var(--alice-blue-tint); }
.map-font-btn + .map-font-btn { border-left: 1px solid var(--alice-border); }

/* ── Knowledge-map v2 controls (SPECS56 §3.4) ─────────────────────
   Reuse the .map-ctrl-btn look + the existing --alice-* token
   vocabulary so the v2 surface is theme-aware (dark / HC /
   color-blind) and matches the controls already in this toolbar.
   Rendered only when knowledge_map_v2_enabled is on. */
.map-v2-ctrls {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    padding-left: 6px;
    margin-left: 2px;
    border-left: 1px solid var(--alice-border);
}
.map-v2-field {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    font-weight: 600;
    color: var(--alice-text-secondary);
}
/* Shrink the canonical .form-select to the compact map-toolbar scale
   without forking the component — same border/focus tokens apply. */
.map-v2-field .form-select {
    width: auto;
    min-width: 48px;
    height: 26px;
    padding: 2px 22px 2px 8px;
    font-size: 11px;
}
/* Toggled-off state for the aria-pressed v2 buttons: the pressed
   (default) look is .map-ctrl-btn; --off de-emphasizes so the two
   states are distinguishable by more than the aria attribute. */
.map-ctrl-btn[aria-pressed] { border-color: var(--alice-blue); color: var(--alice-text); }
.map-ctrl-btn--off,
.map-ctrl-btn[aria-pressed="false"] {
    border-color: var(--alice-border);
    color: var(--alice-text-tertiary);
    background: var(--alice-bg);
}
.map-v2-status {
    font-size: 11px;
    color: var(--alice-text-secondary);
    min-width: 60px;
}

/* ── EasyMDE overrides for ALICE theme ───────────────────────── */

.EasyMDEContainer {
    border-radius: var(--radius-md);
    border-color: var(--alice-border);
}

.EasyMDEContainer .CodeMirror {
    background: var(--alice-surface);
    color: var(--alice-text);
    border-color: var(--alice-border);
    font-family: var(--font-mono, 'Courier New', monospace);
    font-size: 13px;
}

.EasyMDEContainer .editor-toolbar {
    background: var(--alice-bg);
    border-color: var(--alice-border);
}

.EasyMDEContainer .editor-toolbar button {
    color: var(--alice-text-secondary);
}

.EasyMDEContainer .editor-toolbar button:hover,
.EasyMDEContainer .editor-toolbar button.active {
    background: var(--alice-blue-tint);
    border-color: var(--alice-border);
    color: var(--alice-blue);
}

/* Sticky toolbar for the currently-focused EasyMDE editor.
   The .is-focused class is toggled on .EasyMDEContainer by
   CodeMirror focus/blur events in each page's MDE init code.
   The top offset reads --mde-sticky-top, which defaults to
   --lingua-tab-top (computed by alice.js as topnav+breadcrumb
   height). Pages with their own sticky bar above the editor
   (e.g. admin lexia's .lingua-tab-bar) override --mde-sticky-top
   to push the toolbar down accordingly. !important defeats
   EasyMDE's own `.editor-toolbar { position: relative }`.

   Both rules EXCLUDE .fullscreen. Fullscreen and Side-by-side (which turns
   fullscreen on) pin the editor and preview panes with `position: fixed`, and
   EasyMDE pins the toolbar to match; an unconditional `position: relative
   !important` left the toolbar behind in the document flow, floating over the
   text halfway down the page while the panes covered the viewport. */
.EasyMDEContainer .editor-toolbar:not(.fullscreen) {
    position: relative !important;
}
.EasyMDEContainer.is-focused .editor-toolbar:not(.fullscreen) {
    position: sticky !important;
    top: var(--mde-sticky-top, var(--lingua-tab-top, 0px));
    z-index: 40;
    background: var(--alice-surface, #fff);
}

/* Fullscreen means fullscreen: EasyMDE stacks its panes at z-index 8/9, which
   the app's own sticky chrome (.topnav--locked is 500) would otherwise sit on
   top of — including the strip at top:0 where the toolbar has to live. */
.EasyMDEContainer .editor-toolbar.fullscreen {
    background: var(--alice-surface, #fff);
    border-bottom: 1px solid var(--alice-border);
    z-index: 620;
}
.EasyMDEContainer .CodeMirror-fullscreen {
    background: var(--alice-surface);
    z-index: 600;
}
.EasyMDEContainer .editor-preview-side {
    background: var(--alice-surface);
    color: var(--alice-text);
    border-color: var(--alice-border);
    z-index: 610;
}

.EasyMDEContainer .editor-preview {
    background: var(--alice-surface);
    color: var(--alice-text);
    border-color: var(--alice-border);
}

/* ── Top navigation (SPECS12) ──────────────────────────────── */

.topnav {
    display: flex;
    align-items: stretch;
    height: 44px;
    background: var(--alice-surface);
    border-bottom: 1px solid var(--alice-border);
    z-index: 200;
    top: 0;
}

.topnav--locked {
    position: sticky;
    top: 0;
    z-index: 500;
    box-shadow: 0 1px 8px rgba(0,0,0,0.08);
}

.topnav-inner {
    display: flex;
    align-items: center;
    width: 100%;
    max-width: var(--content-max-width, 1200px);
    margin: 0 auto;
    padding: 0 16px;
    gap: 0;
    position: relative;
}

/* Brand slot (top-left): wraps either the System logo <img> or the text
   wordmark, and owns the trailing gap before the nav links. */
.topnav-brand {
    display: inline-flex;
    align-items: center;
    text-decoration: none;
    margin-right: 24px;
    flex-shrink: 0;
}

/* Per-System logo. The top bar is var(--alice-surface) and the logo sits
   straight on it, transparent. A System with an SVG logo gets two images
   (partials/topnav.html): the logo as drawn, and its dark variant with the
   lettering lightened for the dark bar (branding.dark_variant_svg); the
   theme picks one. A PNG-only logo is one image on a white chip in dark. */
.topnav-logo {
    display: block;
    /* 36px in the 44px bar: 4px of air above and below, close to the
       burger button's 6px; no chip, no border, no inner padding
       (2026-09-16). The SVGs are cropped to their marks, so the whole
       36px is logo. */
    height: 36px;
    width: auto;
    max-width: 260px;
    background: none;
    padding: 0;
    border-radius: 0;
    box-shadow: none;
}
/* The dark variant shows exactly where the dark tokens apply: th-dark, or
   the OS in dark with no theme chosen (the rule at the top of this file). */
.topnav-logo--dark { display: none; }
html.th-dark .topnav-logo--light { display: none; }
html.th-dark .topnav-logo--dark { display: block; }
@media (prefers-color-scheme: dark) {
    html:not(.th-dark):not(.th-light):not(.th-hico):not(.th-deut):not(.th-prot):not(.th-trit) .topnav-logo--light { display: none; }
    html:not(.th-dark):not(.th-light):not(.th-hico):not(.th-deut):not(.th-prot):not(.th-trit) .topnav-logo--dark { display: block; }
}
/* A PNG-only logo cannot be redrawn: in the dark themes it keeps a white
   chip, inside the same 36px, so its dark type stays legible. */
body.th-dark .topnav-logo:not(.topnav-logo--light):not(.topnav-logo--dark) {
    box-sizing: border-box;
    background: #fff;
    padding: 2px 6px;
    border-radius: 6px;
}
@media (prefers-color-scheme: dark) {
    body:not(.th-light) .topnav-logo:not(.topnav-logo--light):not(.topnav-logo--dark) {
        box-sizing: border-box;
        background: #fff;
        padding: 2px 6px;
        border-radius: 6px;
    }
}

.topnav-wordmark {
    font-family: Georgia, "Times New Roman", serif;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--alice-text);
    text-decoration: none;
}

.topnav-links {
    display: flex;
    align-items: stretch;
    flex: 1;
    gap: 0;
}

.nav-scroll-top {
    background: transparent;
    border: 1px solid var(--alice-border);
    border-radius: 4px;
    color: var(--alice-text-secondary);
    cursor: pointer;
    line-height: 0;
    padding: 4px 8px;
    margin-right: 8px;
    align-self: center;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.nav-scroll-top:hover {
    color: var(--alice-text);
    border-color: var(--alice-text-secondary);
}
.nav-scroll-top.hidden { display: none; }

.topnav-user {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-shrink: 0;
    margin-left: auto;
    font-size: 12px;
    color: var(--alice-text-secondary);
}

/* ── Burger category (collapsible, SPECS12) ───────────────── */

.burger-category {
    border-bottom: 1px solid var(--alice-border);
}

.burger-category:last-of-type { border-bottom: none; }

.burger-category-header {
    padding: 8px 16px;
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--alice-text-secondary);
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    user-select: none;
}

.burger-category-header::-webkit-details-marker { display: none; }

.burger-category-header::after {
    content: '+';
    font-size: 13px;
    font-weight: 400;
    color: var(--alice-text-secondary);
    transition: transform 0.15s;
}

.burger-category[open] .burger-category-header::after {
    content: '\2212';
}

/* A top-level direct link that sits at the category-header tier but navigates
   instead of expanding: drop the +/- marker, add a hover affordance. */
.burger-category-header--link { text-decoration: none; }
.burger-category-header--link::after { content: none; }
.burger-category-header--link:hover {
    background: var(--alice-bg);
    color: var(--alice-text);
}

.burger-category-body {
    padding-bottom: 4px;
}

.burger-sublabel {
    padding: 4px 16px 2px;
    font-size: 9px;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--alice-text-secondary);
}

/* ── Burger toggle (nav lock switch, SPECS12) ─────────────── */

.burger-toggle {
    display: flex;
    align-items: center;
    gap: 10px;
}

.burger-toggle-track {
    display: inline-flex;
    align-items: center;
    width: 32px;
    height: 18px;
    background: var(--alice-border);
    border-radius: 9px;
    flex-shrink: 0;
    transition: background 0.2s;
    position: relative;
}

.burger-toggle--on .burger-toggle-track {
    background: var(--alice-blue);
}

.burger-toggle-thumb {
    position: absolute;
    left: 2px;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: #fff;
    transition: left 0.2s;
    box-shadow: 0 1px 3px rgba(0,0,0,0.2);
}

.burger-toggle--on .burger-toggle-thumb {
    left: 16px;
}

/* ── Burger footer (SPECS12) ──────────────────────────────── */

.burger-footer {
    border-top: 1px solid var(--alice-border);
    padding-top: 4px;
    margin-top: 4px;
}

.burger-item--danger {
    color: var(--alice-warning);
}

.burger-item--active {
    font-weight: 700;
    color: var(--alice-blue);
}

/* ── Mobile: hide desktop nav links, show burger nav (SPECS12) ── */

@media (max-width: 640px) {
    .topnav-links { display: none; }
    .burger-category--mobile { display: block; }
}

@media (min-width: 641px) {
    .burger-category--mobile { display: none; }
}

/* ── Syllabus START/END row indicators (SPECS14) ───────────── */

.syllabus-row--capstone {
    background: rgba(239, 159, 39, 0.05);
}

.syllabus-row--start {
    background: rgba(29, 158, 117, 0.05);
}

.syllabus-start-mark {
    color: var(--alice-teal);
    font-size: 0.85rem;
    flex-shrink: 0;
}

.syllabus-title-row {
    display: flex;
    align-items: center;
    gap: 6px;
}

.syllabus-tag {
    display: block;
    font-size: 0.78rem;
    color: var(--alice-text-secondary);
    margin-top: 2px;
}

/* ── Pagina title bar (SPECS14) ──────────────────────────────── */

.pagina-title-bar {
    margin-bottom: 16px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--alice-border);
}

.pagina-title {
    font-size: 1.4rem;
    font-weight: 700;
    margin: 0 0 4px 0;
    color: var(--alice-text);
    line-height: 1.2;
}

.pagina-title-link {
    color: inherit;
    text-decoration: none;
    border-bottom: 2px solid var(--alice-amber);
    padding-bottom: 1px;
}

.pagina-title-link:hover { color: var(--alice-blue); }



/* ── Content tab edit button (SPECS14) ───────────────────────── */



/* ── Assessment tab with survey (SPECS14) ────────────────────── */

.pagina-assess-content + .pagina-submission,
.pagina-assess-content + .survey-section {
    margin-top: 24px;
    padding-top: 20px;
    border-top: 1px solid var(--alice-border);
}

.survey-section--with-submission {
    margin-top: 24px;
    padding-top: 20px;
    border-top: 1px solid var(--alice-border);
}

/* ── Segmented progress bar (SPECS15) ──────────────────────── */

.progress-bar-wrap {
    display: flex;
    flex-direction: column;
    gap: 2px;
    /* Smaller floor so the tab bar has room on a single row even
       on narrower viewports. The wrap still grows to fit its
       labels, but we keep them compact via the smaller font sizes
       below. */
    min-width: 110px;
    flex-shrink: 0;
}

.progress-bar-track {
    display: flex;
    height: 4px;
    border-radius: 2px;
    overflow: hidden;
    background: var(--alice-bg);
    border: 1px solid var(--alice-border);
}

.progress-seg {
    height: 100%;
    transition: width 0.3s ease;
    flex-shrink: 0;
}

.progress-seg--graded { background: var(--alice-teal); }
.progress-seg--submitted { background: var(--alice-amber); }
.progress-seg--read { background: var(--alice-blue); }
.progress-seg--pending { background: var(--alice-gray); }

.progress-labels {
    display: flex;
    align-items: center;
    gap: 3px;
    /* 8px at the architect's request (2026-08-25): at 11px this strip
       overflowed the tab-bar row on a crowded course (MDC 4153 carries seven
       tabs) once the browser was zoomed. It was 7px before, which was raised
       for legibility — 8px is the floor agreed after that, and browser zoom
       still scales it, which is what 1.4.4 actually requires. */
    font-size: 8px;
    white-space: nowrap;
}

.progress-label {
    display: flex;
    align-items: center;
    gap: 2px;
    /* Override the older global .progress-label rule (font-size: 0.8rem)
       used by the single-color .progress-fill widget. Without this the
       percentages render at ~12.8px and overflow the tab-bar row. */
    font-size: 8px;
    margin-top: 0;
}

/* Each label color tracks its bar segment. Note that we use
   --alice-amber (NOT --alice-warning) for "submitted" so the label
   matches the bar segment colour in every theme — and crucially in
   tritanopia, where --alice-warning collapses to the same vermilion
   as --alice-blue (used by "read"), making the two columns
   indistinguishable. With --alice-amber the submitted label stays
   the reddish-purple #CC79A7 in trit, distinct from the read label. */
.progress-label--graded { color: var(--alice-teal-text); }
.progress-label--submitted { color: var(--alice-amber-text); }
.progress-label--read { color: var(--alice-blue-text); }
.progress-label--pending { color: var(--alice-gray-text); }

.progress-label-text {
    font-size: 8px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.progress-label-sep {
    color: var(--alice-gray-text);
    font-size: 8px;
}

/* ── Tab bar row (tabs + progress on same line) ──────────────── */

.tab-bar-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 0;
    border-bottom: 1px solid var(--alice-border);
}

.tab-bar-progress {
    flex-shrink: 0;
}

@media (max-width: 640px) {
    .progress-labels { display: none; }
    .progress-bar-track { height: 8px; }
    .progress-bar-wrap { min-width: 80px; }
}

/* ── Inline asset table (SPECS15) ──────────────────────────── */



.asset-inline-table {
    width: 100%;
    margin-bottom: 8px;
}

.asset-url-cell {
    max-width: 240px;
    overflow: hidden;
}

.asset-url-link {
    font-size: 11px;
    color: var(--alice-blue);
    word-break: break-all;
}

/* A table cell. `display: flex` took it out of the table, so its bottom border
   stopped short of the row's whenever the row grew past one line (the stored
   filename under the URL made that every file row). The Edit button and the
   inline Delete form already sit side by side, 4px apart. */
.asset-actions {
    white-space: nowrap;
}

.btn-ghost--danger {
    color: var(--alice-warning);
}

.btn-ghost--danger:hover {
    color: var(--alice-text);
    background: rgba(186, 117, 23, 0.08);
}

.asset-empty {
    font-size: 12px;
    color: var(--alice-text-secondary);
    font-style: italic;
    margin: 4px 0 8px 0;
}

.asset-add-details {
    margin-top: 8px;
}

.asset-add-summary {
    font-size: 12px;
    font-weight: 600;
    color: var(--alice-blue);
    cursor: pointer;
    padding: 4px 0;
    list-style: none;
    user-select: none;
}

.asset-add-summary::-webkit-details-marker { display: none; }

.asset-add-form {
    margin-top: 8px;
    padding: 12px;
    background: var(--alice-bg);
    border: 1px solid var(--alice-border);
    border-radius: 6px;
}

/* One column (architect, 2026-09-02): the rows stack, and each .form-row's
   own 180px/1fr split puts the label left and the field right — two rows
   side by side halved the width of the TEXT asset's body editor. */
.asset-edit-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 4px 16px;
}

.asset-editing {
    background: var(--alice-amber-tint);
}

/* ── Nuntius protected column (SPECS15) ────────────────────── */

.nuntius-col-protected {
    width: 80px;
    text-align: center;
}



/* ── Admin collapsible sections ─────────────────────── */
.admin-section {
    margin: 8px 0;
}
.admin-section > summary {
    cursor: pointer;
    user-select: none;
    padding: 6px 0;
    list-style: none;
}
.admin-section > summary::-webkit-details-marker { display: none; }
.admin-section > summary h2,
.admin-section > summary h3 {
    font-size: 14px;
}
.admin-section > summary::before {
    content: '\25B6';
    display: inline-block;
    width: 16px;
    font-size: 10px;
    transition: transform 0.15s;
}
.admin-section[open] > summary::before {
    transform: rotate(90deg);
}

/* ── The ⓘ beside a field label ──────────────────────
   A native <details>, so it is keyboard-operable and screen-reader-announced
   with no script of its own. The panel is absolutely positioned: an author
   opens it WHILE writing, and a panel that pushed the textarea down would move
   the very thing they are looking at. It hangs over the form instead. */
.syntax-help {
    position: relative;
    display: inline-block;
    margin-left: 6px;
    vertical-align: middle;
    font-weight: 400;
}
.syntax-help > summary {
    list-style: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px; height: 16px;
    /* --alice-blue-TEXT, not --alice-blue: the plain hue is a fill colour and
       measures 3.54:1 as a glyph in the tritanopia palette. The -text variant
       is the one the design system holds above 4.5:1 in every theme. */
    border: 1px solid var(--alice-blue-text, #185FA5);
    border-radius: 50%;
    color: var(--alice-blue-text, #185FA5);
    font-size: 11px;
    font-style: italic;
    font-family: Georgia, 'Times New Roman', serif;
    line-height: 1;
    user-select: none;
}
.syntax-help > summary::-webkit-details-marker { display: none; }
.syntax-help > summary:hover,
.syntax-help[open] > summary {
    /* Filled with the same -text token, so the open chip inherits the ratio the
       closed one was measured at — contrast is symmetric, and a literal #fff
       here measured 2.11:1 in dark mode, where --alice-blue is a LIGHT blue. */
    background: var(--alice-blue-text, #185FA5);
    color: var(--alice-bg, #fff);
}
.syntax-help > summary:focus-visible {
    outline: 2px solid var(--alice-blue, #185FA5);
    outline-offset: 2px;
}
.syntax-help__panel {
    position: absolute;
    z-index: 40;
    top: calc(100% + 6px);
    left: 0;
    width: min(620px, 78vw);
    max-height: 70vh;
    overflow-y: auto;
    padding: 12px 14px;
    background: var(--alice-surface, #fff);
    border: 1px solid var(--alice-border, #e0dfd8);
    border-radius: 6px;
    box-shadow: 0 4px 14px rgba(0,0,0,0.12);
    font-size: 13px;
    font-weight: 400;
    line-height: 1.5;
    color: var(--alice-text);
    text-align: left;
    white-space: normal;      /* the label column is narrow; this is not */
}
.syntax-help__panel h4 {
    margin: 0 0 6px;
    font-size: 13px;
    font-weight: 600;
}
.syntax-help__panel h4 + p { margin: 0 0 10px; }
.syntax-help__table {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 12px;
}
.syntax-help__table th,
.syntax-help__table td {
    border-bottom: 1px solid var(--alice-border, #e0dfd8);
    padding: 5px 8px 5px 0;
    text-align: left;
    vertical-align: top;
}
.syntax-help__table th {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--alice-text-secondary, #706F69);
}
.syntax-help__table td:first-child { white-space: nowrap; }
.syntax-help__panel code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 12px;
    background: var(--alice-bg, #f6f5f0);
    border-radius: 3px;
    padding: 1px 4px;
}
/* The example is one long line an author copies — it scrolls sideways rather
   than wrapping, because a wrapped path is a path that gets pasted broken. */
.syntax-help__example {
    margin: 0 0 12px;
    padding: 8px;
    background: var(--alice-bg, #f6f5f0);
    border: 1px solid var(--alice-border, #e0dfd8);
    border-radius: 4px;
    overflow-x: auto;
}
.syntax-help__example code { background: none; padding: 0; }
.syntax-help__notes {
    margin: 0;
    padding-left: 18px;
    color: var(--alice-text-secondary, #706F69);
}
.syntax-help__notes li { margin-bottom: 5px; }
.syntax-help__notes li:last-child { margin-bottom: 0; }

/* ── Reorder & action buttons right-aligned ─────────── */
.reorder-buttons {
    white-space: nowrap;
    text-align: right;
}
.admin-table .btn-ghost.btn-sm {
    padding: 2px 6px;
    font-size: 13px;
    min-width: 0;
}

/* ── Muted default-text column ──────────────────────── */
.text-muted {
    color: var(--alice-text-secondary);
    font-size: 12px;
}

/* ── Rubric editor (visual + JSON side by side) ─────── */
.rubric-editor {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
    margin-top: 4px;
}
.rubric-editor-visual {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.rubric-band-row {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 6px;
    border: 1px solid var(--alice-border);
    border-radius: 4px;
    background: var(--alice-bg);
}
.rubric-band-row-top {
    display: flex;
    align-items: center;
    gap: 4px;
}
.rubric-band-row .rubric-label {
    flex: 1;
}
.rubric-desc {
    width: 100%;
    font-size: 12px;
    padding: 4px 6px;
    resize: vertical;
}
.rubric-band-row input[type="number"] {
    width: 60px;
    padding: 4px;
    font-size: 12px;
}
.rubric-band-row input[type="text"] {
    flex: 1;
    padding: 4px;
    font-size: 12px;
}
.rubric-editor-json textarea {
    width: 100%;
    height: 100%;
    resize: vertical;
}

/* ── Inbox tabs (SPECS26) ───────────────────────────── */
.inbox-tab-bar {
    display: flex;
    flex-direction: row;
    gap: 4px;
    margin: 12px 0 0 0;
    border-bottom: 1px solid var(--alice-border);
}
.inbox-tab-bar .pagina-tab-btn {
    flex: 0 0 auto;
}


/* ── Home inbox card (SPECS26) ──────────────────────── */
/* SPECS28 fix: Grading page assessment cards. Each item lives in
   its own card so the page reads as a stack of independent units. */
.grade-card {
    background: var(--alice-surface);
    border: 1px solid var(--alice-border);
    border-radius: 8px;
    padding: 18px 22px;
    margin-bottom: 18px;
}
.grade-card-head {
    display: flex;
    align-items: baseline;
    gap: 12px;
    margin-bottom: 12px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--alice-border);
}
.grade-card-title {
    margin: 0;
    font-size: 16px;
}
.grade-prompt,
.grade-response {
    margin-bottom: 14px;
}

/* Prompt and Response visual boxes on the grading page.
   Each gets a distinct background so the assessor can
   instantly tell what is the prompt vs. the nauta's work. */
.grade-prompt-box,
.grade-response-box {
    margin-bottom: 16px;
    border-radius: 6px;
    overflow: hidden;
}
.grade-section-label {
    display: block;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .06em;
    padding: 6px 14px;
    margin: 0;
}
.grade-section-content {
    padding: 12px 14px;
    font-size: 14px;
    line-height: 1.6;
}
.grade-section-ts {
    float: right;
    font-size: 10px;
    font-weight: 400;
    opacity: 0.7;
}

/* Prompt: darker/muted background — this is reference material */
.grade-prompt-box .grade-section-label {
    background: var(--alice-bg);
    color: var(--alice-text-secondary);
    border-bottom: 1px solid var(--alice-border);
}
.grade-prompt-box .grade-section-content {
    background: var(--alice-surface);
    color: var(--alice-text);
    border: 1px solid var(--alice-border);
    border-top: none;
    border-radius: 0 0 6px 6px;
}

/* Response: surface background — the nauta's work */
.grade-response-box .grade-section-label {
    background: var(--alice-blue-tint);
    color: var(--alice-blue);
    border-bottom: 1px solid var(--alice-border);
}
.grade-response-box .grade-section-content {
    background: var(--alice-surface);
    border: 1px solid var(--alice-border);
    border-top: none;
    border-radius: 0 0 6px 6px;
}

/* Previous feedback: teal header — assessor's prior comments */
.grade-feedback-box .grade-section-label {
    background: var(--alice-teal-tint);
    color: var(--alice-teal);
    border-bottom: 1px solid var(--alice-border);
}
.grade-feedback-box .grade-section-content {
    background: var(--alice-surface);
    border: 1px solid var(--alice-border);
    border-top: none;
    border-radius: 0 0 6px 6px;
}

/* AI assessment: green header — instructor-only per-question AI draft (012.134).
   Sits in the right column of the round row (student on the left). */
.grade-ai-box { margin-bottom: 16px; border-radius: 6px; overflow: hidden; }
.grade-ai-box .grade-section-label {
    background: rgba(22, 163, 74, 0.12);
    color: #15803d;
    border-bottom: 1px solid var(--alice-border);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}
.grade-ai-box .grade-section-content {
    background: var(--alice-surface);
    border: 1px solid var(--alice-border);
    border-top: none;
    border-radius: 0 0 6px 6px;
}
/* One label layout for the response and AI boxes (architect, 2026-09-02):
   title grows, the timestamp sits at the right edge just LEFT of the icons.
   Flex neutralises .grade-section-ts's float in these two boxes. */
.grade-response-box > .grade-section-label,
.grade-ai-box > .grade-section-label {
    display: flex;
    align-items: center;
    gap: 8px;
}
.grade-response-box > .grade-section-label > span:first-child,
.grade-ai-box > .grade-section-label > span:first-child {
    flex: 1 1 auto;
}
.grade-response-box > .grade-section-label .grade-section-ts,
.grade-ai-box > .grade-section-label .grade-section-ts {
    float: none;
    margin-left: auto;
}
/* The student side's copy wears its box's blue, not the AI green. */
.grade-response-box .grade-copy-ai {
    border-color: rgba(37, 99, 235, 0.45);
    color: var(--alice-blue, #1d4ed8);
}

.grade-copy-ai {
    background: transparent;
    border: 1px solid rgba(22, 163, 74, 0.45);
    color: #15803d;
    border-radius: 4px;
    cursor: pointer;
    font-size: 13px;
    line-height: 1;
    padding: 3px 7px;
}
.grade-copy-ai:hover { background: rgba(22, 163, 74, 0.12); }
.grade-copy-ai.copied { background: #16a34a; color: #fff; border-color: #16a34a; }

/* Student | AI two-column split on each round row (012.134). Collapses to a
   single column on narrow viewports. */
.grade-round-cols {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
    align-items: start;
}
.grade-round-cols > .grade-response-box,
.grade-round-cols > .grade-ai-box { margin-bottom: 16px; }
@media (max-width: 720px) {
    .grade-round-cols { grid-template-columns: 1fr; gap: 0; }
}

/* "Previous feedback" callout. Uses a left-border accent on the
   surface color (same as the rest of the card) so the text remains
   readable in every theme. The previous .feedback-block rule painted
   over text in dark mode. */




/* SPECS28: Reusable inbox panel (used at home, opus, and pagina).
   The tab bar sits on the page background; only the table + pagination
   are wrapped in a card so the active tab visually attaches to it. */
.inbox-panel-component {
    margin: 1.5rem 0;
}
.inbox-panel-scope {
    margin: 0 0 8px 0;
    font-size: 13px;
    color: var(--alice-text-secondary);
}
.inbox-content-card {
    background: var(--alice-surface);
    border: 1px solid var(--alice-border);
    border-top: none;       /* the active tab supplies the top edge */
    border-radius: 0 0 8px 8px;
    padding: 16px 18px;
    margin-top: 0;          /* sit just below the tab bar so its
                               border-bottom remains visible as a
                               horizontal divider, except where the
                               active tab punches into it via -2px
                               margin-bottom on the button */
}
/* Bare-mode wrapper used when the macro is rendered inside an
   already-card panel (opus and pagina detail). No background or
   border — the outer panel supplies the card. */
.inbox-content-bare {
    margin: 0;
}
.inbox-table {
    margin-top: 0;
}
/* Clickable sortable headers (window.inboxSort in inbox_table.js). Promoted
   here from partials/inbox_panel.html so ANY table.inbox-sortable-table can
   sort system-wide (record lists, admin tables). */
.inbox-sortable { cursor: pointer; user-select: none; }
.inbox-sortable:hover { color: var(--alice-blue); }
.inbox-sortable.sort-asc::after  { content: ' \25B2'; font-size: 0.8em; }
.inbox-sortable.sort-desc::after { content: ' \25BC'; font-size: 0.8em; }
/* TYPE column (first th/td) carries a pill from `inbox_kind_badge`.
   Without nowrap the pill wraps to two lines on narrower viewports —
   the user-visible "Pending grade" / "Graded by me" / etc. read as
   broken visual badges. Lock the column to a single line; the rest
   of the row can wrap as needed. Same rule covers Received, Sent,
   and Tokens (Workspace) tabs because all three render through the
   same .inbox-table macro. */
.inbox-table th:first-child,
.inbox-table td:first-child {
    white-space: nowrap;
}
.inbox-sort-link {
    color: inherit;
    text-decoration: none;
    white-space: nowrap;
}
.inbox-sort-link:hover {
    color: var(--alice-blue);
}
.inbox-pagination {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 12px;
    justify-content: center;
}
.inbox-page-status {
    font-size: 12px;
    color: var(--alice-text-secondary);
    padding: 0 8px;
}
.inbox-page-disabled {
    opacity: 0.4;
    cursor: not-allowed;
}
/* Sticky-top "assess in progress" rows: amber tint + accent stripe so
   an assessor's drafted-but-unfinalized work is impossible to miss
   among unclaimed pending-grade rows. */
.inbox-table tr.inbox-row-resume {
    background: var(--alice-amber-tint);
    box-shadow: inset 3px 0 0 var(--alice-amber);
}
.inbox-table tr.inbox-row-resume:hover {
    background: var(--alice-amber-tint);
    filter: brightness(0.97);
}
.inbox-table tr.inbox-row-resume .resume-hint {
    color: var(--alice-warning);
    font-weight: 600;
}

/* ── Student assessments (SPECS26) ──────────────────── */
.student-assessments {
    margin: 16px 0;
}

.assessment-next-round-header {
    background: var(--alice-blue-tint);
    color: var(--alice-blue);
    font-weight: 700;
    font-size: 13px;
    text-transform: uppercase;
    letter-spacing: .06em;
    padding: 10px 16px;
    margin: 20px 0 14px 0;
    border-radius: 6px 6px 0 0;
    border: 1px solid var(--alice-border);
    border-bottom: none;
}




.assessment-form {
    margin-top: 8px;
}


.assessment-submit-all {
    margin-top: 24px;
    padding-top: 16px;
    border-top: 2px solid var(--alice-border);
}

/* ── Per-opus default rubric editor ─────────────────── */
.rubric-default-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 12px;
}
.rubric-default-table th,
.rubric-default-table td {
    padding: 4px 6px;
    text-align: left;
    border-bottom: 1px solid var(--alice-border);
}
.rubric-default-table th {
    font-weight: 600;
    color: var(--alice-text-secondary);
}
.rubric-default-table input[type="number"] {
    width: 50px;
    padding: 4px;
}
.rubric-default-table input[type="text"] {
    width: 100%;
    padding: 4px;
}

/* ── Item translation card ──────────────────────────── */
.item-lang-card {
    border: 1px solid var(--alice-border);
    border-radius: 6px;
    padding: 12px;
    margin-bottom: 12px;
    background: var(--alice-bg);
}
.item-lang-card-head {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 10px;
    font-size: 12px;
}

.item-lang-card-slo {
    color: var(--alice-text-secondary);
}
.item-lang-default,
.item-lang-translated,
.item-lang-choices,
.item-lang-rubric {
    margin-bottom: 10px;
}
.default-prompt {
    font-size: 12px;
    color: var(--alice-text-secondary);
    background: var(--alice-bg);
    border: 1px solid var(--alice-border);
    border-radius: 4px;
    padding: 6px 8px;
    white-space: pre-wrap;
    font-family: inherit;
    margin: 0;
}
.choices-trans-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.choices-trans-row {
    display: flex;
    align-items: center;
    gap: 6px;
}

.rubric-trans-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 12px;
}
.rubric-trans-table th,
.rubric-trans-table td {
    padding: 4px 8px;
    text-align: left;
    border-bottom: 1px solid var(--alice-border);
}
.rubric-trans-table th {
    font-weight: 600;
    color: var(--alice-text-secondary);
}

/* ── Choices editor (radio/checkbox items) ──────────── */
.choices-editor {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.choices-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
}


.choice-row .form-input {
    flex: 1;
}
.choices-row.hidden { display: none; }

/* ── Compact rubric display in translation lists ────── */



/* ── Pagina-empty paragraph ─────────────────────────── */
.pagina-empty {
    font-size: 12px;
    color: var(--alice-text-secondary);
    font-style: italic;
    margin: 4px 0 8px 0;
}

/* ── BONUM panel (SPECS29) ──────────────────────────── */




.bonum-add-file {
    flex: 0 0 auto;
    font-size: 13px;
    color: var(--alice-text);
}















/* ── Admin summary cards (used by bonum/usage.html) ─── */
/* Roster controls — one line under the summary cards: the filters on the
   left, the page's actions on the right. Assembled by the controls slot in
   partials/nauta_roster_table.html so a page cannot end up with a bar above
   the cards and another beside them.

   `flex-wrap` is not decoration: at 320px the two selects plus three buttons
   cannot share a line, and this row must fold rather than push the page into
   a horizontal scroll (1.4.10 Reflow). `margin-left:auto` pushes the actions
   right only while there is room; once the row wraps the group starts a line
   of its own instead of being stranded against the edge. */
/* The inbox pin column (migration 528): a quiet stack of icon-only
   controls — the tack, then up/down once pinned. Muted until pinned so the
   column reads as an affordance, not a state. */
.inbox-pin-head { width: 34px; }
.inbox-pin-cell { width: 34px; text-align: center; vertical-align: top; }
.inbox-pin-cell form { margin: 0; }
.inbox-pin-btn {
    border: none;
    background: none;
    cursor: pointer;
    padding: 2px 4px;
    font-size: 12px;
    color: var(--alice-text-tertiary, #999);
}
.inbox-pin-btn:hover { color: var(--alice-text); }
.inbox-pin-btn--on { color: var(--alice-teal); }

/* The section strip's filter select (partials/section_strip.html), sized
   HERE and nowhere else — and sized like .chorus-selector-select, so the
   strip's dropdown is the SAME dropdown on every surface (architect,
   2026-08-29: "the smaller one"): 12px type, 26px tall, exactly the strip's
   buttons. */
.section-strip-filter .form-select {
    max-width: 260px;
    min-width: 170px;
    font-size: 12px;
    padding: 4px 8px;
}
.section-strip-filter .form-label {
    font-size: 12px;
}

.roster-controls {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 16px;
}
.roster-controls__filter {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    /* Shrinkable. Without min-width:0 a flex item refuses to go below its
       content's intrinsic width, so a long section name would hold the whole
       row open and push the buttons onto a second line. */
    min-width: 0;
}
/* The two dropdowns are sized HERE rather than by an inline style on each
   <select>, so they cannot drift apart — they were 250px and 280px, set in
   two different files.

   They shrink before the row wraps: a section named "Section 001 — Tuesday
   evening" should ellipsise rather than take the line to itself. 110px still
   shows enough of an option to tell them apart, and the full text is one
   click away in the open list. */
.roster-controls .form-select {
    flex: 0 1 auto;
    min-width: 110px;
    max-width: 170px;
    text-overflow: ellipsis;
}
.roster-controls .form-label {
    white-space: nowrap;
    flex: 0 0 auto;
}
.roster-controls__actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    /* No margin-left:auto since 2026-08-29: the roster's left group grows
       (flex:1) to push the whole strip — Category, Section, Go to section,
       Metrics — right as ONE cluster; auto here would tear Metrics off it. */
}
@media (max-width: 640px) {
    /* Once the row has folded, "right-aligned" means "far from everything
       else". Let the actions sit where they land. */
    .roster-controls__actions { margin-left: 0; }
}
/* The category filter inside the roster's selection bar (2026-10-02): the
   bar's own 13px scale, so the filter does not set the bar's height. */
.roster-nudge-bar .form-label { font-size: 13px; white-space: nowrap; }
.roster-nudge-bar .form-select {
    flex: 0 1 auto;
    min-width: 110px;
    max-width: 200px;
    font-size: 13px;
    text-overflow: ellipsis;
}

.admin-summary-row {
    display: flex;
    gap: 16px;
    margin: 16px 0 24px 0;
    flex-wrap: wrap;
}
.admin-summary-card {
    flex: 1 1 160px;
    min-width: 140px;
    background: var(--alice-surface);
    border: 1px solid var(--alice-border);
    border-radius: 6px;
    padding: 16px 20px;
}
.admin-summary-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: var(--alice-text-secondary);
    margin-bottom: 6px;
}
.admin-summary-value {
    font-size: 22px;
    font-weight: 700;
    color: var(--alice-text);
}
.admin-help {
    color: var(--alice-text-secondary);
    font-size: 13px;
    max-width: 720px;
    margin: 8px 0 16px 0;
}

/* ── Warning / danger buttons (admin destructive ops) ──
   Styled to match .btn-secondary (light bordered button) with
   amber/red accents on text + border, instead of solid fills.
   This keeps the action row visually consistent with the
   neutral Manage button while still signalling severity. */


.btn-danger {
    display: inline-block;
    padding: 0.4rem 0.85rem;
    font-size: 0.85rem;
    color: #c0392b;
    background-color: var(--alice-surface);
    border: 1px solid #c0392b;
    border-radius: 4px;
    cursor: pointer;
    text-align: center;
    text-decoration: none;
    font-family: inherit;
    transition: background-color .15s;
}
.btn-danger:hover {
    background-color: rgba(192, 57, 43, 0.08);
}
.btn-warn.btn-sm,
.btn-danger.btn-sm {
    padding: 0.25rem 0.65rem;
    font-size: 0.78rem;
}

/* ── Site footer (every page) ─────────────────────── */
.alice-footer {
    margin-top: 48px;
    padding: 16px 0;
    border-top: 1px solid var(--alice-border);
    background: var(--alice-bg);
    color: var(--alice-text-secondary);
    font-size: 12px;
}
.alice-footer__inner {
    max-width: var(--content-max-width, 960px);
    margin: 0 auto;
    padding: 0 16px;
    text-align: center;
}

/* ── Admin opus list: visually separate destructive column ──
   Constructive actions (Manage) sit in their own narrow column;
   destructive actions (Archive, Delete) live in the rightmost
   column, marked with a thick left border and a faint warning
   tint so a careless cursor can't accidentally drift across the
   boundary. */
.opus-admin-table th.opus-destructive-col,
.opus-admin-table td.opus-destructive-col {
    border-left: 3px double #c0392b;
    background: rgba(192, 57, 43, 0.04);
    padding-left: 14px;
}
.opus-admin-table th.opus-destructive-col {
    color: #c0392b;
}

/* ── Opus delete modal (SPECS-admin destructive) ── */
.opus-delete-modal {
    position: fixed;
    inset: 0;
    z-index: 9000;
    display: flex;
    align-items: center;
    justify-content: center;
}
.opus-delete-modal.hidden {
    display: none;
}
.opus-delete-modal__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
}
.opus-delete-modal__box {
    position: relative;
    background: var(--alice-surface);
    color: var(--alice-text);
    border: 2px solid var(--alice-warning, #c0392b);
    border-radius: 8px;
    padding: 24px 28px;
    max-width: 540px;
    width: calc(100% - 32px);
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.4);
    max-height: 90vh;
    overflow-y: auto;
}

/* ── Dense N×N matrix grids on /admin/opus/{id}/matrix ──
   Each matrix is wrapped in a bordered scroll container, with
   explicit visible gridlines so every cell is clearly delimited. */
.matrix-grid-wrap {
    overflow-x: auto;
    margin-bottom: 12px;
    border: 2px solid #888780;
    border-radius: 6px;
    background: var(--alice-surface);
    padding: 0;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
}
.matrix-grid {
    border-collapse: collapse;
    font-size: 11px;
    font-family: var(--font-sans);
    border: 1px solid #888780;
}
.matrix-grid th,
.matrix-grid td {
    border: 1px solid #b8b8b0;
    padding: 3px 6px;
    text-align: center;
    min-width: 32px;
    height: 22px;
    white-space: nowrap;
}
.matrix-grid thead th,
.matrix-grid tbody th {
    background: var(--alice-bg);
    color: var(--alice-text-secondary);
    font-weight: 600;
    border-color: #888780;
    position: sticky;
    z-index: 1;
}
.matrix-grid thead th { top: 0; }
.matrix-grid tbody th { left: 0; }
.matrix-corner {
    z-index: 2 !important;
    top: 0;
    left: 0;
}
.matrix-cell-set {
    background: var(--alice-blue-tint);
    color: var(--alice-blue);
    font-weight: 600;
}
.matrix-cell-diag {
    background: var(--alice-bg);
    color: var(--alice-text-secondary);
}
.matrix-cell-inf {
    color: #a8a6a0;
    background: var(--alice-surface);
    font-style: italic;
}
.matrix-flat-details {
    margin: 0 0 24px 0;
}
.matrix-flat-details summary {
    cursor: pointer;
    color: var(--alice-text-secondary);
    font-size: 12px;
    padding: 4px 0;
    user-select: none;
}
.matrix-flat-details summary:hover {
    color: var(--alice-blue);
}
.matrix-flat-details[open] summary {
    margin-bottom: 8px;
}

/* ── Chorus membership dual-pane ──────────────────── */
.chorus-dual-pane {
    display: flex;
    gap: 0;
    margin-top: 16px;
    align-items: flex-start;
}
.chorus-pane {
    flex: 1 1 0;
    min-width: 0;
}
/* THE SPACE GOES WHERE THE COLUMNS ARE (architect, 2026-08-28: "decrease the
   column for available users to be 30% width … maximize the width of members
   so that there is not horizontal scrolling").

   The rule above gave Available 1.3x the room on the grounds it had four
   columns to Members' two. Both halves of that stopped being true: Created was
   dropped from Available on 2026-08-24, and Members has since gained Signed in,
   AI and Submission — so the pane with the most to show had the least room and
   scrolled sideways, while the pane you are finished with as soon as you have
   picked somebody had the most. Available is a name and an address to
   recognise a person by; Members is the working surface. */
.chorus-pane--left {
    /* 30% plus the 40px the action column gave up when its buttons became
       arrows, so the width is conserved rather than reflowed. calc, not a
       rounded-up percentage, so the two numbers stay visibly related. */
    flex: 0 0 calc(30% + 40px);
}
/* At 30% the FERPA column falls off the end, and `.chorus-table-scroll` only
   scrolls vertically — so it was not narrow, it was GONE, with no way to reach
   it. The narrow pane gets a horizontal scrollbar of its own; the wide one
   still must not need one, which is the whole point of the split. */
.chorus-pane--left .chorus-table-scroll {
    overflow-x: auto;
}
.chorus-pane--left .chorus-table-scroll .admin-table {
    min-width: 320px;
}
.chorus-pane--right {
    flex: 1 1 auto;
    min-width: 0;
}
.chorus-pane h3 {
    margin: 0 0 8px 0;
    font-size: 14px;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--alice-text-secondary);
}
.chorus-table-scroll {
    /* Fixed height so both panes (Available / Members) reserve the
       same vertical space regardless of row count. ≈10 rows of 34px
       (12px font + 9+9 padding + 1px border) plus the 31px header. */
    height: 380px;
    overflow-y: auto;
    border: 1px solid var(--alice-border);
    border-radius: 6px;
    background: var(--alice-surface);
}
.chorus-table-scroll .admin-table {
    margin: 0;
    font-size: 12px;
}
.chorus-actions-col {
    /* Halved with the buttons (architect, 2026-08-28): they carry an arrow
       now, not a word, and 90px of column for a 16px glyph is width the
       Available pane wants. The 40px this gives up is handed straight back
       there — see .chorus-pane--left. */
    flex: 0 0 50px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 0 8px;
    min-height: 200px;
}
.chorus-action-btn {
    width: 40px;
    text-align: center;
    padding-left: 0;
    padding-right: 0;
}
.chorus-sortable {
    cursor: pointer;
    user-select: none;
}
.chorus-sortable:hover {
    color: var(--alice-blue);
}
@media (max-width: 768px) {
    .chorus-dual-pane { flex-direction: column; }
    .chorus-actions-col {
        flex-direction: row;
        min-height: auto;
        padding: 8px 0;
    }
}

/* ── Grading summary (assessor, Received tab) ────── */
.grading-summary {
    margin-bottom: 16px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--alice-border);
}
.grading-summary-table {
    width: auto;
    min-width: 320px;
    font-size: 13px;
}
.grading-badge {
    display: inline-block;
    min-width: 22px;
    text-align: center;
    padding: 1px 6px;
    border-radius: 10px;
    font-weight: 700;
    font-size: 12px;
}
.grading-badge--ungraded {
    background: var(--alice-amber);
    color: #1a1a1a;
}
.grading-badge--zero {
    color: var(--alice-text-secondary);
}
.grading-filter-note {
    font-size: 12px;
    color: var(--alice-text-secondary);
    margin: 8px 0 0 0;
    font-style: italic;
}

/* ── Global in-app confirm modal ──────────────────── */
.alice-confirm {
    position: fixed;
    inset: 0;
    z-index: 10000;
    display: flex;
    align-items: center;
    justify-content: center;
}
.alice-confirm.hidden { display: none; }
.alice-confirm__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
}
.alice-confirm__box {
    position: relative;
    background: var(--alice-surface);
    color: var(--alice-text);
    border: 1px solid var(--alice-border);
    border-radius: 8px;
    padding: 24px 28px;
    max-width: 420px;
    width: calc(100% - 32px);
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
.alice-confirm__box p {
    margin: 0 0 16px 0;
    font-size: 14px;
    line-height: 1.5;
}
.alice-confirm__actions {
    display: flex;
    gap: 8px;
    justify-content: flex-end;
}

/* ── Native <dialog>, for the roster's pickers ──────────────
   NOT `.alice-modal`, which is a div-inside-a-backdrop and sets
   `display:flex` — on a <dialog> that overrides the browser's own
   `display:none` and the thing is open from the moment the page loads. A
   <dialog> brings its focus trap, its Esc key and its ::backdrop for free,
   so what it needs from CSS is a box, not a positioning system. */
.alice-dialog {
    border: 1px solid var(--alice-border);
    border-radius: 8px;
    background: var(--alice-surface);
    color: var(--alice-text);
    padding: 18px 20px;
    width: min(680px, 92vw);
    max-height: 86vh;
    overflow-y: auto;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
.alice-dialog::backdrop { background: rgba(0, 0, 0, 0.45); }
.alice-dialog h3, .alice-dialog h4 { color: var(--alice-text); }

/* The recipient row above the composer. */
.recip-row {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-bottom: 10px;
}
.recip-count { font-variant-numeric: tabular-nums; }

/* ── Orchestration clipboard modal (COPY/PASTE substrate) ── */
.alice-modal-backdrop {
    position: fixed;
    inset: 0;
    z-index: 10000;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.45);
    padding: 24px;
}
.alice-modal {
    position: relative;
    background: var(--alice-surface);
    color: var(--alice-text);
    border: 1px solid var(--alice-border);
    border-radius: 8px;
    max-width: 800px;
    width: 100%;
    max-height: 80vh;
    overflow: hidden;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
    display: flex;
    flex-direction: column;
}
.alice-modal__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 24px;
    border-bottom: 1px solid var(--alice-border);
}
.alice-modal__header h2 {
    margin: 0;
    font-size: 18px;
}
.alice-modal__close {
    background: transparent;
    border: 0;
    font-size: 24px;
    line-height: 1;
    cursor: pointer;
    color: var(--alice-text);
    padding: 0 8px;
}
.alice-modal__body {
    padding: 16px 24px;
    overflow: auto;
}
.alice-modal__preview {
    border-top: 1px solid var(--alice-border);
    padding-top: 1em;
}

/* ── Assessor dashboard (Home page) ───────────────── */


.assessor-dashboard .admin-table {
    margin: 0;
    font-size: 13px;
}

/* ── Chorus selector (title-level dropdown) ──────── */
.title-chorus-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}
.title-chorus-row h1 {
    margin: 0;
}
.chorus-selector-form {
    flex-shrink: 0;
    margin: 0;
}

/* The lexia editor's assignment-availability group. It is a <fieldset>, so its
   border and padding inset every row it holds from the form around it, and its
   Opens/Closes inputs sat a hair right of every other field on the page.
   Trimming the label column by exactly that inset puts the inputs back on the
   shared vertical line; the labels stay indented, which is what reads as "these
   two belong together". */
.pagina-work-window .form-row {
    grid-template-columns: calc(180px - 0.8rem - 1px) 1fr;
}

/* The course header's actions (Export LaTeX, Roster, Performance), grouped at
   the right edge. `margin-left: auto` takes ALL of the row's free space, so the
   parent's `space-between` has none left to spread between the title, the
   section selector and each button — which is what strung them across the
   width. The gap here is the separation BETWEEN the buttons; the parent's 16px
   gap keeps this group off the selector. */
.title-chorus-row .title-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
    flex-shrink: 0;
}

/* "Export LaTeX" button on the opus header. Visible only to
   nautae with PRODUCE_DOCUMENTS munus (template-gated). Quiet
   default state so it doesn't crowd the title; brightens on
   hover. The action downloads a ZIP — Content-Disposition is
   set by the server. */
.opus-export-latex-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 10px;
    border: 1px solid var(--alice-border);
    border-radius: 4px;
    background: var(--alice-surface);
    color: var(--alice-text);
    font-size: 0.78rem;
    font-weight: 500;
    text-decoration: none;
    flex-shrink: 0;
    cursor: pointer;
    transition: background-color 0.12s, border-color 0.12s;
}
.opus-export-latex-btn:hover {
    background: var(--alice-bg-hover, rgba(0,0,0,0.04));
    border-color: var(--alice-blue, #2563eb);
    color: var(--alice-blue, #2563eb);
}
.chorus-selector-select {
    font-size: 12px;
    padding: 4px 8px;
    border: 1px solid var(--alice-border);
    border-radius: 4px;
    background: var(--alice-surface);
    color: var(--alice-text);
    cursor: pointer;
    font-family: inherit;
}
.chorus-selector-badge {
    font-size: 12px;
    padding: 3px 10px;
    border: 1px solid var(--alice-border);
    border-radius: 10px;
    background: var(--alice-bg);
    color: var(--alice-text-secondary);
    white-space: nowrap;
}
/* ════════════════════════════════════════════════════════════════════
   HOME HUB — the clean entry point (three big cards)
   ════════════════════════════════════════════════════════════════════ */
.home-hub { max-width: 1100px; }
.home-hub__title { margin: 0 0 4px; }
.home-hub__lede {
    color: var(--alice-text-secondary);
    margin: 0 0 1.75rem;
    font-size: 1.05rem;
}
.home-hub__grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 1.5rem;
}
/* Creators see 4 cards — a fixed 2x2 on desktop (Inbox + New Chat on top,
   New project + New collection below); one column on narrow screens. */
.home-hub__grid--2x2 { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 720px) {
    .home-hub__grid--2x2 { grid-template-columns: 1fr; }
}
.home-hub__card {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    text-align: left;
    border: 1px solid var(--alice-border);
    border-radius: 12px;
    padding: 2rem 1.75rem;
    min-height: 200px;
    background: var(--alice-surface);
    color: inherit;
    text-decoration: none;
    cursor: pointer;
    font: inherit;
    transition: border-color .15s ease, transform .15s ease, box-shadow .15s ease;
}
.home-hub__card:hover {
    border-color: var(--alice-teal);
    transform: translateY(-3px);
    box-shadow: 0 8px 28px rgba(0, 0, 0, 0.16);
    text-decoration: none;
}
.home-hub__icon { font-size: 2.75rem; line-height: 1; margin-bottom: 0.75rem; }
.home-hub__card h2 { margin: 0 0 0.5rem; font-size: 1.35rem; }
.home-hub__card p {
    margin: 0 0 1.25rem;
    color: var(--alice-text-secondary);
    font-size: 0.98rem;
    line-height: 1.5;
}
.home-hub__cta { margin-top: auto; font-weight: 600; color: var(--alice-teal); }

/* New-project modal */
.home-modal-overlay {
    position: fixed; inset: 0; z-index: 500;
    display: flex; align-items: center; justify-content: center;
    background: rgba(0, 0, 0, 0.5); padding: 1rem;
}
.home-modal {
    background: var(--alice-surface);
    border: 1px solid var(--alice-border);
    border-radius: 12px; padding: 1.75rem;
    width: min(460px, 94vw);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
}
.home-modal h2 { margin: 0 0 1rem; }
.home-modal .form-input {
    width: 100%; padding: 0.6rem 0.75rem; border-radius: 8px;
    border: 1px solid var(--alice-border);
    background: var(--alice-bg, var(--alice-surface));
    color: var(--alice-text); font: inherit; margin-top: 0.35rem;
}
.home-modal__actions {
    display: flex; justify-content: flex-end; gap: 0.75rem; margin-top: 1.5rem;
}
.home-modal__actions .btn-cancel {
    background: transparent; border: 1px solid var(--alice-border);
    color: var(--alice-text); padding: 0.55rem 1rem; border-radius: 8px;
    cursor: pointer; font: inherit;
}

/* ════════════════════════════════════════════════════════════════════
   INBOX tables — client-side search + pagination (inbox_table.js)
   ════════════════════════════════════════════════════════════════════ */
.inbox-toolbar { margin: 0 0 10px; }
.inbox-search {
    width: 100%;
    max-width: 380px;
    padding: 0.45rem 0.7rem;
    border: 1px solid var(--alice-border);
    border-radius: 6px;
    background: var(--alice-bg, var(--alice-surface));
    color: var(--alice-text);
    font: inherit;
}
.inbox-client-pager {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: 12px;
}
.inbox-client-pager .inbox-per-page {
    margin-left: auto;
    max-width: 130px;
}

/* ════════════════════════════════════════════════════════════════════
   FREE CHAT — the agent panel IS the page (centered, full central space)
   ════════════════════════════════════════════════════════════════════ */
body.free-chat-page .agent-panel {
    left: 50%;
    right: auto;
    transform: translateX(-50%);
    width: min(960px, 94vw) !important;
    max-width: 960px !important;
    min-width: 0 !important;
    box-shadow: 0 8px 40px rgba(0, 0, 0, 0.22);
}
body.free-chat-page #agent-toggle,
body.free-chat-page .agent-panel-resizer { display: none; }
/* The centered panel doesn't sit on the right, so don't reserve a right
   gutter or shift the top chrome (undoes the .agent-panel-open reflow). */
@media (min-width: 601px) {
    body.free-chat-page.agent-panel-open { padding-right: 0; }
    body.free-chat-page.agent-panel-open .topnav,
    body.free-chat-page.agent-panel-open .system-banner,
    body.free-chat-page.agent-panel-open .impersonation-banner { margin-right: 0; }
}

/* ── Inbox: FERPA badge, merged when-cell, header filters ──────────────
   A privacy notice is the one Inbox row carrying a legal obligation, so it
   reads red rather than sharing the teal "notification" badge. */
.nav-role-badge.inbox-badge-comm {
    background: var(--alice-teal-soft);
    color: var(--alice-teal-text);
    border-color: var(--alice-teal-text);
}
.nav-role-badge.inbox-badge-ferpa {
    background: color-mix(in srgb, var(--alice-danger, #c0392b) 12%, transparent);
    color: var(--alice-danger, #c0392b);
    border-color: var(--alice-danger, #c0392b);
    font-weight: 600;
    letter-spacing: .04em;
}
/* Dark theme: the light-theme badge is dark red text on a 12% red tint, which
   over a dark surface computes to 2.82:1 — the badge that matters most became
   the least readable one. Light red text on a darker tint restores it to
   6.8:1, and keeps the colour reading as "red" rather than washing to grey. */
html.th-dark .nav-role-badge.inbox-badge-ferpa,
body.th-dark .nav-role-badge.inbox-badge-ferpa {
    background: #312120;
    color: #F1948A;
    border-color: #B4564A;
}
/* ...and the same for OS dark with no explicit choice, which is the DEFAULT
   state. Scoping the fix to `.th-dark` alone fixed it only for users who had
   opened the theme menu; everyone else kept the 2.82:1 badge. The guard list
   mirrors the token block above so an explicitly-chosen theme still wins. */
@media (prefers-color-scheme: dark) {
    html:not(.th-light):not(.th-hico):not(.th-deut):not(.th-prot):not(.th-trit)
        .nav-role-badge.inbox-badge-ferpa {
        background: #312120;
        color: #F1948A;
        border-color: #B4564A;
    }
}

/* The timestamp now shares a cell with the person; it stays subordinate. */
.inbox-cell-when {
    color: var(--alice-text-muted, #6b7280);
    font-size: 11px;
}
/* Per-column filter row, sitting directly under the header labels. */
.inbox-filter-row td {
    padding: 2px 6px 6px;
    border-bottom: 1px solid var(--alice-border, #e5e7eb);
    background: var(--alice-surface-2, transparent);
}
.inbox-filter-select {
    width: 100%;
    min-width: 6rem;
    font-size: 12px;
    padding: 2px 4px;
    border: 1px solid var(--alice-border, #d1d5db);
    border-radius: 4px;
    background: var(--alice-surface, #fff);
    color: inherit;
}
.inbox-filter-select.is-active {
    border-color: var(--alice-accent, #2a7);
    font-weight: 600;
}
/* Notification modal: metadata block and the redacted-prompt evidence. */
.comm-modal-meta {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 2px 12px;
    margin: 0 0 1rem;
    font-size: 13px;
}
.comm-modal-meta dt { color: var(--alice-text-muted, #6b7280); }
.comm-modal-meta dd { margin: 0; }
.comm-modal-evidence {
    margin-top: 1rem;
    padding-top: .75rem;
    border-top: 1px solid var(--alice-border, #e5e7eb);
}
.comm-modal-evidence h3 { font-size: 13px; margin: 0 0 .4rem; }
.comm-modal-prompt {
    white-space: pre-wrap;
    word-break: break-word;
    background: var(--alice-surface-2, #f6f7f9);
    border: 1px solid var(--alice-border, #e5e7eb);
    border-radius: 6px;
    padding: .6rem .7rem;
    font-size: 12.5px;
    max-height: 40vh;
    overflow: auto;
}
.comm-modal-note { font-size: 11.5px; margin: .4rem 0 0; }

/* ── Narrow viewports: keep the burger on screen ──────────────────────
   WCAG 1.4.10 Reflow (AA). At 320px the top bar laid out to 331px, so the
   burger button — measured at left:299 right:331 — sat outside the viewport
   on every page. It is the ONLY route to the Privacy, Help and Accessibility
   menus, which makes it the worst possible control to lose at the width a
   low-vision user is most likely to be using.

   The e-mail chip is what gives way: the same identity is one tap into the
   burger menu, whereas the burger has no alternative route. Tightening the
   paddings and gaps recovers the rest. */
@media (max-width: 480px) {
    .topnav-inner  { padding: 0 8px; }
    .topnav-brand  { margin-right: 8px; }
    .topnav-user   { gap: 6px; min-width: 0; }
    .nav-email     { display: none; }
    .nav-mode-toggle { min-width: 0; }
}

/* Wide data tables scroll inside their own container rather than widening the
   document — the reflow exception for content that needs two-dimensional
   layout. Without this the whole page scrolls sideways and every other
   control drifts off-screen with it. */
@media (max-width: 640px) {
    .admin-table,
    .inbox-table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; }

    /* Tab strips are the other thing that widened the document: laid out
       flex-nowrap, the lexia bar reached 584px and the course bar 606px on a
       320px screen, dragging every other control off with them. Scrolling the
       strip keeps the page itself still. Keyboard access is unaffected —
       focusing a tab scrolls it into view. */
    .pagina-tab-bar,
    .crs-tab-bar,
    .inbox-tab-bar,
    .agent-tab-list {
        overflow-x: auto;
        flex-wrap: nowrap;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: thin;
    }
}

/* ── Gradebook (section members page, 2026-08-20) ────────
   A students × assignments matrix. Two things make it readable that a plain
   .admin-table does not:

   1. The column headers are LEXIA TITLES, which are sentences. Left to wrap,
      ten of them build a header four lines tall that pushes every score below
      the fold. They are clipped to one line with the full title in `title=`,
      so nothing is lost — it moves to the hover.
   2. The name column STICKS. Scrolling right to reach a later assignment
      otherwise takes the student's name off screen, and a column of numbers
      with no names attached is not a gradebook. */
.gradebook-table th,
.gradebook-table td { white-space: nowrap; }
.gradebook-table thead th {
    max-width: 9rem;
    overflow: hidden;
    text-overflow: ellipsis;
}
.gradebook-table th:first-child,
.gradebook-table td:first-child {
    position: sticky;
    left: 0;
    z-index: 1;
    min-width: 12rem;
    max-width: 16rem;
    overflow: hidden;
    text-overflow: ellipsis;
    /* Opaque, or the scrolled-under cells show through the sticky column. */
    background: var(--alice-surface);
}
.gradebook-table tr:hover td:first-child { background: var(--alice-bg); }
.gradebook-col-link { text-decoration: none; }
.gradebook-col-link:hover { text-decoration: underline; }

/* ── Lexia colour chip (user-facing lexia title) ──────────────────────────
   A rounded square carrying the colour this lexia wears on the course map, so
   a student can tell which branch they are standing in without going back to
   the map to check.

   The colour resolves server-side in the map's own precedence — per-lexia
   override (migration 154), then the branch colour (migration 478) — and lands
   as an inline background-color. When neither exists there is no inline style
   and the type class below supplies the default, read from the SAME
   --alice-* variables alice_map.js uses. That is deliberate: two hard-coded
   palettes would drift, and a student would be shown one colour on the map and
   a different one on the page.

   Decorative only. It is aria-hidden and never the sole carrier of meaning. */
.lexia-color-chip {
    display: inline-block;
    width: 0.72em;
    height: 0.72em;
    border-radius: 0.18em;
    margin-right: 0.45em;
    vertical-align: baseline;
    flex-shrink: 0;
    /* A pale branch colour on a pale page would vanish; a hairline keeps the
       square legible in both themes without asserting a colour of its own. */
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.18);
    background-color: var(--alice-blue);
}
/* Type defaults, mirroring alice_map.js: START/END teal, EXAMPLE amber,
   INTERMEDIATE and untyped blue (the map's base node style). */
.lexia-color-chip--start,
.lexia-color-chip--end          { background-color: var(--alice-teal); }
.lexia-color-chip--example      { background-color: var(--alice-amber); }
.lexia-color-chip--intermediate,
.lexia-color-chip--none         { background-color: var(--alice-blue); }

/* ── Author colour: a closed, contrast-checked palette ────────────────────
   Markdown `==accent|text==` renders <span class="md-c-accent">. Authors pick
   from these five and cannot express anything else, which is the point: the
   request was for ACCESSIBLE colour, and a free picker cannot promise that —
   it lets an author put yellow on white. Each value below clears 4.5:1 against
   the surface it sits on, in light and dark.

   These are also why the feature needs no author HTML: a class carries the
   colour, so the `| markdown` filter keeps escaping <span style="…"> and the
   revert of 2026-08-21 stays intact. */
:root {
    --md-c-accent:  #1B5FA8;
    --md-c-caution: #8A5200;
    --md-c-danger:  #B3261E;
    --md-c-success: #1B6B45;
    --md-c-muted:   #5F5F5A;
}
html.th-dark {
    --md-c-accent:  #7FB3F0;
    --md-c-caution: #E8B25C;
    --md-c-danger:  #F2938C;
    --md-c-success: #7FD0A6;
    --md-c-muted:   #A8A6A0;
}
@media (prefers-color-scheme: dark) {
    html:not(.th-dark):not(.th-light):not(.th-hico):not(.th-deut):not(.th-prot):not(.th-trit) {
        --md-c-accent:  #7FB3F0;
        --md-c-caution: #E8B25C;
        --md-c-danger:  #F2938C;
        --md-c-success: #7FD0A6;
        --md-c-muted:   #A8A6A0;
    }
}
.md-c-accent  { color: var(--md-c-accent); }
.md-c-caution { color: var(--md-c-caution); }
.md-c-danger  { color: var(--md-c-danger); }
.md-c-success { color: var(--md-c-success); }
.md-c-muted   { color: var(--md-c-muted); }

/* ── Numbered code blocks ─────────────────────────────────────────────────
   CSS counts elements, not lines of text, so the renderer emits one
   <span class="cl"> per line (app/templates.py) and the counter runs over
   those. The numbers are generated content: they are NOT part of the text, so
   selecting the code and copying it yields the code alone. Baking numbers into
   the text would have made every copied snippet unrunnable. */
pre.code-numbered {
    counter-reset: alice-cl;
    overflow-x: auto;
}
pre.code-numbered > code > .cl {
    display: block;
    counter-increment: alice-cl;
}
pre.code-numbered > code > .cl::before {
    content: counter(alice-cl);
    display: inline-block;
    width: 2.2em;
    margin-right: 1em;
    text-align: right;
    color: var(--alice-text-tertiary);
    /* Belt and braces alongside generated content: some browsers will still
       put ::before text on the clipboard for a whole-page selection. */
    -webkit-user-select: none;
    user-select: none;
}

/* ── Markdown editor colour swatches ──────────────────────────────────────
   Toolbar buttons that show the colour they apply, because a row of identical
   icons labelled only by tooltip makes the author hunt. The letter A is the
   conventional "text colour" mark and keeps the button legible for anyone who
   cannot distinguish the swatches — colour is not the only cue. */
.editor-toolbar button.alice-mde-swatch { position: relative; }
.editor-toolbar button.alice-mde-swatch::after {
    content: 'A';
    font-weight: 700;
    font-size: 12px;
    line-height: 1;
}
.editor-toolbar button.alice-mde-swatch--accent::after  { color: var(--md-c-accent); }
.editor-toolbar button.alice-mde-swatch--caution::after { color: var(--md-c-caution); }
.editor-toolbar button.alice-mde-swatch--danger::after  { color: var(--md-c-danger); }
.editor-toolbar button.alice-mde-swatch--success::after { color: var(--md-c-success); }
.editor-toolbar button.alice-mde-swatch--muted::after   { color: var(--md-c-muted); }

/* ── 155: agent change-set card, workbench lock banner, diff fragment ── */
.agent-changeset {
  margin: 8px 0 12px; padding: 10px 12px;
  border: 1px solid var(--alice-border); border-left: 4px solid var(--alice-amber, #b45309);
  border-radius: 6px; background: var(--alice-surface); color: var(--alice-text); font-size: 13px;
}
.agent-changeset[data-state="APPROVED"], .agent-changeset[data-state="APPLIED_AUTO"] { border-left-color: var(--alice-green, #15803d); }
.agent-changeset[data-state="REJECTED"], .agent-changeset[data-state="EXPIRED"], .agent-changeset[data-state="REAPED"] { border-left-color: var(--alice-text-secondary); }
.agent-changeset__head { display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; font-weight: 600; margin-bottom: 6px; }
.agent-changeset__mode, .agent-changeset__state { font-weight: 400; color: var(--alice-text-secondary); }
.agent-changeset__hx { font-family: monospace; font-size: 12px; }
.agent-changeset__paths { list-style: none; margin: 0 0 6px; padding: 0; max-height: 240px; overflow: auto; }
.agent-changeset__file summary { cursor: pointer; display: flex; gap: 8px; align-items: baseline; }
.agent-changeset__file .op { font-family: monospace; width: 1.2em; }
.agent-changeset__file .op[data-op="A"] { color: var(--alice-green, #15803d); }
.agent-changeset__file .op[data-op="D"] { color: var(--alice-red, #b91c1c); }
.agent-changeset__file .stat { color: var(--alice-text-secondary); font-size: 12px; }
.agent-changeset__note { margin: 4px 0; color: var(--alice-text-secondary); }
.agent-changeset__downloads { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0; }
.agent-changeset__actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 6px; }
.agent-changeset__status { color: var(--alice-text-secondary); }
.agent-changeset__commit-form { display: flex; flex-wrap: wrap; gap: 6px; width: 100%; }
.agent-changeset__commit-form input { flex: 1 1 140px; }
.agent-diff { margin: 0; padding: 6px 8px; overflow: auto; max-height: 360px; font-size: 12px; line-height: 1.4; background: var(--alice-bg, #fafafa); }
.agent-diff .diff-add { color: var(--alice-green, #15803d); }
.agent-diff .diff-del { color: var(--alice-red, #b91c1c); }
.agent-diff .diff-hunk { color: var(--alice-blue, #1d4ed8); }
.agent-diff .diff-meta { color: var(--alice-text-secondary); }
#agent-chg-mode { margin-left: auto; font-size: 12px; }
.repo-chg-banner { display: flex; gap: 10px; align-items: center; padding: 6px 12px; background: var(--alice-amber-bg, #fff7ed);
  border-bottom: 1px solid var(--alice-amber, #b45309); color: var(--alice-text); font-size: 0.9rem; }
.repo-chg-badge { font-size: 0.8rem; padding: 1px 6px; border-radius: 10px; border: 1px solid var(--alice-amber, #b45309); }


/* ── V4/157: per-user agent templates (my templates list, designer, AGENT asset card, repo banner) ── */
.tpl-list { display: grid; gap: 0.75rem; grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr)); }
.tpl-card { border: 1px solid var(--alice-border, #ddd); border-radius: 6px; padding: 0.75rem; background: var(--alice-surface, #fff); }
.tpl-card__head { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.tpl-card__actions { display: flex; gap: 0.35rem; flex-wrap: wrap; margin-top: 0.5rem; }
.tpl-hash { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 0.8em; }
.tpl-readonly { margin: 0.5rem 0; padding: 0.5rem 0.75rem; border-left: 3px solid var(--alice-warn, #c7861a); background: var(--alice-bg, #f8f8f8); }
.repo-agent-banner { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }

/* ── V4/156: declared sub-agents (delegate rows, the picker, the Flow tab's child cards) ── */
.max-delegate-rows .max-delegate-stale { margin: 0.5rem 0; padding: 0.5rem 0.75rem; border-left: 3px solid var(--alice-warn, #c7861a); background: var(--alice-bg, #f8f8f8); }
.max-delegate-rows .max-delegate-disabled { margin: 0.5rem 0; padding: 0.5rem 0.75rem; border-left: 3px solid var(--alice-border, #ddd); color: var(--alice-text-secondary, #666); }
#max-delegate-pick { max-width: 28rem; border: 1px solid var(--alice-border, #ddd); border-radius: 6px; padding: 1rem 1.25rem; background: var(--alice-surface, #fff); color: var(--alice-text, #111); }
#max-delegate-pick h2 { margin-top: 0; font-size: 1rem; }
#max-delegate-pick select { width: 100%; margin: 0.25rem 0 0.5rem; }
#max-delegate-pick .max-delegate-pick-actions { display: flex; gap: 0.5rem; justify-content: flex-end; }
#max-delegate-pick-status { min-height: 1.2em; font-size: 0.85em; color: var(--alice-danger, #b00); }
.agent-flaws-card--depth-1 { margin-left: calc(12px * 1); }
.agent-flaws-card--depth-2 { margin-left: calc(12px * 2); }
.agent-flaws-card--depth-3 { margin-left: calc(12px * 3); }
.agent-flaws-card--depth-4 { margin-left: calc(12px * 4); }
.agent-flaws-card--refused { border-left: 3px solid var(--alice-danger, #b00); }
.agent-delegate-foot { font-size: 0.8em; color: var(--alice-text-secondary, #666); margin: 0.35rem 0 0; }

/* A counting stat card is a door to the rows it counts (2026-09-16):
   the number and the title link to the filtered list; hover says so. */
.indicator-card .stat-link,
.indicator-card h3 a { text-decoration: none; }
.indicator-card .stat-link:hover,
.indicator-card h3 a:hover { text-decoration: underline; }


/* ── Tabula type scale (2026-09-16) ─────────────────────────────
   A dashboard, a record and a collection list opened through /n/ read at
   the Inbox's size, which is the course's: 14px body copy, 13px tables and
   tabs, 12px asides, 15px/600 card headings. The cards were inheriting the
   16px page base, so the same portfolio looked heavier on its dashboards
   than in the Inbox one click away. Scoped by body.tabula-page, set by the
   node router, so the admin builder pages keep their own size. */
.dash-nav__tab { font-size: 0.88rem; }
.indicator-card h3 { font-size: 1rem; }
body.tabula-page main { font-size: 14px; }
body.tabula-page main p,
body.tabula-page .indicator-card,
body.tabula-page .dash-grid { font-size: 14px; }
body.tabula-page .indicator-card h3 { font-size: 15px; font-weight: 600; }
body.tabula-page .dash-nav__tab { font-size: 13px; }
body.tabula-page main table { font-size: 13px; }
body.tabula-page main .text-muted { font-size: 12px; }


/* ── Vela course screenshots (2026-09-16) ─────────────────────────
   Captured at a 960px viewport (tools/build_vela_manual.py), shown centred
   at the lesson column's width, so the type in the picture reads a shade
   smaller than the page's own and nothing is ever upscaled past the
   capture. The same frame and shadow the ALICE manual's pictures get. */
.pagina-content img[src*="vela_manual"] {
    display: block;
    max-width: 100%;
    height: auto;
    margin: 14px auto;
    border: 1px solid var(--alice-border);
    border-radius: 6px;
    box-shadow: 0 1px 4px rgba(0,0,0,0.06);
}
/* A full-width capture fills the column; a crop (the builder writes its
   CSS size as {width=N center}) keeps that size and is never upscaled. */
.pagina-content img[src*="vela_manual"]:not([style*="width"]) {
    width: 100%;
    max-width: 960px;
}

/* ── Grades for Canvas (opus/grades.html) ─────────────────────────────
 * Two upload boxes side by side, a matching table that reads left to right
 * (WeBWorK set → lesson → Canvas column), then the preview. A lowered grade is
 * marked by tint AND by the WeBWorK figure printed beside it, never colour alone.
 */
.cnv-intro { max-width: 60rem; color: var(--alice-text-secondary); }
.cnv-uploads {
    display: grid; gap: 16px; margin: 16px 0;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.cnv-uploads--one { grid-template-columns: 1fr; }
.cnv-box {
    border: 1px solid var(--alice-border); border-radius: var(--radius-md);
    background: var(--alice-surface); padding: 14px 16px;
}
.cnv-box__title { font-size: 1rem; margin: 0 0 4px; }
.cnv-box__help { font-size: 0.8rem; color: var(--alice-text-secondary); margin: 0 0 10px; max-width: 60rem; }
.cnv-drop {
    position: relative; display: flex; align-items: center; justify-content: center;
    min-height: 84px; padding: 12px; text-align: center; cursor: pointer;
    border: 2px dashed var(--alice-border-strong); border-radius: var(--radius-md);
    color: var(--alice-text-secondary); font-size: 0.85rem;
}
.cnv-drop:hover, .cnv-drop--over { border-color: var(--alice-focus); color: var(--alice-text); }
.cnv-drop:focus-within { outline: 2px solid var(--alice-focus); outline-offset: 2px; }
.cnv-drop__input {
    position: absolute; inset: 0; width: 100%; height: 100%;
    opacity: 0; cursor: pointer;
}
.cnv-chips { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.cnv-chip {
    display: inline-flex; align-items: center; gap: 4px; max-width: 100%;
    padding: 2px 4px 2px 10px; font-size: 0.78rem;
    border: 1px solid var(--alice-border); border-radius: 999px; background: var(--alice-bg);
}
.cnv-chip__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cnv-chip__x {
    border: 0; background: transparent; color: var(--alice-text-secondary);
    font-size: 1rem; line-height: 1; padding: 2px 6px; border-radius: 999px; cursor: pointer;
}
.cnv-chip__x:hover, .cnv-chip__x:focus-visible { color: var(--alice-danger); background: var(--alice-surface); }
.cnv-errlist { margin: 0; padding-left: 18px; }
.cnv-step { margin-top: 20px; }
.cnv-step h2 { font-size: 1.1rem; margin-bottom: 4px; }
/* position: relative contains the cells' .sr-only text, which is absolutely
   positioned and otherwise escapes this scroller and widens the page. */
.cnv-table-wrap { position: relative; overflow-x: auto; border: 1px solid var(--alice-border); border-radius: var(--radius-md); }
.cnv-map-table td { vertical-align: top; }
.cnv-set strong { word-break: break-word; }
.cnv-sub { font-size: 0.75rem; color: var(--alice-text-tertiary); margin-top: 2px; }
.cnv-arrow { color: var(--alice-text-tertiary); padding-left: 0 !important; padding-right: 0 !important; }
.cnv-select { max-width: 22rem; width: 100%; font-size: 0.8rem; padding: 4px 6px; }
.cnv-badge {
    display: inline-block; margin-top: 4px; padding: 1px 8px; font-size: 0.7rem;
    border-radius: 999px; background: var(--alice-blue-tint); color: var(--alice-blue-text);
}
.cnv-badge--guess { background: var(--alice-amber-tint); color: var(--alice-amber-text); }
.cnv-checks { list-style: none; margin: 0; padding: 0; font-size: 0.78rem; }
.cnv-chk { margin: 1px 0; }
.cnv-chk__glyph { display: inline-block; width: 1.1em; text-align: center; font-weight: 700; }
.cnv-chk--ok { color: var(--alice-text-secondary); }
.cnv-chk--warn { color: var(--alice-amber-text); }
.cnv-chk--error { color: var(--alice-danger); font-weight: 600; }
.cnv-row--open td { background: var(--alice-bg); }
.cnv-actions {
    display: flex; align-items: center; justify-content: flex-end; gap: 12px;
    flex-wrap: wrap; margin-top: 14px;
}
.cnv-status { margin: 0; font-size: 0.85rem; color: var(--alice-text-secondary); flex: 1 1 16rem; }
.cnv-readonly { font-size: 0.75rem; color: var(--alice-text-tertiary); text-align: right; margin: 4px 0 0; }
.cnv-summary { margin: 6px 0 10px; padding-left: 18px; font-size: 0.85rem; }
.cnv-warnline { color: var(--alice-amber-text); }
.cnv-filter { display: inline-flex; align-items: center; gap: 6px; font-size: 0.85rem; margin-bottom: 6px; }
.cnv-legend { font-size: 0.75rem; color: var(--alice-text-secondary); margin: 0 0 8px; }
.cnv-legend .cnv-cell { margin-left: 10px; }
.cnv-pv-table th.cnv-name { font-weight: 500; text-transform: none; letter-spacing: 0;
    font-size: 13px; color: var(--alice-text); white-space: nowrap; }
.cnv-login { font-family: var(--font-mono, monospace); font-size: 0.78rem; color: var(--alice-text-secondary); word-break: break-all; }
.cnv-num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.cnv-cell { display: inline-block; min-width: 2.5em; padding: 1px 6px; border-radius: 4px; }
.cnv-cell--withheld { background: var(--alice-amber-tint); color: var(--alice-amber-text); font-weight: 600; }
.cnv-cell--kept { color: var(--alice-text-tertiary); border: 1px dashed var(--alice-border-strong); }
.cnv-ww { margin-left: 4px; font-size: 0.72rem; color: var(--alice-text-tertiary); }
.cnv-only-diff tr.cnv-row--same { display: none; }
/* Phones: each matching row becomes a card, the column names ride on the cells. */
@media (max-width: 640px) {
    .cnv-map-table thead { display: none; }
    .cnv-map-table, .cnv-map-table tbody, .cnv-map-table tr, .cnv-map-table td { display: block; width: 100%; box-sizing: border-box; }
    .cnv-map-table tr { border-bottom: 1px solid var(--alice-border); padding: 4px 0; }
    .cnv-map-table td { border-bottom: 0; padding: 6px 12px; }
    .cnv-map-table td.cnv-arrow { display: none; }
    .cnv-map-table td[data-label]::before {
        content: attr(data-label); display: block; margin-bottom: 3px;
        font-size: 11px; text-transform: uppercase; letter-spacing: .06em;
        color: var(--alice-text-tertiary);
    }
    .cnv-select { max-width: none; }
}

/* ── Grade page: the AI's verdict per problem (admin/grade.html) ──────
 * The header's right half holds one cell per problem (number, name, suggested
 * band); the same list, one column wide, sticks beside the cards. The lowest
 * band is tinted and marked ▼; a verdict that needs the instructor is outlined
 * and marked !, so neither reading depends on colour.
 */
.grade-context { display: flex; flex-wrap: wrap; gap: 12px 24px; align-items: flex-start; }
.grade-context__meta { flex: 1 1 18rem; min-width: 0; margin: 0; overflow-wrap: anywhere; }
.grade-summary { flex: 1 1 24rem; min-width: 0; }
.grade-summary__head {
    margin: 0 0 6px; font-size: 11px; font-weight: 600; text-transform: uppercase;
    letter-spacing: .06em; color: var(--alice-text-tertiary);
}
.grade-summary__grid {
    list-style: none; margin: 0; padding: 0; display: grid; gap: 4px;
    grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));
}
.grade-summary__legend { margin: 6px 0 0; font-size: 11px; color: var(--alice-text-tertiary); }
.grade-sum {
    display: flex; flex-direction: column; gap: 2px; height: 100%; box-sizing: border-box;
    padding: 4px 6px; font-size: 12px; line-height: 1.25; text-decoration: none;
    color: var(--alice-text); background: var(--alice-surface);
    border: 1px solid var(--alice-border); border-radius: 6px;
}
/* These cells are controls. The site-wide `li > a:not(...)` prose-link rule
   (specificity 0,4,2) underlines every anchor in a list item and cut through
   every line of them; nothing short of !important outranks it here. */
.grade-summary a.grade-sum, .grade-rail a.grade-rail__item { text-decoration: none !important; }
.grade-sum:hover, .grade-sum:focus-visible { border-color: var(--alice-focus); }
.grade-sum__n { font-weight: 700; font-variant-numeric: tabular-nums; }
.grade-sum__mark { margin-left: 4px; font-weight: 700; }
.grade-sum__name {
    font-size: 11px; color: var(--alice-text-secondary); overflow: hidden;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.grade-sum__band { font-weight: 600; }
.grade-sum--below { background: var(--alice-amber-tint); border-color: var(--alice-amber); }
.grade-sum--below .grade-sum__band, .grade-sum--below .grade-sum__mark,
.grade-sum--below .grade-rail__band { color: var(--alice-amber-text); }
.grade-sum--review { border: 1px dashed var(--alice-danger); }
.grade-sum--review .grade-sum__mark { color: var(--alice-danger); }
.grade-sum--none .grade-sum__band { color: var(--alice-text-tertiary); font-weight: 400; }
.grade-sum.is-current, .grade-rail__item.is-current { outline: 2px solid var(--alice-focus); outline-offset: 1px; }
.grade-card { scroll-margin-top: 12px; }
/* ?section=NAME scrolls a collapsible section to the top (alice.js); clear
   the sticky top bar so its heading is not hidden under it. */
details.admin-section { scroll-margin-top: 64px; }

.grade-assess-layout--rail {
    display: grid; grid-template-columns: minmax(0, 1fr) 30px; gap: 8px; align-items: start;
}
.grade-assess-main { min-width: 0; }
.grade-rail {
    position: sticky; top: 12px; max-height: calc(100vh - 24px);
    overflow-y: auto; overflow-x: visible; padding: 5px 0;
}
.grade-rail ol { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 3px; }
.grade-rail__item {
    display: flex; flex-direction: column; align-items: center;
    padding: 3px 0; line-height: 1.1; text-decoration: none; color: var(--alice-text);
    background: var(--alice-surface); border: 1px solid var(--alice-border); border-radius: 5px;
}
.grade-rail__item:hover, .grade-rail__item:focus-visible { border-color: var(--alice-focus); }
.grade-rail__item.grade-sum--below { background: var(--alice-amber-tint); border-color: var(--alice-amber); }
.grade-rail__item.grade-sum--review { border: 1px dashed var(--alice-danger); }
.grade-rail__n { font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums; }
.grade-rail__band { font-size: 9px; letter-spacing: .02em; color: var(--alice-text-secondary); }
.grade-rail__mark { margin-left: 1px; font-size: 9px; font-weight: 700; color: var(--alice-amber-text); }
.grade-rail__item.grade-sum--review .grade-rail__mark { color: var(--alice-danger); }
/* Under a locked top bar the bar and the breadcrumb stay pinned, and both the
   rail and a problem jumped to would slide beneath them. --lingua-tab-top is
   their combined height, kept current by alice.js (updateNavOffsets). */
body.nav-locked .grade-rail {
    top: calc(var(--lingua-tab-top, 44px) + 12px);
    max-height: calc(100vh - var(--lingua-tab-top, 44px) - 24px);
}
body.nav-locked .grade-card { scroll-margin-top: calc(var(--lingua-tab-top, 44px) + 12px); }
@media (max-width: 640px) {
    .grade-assess-layout--rail { display: block; }
    .grade-rail { display: none; }
}
