        :root {
            --primary-color: #4338ca;
            --primary-hover: #3730a3;
            --secondary-color: #475569;
            --success-color: #059669;
            --warning-color: #d97706;
            --danger-color: #dc2626;
            --info-color: #0891b2;
            /* The two greens of the u2i logo, for the places that are about u2i
               people rather than about the app: the endorsement pages. */
            --u2i-green: #408E3C;
            --u2i-lime: #94C73C;
            --bg-light: #f1f5f9;
            --card-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
            --card-hover-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);

            /* Even more vibrant background tints for highlights */
            --success-bg-vibrant: #34d399;
            --warning-bg-vibrant: #fbbf24;
            --danger-bg-vibrant: #f87171;
            --info-bg-vibrant: #22d3ee;
        }

        [data-bs-theme="dark"] {
            --bg-light: #0f172a;
            --card-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.5), 0 2px 4px -2px rgb(0 0 0 / 0.5);
            --card-hover-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.5), 0 4px 6px -4px rgb(0 0 0 / 0.5);
        }

        body {
            font-family: 'Inter', sans-serif;
            background-color: var(--bg-light);
            transition: background-color 0.3s, color 0.3s;
        }

        html {
            scroll-padding-top: 70px;
        }

        .navbar {
            box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1);
            padding: 0.5rem 0;
            transition: background-color 0.3s;
        }

        [data-bs-theme="light"] .navbar {
            background-color: #ffffff !important;
        }

        [data-bs-theme="dark"] .navbar {
            background-color: #1e293b !important;
            box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.5);
        }

        /* endorse.u2i.com serves one page, so the navigation gets out of the
           way there: it sits above the viewport and slides down when the
           pointer reaches the top edge (or a tap on the same strip, or a
           keyboard focus lands inside it). The peek strip is a sibling *before*
           the nav so `:hover ~ .navbar` can reveal it; the nav leaves the
           document flow, so .chrome-hidden-page pays back the space it used. */
        /* No navigation bar sits above the page on an endorsement host, so it
           pays back the space the bar would have used. */
        .chrome-hidden-page { padding-top: 1.25rem; }

        .navbar-brand {
            font-weight: 700;
            color: var(--primary-color) !important;
            letter-spacing: -0.025em;
        }

        .nav-link {
            font-weight: 500;
            transition: all 0.2s;
            padding: 0.5rem 0.6rem !important;
            font-size: 0.92rem;
            white-space: nowrap;
        }

        .nav-link .bi {
            opacity: 0.75;
            margin-right: 0.35rem;
        }

        .navbar .btn { white-space: nowrap; }

        [data-bs-theme="light"] .nav-link {
            color: #475569 !important;
        }

        .nav-link:hover {
            color: var(--primary-color) !important;
            background-color: rgba(79, 70, 229, 0.04);
            border-radius: 0.5rem;
        }

        .nav-link.active {
            color: var(--primary-color) !important;
            background-color: rgba(79, 70, 229, 0.08);
            border-radius: 0.5rem;
        }

        [data-bs-theme="dark"] .nav-link.active {
            background-color: rgba(255, 255, 255, 0.1);
            color: #fff !important;
        }

        [data-bs-theme="dark"] .nav-link:hover {
            background-color: rgba(255, 255, 255, 0.05);
            color: #fff !important;
        }

        .btn-primary {
            background-color: var(--primary-color);
            border-color: var(--primary-color);
            font-weight: 500;
            padding: 0.35rem 1rem;
            border-radius: 0.5rem;
            transition: all 0.2s;
        }

        .btn-sm {
            padding: 0.25rem 0.75rem;
            border-radius: 0.375rem;
            font-size: 0.875rem;
        }

        .btn-primary:hover {
            background-color: var(--primary-hover);
            border-color: var(--primary-hover);
            transform: translateY(-1px);
        }

        .card {
            border: none;
            border-radius: 0.5rem;
            box-shadow: var(--card-shadow);
            transition: transform 0.2s, box-shadow 0.2s, background-color 0.3s;
        }

        .card-hover:hover {
            transform: translateY(-2px);
            box-shadow: var(--card-hover-shadow);
        }

        .form-check-input {
            cursor: pointer;
            border-color: #cbd5e1;
        }

        .form-check-input:checked {
            background-color: var(--primary-color);
            border-color: var(--primary-color);
        }

        [data-bs-theme="light"] .form-check-input:not(:checked) {
            background-color: #fff;
            border: 1px solid #94a3b8;
        }

        .list-group-item {
            border-left: none;
            border-right: none;
            padding: 1rem 1.25rem;
            transition: all 0.2s;
            border-bottom: 1px solid rgba(0,0,0,0.05);
        }

        [data-bs-theme="dark"] .list-group-item {
            border-bottom: 1px solid rgba(255,255,255,0.05);
        }

        .list-group-item:first-child { border-top: none; }
        .list-group-item:last-child { border-bottom: none; }

        .list-group-item:hover {
            background-color: rgba(79, 70, 229, 0.03);
        }

        [data-bs-theme="dark"] .list-group-item:hover {
            background-color: rgba(255, 255, 255, 0.03);
        }

        .badge {
            font-weight: 600;
            padding: 0.35em 0.65em;
            border-radius: 0.375rem;
        }

        .extra-small { font-size: 0.72rem; }

        /* Bootstrap 5 stops at `btn-sm`, but the dense tables here need one step
           smaller — and the markup has been asking for `btn-xs`/`btn-group-xs`
           all along, with nothing defining them, so those controls rendered at
           full size. Sized through Bootstrap's own button variables so variants
           (outline, link, .btn-score) keep working untouched. */
        .btn-xs,
        .btn-group-xs > .btn {
            --bs-btn-padding-y: 0.1rem;
            --bs-btn-padding-x: 0.4rem;
            --bs-btn-font-size: 0.72rem;
            --bs-btn-border-radius: var(--bs-border-radius-sm);
            line-height: 1.4;
        }
        /* A one-character score button would otherwise be narrower than the
           finger aiming at it, and narrower than "N/E" beside it. */
        .btn-group-xs > .btn-score { min-width: 1.7rem; }

        /* Professional compact table headers */
        .table > thead th {
            font-size: 0.7rem;
            text-transform: uppercase;
            letter-spacing: 0.05em;
            font-weight: 600;
            color: var(--secondary-color);
        }
        [data-bs-theme="dark"] .table > thead th { color: #94a3b8; }

        .card-header {
            background: transparent;
            font-weight: 600;
            border-bottom: 1px solid var(--bs-border-color);
        }

        .dropdown-menu {
            border-radius: 0.6rem;
            font-size: 0.875rem;
            overflow: hidden;
        }
        .dropdown-item { padding: 0.45rem 1rem; }
        .dropdown-item .bi { opacity: 0.7; }

        .major-header { background: rgba(0,0,0,0.05); font-weight: 600; }
        [data-bs-theme="dark"] .major-header { background: rgba(255,255,255,0.05); }

        .category-header { font-style: normal; color: #64748b; font-weight: 500; }

        .form-control, .form-select {
            border-radius: 0.5rem;
            padding: 0.6rem 0.75rem;
            border: 1px solid #e2e8f0;
        }

        [data-bs-theme="dark"] .form-control, [data-bs-theme="dark"] .form-select {
            border-color: #334155;
            background-color: #1e293b;
            color: #f1f5f9;
        }

        .form-control:focus {
            box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
            border-color: var(--primary-color);
        }

        .theme-toggle {
            cursor: pointer;
            padding: 0.5rem;
            border-radius: 50%;
            width: 40px;
            height: 40px;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: background-color 0.2s;
            color: var(--secondary-color);
            border: none;
            background: none;
        }

        .theme-toggle:hover {
            background-color: rgba(0,0,0,0.05);
            color: var(--primary-color);
        }

        [data-bs-theme="dark"] .theme-toggle:hover {
            background-color: rgba(255,255,255,0.1);
        }

        /* ---- Shared graphical helpers ---- */
        .icon-avatar {
            width: 42px; height: 42px; border-radius: 12px;
            display: inline-flex; align-items: center; justify-content: center;
            font-size: 1.2rem; flex-shrink: 0;
        }
        /* The endorsement mark in a page header: it answers a hover with a small lift. */
        .endorse-mark { flex-shrink: 0; filter: drop-shadow(0 4px 8px rgba(64,142,60,.28)); transition: transform .25s cubic-bezier(.34,1.56,.64,1); }
        .endorse-mark:hover { transform: rotate(-6deg) scale(1.08); }
        @media (prefers-reduced-motion: reduce) { .endorse-mark { transition: none; } .endorse-mark:hover { transform: none; } }
        .icon-avatar-sm { width: 30px; height: 30px; border-radius: 8px; font-size: 0.95rem; }
        .icon-avatar.tint-primary { background: rgba(79,70,229,.12); color: var(--primary-color); }
        .icon-avatar.tint-success { background: rgba(5,150,105,.14); color: var(--success-color); }
        .icon-avatar.tint-info { background: rgba(8,145,178,.14); color: var(--info-color); }
        .icon-avatar.tint-warning { background: rgba(217,119,6,.15); color: var(--warning-color); }
        .icon-avatar.tint-secondary { background: rgba(71,85,105,.12); color: var(--secondary-color); }
        .icon-avatar.tint-u2i { background: rgba(64,142,60,.13); color: var(--u2i-green); }
        .text-u2i-gradient {
            background: linear-gradient(135deg, var(--u2i-lime), var(--u2i-green));
            -webkit-background-clip: text; background-clip: text; color: transparent;
        }
        .icon-avatar.tint-danger { background: rgba(220,38,38,.12); color: var(--danger-color); }
        .icon-avatar.grad { background: linear-gradient(135deg, var(--primary-color), var(--info-color)); color: #fff; box-shadow: 0 4px 10px -3px rgba(79,70,229,.5); }

        .stat-card {
            border: 1px solid var(--bs-border-color); border-radius: .6rem;
            padding: .5rem .8rem; display: flex; align-items: center; gap: .6rem; background: var(--bs-body-bg);
        }
        .stat-card .val { font-size: 1.15rem; font-weight: 700; line-height: 1; }
        .stat-card .lbl { font-size: .66rem; text-transform: uppercase; letter-spacing: .04em; color: var(--secondary-color); }

        .avatar-initials {
            width: 34px; height: 34px; border-radius: 50%;
            display: inline-flex; align-items: center; justify-content: center;
            font-weight: 600; font-size: .8rem; color: #fff; flex-shrink: 0;
            background: linear-gradient(135deg, var(--primary-color), var(--info-color));
        }
        .avatar-initials-sm { width: 26px; height: 26px; font-size: .68rem; }
        .page-head-icon { /* header icon + title alignment */
            display: flex; align-items: center; gap: .75rem;
        }

        /* ---- Notification bell ---- */
        .notif-bell { position: relative; }
        .notif-badge {
            position: absolute; top: 2px; right: 0;
            min-width: 16px; height: 16px; padding: 0 4px;
            border-radius: 8px; background: var(--danger-color); color: #fff;
            font-size: 0.62rem; font-weight: 700; line-height: 16px; text-align: center;
        }
        .notif-menu { width: 350px; max-height: 430px; overflow-y: auto; }
        .notif-item { white-space: normal; }
        .notif-item.unread { background: rgba(79, 70, 229, 0.05); }
        [data-bs-theme="dark"] .notif-item.unread { background: rgba(129, 140, 248, 0.08); }
        .notif-item .notif-dot {
            width: 8px; height: 8px; border-radius: 50%;
            background: var(--primary-color); flex-shrink: 0; margin-top: 6px;
        }

        /* --- Score levels 0-3 -------------------------------------------
           0 to 3 is an ordered scale, so it gets ONE hue stepped from light
           to dark rather than traffic lights. Red/amber/green are four
           unrelated hues: nothing about them says 2 sits between 1 and 3,
           and the old ramp made 0 the heaviest colour on the page, so weight
           ran backwards at the bottom end.

           The 0-100% totals share these steps (see `score_percent_class`): a
           total is the same scale as a share of its maximum, so it belongs on
           the same ramp, not a second palette that grades it differently.

           Steps are taken from a blue ramp and validated per theme: every
           fill clears 2:1 against its own surface, adjacent steps differ
           enough in lightness to be told apart, and each --ink pairing
           clears 4.5:1 on its fill. Dark is stepped for the dark surface
           rather than flipped, so 3 stays the most prominent in both.
           Step 450 (#2a78d6) is deliberately absent: no ink reaches 4.5:1
           on it.

           The four steps are spread as far apart as those limits allow, so
           neighbouring scores are easy to tell apart: 0 to 3 spans 4.5:1 on
           light and 4.2:1 on dark. What caps it is the end nearest the
           surface — on light nothing may be paler than #86b6ef (2.11:1 on
           white) and on dark nothing darker than #1c5cab (2.33:1 on
           #212529), or the lowest score stops being visible as a mark at
           all.

           The number itself is always rendered, so colour is never the only
           thing carrying the value. */
        [data-bs-theme="light"] {
            --score-0-fill: #86b6ef; --score-0-ink: #0b1220;
            --score-1-fill: #3987e5; --score-1-ink: #0b1220;
            --score-2-fill: #1c5cab; --score-2-ink: #ffffff;
            --score-3-fill: #1e529a; --score-3-ink: #ffffff;
        }
        [data-bs-theme="dark"] {
            --score-0-fill: #1c5cab; --score-0-ink: #ffffff;
            --score-1-fill: #3987e5; --score-1-ink: #0b1220;
            --score-2-fill: #86b6ef; --score-2-ink: #0b1220;
            --score-3-fill: #eff6ff; --score-3-ink: #0b1220;
        }

        .score-badge { font-variant-numeric: tabular-nums; }
        .score-0 { background-color: var(--score-0-fill); color: var(--score-0-ink); }
        .score-1 { background-color: var(--score-1-fill); color: var(--score-1-ink); }
        .score-2 { background-color: var(--score-2-fill); color: var(--score-2-ink); }
        .score-3 { background-color: var(--score-3-fill); color: var(--score-3-ink); }
        /* Not evaluated is not a level: no fill, so it reads as outside the scale. */
        .score-ne {
            background-color: transparent;
            color: var(--bs-secondary-color);
            border: 1px solid var(--bs-border-color);
        }

        /* Review flags: a competency whose examples record a higher level than
           the score it was given (see eval_app/review_flags.py). Three steps of
           amber, deliberately off the blue score ramp — a flag is not a score,
           and the two appear in the same row.

           Read the same way as the score ramp: on light the pale end is the
           mildest mismatch, on dark it inverts, so the strongest flag is the
           heaviest mark against its surface either way. The tier is also
           spelled out in the badge's words, so nothing depends on telling two
           ambers apart.

           The row tints are the same hue at a fraction of its strength: enough
           to pick a row out while scanning a long table, never enough to fight
           with the text in it. */
        [data-bs-theme="light"] {
            --review-1-fill: #fde68a; --review-1-ink: #0b1220;
            --review-2-fill: #d97706; --review-2-ink: #ffffff;
            --review-3-fill: #92400e; --review-3-ink: #ffffff;
            --review-1-row: rgba(217, 119, 6, 0.07);
            --review-2-row: rgba(217, 119, 6, 0.12);
            --review-3-row: rgba(217, 119, 6, 0.18);
        }
        [data-bs-theme="dark"] {
            --review-1-fill: #b45309; --review-1-ink: #ffffff;
            --review-2-fill: #f59e0b; --review-2-ink: #0b1220;
            --review-3-fill: #fcd34d; --review-3-ink: #0b1220;
            /* A warm wash needs more of itself to show on a dark surface than
               on a white one: at the light theme's strengths these rows were
               indistinguishable from the rest of the table. */
            --review-1-row: rgba(252, 211, 77, 0.09);
            --review-2-row: rgba(252, 211, 77, 0.16);
            --review-3-row: rgba(252, 211, 77, 0.24);
        }
        .review-flag {
            font-size: 0.6rem;
            font-weight: 600;
            padding: 0.15rem 0.35rem;
        }
        .review-flag i { font-size: 0.6rem; }
        .review-flag-1 { background-color: var(--review-1-fill); color: var(--review-1-ink); }
        .review-flag-2 { background-color: var(--review-2-fill); color: var(--review-2-ink); }
        .review-flag-3 { background-color: var(--review-3-fill); color: var(--review-3-ink); }

        /* The score picker. An unselected button keeps body-coloured text —
           the ramp step is too light to be legible as text on the surface —
           and carries its level as a border tint. Selected buttons fill. */
        .btn-score {
            background-color: transparent;
            color: var(--bs-body-color);
            border: 1px solid var(--bs-border-color);
        }
        .btn-score.score-level-0 { border-color: var(--score-0-fill); }
        .btn-score.score-level-1 { border-color: var(--score-1-fill); }
        .btn-score.score-level-2 { border-color: var(--score-2-fill); }
        .btn-score.score-level-3 { border-color: var(--score-3-fill); }
        /* Two ways a level gets picked: `is-active` on the detail page's ajax
           buttons, and a checked radio on the evaluation form. Same fill. */
        .btn-score.score-level-0.is-active,
        .btn-check:checked + .btn-score.score-level-0 { background-color: var(--score-0-fill); color: var(--score-0-ink); }
        .btn-score.score-level-1.is-active,
        .btn-check:checked + .btn-score.score-level-1 { background-color: var(--score-1-fill); color: var(--score-1-ink); }
        .btn-score.score-level-2.is-active,
        .btn-check:checked + .btn-score.score-level-2 { background-color: var(--score-2-fill); color: var(--score-2-ink); }
        .btn-score.score-level-3.is-active,
        .btn-check:checked + .btn-score.score-level-3 { background-color: var(--score-3-fill); color: var(--score-3-ink); }
        .btn-score.score-level-ne.is-active,
        .btn-check:checked + .btn-score.score-level-ne {
            background-color: var(--bs-secondary-bg);
            color: var(--bs-body-color);
            border-color: var(--bs-secondary-color);
        }
        .btn-score:hover { border-color: var(--bs-body-color); }

        /* A competency is a short name plus a qualifier saying which slice of
           that name it covers. The qualifier is set smaller and lighter so the
           name still leads, while two competencies sharing a name stay
           tellable apart. */
        .competency-label-qualifier { font-size: 0.8em; font-weight: 400; line-height: 1.25; }
        .competency-label-qualifier.competency-label-inline { display: inline; }
        .competency-label-qualifier.competency-label-inline::before { content: " \2014 "; }
