/* =============================================================================
   HELIX DESIGN SYSTEM  —  APPLICATION SURFACES
   -----------------------------------------------------------------------------
   Product-specific surfaces: navigation, the staff shell, dashboards, course
   and assessment screens, auth, plus motion, skeletons and empty states.

   Load order: after design-system.css and design-components.css. This file is
   last, so it also carries the corrective overrides for legacy hard-coded
   colours in the Odoo bundle and in per-page <style> blocks.
   ========================================================================== */

/* =============================================================================
   14  TOP NAVIGATION (upbar)
   -----------------------------------------------------------------------------
   Sticky, quiet, and legible. The bar itself is a token surface with a
   hairline base; the active item is marked with a short underline rather
   than a filled block, so a long menu stays calm.
   ========================================================================== */

#top {
    font-family: var(--hx-font-body);
    position: sticky;
    top: 0;
    z-index: 1030;
    background-color: var(--hx-surface);
    border-bottom: 1px solid var(--hx-line);
    box-shadow: var(--hx-shadow-xs);
    transition: box-shadow var(--hx-dur) var(--hx-ease),
                background-color var(--hx-dur) var(--hx-ease);
}

/* Raised state once the page scrolls — set by ui-kit.js. */
#top.hx-scrolled {
    box-shadow: var(--hx-shadow-md);
}

#top .navbar,
#top nav.navbar {
    background-color: transparent !important;
    box-shadow: none !important;
}

#top #main_nav > .container.upbar-desktop-row,
#top #main_nav > .container.d-flex {
    min-height: var(--hx-topbar-h);
    gap: 0.5rem 0.85rem;
}

/* --- Menu links ---------------------------------------------------------- */

#top #top_menu .nav-item .nav-link,
#top .top_menu .nav-item .nav-link {
    position: relative;
    color: var(--hx-muted);
    font-size: var(--hx-t-base) !important;
    font-weight: 600;
    letter-spacing: -0.004em;
    padding: 0.5rem 0.7rem !important;
    border-radius: var(--hx-r-xs);
    white-space: nowrap;
    transition: color var(--hx-dur-fast) var(--hx-ease),
                background-color var(--hx-dur-fast) var(--hx-ease);
}

#top #top_menu .nav-item .nav-link:hover,
#top .top_menu .nav-item .nav-link:hover {
    color: var(--hx-ink);
    background-color: var(--hx-surface-2);
}

#top #top_menu .nav-item .nav-link.active,
#top .top_menu .nav-item .nav-link.active {
    color: var(--hx-primary-strong);
}

/* Active marker: a short, centred rule that grows in. */
#top #top_menu .nav-item .nav-link::after,
#top .top_menu .nav-item .nav-link::after {
    content: "";
    position: absolute;
    left: 50%;
    bottom: 0.15rem;
    height: 2px;
    width: 0;
    border-radius: var(--hx-r-pill);
    background: var(--hx-primary);
    transform: translateX(-50%);
    transition: width var(--hx-dur) var(--hx-ease);
}

#top #top_menu .nav-item .nav-link.active::after,
#top .top_menu .nav-item .nav-link.active::after {
    width: calc(100% - 1.4rem);
}

/* Offcanvas (mobile) nav links get a full-width row treatment instead. */
#top #top_menu_collapse_mobile .nav-link {
    color: var(--hx-ink-2);
    font-weight: 600;
    padding: 0.7rem 0.75rem;
    border-radius: var(--hx-r-sm);
    min-height: 2.75rem;
    display: flex;
    align-items: center;
}

#top #top_menu_collapse_mobile .nav-link:hover,
#top #top_menu_collapse_mobile .nav-link:focus {
    background-color: var(--hx-surface-2);
    color: var(--hx-ink);
}

#top #top_menu_collapse_mobile .nav-link.active {
    background-color: var(--hx-primary-soft);
    color: var(--hx-primary-strong);
}

#top #top_menu_collapse_mobile .nav-link::after {
    display: none;
}

/* --- Brand --------------------------------------------------------------- */

#top .navbar-brand.logo img,
#wrapwrap #top .navbar-brand.logo img {
    border-radius: var(--hx-r-xs);
    transition: transform var(--hx-dur) var(--hx-ease);
}

#top .navbar-brand.logo img.hx-logo-light,
#wrapwrap #top .navbar-brand.logo img.hx-logo-light,
#top nav[aria-label="Mobile"] .navbar-brand.logo img.hx-logo-light,
#wrapwrap #top nav[aria-label="Mobile"] .navbar-brand.logo img.hx-logo-light,
html[data-theme="dark"] #top .navbar-brand.logo img.hx-logo-dark,
html[data-theme="dark"] #wrapwrap #top .navbar-brand.logo img.hx-logo-dark,
html[data-theme="dark"] #top nav[aria-label="Mobile"] .navbar-brand.logo img.hx-logo-dark,
html[data-theme="dark"] #wrapwrap #top nav[aria-label="Mobile"] .navbar-brand.logo img.hx-logo-dark {
    max-height: 52px !important;
    max-width: min(120px, 34vw) !important;
}

#top .navbar-brand.logo img.hx-logo-dark,
#wrapwrap #top .navbar-brand.logo img.hx-logo-dark {
    display: none !important;
}

html[data-theme="dark"] #top .navbar-brand.logo img.hx-logo-light,
html[data-theme="dark"] #wrapwrap #top .navbar-brand.logo img.hx-logo-light {
    display: none !important;
}

html[data-theme="dark"] #top .navbar-brand.logo img.hx-logo-dark,
html[data-theme="dark"] #wrapwrap #top .navbar-brand.logo img.hx-logo-dark {
    display: block !important;
}

#top .navbar-brand.logo:hover img {
    transform: scale(1.03);
}

/* --- Primary call to action --------------------------------------------- */

#top .btn_cta.btn.btn-primary {
    background-color: var(--hx-primary-solid);
    border-color: var(--hx-primary-solid);
    color: var(--hx-on-primary);
    font-size: var(--hx-t-sm) !important;
    font-weight: 600;
    padding: 0.5rem 1.05rem !important;
    border-radius: var(--hx-r-sm);
    min-height: 2.4rem;
    box-shadow: 0 2px 10px var(--hx-primary-ring) !important;
    transition: background-color var(--hx-dur-fast) var(--hx-ease),
                border-color var(--hx-dur-fast) var(--hx-ease),
                box-shadow var(--hx-dur-fast) var(--hx-ease),
                transform var(--hx-dur-fast) var(--hx-ease);
}

#top .btn_cta.btn.btn-primary:hover,
#top .btn_cta.btn.btn-primary:focus,
#top .btn_cta.btn.btn-primary:active {
    background-color: var(--hx-primary-solid-hover);
    border-color: var(--hx-primary-solid-hover);
    color: var(--hx-on-primary);
    box-shadow: 0 4px 18px var(--hx-primary-ring) !important;
}

#top .btn_cta.btn.btn-primary:active {
    transform: translateY(1px);
}

/* Logout keeps its danger semantics in the parent portal. */
#top.upbar-parent-portal #parent-upbar-actions .parent-upbar-logout {
    background-color: var(--hx-danger) !important;
    border-color: var(--hx-danger) !important;
    color: #FFFFFF !important;
    border-radius: var(--hx-r-sm);
    min-height: 2.4rem;
    box-shadow: 0 2px 10px var(--hx-danger-ring) !important;
}

#top.upbar-parent-portal #parent-upbar-actions .parent-upbar-logout:hover,
#top.upbar-parent-portal #parent-upbar-actions .parent-upbar-logout:focus {
    filter: brightness(1.06);
}

/* --- Sign-in / register links ------------------------------------------- */

#top #desktop-signin-btn .nav-link,
#top #mobile-signin-btn .nav-link {
    color: var(--hx-muted);
    font-weight: 600;
    font-size: var(--hx-t-sm);
    border-radius: var(--hx-r-xs);
    transition: color var(--hx-dur-fast) var(--hx-ease),
                background-color var(--hx-dur-fast) var(--hx-ease);
}

#top #desktop-signin-btn .nav-link:hover,
#top #mobile-signin-btn .nav-link:hover {
    color: var(--hx-ink);
    background-color: var(--hx-surface-2);
}

/* --- Profile menu -------------------------------------------------------- */

#top .upbar-profile-toggle {
    color: var(--hx-ink-2);
    font-size: var(--hx-t-sm);
    font-weight: 600;
    border-radius: var(--hx-r-pill) !important;
    padding: 0.28rem 0.6rem 0.28rem 0.3rem !important;
    min-height: 2.25rem;
    transition: background-color var(--hx-dur-fast) var(--hx-ease),
                color var(--hx-dur-fast) var(--hx-ease);
}

#top .upbar-profile-toggle:hover,
#top .upbar-profile-toggle:focus,
#top .upbar-profile-toggle.show {
    color: var(--hx-ink);
    background-color: var(--hx-surface-2);
}

#top .upbar-profile-avatar {
    background: var(--hx-primary-solid);
    color: var(--hx-on-primary);
    border: 1px solid var(--hx-primary-ring);
    box-shadow: var(--hx-shadow-xs);
    font-family: var(--hx-font-display);
    font-weight: 700;
}

#top .upbar-profile-menu {
    border: 1px solid var(--hx-line);
    border-radius: var(--hx-r-md);
    box-shadow: var(--hx-shadow-lg);
    padding: 0.35rem;
    min-width: 14rem;
}

#top .upbar-profile-menu .dropdown-item {
    font-size: var(--hx-t-base);
    padding: 0.55rem 0.7rem;
    border-radius: var(--hx-r-xs);
    min-height: 2.5rem;
    display: flex;
    align-items: center;
}

/* --- Theme toggle (injected by ui-kit.js) -------------------------------- */

.hx-theme-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    flex-shrink: 0;
    border: 1px solid var(--hx-line);
    border-radius: var(--hx-r-pill);
    background: var(--hx-surface-2);
    color: var(--hx-muted);
    cursor: pointer;
    padding: 0;
    line-height: 1;
    transition: background-color var(--hx-dur-fast) var(--hx-ease),
                color var(--hx-dur-fast) var(--hx-ease),
                border-color var(--hx-dur-fast) var(--hx-ease),
                transform var(--hx-dur-fast) var(--hx-ease);
}

.hx-theme-toggle:hover {
    background: var(--hx-primary-soft);
    border-color: var(--hx-primary-ring);
    color: var(--hx-primary-strong);
}

.hx-theme-toggle:active {
    transform: scale(0.94);
}

.hx-theme-toggle i {
    font-size: 0.9rem;
    transition: transform var(--hx-dur-slow) var(--hx-ease),
                opacity var(--hx-dur-fast) var(--hx-ease);
}

/* The icon swap reads as a rotation, not a flicker. */
.hx-theme-toggle.hx-swapping i {
    transform: rotate(180deg) scale(0.6);
    opacity: 0;
}

/* =============================================================================
   15  STAFF / ADMIN SHELL
   -----------------------------------------------------------------------------
   The staff portal keeps its sidebar layout. What changes is the material:
   token surfaces, a legible rail, and an active state that reads as a
   selected row rather than a coloured brick.
   ========================================================================== */

body.staff-portal {
    background-color: var(--hx-bg) !important;
    background-image: var(--hx-bg-tint);
}

body.staff-portal #wrapwrap {
    background: transparent;
    padding-top: var(--staff-topbar-h, 3.5rem);
}

:root {
    --staff-topbar-h: 3.75rem;
}

.staff-sidebar {
    background: var(--hx-surface);
    border-right: 1px solid var(--hx-line);
    color: var(--hx-muted);
    box-shadow: none;
}

[data-theme="dark"] .staff-sidebar {
    background: var(--hx-surface-sunken);
}

.staff-sidebar__brand {
    border-bottom: 1px solid var(--hx-line);
    color: var(--hx-ink);
    padding: 0.9rem 1rem;
    min-height: var(--staff-topbar-h);
}

.staff-sidebar__brand:hover {
    color: var(--hx-ink);
}

.staff-sidebar__brand-mark {
    background: linear-gradient(140deg, var(--hx-primary-strong), var(--hx-accent-strong));
    color: #FFFFFF;
    border-radius: var(--hx-r-sm);
    font-family: var(--hx-font-display);
    box-shadow: 0 3px 12px var(--hx-primary-ring);
}

.staff-sidebar__brand-text strong {
    font-family: var(--hx-font-display);
    color: var(--hx-ink);
    font-size: 0.95rem;
    letter-spacing: -0.015em;
}

.staff-sidebar__brand-text span {
    color: var(--hx-muted);
    opacity: 1;
    font-size: var(--hx-t-xs);
    font-weight: 600;
}

/* Group labels are small (11px) and uppercase, so they take the stronger
   muted token — muted-2 lands at only 3.35:1 on the sidebar surface. */
.staff-nav-group__label {
    color: var(--hx-muted);
    opacity: 1;
    font-size: 0.6875rem;
    letter-spacing: 0.09em;
    padding: 0 0.6rem 0.4rem;
}

.staff-nav-link {
    position: relative;
    color: var(--hx-muted);
    font-size: var(--hx-t-base);
    font-weight: 600;
    border-radius: var(--hx-r-sm);
    padding: 0.58rem 0.7rem;
    min-height: 2.5rem;
    transition: background-color var(--hx-dur-fast) var(--hx-ease),
                color var(--hx-dur-fast) var(--hx-ease);
}

.staff-nav-link i {
    opacity: 1;
    color: var(--hx-muted-2);
    transition: color var(--hx-dur-fast) var(--hx-ease);
}

.staff-nav-link:hover {
    background: var(--hx-surface-2);
    color: var(--hx-ink);
}

.staff-nav-link:hover i {
    color: var(--hx-ink-2);
}

.staff-nav-link.active {
    background: var(--hx-primary-soft);
    color: var(--hx-primary-strong);
}

.staff-nav-link.active i {
    color: var(--hx-primary-strong);
}

/* Selected-row marker on the leading edge. */
.staff-nav-link.active::before {
    content: "";
    position: absolute;
    left: -0.7rem;
    top: 50%;
    transform: translateY(-50%);
    width: 3px;
    height: 1.35rem;
    border-radius: 0 var(--hx-r-pill) var(--hx-r-pill) 0;
    background: var(--hx-primary);
}

.staff-sidebar__foot {
    border-top: 1px solid var(--hx-line);
}

.staff-sidebar__foot a {
    color: var(--hx-muted);
    border-radius: var(--hx-r-sm);
    font-size: var(--hx-t-sm);
    font-weight: 600;
    min-height: 2.4rem;
    transition: background-color var(--hx-dur-fast) var(--hx-ease),
                color var(--hx-dur-fast) var(--hx-ease);
}

.staff-sidebar__foot a:hover {
    background: var(--hx-surface-2);
    color: var(--hx-ink);
}

/* --- Staff topbar -------------------------------------------------------- */

.staff-topbar {
    height: var(--staff-topbar-h);
    background: var(--hx-surface);
    border-bottom: 1px solid var(--hx-line);
    box-shadow: var(--hx-shadow-xs);
    padding: 0 1.15rem;
}

.staff-topbar__menu {
    background: var(--hx-surface-2);
    border: 1px solid var(--hx-line);
    border-radius: var(--hx-r-sm);
    color: var(--hx-ink-2);
    width: 2.4rem;
    height: 2.4rem;
    transition: background-color var(--hx-dur-fast) var(--hx-ease);
}

.staff-topbar__menu:hover {
    background: var(--hx-surface-3);
}

.staff-topbar__title {
    font-family: var(--hx-font-display);
    color: var(--hx-ink);
    font-size: var(--hx-t-lg);
    font-weight: 700;
    letter-spacing: -0.018em;
}

.staff-topbar__subtitle {
    color: var(--hx-muted);
    font-size: var(--hx-t-xs);
}

.staff-role-pill {
    background: var(--hx-primary-soft);
    color: var(--hx-primary-strong);
    border-radius: var(--hx-r-pill);
    font-size: 0.6875rem;
    letter-spacing: 0.06em;
    padding: 0.3rem 0.6rem;
}

.staff-topbar__profile {
    background: var(--hx-surface-2);
    border: 1px solid var(--hx-line);
    border-radius: var(--hx-r-pill);
    color: var(--hx-ink-2);
    font-size: var(--hx-t-sm);
    min-height: 2.25rem;
    transition: background-color var(--hx-dur-fast) var(--hx-ease),
                border-color var(--hx-dur-fast) var(--hx-ease);
}

.staff-topbar__profile:hover {
    background: var(--hx-surface-3);
    border-color: var(--hx-line-strong);
    color: var(--hx-ink);
}

.staff-topbar__avatar {
    background: linear-gradient(140deg, var(--hx-primary-strong), var(--hx-accent-strong));
    color: #FFFFFF;
    font-family: var(--hx-font-display);
    font-weight: 700;
}

.staff-backdrop {
    background: rgba(4, 16, 14, 0.5);
    backdrop-filter: blur(2px);
}

/* --- Staff tool tiles ---------------------------------------------------- */

.staff-tools-grid {
    gap: 0.85rem;
}

.staff-tool-card {
    background: var(--hx-surface);
    border: 1px solid var(--hx-line);
    border-radius: var(--hx-r-lg);
    color: var(--hx-ink-2);
    padding: 1.05rem;
    box-shadow: var(--hx-shadow-xs);
    overflow: hidden;
    transition: border-color var(--hx-dur) var(--hx-ease),
                box-shadow var(--hx-dur) var(--hx-ease),
                transform var(--hx-dur) var(--hx-ease);
}

/* A quiet wash that only appears on hover — signals affordance without
   decorating the resting state. */
.staff-tool-card::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(140deg, var(--hx-primary-softer), transparent 65%);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--hx-dur) var(--hx-ease);
}

.staff-tool-card:hover {
    border-color: var(--hx-primary-ring);
    box-shadow: var(--hx-shadow-lg);
    transform: translateY(-3px);
    color: var(--hx-ink);
}

.staff-tool-card:hover::after {
    opacity: 1;
}

.staff-tool-card__icon {
    background: var(--hx-primary-soft);
    color: var(--hx-primary-strong);
    border-radius: var(--hx-r-sm);
    width: 2.5rem;
    height: 2.5rem;
    font-size: 1rem;
    transition: transform var(--hx-dur) var(--hx-ease-spring),
                background-color var(--hx-dur) var(--hx-ease);
}

.staff-tool-card:hover .staff-tool-card__icon {
    transform: scale(1.08) rotate(-4deg);
}

.staff-tool-card strong {
    font-family: var(--hx-font-display);
    color: var(--hx-ink);
    font-size: 0.95rem;
    letter-spacing: -0.012em;
}

.staff-tool-card span {
    color: var(--hx-muted);
    font-size: var(--hx-t-xs);
}

/* =============================================================================
   16  PAGE HERO
   -----------------------------------------------------------------------------
   The marketing hero keeps its photograph but gains a graded scrim so the
   title always clears 4.5:1, plus a brand-tinted wash at the base.
   ========================================================================== */

.page-hero {
    border-radius: 0;
    isolation: isolate;
}

.page-hero__overlay {
    background: linear-gradient(180deg,
                rgba(4, 16, 14, 0.42) 0%,
                rgba(4, 16, 14, 0.62) 55%,
                rgba(4, 16, 14, 0.78) 100%),
                linear-gradient(115deg,
                rgba(13, 148, 136, 0.30) 0%,
                transparent 55%,
                rgba(124, 58, 237, 0.22) 100%);
}

.page-hero__bg {
    transform: scale(1.02);
    transition: transform 1.4s var(--hx-ease);
}

.page-hero h1 {
    font-family: var(--hx-font-display);
    color: #FFFFFF;
    letter-spacing: -0.026em;
    text-shadow: 0 2px 18px rgba(0, 0, 0, 0.32);
}

.page-hero p,
.page-hero .text-white-50 {
    color: rgba(255, 255, 255, 0.82) !important;
    font-size: var(--hx-t-md);
}

/* =============================================================================
   17  DASHBOARDS
   -----------------------------------------------------------------------------
   The KPI language is unified across student, parent and staff. Status colour
   is retained (it carries meaning) but rendered as a graded surface with a
   luminous edge instead of a flat block of saturated paint.
   ========================================================================== */

/* Re-point the legacy dashboard variables at Helix tokens. Every rule in
   student_dashboard.css / course_performance.css / staff_shell.css that
   consumes these now themes automatically. */
body.student-dashboard,
body.parent-dashboard {
    --sd-ink: var(--hx-ink);
    --sd-muted: var(--hx-muted);
    --sd-line: var(--hx-line);
    --sd-surface: var(--hx-bg);
    --sd-panel: var(--hx-surface);
    --sd-accent: var(--hx-primary);
    --sd-accent-soft: var(--hx-primary-soft);
    --sd-green: var(--hx-success);
    --sd-yellow: var(--hx-warn);
    --sd-red: var(--hx-danger);
    background-color: var(--hx-bg);
    background-image: var(--hx-bg-tint);
    color: var(--hx-ink-2);
}

body.staff-portal {
    --staff-ink: var(--hx-ink);
    --staff-muted: var(--hx-muted);
    --staff-line: var(--hx-line);
    --staff-surface: var(--hx-bg);
    --staff-panel: var(--hx-surface);
    --staff-accent: var(--hx-primary);
    --staff-accent-soft: var(--hx-primary-soft);
}

/* --- Panels -------------------------------------------------------------- */

.sd-panel,
.cp-panel {
    background: var(--hx-surface);
    border: 1px solid var(--hx-line);
    border-radius: var(--hx-r-lg);
    box-shadow: var(--hx-shadow-sm);
}

.sd-panel__header,
.cp-panel__header {
    background: var(--hx-surface-2);
    border-bottom: 1px solid var(--hx-line);
    color: var(--hx-ink);
    font-family: var(--hx-font-display);
    font-weight: 600;
    border-radius: var(--hx-r-lg) var(--hx-r-lg) 0 0;
}

.sd-section-title,
.cp-section-title {
    font-family: var(--hx-font-display);
    color: var(--hx-ink);
    font-size: var(--hx-t-md);
    font-weight: 700;
    letter-spacing: -0.014em;
}

/* --- KPI tiles ----------------------------------------------------------- */

.sd-kpi,
.cp-kpi,
.stat-card {
    position: relative;
    border: 1px solid transparent;
    border-radius: var(--hx-r-lg);
    color: #FFFFFF;
    overflow: hidden;
    isolation: isolate;
    box-shadow: var(--hx-shadow-md);
    transition: transform var(--hx-dur) var(--hx-ease),
                box-shadow var(--hx-dur) var(--hx-ease);
}

/* A single specular highlight across the top edge gives the tile depth
   without resorting to a heavy gradient. */
.sd-kpi::before,
.cp-kpi::before,
.stat-card::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(150deg,
                rgba(255, 255, 255, 0.18) 0%,
                rgba(255, 255, 255, 0.04) 38%,
                rgba(0, 0, 0, 0.10) 100%);
    pointer-events: none;
}

.sd-kpi__label,
.cp-kpi__label,
.stat-label {
    font-size: var(--hx-t-sm);
    font-weight: 600;
    opacity: 0.9;
    letter-spacing: 0.005em;
}

.sd-kpi__value,
.cp-kpi__value,
.stat-number {
    font-family: var(--hx-font-display);
    font-weight: 700;
    letter-spacing: -0.028em;
    font-variant-numeric: tabular-nums;
}

.sd-kpi__hint,
.cp-kpi__hint {
    opacity: 0.82;
}

.sd-kpi__icon,
.cp-kpi__icon,
.stat-icon {
    opacity: 0.7;
    transition: transform var(--hx-dur) var(--hx-ease-spring),
                opacity var(--hx-dur) var(--hx-ease);
}

/* Semantic fills — gradient versions of the same status colours. */
.sd-kpi--neutral, .cp-kpi--neutral, .stat-primary, .stat-card {
    background: linear-gradient(145deg, var(--hx-primary-strong), var(--hx-primary-deep));
}

.sd-kpi--green, .cp-kpi--green, .stat-success {
    background: linear-gradient(145deg, #15803D, #14532D);
}

.sd-kpi--yellow, .cp-kpi--yellow, .stat-warning {
    background: linear-gradient(145deg, #B45309, #78350F);
}

.sd-kpi--red, .cp-kpi--red {
    background: linear-gradient(145deg, #DC2626, #7F1D1D);
}

[data-theme="dark"] .sd-kpi--neutral,
[data-theme="dark"] .cp-kpi--neutral,
[data-theme="dark"] .stat-primary,
[data-theme="dark"] .stat-card {
    background: linear-gradient(145deg, #0F766E, #0B3F3A);
}

[data-theme="dark"] .sd-kpi--green,
[data-theme="dark"] .cp-kpi--green,
[data-theme="dark"] .stat-success {
    background: linear-gradient(145deg, #166534, #0C2E1A);
}

[data-theme="dark"] .sd-kpi--yellow,
[data-theme="dark"] .cp-kpi--yellow,
[data-theme="dark"] .stat-warning {
    background: linear-gradient(145deg, #92400E, #422006);
}

[data-theme="dark"] .sd-kpi--red,
[data-theme="dark"] .cp-kpi--red {
    background: linear-gradient(145deg, #B91C1C, #450A0A);
}

/* Clickable tiles: lift plus a soft glow, and the icon leans in. */
.sd-kpi-clickable,
.cp-kpi-clickable,
.stat-card-clickable {
    cursor: pointer;
}

.sd-kpi-clickable:hover,
.cp-kpi-clickable:hover,
.stat-card-clickable:hover {
    transform: translateY(-4px);
    box-shadow: var(--hx-shadow-xl);
}

.sd-kpi-clickable:hover .sd-kpi__icon,
.cp-kpi-clickable:hover .cp-kpi__icon,
.stat-card-clickable:hover .stat-icon {
    transform: scale(1.12) rotate(-5deg);
    opacity: 0.95;
}

.sd-kpi-clickable:focus-visible,
.cp-kpi-clickable:focus-visible,
.stat-card-clickable:focus-visible {
    outline: 2px solid #FFFFFF;
    outline-offset: 3px;
}

/* --- Course header tile -------------------------------------------------- */

.sd-course-header {
    background: linear-gradient(145deg, var(--hx-accent-strong), #3B1D8F);
    border-radius: var(--hx-r-lg);
    box-shadow: var(--hx-shadow-md);
    position: relative;
    overflow: hidden;
}

[data-theme="dark"] .sd-course-header {
    background: linear-gradient(145deg, #5B21B6, #2E1065);
}

.sd-course-header__title {
    font-family: var(--hx-font-display);
    letter-spacing: -0.022em;
}

/* --- Mini stats, chips, activity ---------------------------------------- */

.sd-mini {
    background: var(--hx-surface-2);
    border: 1px solid var(--hx-line);
    border-radius: var(--hx-r-md);
    transition: border-color var(--hx-dur-fast) var(--hx-ease),
                background-color var(--hx-dur-fast) var(--hx-ease);
}

.sd-mini:hover {
    border-color: var(--hx-primary-ring);
    background: var(--hx-primary-softer);
}

.sd-mini .k {
    color: var(--hx-muted);
    font-size: var(--hx-t-xs);
    font-weight: 600;
}

.sd-mini .v {
    color: var(--hx-ink);
    font-family: var(--hx-font-display);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.sd-chip,
.inbox-chip,
.practice-progress-pill,
.qb-points-pill,
.qb-keyword-chip,
.course-curriculum-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    background: var(--hx-primary-soft);
    color: var(--hx-primary-strong);
    border: 0;
    border-radius: var(--hx-r-pill);
    font-size: var(--hx-t-xs);
    font-weight: 600;
    padding: 0.25rem 0.6rem;
    line-height: 1.4;
}

.sd-activity-item {
    background: var(--hx-surface-2);
    border-left: 3px solid var(--hx-primary);
    border-radius: 0 var(--hx-r-sm) var(--hx-r-sm) 0;
    color: var(--hx-ink-2);
    transition: background-color var(--hx-dur-fast) var(--hx-ease),
                transform var(--hx-dur-fast) var(--hx-ease);
}

.sd-activity-item:hover {
    background: var(--hx-primary-softer);
    transform: translateX(2px);
}

.cp-empty {
    border: 1px dashed var(--hx-line-strong);
    border-radius: var(--hx-r-md);
    color: var(--hx-muted);
    background: var(--hx-surface-2);
}

/* --- Curriculum toggle group -------------------------------------------- */

.cp-curr-btn,
.curriculum-btn {
    border: 1px solid var(--hx-line-strong);
    background: var(--hx-surface);
    color: var(--hx-muted);
    border-radius: var(--hx-r-pill);
    padding: 0.5rem 1.15rem;
    font-weight: 600;
    font-size: var(--hx-t-sm);
    min-height: 2.4rem;
    transition: background-color var(--hx-dur-fast) var(--hx-ease),
                color var(--hx-dur-fast) var(--hx-ease),
                border-color var(--hx-dur-fast) var(--hx-ease),
                box-shadow var(--hx-dur-fast) var(--hx-ease);
}

/* The Courses page sets these colours inline and with !important, at a
   contrast of roughly 3.4:1. Matching that weight is the only way to reach
   them from the design system, and it lifts the inactive state to AA. */
.cp-curr-btn:not(.active),
.curriculum-btn:not(.active) {
    background: var(--hx-surface) !important;
    color: var(--hx-ink-2) !important;
    border: 1px solid var(--hx-line-strong) !important;
}

.cp-curr-btn:hover,
.curriculum-btn:hover,
.cp-curr-btn:not(.active):hover,
.curriculum-btn:not(.active):hover {
    border-color: var(--hx-primary-ring) !important;
    color: var(--hx-primary-strong) !important;
    background: var(--hx-primary-softer) !important;
    transform: translateY(-1px);
}

.cp-curr-btn.active,
.curriculum-btn.active {
    background: var(--hx-primary-solid) !important;
    border-color: var(--hx-primary-solid) !important;
    color: var(--hx-on-primary) !important;
    box-shadow: 0 4px 14px var(--hx-primary-ring) !important;
    transform: none;
}

/* =============================================================================
   18  COURSE AND LEARNING SURFACES
   ========================================================================== */

.course-card {
    background: var(--hx-surface);
    border: 1px solid var(--hx-line);
    border-radius: var(--hx-r-lg);
    box-shadow: var(--hx-shadow-sm);
    overflow: hidden;
}

.course-card-img,
.course-hero-img {
    overflow: hidden;
}

.course-card-img img {
    transition: transform var(--hx-dur-slow) var(--hx-ease);
}

.course-card:hover .course-card-img img {
    transform: scale(1.04);
}

.course-card-title {
    font-family: var(--hx-font-display);
    color: var(--hx-ink);
    font-weight: 700;
    letter-spacing: -0.016em;
}

.course-card-instructor,
.course-card-meta {
    color: var(--hx-muted);
    font-size: var(--hx-t-sm);
}

/* Lecture / quiz / homework rows.
   These are drawn as boxes, so they must also carry the inset a box needs.
   Several of them (notably .lecture-video-row) ship with `padding: 0.6rem 0`
   from their page stylesheet — fine when they were plain rows separated by a
   rule, but as soon as a border is added the text lands on it. The padding
   below is part of the same decision as the border and travels with it. */
.lecture-item,
.lecture-video-row,
.quiz-card,
.hw-card,
.cd-stream-card,
.live-session-event-card,
.qb-card,
.search-result-item,
.notif-list .list-group-item {
    background: var(--hx-surface);
    border: 1px solid var(--hx-line);
    border-radius: var(--hx-r-md);
    color: var(--hx-ink-2);
    transition: border-color var(--hx-dur-fast) var(--hx-ease),
                background-color var(--hx-dur-fast) var(--hx-ease),
                box-shadow var(--hx-dur-fast) var(--hx-ease);
}

/* Specificity note: the page stylesheet sets this at
   `.lecture-video-list .lecture-video-row` (0,2,0), so the inset has to be
   declared at least as strongly to reach it. */
.lecture-video-list .lecture-video-row,
.lecture-video-row,
.lecture-item,
.quiz-card,
.hw-card,
.cd-stream-card,
.live-session-event-card,
.search-result-item {
    padding: 0.7rem 0.95rem;
    margin-bottom: 0.5rem;
}

.lecture-video-list .lecture-video-row:last-child {
    /* The page removes the bottom padding on the last row to sit flush with a
       separator that no longer exists now the row is a box. */
    padding-bottom: 0.7rem;
    margin-bottom: 0;
}

.lecture-video-row:hover,
.search-result-item:hover,
.notif-list .list-group-item:hover {
    border-color: var(--hx-primary-ring);
    background: var(--hx-primary-softer);
}

.lecture-videos-panel,
.qb-picker-list-scroll,
.ui-list-scroll {
    border-radius: var(--hx-r-md);
}

.lecture-prereq-callout {
    background: var(--hx-warn-soft);
    border: 1px solid var(--hx-warn-ring);
    border-radius: var(--hx-r-md);
    color: var(--hx-ink-2);
}

.lecture-prereq-callout-icon {
    color: var(--hx-warn);
}

/* Practice / quiz taking */
.practice-mode-card {
    background: var(--hx-surface);
    border: 1px solid var(--hx-line);
    border-radius: var(--hx-r-lg);
    box-shadow: var(--hx-shadow-sm);
}

.practice-mode-icon {
    flex: 0 0 auto;
    align-self: flex-start;
    width: 0.625rem;
    height: 1.625rem;
    margin-top: 0.15rem;
    background: var(--hx-primary);
    color: transparent;
    border-radius: 999px;
}

.practice-mode-icon i {
    display: none;
}

.practice-mode-card--mistakes .practice-mode-icon {
    background: var(--bs-danger, #dc3545);
    color: transparent;
}

.practice-alloc-avail {
    margin-top: 0.35rem;
    line-height: 1.35;
    max-width: 16rem;
}
.practice-alloc-avail--warn {
    font-weight: 500;
}

.practice-dock {
    background: var(--hx-surface);
    border-top: 1px solid var(--hx-line);
    box-shadow: 0 -8px 26px rgba(11, 31, 28, 0.09);
    backdrop-filter: saturate(1.4);
}

[data-theme="dark"] .practice-dock {
    box-shadow: 0 -8px 26px rgba(0, 0, 0, 0.5);
}

.practice-results-hero {
    background: linear-gradient(145deg, var(--hx-primary-strong), var(--hx-primary-deep));
    border-radius: var(--hx-r-xl);
    color: #FFFFFF;
    box-shadow: var(--hx-shadow-lg);
}

.practice-written-answer-box,
.practice-written-input,
.qb-written-panel {
    background: var(--hx-surface-2);
    border: 1px solid var(--hx-line);
    border-radius: var(--hx-r-md);
    color: var(--hx-ink-2);
}

.qb-option {
    border-radius: var(--hx-r-sm);
    transition: background-color var(--hx-dur-fast) var(--hx-ease);
}

.mistakes-wrong-pill {
    background: var(--hx-danger-soft);
    color: var(--hx-danger);
    border-radius: var(--hx-r-pill);
    font-weight: 600;
}

.quiz-timer-bar {
    background: var(--hx-surface-3);
    border-radius: var(--hx-r-pill);
    overflow: hidden;
}

/* Learning option tiles on the homepage keep their colour coding. */
.learning-option-card {
    background: var(--hx-surface);
    border: 1px solid var(--hx-line);
    border-radius: var(--hx-r-lg);
    box-shadow: var(--hx-shadow-sm);
}

.learning-option-icon-wrap {
    border-radius: var(--hx-r-md);
}

/* Messaging / inbox */
.inbox-shell,
.inbox-list-pane,
.inbox-detail-pane {
    background: var(--hx-surface);
    border: 1px solid var(--hx-line);
    border-radius: var(--hx-r-lg);
}

.inbox-list-head,
.inbox-toolbar {
    background: var(--hx-surface-2);
    border-bottom: 1px solid var(--hx-line);
    color: var(--hx-ink);
}

.inbox-msg-name,
.inbox-from-name {
    color: var(--hx-ink);
    font-weight: 600;
}

.inbox-msg-preview,
.inbox-msg-meta,
.inbox-reply-meta {
    color: var(--hx-muted);
    font-size: var(--hx-t-sm);
}

.inbox-dot {
    background: var(--hx-primary);
}

.inbox-placeholder {
    color: var(--hx-muted);
}

/* Profile */
.profile-card__identity {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1.25rem;
    padding-bottom: 1rem;
    border-bottom: 1px solid var(--hx-line);
}

.profile-card__identity-text {
    min-width: 0;
}

.profile-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 4rem;
    height: 4rem;
    border-radius: 50%;
    background: var(--hx-primary-solid);
    color: var(--hx-on-primary);
    border: 2px solid var(--hx-primary-soft);
    box-shadow: var(--hx-shadow-sm), 0 0 0 1px var(--hx-primary-ring);
    font-family: var(--hx-font-display);
    font-size: 1.5rem;
    font-weight: 700;
    line-height: 1;
    letter-spacing: 0;
    box-sizing: border-box;
    user-select: none;
}

.profile-dl dt {
    color: var(--hx-muted);
    font-size: var(--hx-t-sm);
    font-weight: 600;
}

.profile-dl dd {
    color: var(--hx-ink);
}

.profile-devices {
    margin-top: 1.35rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--hx-line);
}

.profile-devices__title {
    font-family: var(--hx-font-display);
    font-size: var(--hx-t-md);
    font-weight: 600;
    color: var(--hx-ink);
    margin: 0 0 0.5rem;
}

.profile-devices__policy {
    color: var(--hx-muted);
    font-size: var(--hx-t-sm);
    line-height: 1.5;
    margin: 0 0 1rem;
}

.profile-devices__list {
    display: flex;
    flex-direction: column;
    gap: 0.65rem;
    margin-bottom: 1rem;
}

.profile-device-card {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem 0.85rem;
    max-width: 100%;
    min-width: 0;
    padding: 0.75rem 1rem;
    border: 1px solid var(--hx-line);
    border-radius: var(--hx-r-md);
    background: var(--hx-surface-2);
}

.profile-device-card__name {
    font-weight: 600;
    color: var(--hx-ink);
    min-width: 0;
    overflow-wrap: anywhere;
}

.profile-device-card__badges {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
}

.profile-cat-option {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.55rem 0.75rem;
    border: 1px solid var(--hx-line);
    border-radius: var(--hx-r-sm, 8px);
    cursor: pointer;
    margin: 0;
}

.profile-cat-option.is-disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

.profile-device-warning {
    margin-top: 1.5rem;
    padding: 1.15rem 1.2rem;
    background: var(--hx-danger-soft);
    border: 2px solid var(--hx-danger);
    border-radius: var(--hx-r-lg);
    color: var(--hx-ink);
}

.profile-device-warning__title {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    color: var(--hx-danger);
    font-family: var(--hx-font-display);
    font-size: 1.05rem;
    font-weight: 700;
    line-height: 1.35;
    margin: 0 0 0.75rem;
}

.profile-device-warning__icon {
    flex-shrink: 0;
    line-height: 1.35;
}

.profile-device-warning p {
    margin: 0 0 0.7rem;
    overflow-wrap: anywhere;
}

.profile-device-warning__close {
    margin-bottom: 0 !important;
}

@media (max-width: 575.98px) {
    .profile-device-card {
        align-items: flex-start;
        flex-direction: column;
    }

    .profile-device-warning {
        padding: 1rem;
    }

    .profile-device-warning__title {
        font-size: 1rem;
    }
}

/* Toast-style page notifications used by course pages. */
.course-notif-icon,
.notification-icon {
    color: var(--hx-primary-strong);
}

/* =============================================================================
   19  AUTH SURFACES
   -----------------------------------------------------------------------------
   Login / register / password screens get a focused, centred card on a
   brand-washed ground.
   ========================================================================== */

/* Only the actual card gets card treatment.
   `.oe_login_form` is the <form> and `.field-login` is a single field wrapper
   — neither is a surface. Boxing them drew a border tight around the email
   input with no padding, so the control collided with its own frame. */
.oe_website_login_container .card {
    background: var(--hx-surface) !important;
    border: 1px solid var(--hx-line) !important;
    border-radius: var(--hx-r-xl) !important;
    box-shadow: var(--hx-shadow-xl) !important;
    color: var(--hx-ink-2);
}

.oe_login_form,
.field-login {
    background: transparent;
    border: 0;
    box-shadow: none;
}

.oe_website_login_container .card-body {
    padding: clamp(1.35rem, 1rem + 1.5vw, 2.25rem);
}

.oe_website_login_container h1,
.oe_website_login_container h2,
.oe_website_login_container h3 {
    font-family: var(--hx-font-display);
    color: var(--hx-ink);
    letter-spacing: -0.024em;
}

.oe_login_buttons .btn {
    width: 100%;
    min-height: 2.85rem;
    font-size: var(--hx-t-md);
}

/* Password reveal toggle */
.o_show_password {
    color: var(--hx-muted);
    background: transparent;
    border-color: var(--hx-line-strong);
}

.o_show_password:hover {
    color: var(--hx-ink);
}

/* =============================================================================
   20  FOOTER
   ========================================================================== */

.site-footer {
    background-color: var(--hx-surface);
    border-top: 1px solid var(--hx-line);
}

.site-footer-copy {
    color: var(--hx-muted) !important;
    font-size: var(--hx-t-xs);
}

.site-footer-social-link {
    color: var(--hx-primary-strong);
    background: var(--hx-primary-soft);
    width: 1.85rem;
    height: 1.85rem;
    transition: background-color var(--hx-dur-fast) var(--hx-ease),
                color var(--hx-dur-fast) var(--hx-ease),
                transform var(--hx-dur-fast) var(--hx-ease);
}

.site-footer-social-link:hover,
.site-footer-social-link:focus {
    color: var(--hx-on-primary);
    background: var(--hx-primary-solid);
    transform: translateY(-2px);
}

/* =============================================================================
   20a  CONTINUOUS MEDIA MARQUEE
   -----------------------------------------------------------------------------
   An always-moving strip of image cards, built by ui-kit.js from the markup
   that is already on the page. Motion starts on load and loops forever, so it
   reads like a piece of video rather than something you have to scroll to
   trigger.

   How the seamless loop works: the track holds the original cards followed by
   one aria-hidden copy of them. Animating the track by exactly one "set"
   width lands copy #1 precisely where the original started, so the wrap is
   invisible. With a flex `gap` of G between all items, one set is NOT 50% of
   the track — the track has 2N-1 gaps, not 2N — so the travel distance is
   50% PLUS half a gap. That is what the calc() below is for; using a plain
   -50% leaves a visible stutter every cycle.

   Accessibility note: this strip runs continuously with no pause control and
   no pause on hover, which is a deliberate product decision. WCAG 2.2.2
   ("Pause, Stop, Hide") normally asks for a way to stop content that moves
   automatically for more than five seconds. The product requirement for these
   three marquees is automatic LEFT → RIGHT travel even when the OS requests
   reduced motion, so they are exempted from the reduced-motion kill-switch.
   Other page motion still honours prefers-reduced-motion.
   ========================================================================== */

.hx-marquee {
    --hx-mq-gap: 1.25rem;
    position: relative;
    margin-top: 0.5rem;
    /* The wrapper is inserted INSIDE a Bootstrap .row. On a row that carries
       row-cols-* the `> *` selector would otherwise size this wrapper to a
       column width (20% on row-cols-xl-5) instead of the full track. */
    flex: 1 1 100% !important;
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0;
}

.hx-marquee__viewport {
    overflow: hidden;
    /* Soft edges so cards fade in and out instead of being chopped off. */
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
    mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
}

.hx-marquee__track {
    display: flex;
    flex-wrap: nowrap;
    /* Stretch, not flex-start: cards whose text runs to different lengths
       still share one height, so the strip has a straight baseline. */
    align-items: stretch;
    gap: var(--hx-mq-gap);
    width: max-content;
    animation: hx-marquee-scroll var(--hx-mq-duration, 40s) linear infinite;
    will-change: transform;
}

/* Opposite travel for the second strip on a page. */
.hx-marquee--reverse .hx-marquee__track {
    animation-direction: reverse;
}

@keyframes hx-marquee-scroll {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(calc(-50% - (var(--hx-mq-gap) / 2)), 0, 0); }
}

/* The strip runs continuously and is not pausable by the reader — no control,
   and no pause on hover. These three marquees also keep animating when the
   OS requests reduced motion (see the exemption at the bottom of this file). */

/* Bootstrap column classes come along with the original markup; in a flex
   track they must not dictate width. */
.hx-marquee__track > .hx-marquee__item {
    flex: 0 0 auto !important;
    width: clamp(15rem, 23vw, 20rem) !important;
    max-width: none !important;
    padding-left: 0 !important;
    padding-right: 0 !important;
    margin: 0 !important;
    opacity: 1 !important;
    transform: none !important;
}

.hx-marquee__item .key-image-slot {
    margin-bottom: 0.65rem !important;
}

/* The scroll-parallax treatment scales images to 1.3 to give the drift some
   headroom. The marquee has no drift, so that would just crop the photo for
   nothing — show it close to full frame, and keep a gentle push on hover
   (hovering also pauses the strip, so it is a real chance to look). */
/* Specificity note: the parallax rule below is `.s_key_images .key-image-slot
   img` (0,2,1) and appears later in this file, so a plain
   `.hx-marquee__item .key-image-slot img` would tie and lose. The extra class
   here puts the marquee ahead of it deliberately. */
.s_key_images .hx-marquee__item .key-image-slot img,
.hx-marquee .hx-marquee__item .key-image-slot img {
    --hx-img-scale: 1.02;
}

.s_key_images .hx-marquee__item .key-image-slot:hover img,
.hx-marquee .hx-marquee__item .key-image-slot:hover img {
    --hx-img-scale: 1.09;
}

.hx-marquee__item > .h1-fs {
    font-family: var(--hx-font-display);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.03em;
    color: var(--hx-primary-strong);
    margin-bottom: 0.35rem;
}

.hx-marquee__item > p:last-child {
    font-size: var(--hx-t-sm);
    color: var(--hx-muted);
    margin-bottom: 0;
}

/* --- Reduced motion -------------------------------------------------------
   Keep the verified single-row track. Do not wrap, and do not switch the
   viewport to overflow-x: auto — that produced a manually scrollable
   carousel. Automatic hx-marquee-scroll continues; the global * kill-switch
   later in this file is overridden only for .hx-marquee__track. */
@media (prefers-reduced-motion: reduce) {
    .hx-marquee__track {
        width: max-content;
        flex-wrap: nowrap;
        justify-content: flex-start;
    }

    .hx-marquee__viewport {
        overflow: hidden;
    }
}

/* =============================================================================
   20b  SCROLL-DRIVEN MEDIA GRID
   -----------------------------------------------------------------------------
   Applied to the homepage "key images" row. Three layers of motion, each
   doing a different job:

     1. Entrance   each column rises and fades, staggered left to right, so the
                   group reads as one gesture rather than four separate pops.
     2. Wipe       the image slot uncovers bottom-up via clip-path while the
                   number and caption follow slightly behind.
     3. Parallax   while the section is on screen the image drifts inside its
                   slot, so the photography feels like it has depth behind the
                   page rather than being pasted onto it.

   The slot already has a fixed aspect-ratio and overflow:hidden, and the image
   is only ever moved with transform — so none of this costs a single pixel of
   layout shift, and it all runs on the compositor.

   The hidden resting state is gated behind .hx-media-ready, which ui-kit.js
   adds at runtime. If the script never runs, every image is simply visible.
   ========================================================================== */

.hx-media-scroll .hx-media-item {
    /* Per-item stagger, set inline by ui-kit.js. */
    --hx-i-delay: 0ms;
}

/* The armed (hidden) state carries NO transition, so arming the row at runtime
   snaps rather than visibly fading the images out first. The transition lives
   on the revealed state, which is the direction that should animate. */
.hx-media-scroll.hx-media-ready .hx-media-item {
    opacity: 0;
    transform: translateY(56px) scale(0.94);
    transition: none;
}

.hx-media-scroll.hx-media-ready.hx-in .hx-media-item {
    opacity: 1;
    transform: none;
    transition: opacity 700ms var(--hx-ease),
                transform 820ms var(--hx-ease-spring);
    transition-delay: var(--hx-i-delay);
}

/* --- The slot: bottom-up wipe + a resting frame --------------------------- */

.s_key_images .key-image-slot,
.hx-media-scroll .key-image-slot {
    /* The sheen ::after is absolutely positioned against this box. The
       homepage already sets position:relative inline; this guarantees it for
       any other grid that opts in via [data-hx-media-scroll]. */
    position: relative;
    overflow: hidden;
    border-radius: var(--hx-r-md);
    box-shadow: var(--hx-shadow-sm);
    background: var(--hx-surface-3);
    transition: box-shadow var(--hx-dur) var(--hx-ease),
                transform var(--hx-dur) var(--hx-ease);
}

.hx-media-scroll.hx-media-ready .key-image-slot {
    clip-path: inset(0 0 100% 0);
    transition: none;
}

.hx-media-scroll.hx-media-ready.hx-in .key-image-slot {
    clip-path: inset(0 0 0 0);
    transition: clip-path 900ms var(--hx-ease),
                box-shadow var(--hx-dur) var(--hx-ease),
                transform var(--hx-dur) var(--hx-ease);
    transition-delay: calc(var(--hx-i-delay) + 100ms), 0ms, 0ms;
}

/* A light sweep that crosses the image once, as the wipe uncovers it. It is
   purely decorative, sits above the photo, and never intercepts a pointer. */
.hx-media-scroll .key-image-slot::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 2;
    pointer-events: none;
    opacity: 0;
    background: linear-gradient(105deg,
                transparent 35%,
                rgba(255, 255, 255, 0.42) 50%,
                transparent 65%);
    transform: translateX(-100%);
}

.hx-media-scroll.hx-media-ready.hx-in .key-image-slot::after {
    animation: hx-media-sheen 1150ms var(--hx-ease) forwards;
    animation-delay: calc(var(--hx-i-delay) + 260ms);
}

@keyframes hx-media-sheen {
    0%   { opacity: 0;   transform: translateX(-100%); }
    18%  { opacity: 1; }
    82%  { opacity: 1; }
    100% { opacity: 0;   transform: translateX(100%); }
}

/* --- The image: parallax drift + hover push ------------------------------
   The scale headroom (1.14) is what the parallax translate moves within, so
   the slot never shows an empty edge. Transitioning transform lets the drift
   trail the scroll slightly, which is what makes it read as smooth rather
   than mechanical, and it doubles as the hover easing. */

.s_key_images .key-image-slot img,
.hx-media-scroll .key-image-slot img {
    --hx-img-scale: 1.3;
    --hx-par: 0px;
    transform: translate3d(0, var(--hx-par), 0) scale(var(--hx-img-scale));
    transition: transform 260ms var(--hx-ease);
    will-change: transform;
}

.s_key_images .key-image-slot:hover,
.hx-media-scroll .key-image-slot:hover {
    box-shadow: var(--hx-shadow-lg);
    transform: translateY(-3px);
}

.s_key_images .key-image-slot:hover img,
.hx-media-scroll .key-image-slot:hover img {
    --hx-img-scale: 1.38;
}

/* --- Number and caption follow the image --------------------------------- */

.hx-media-scroll .hx-media-item > .h1-fs {
    font-family: var(--hx-font-display);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.03em;
    color: var(--hx-primary-strong);
}

.hx-media-scroll.hx-media-ready .hx-media-item > .h1-fs {
    opacity: 0;
    transform: translateY(14px);
    transition: opacity 520ms var(--hx-ease), transform 520ms var(--hx-ease);
    transition-delay: var(--hx-i-delay);
}

.hx-media-scroll.hx-media-ready .hx-media-item > p:last-child {
    opacity: 0;
    transform: translateY(12px);
    transition: opacity 520ms var(--hx-ease), transform 520ms var(--hx-ease);
    transition-delay: calc(var(--hx-i-delay) + 220ms);
}

.hx-media-scroll.hx-media-ready.hx-in .hx-media-item > .h1-fs,
.hx-media-scroll.hx-media-ready.hx-in .hx-media-item > p:last-child {
    opacity: 1;
    transform: none;
}

/* Once the entrance has finished, drop the compositing hints. */
.hx-media-scroll.hx-settled .hx-media-item {
    will-change: auto;
}

/* Motion off: everything visible, no drift, no wipe. */
@media (prefers-reduced-motion: reduce) {
    .hx-media-scroll.hx-media-ready .hx-media-item,
    .hx-media-scroll.hx-media-ready .hx-media-item > .h1-fs,
    .hx-media-scroll.hx-media-ready .hx-media-item > p:last-child {
        opacity: 1 !important;
        transform: none !important;
    }

    .hx-media-scroll.hx-media-ready .key-image-slot {
        clip-path: none !important;
    }

    .hx-media-scroll .key-image-slot::after,
    .hx-media-scroll.hx-media-ready.hx-in .key-image-slot::after {
        animation: none !important;
        opacity: 0 !important;
    }

    .s_key_images .key-image-slot img,
    .hx-media-scroll .key-image-slot img {
        --hx-par: 0px !important;
        transform: scale(1.01);
        transition: none;
        will-change: auto;
    }

    .s_key_images .key-image-slot:hover,
    .hx-media-scroll .key-image-slot:hover {
        transform: none;
    }
}

/* =============================================================================
   21  MOTION, REVEALS AND PAGE TRANSITIONS
   -----------------------------------------------------------------------------
   Motion is used for three jobs only: to show where new content came from,
   to confirm a press, and to cover a navigation. Nothing loops, nothing
   blocks input, and every effect is cancelled under reduced motion.
   ========================================================================== */

/* Page entrance. Applied to <body> by ui-kit.js on first paint. */
@keyframes hx-page-in {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
}

body.hx-page-enter #wrap,
body.hx-page-enter main {
    animation: hx-page-in var(--hx-dur-slow) var(--hx-ease) both;
}

/* Scroll reveal. ui-kit.js tags candidates and flips .hx-in via
   IntersectionObserver. The resting state is the FINAL state, so if the
   script never runs the content is still fully visible. */
.hx-reveal {
    opacity: 0;
    transform: translateY(14px);
    transition: opacity var(--hx-dur-slow) var(--hx-ease),
                transform var(--hx-dur-slow) var(--hx-ease);
    transition-delay: var(--hx-reveal-delay, 0ms);
    will-change: opacity, transform;
}

.hx-reveal.hx-in {
    opacity: 1;
    transform: none;
}

/* Once revealed, drop the compositing hint. */
.hx-reveal.hx-in.hx-settled {
    will-change: auto;
    transform: none;
}

/* Staggered grids: children animate in sequence rather than as one block. */
.hx-stagger > * {
    opacity: 0;
    transform: translateY(12px) scale(0.985);
    transition: opacity var(--hx-dur-slow) var(--hx-ease),
                transform var(--hx-dur-slow) var(--hx-ease);
}

.hx-stagger.hx-in > * {
    opacity: 1;
    transform: none;
}

.hx-stagger.hx-in > *:nth-child(1)  { transition-delay: 0ms; }
.hx-stagger.hx-in > *:nth-child(2)  { transition-delay: 55ms; }
.hx-stagger.hx-in > *:nth-child(3)  { transition-delay: 110ms; }
.hx-stagger.hx-in > *:nth-child(4)  { transition-delay: 165ms; }
.hx-stagger.hx-in > *:nth-child(5)  { transition-delay: 220ms; }
.hx-stagger.hx-in > *:nth-child(6)  { transition-delay: 265ms; }
.hx-stagger.hx-in > *:nth-child(7)  { transition-delay: 300ms; }
.hx-stagger.hx-in > *:nth-child(8)  { transition-delay: 330ms; }
.hx-stagger.hx-in > *:nth-child(n+9) { transition-delay: 355ms; }

/* Rows injected into a table or list after a fetch. */
@keyframes hx-row-in {
    from { opacity: 0; transform: translateY(5px); }
    to   { opacity: 1; transform: none; }
}

.hx-row-in {
    animation: hx-row-in var(--hx-dur) var(--hx-ease) both;
}

/* A thin route-progress bar shown during full page navigations. */
.hx-route-progress {
    position: fixed;
    top: 0;
    left: 0;
    height: 2px;
    width: 0;
    z-index: 2000;
    background: linear-gradient(90deg, var(--hx-primary), var(--hx-accent));
    box-shadow: 0 0 10px var(--hx-primary-ring);
    opacity: 0;
    pointer-events: none;
    transition: width var(--hx-dur-slow) var(--hx-ease),
                opacity var(--hx-dur-fast) var(--hx-ease);
}

.hx-route-progress.hx-active {
    opacity: 1;
}

/* =============================================================================
   22  SKELETONS AND LOADING STATES
   -----------------------------------------------------------------------------
   Skeletons mirror the shape of the content they replace so the swap costs
   no layout shift. Shimmer travels once per cycle and is disabled under
   reduced motion (the block stays, only the travel stops).
   ========================================================================== */

.hx-skeleton {
    position: relative;
    overflow: hidden;
    background: var(--hx-surface-3);
    border-radius: var(--hx-r-xs);
    /* Skeletons are decorative; they must not be announced. */
    color: transparent;
    user-select: none;
    pointer-events: none;
}

.hx-skeleton::after {
    content: "";
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    background: linear-gradient(90deg,
                transparent 0%,
                var(--hx-line) 45%,
                var(--hx-line) 55%,
                transparent 100%);
    animation: hx-shimmer 1.35s var(--hx-ease-inout) infinite;
}

@keyframes hx-shimmer {
    100% { transform: translateX(100%); }
}

.hx-skeleton-text {
    height: 0.7rem;
    margin-bottom: 0.5rem;
    border-radius: var(--hx-r-pill);
}

.hx-skeleton-text:last-child {
    margin-bottom: 0;
    width: 65%;
}

.hx-skeleton-title {
    height: 1.05rem;
    width: 42%;
    margin-bottom: 0.85rem;
    border-radius: var(--hx-r-pill);
}

.hx-skeleton-avatar {
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 50%;
    flex-shrink: 0;
}

.hx-skeleton-block {
    height: 8.4rem;
    border-radius: var(--hx-r-lg);
}

.hx-skeleton-card {
    background: var(--hx-surface);
    border: 1px solid var(--hx-line);
    border-radius: var(--hx-r-lg);
    padding: 1.15rem;
}

.hx-skeleton-kpi-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr));
    gap: 0.85rem;
}

/* Buttons that are mid-request: the label stays (so width is stable) and a
   spinner is layered over it. Driven by ui-kit.js. */
.btn.hx-busy {
    color: transparent !important;
    pointer-events: none;
    position: relative;
}

.btn.hx-busy::after {
    content: "";
    position: absolute;
    width: 1rem;
    height: 1rem;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: hx-spin 0.6s linear infinite;
    color: var(--hx-on-primary);
}

.btn-outline-primary.hx-busy::after,
.btn-outline-secondary.hx-busy::after,
.btn-light.hx-busy::after,
.btn-secondary.hx-busy::after {
    color: var(--hx-ink-2);
}

@keyframes hx-spin {
    to { transform: rotate(360deg); }
}

/* A centred, branded block loader for panels that fetch on open. */
.hx-loading-panel {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.85rem;
    padding: var(--hx-s-10) var(--hx-s-4);
    color: var(--hx-muted);
    text-align: center;
}

.hx-loading-dots {
    display: inline-flex;
    gap: 0.3rem;
}

.hx-loading-dots span {
    width: 0.45rem;
    height: 0.45rem;
    border-radius: 50%;
    background: var(--hx-primary);
    animation: hx-pulse-dot 1.1s var(--hx-ease-inout) infinite;
}

.hx-loading-dots span:nth-child(2) { animation-delay: 0.14s; }
.hx-loading-dots span:nth-child(3) { animation-delay: 0.28s; }

@keyframes hx-pulse-dot {
    0%, 60%, 100% { opacity: 0.28; transform: scale(0.8); }
    30%           { opacity: 1;    transform: scale(1); }
}

/* =============================================================================
   23  EMPTY STATES
   -----------------------------------------------------------------------------
   An empty region should explain itself and offer the next step, never show
   a blank rectangle.
   ========================================================================== */

.hx-empty,
.notif-empty,
.assessment-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    text-align: center;
    padding: var(--hx-s-10) var(--hx-s-5);
    border: 1px dashed var(--hx-line-strong);
    border-radius: var(--hx-r-lg);
    background: var(--hx-surface-2);
    color: var(--hx-muted);
}

.hx-empty__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3rem;
    height: 3rem;
    margin-bottom: 0.25rem;
    border-radius: var(--hx-r-md);
    background: var(--hx-primary-soft);
    color: var(--hx-primary-strong);
    font-size: 1.25rem;
}

.hx-empty__title {
    font-family: var(--hx-font-display);
    font-size: var(--hx-t-md);
    font-weight: 700;
    color: var(--hx-ink);
    margin: 0;
}

.hx-empty__text {
    font-size: var(--hx-t-sm);
    color: var(--hx-muted);
    margin: 0;
    max-width: 34ch;
}

.hx-empty .btn {
    margin-top: 0.5rem;
}

/* =============================================================================
   24  LEGACY COLOUR CORRECTIONS
   -----------------------------------------------------------------------------
   The Odoo bundle and several per-page <style> blocks hard-code light-mode
   colours. These rules re-point the ones that would otherwise strand text on
   a same-colour ground in dark mode. Presentation only.
   ========================================================================== */

[data-theme="dark"] .card,
[data-theme="dark"] .modal-content,
[data-theme="dark"] .dropdown-menu,
[data-theme="dark"] .list-group-item,
[data-theme="dark"] .accordion-item,
[data-theme="dark"] .accordion-body,
[data-theme="dark"] .offcanvas,
[data-theme="dark"] .popover {
    background: var(--hx-surface);
    background-image: none;
    color: var(--hx-ink-2);
    border-color: var(--hx-line);
}

/* Login / register / payment pages paint white gradients on .card before Helix. */
[data-theme="dark"] .oe_website_login_container .card,
[data-theme="dark"] .card.shadow,
[data-theme="dark"] .card.shadow-sm {
    background: var(--hx-surface);
    background-image: none;
    color: var(--hx-ink-2);
}

[data-theme="dark"] .bg-white,
[data-theme="dark"] .bg-light,
[data-theme="dark"] .table-light,
[data-theme="dark"] .text-bg-light,
[data-theme="dark"] .text-bg-white {
    background-color: var(--hx-surface-2) !important;
    color: var(--hx-ink-2) !important;
}

[data-theme="dark"] .text-muted {
    color: var(--hx-muted) !important;
}

[data-theme="dark"] .portal-btn:not(.active),
[data-theme="dark"] .curriculum-btn:not(.active) {
    background: var(--hx-surface-3) !important;
    color: var(--hx-ink-2) !important;
    box-shadow: none !important;
}

[data-theme="dark"] .portal-btn:not(.active):hover,
[data-theme="dark"] .curriculum-btn:not(.active):hover {
    background: var(--hx-surface-2) !important;
    color: var(--hx-ink) !important;
}

[data-theme="dark"] .input-group .btn {
    color: var(--hx-ink-2);
    border-color: var(--hx-line-strong);
}

[data-theme="dark"] .navbar-light .navbar-nav .nav-link,
[data-theme="dark"] .navbar-nav .nav-link {
    color: var(--hx-ink-2);
}

[data-theme="dark"] .navbar-light .navbar-nav .nav-link:hover,
[data-theme="dark"] .navbar-nav .nav-link:hover,
[data-theme="dark"] .navbar-light .navbar-nav .nav-link:focus {
    color: var(--hx-ink);
}

[data-theme="dark"] .tooltip-inner {
    background-color: var(--hx-surface-inverse);
    color: var(--hx-on-inverse);
}

[data-theme="dark"] .form-select option {
    background-color: var(--hx-surface);
    color: var(--hx-ink);
}

[data-theme="dark"] .pay-card .card-header,
[data-theme="dark"] .notif-empty {
    background: var(--hx-surface-2);
    background-image: none;
    color: var(--hx-ink-2);
}

[data-theme="dark"] #receipt-feedback.alert-danger {
    background: var(--hx-danger-soft);
    color: #FECACA;
    border-color: var(--hx-danger-ring);
}

[data-theme="dark"] #receipt-feedback.alert-warning {
    background: var(--hx-warn-soft);
    color: #FDE68A;
    border-color: var(--hx-warn-ring);
}

[data-theme="dark"] #receipt-feedback.alert-success {
    background: var(--hx-success-soft);
    color: #BBF7D0;
    border-color: var(--hx-success-ring);
}

[data-theme="dark"] #receipt-feedback.alert-info {
    background: var(--hx-info-soft);
    color: #BFDBFE;
    border-color: var(--hx-info-ring);
}

[data-theme="dark"] .aa-assistant-item.active {
    background: var(--hx-primary-soft);
    border-left-color: var(--hx-primary);
}

[data-theme="dark"] .ms-quiz-review-q,
[data-theme="dark"] .ms-pr-kpi,
[data-theme="dark"] .ms-panel {
    background: var(--hx-surface);
    color: var(--hx-ink-2);
}

/* Anything the legacy CSS painted pure white or near-white. */
[data-theme="dark"] [style*="background: #fff"],
[data-theme="dark"] [style*="background:#fff"],
[data-theme="dark"] [style*="background-color: #fff"],
[data-theme="dark"] [style*="background-color:#fff"],
[data-theme="dark"] [style*="background: white"],
[data-theme="dark"] [style*="background-color: white"] {
    background-color: var(--hx-surface) !important;
    color: var(--hx-ink-2);
}

[data-theme="dark"] [style*="background: #f8f9fa"],
[data-theme="dark"] [style*="background:#f8f9fa"],
[data-theme="dark"] [style*="background-color: #f8f9fa"],
[data-theme="dark"] [style*="background-color:#f8f9fa"] {
    background-color: var(--hx-surface-2) !important;
}

/* Odoo colour-combination wrappers default to a white ground. */
[data-theme="dark"] .o_cc,
[data-theme="dark"] .o_cc1,
[data-theme="dark"] .o_cc2,
[data-theme="dark"] .o_cc5,
[data-theme="dark"] .o_colored_level {
    background-color: transparent !important;
    color: var(--hx-ink-2);
}

[data-theme="dark"] .s_text_block,
[data-theme="dark"] .s_title,
[data-theme="dark"] .s_discovery,
[data-theme="dark"] .s_key_images,
[data-theme="dark"] .s_learning_options {
    background-color: transparent !important;
    color: var(--hx-ink-2);
}

/* Legacy inline .stat-card / dashboard tints resolved through tokens. */
[data-theme="dark"] .dashboard-card,
[data-theme="dark"] .dashboard-card-header {
    background-color: var(--hx-surface);
    color: var(--hx-ink-2);
    border-color: var(--hx-line);
}

/* Images and photographic panels are eased down slightly on dark so they do
   not glare against the deep ground. */
[data-theme="dark"] .page-hero__bg,
[data-theme="dark"] .course-card-img img,
[data-theme="dark"] .course-hero-img img {
    filter: brightness(0.86) saturate(0.95);
}

/* =============================================================================
   25  RESPONSIVE AND ACCESSIBILITY
   ========================================================================== */

/* Shared mobile upbar (d-lg-none / <992px). Same chrome the site already
   shows on phones and portrait tablets — desktop nav is unchanged. */
@media (max-width: 991.98px) {
    #top nav[aria-label="Mobile"] .navbar-brand.logo img.hx-logo-light,
    #wrapwrap #top nav[aria-label="Mobile"] .navbar-brand.logo img.hx-logo-light,
    html[data-theme="dark"] #top nav[aria-label="Mobile"] .navbar-brand.logo img.hx-logo-dark,
    html[data-theme="dark"] #wrapwrap #top nav[aria-label="Mobile"] .navbar-brand.logo img.hx-logo-dark {
        max-height: 40px !important;
        max-width: min(88px, 36vw) !important;
    }

    /* Bootstrap navbar-light keeps a dark hamburger SVG; our theme uses data-theme. */
    [data-theme="dark"] #top nav[aria-label="Mobile"] .navbar-toggler-icon {
        filter: invert(1) contrast(1.15);
        opacity: 1;
    }

    [data-theme="dark"] #top nav[aria-label="Mobile"] .hx-theme-toggle {
        color: #F8FAFC;
        background: rgba(255, 255, 255, 0.14);
        border-color: rgba(255, 255, 255, 0.42);
    }

    [data-theme="dark"] #top nav[aria-label="Mobile"] .hx-theme-toggle:hover,
    [data-theme="dark"] #top nav[aria-label="Mobile"] .hx-theme-toggle:focus,
    [data-theme="dark"] #top nav[aria-label="Mobile"] .hx-theme-toggle:active {
        color: #FFFFFF;
        background: rgba(255, 255, 255, 0.24);
        border-color: #FFFFFF;
    }

    /* Staff hamburger is already shown only below 992px. */
    [data-theme="dark"] .staff-topbar__menu {
        color: var(--hx-ink);
        background: var(--hx-surface-2);
        border-color: var(--hx-line-strong);
    }
}

/* Comfortable touch targets on coarse pointers. Applies to phones and
   tablets regardless of viewport width. */
@media (pointer: coarse) {
    .btn,
    .nav-link,
    .dropdown-item,
    .page-link,
    .form-control,
    .form-select,
    .staff-nav-link,
    .site-footer-social-link,
    .hx-theme-toggle {
        min-height: 2.75rem;
    }

    .btn-sm {
        min-height: 2.5rem;
        padding-left: 0.85rem;
        padding-right: 0.85rem;
    }

    .form-check-input {
        width: 1.25rem;
        height: 1.25rem;
    }

    /* Hover lifts are pointless on touch and cause sticky states. */
    .course-card:hover,
    .staff-tool-card:hover,
    .sd-kpi-clickable:hover,
    .cp-kpi-clickable:hover,
    .stat-card-clickable:hover,
    a.card:hover,
    .card-hoverable:hover {
        transform: none;
    }
}

/* iOS zooms the page when a control smaller than 16px receives focus. */
@media (max-width: 767.98px) {
    .form-control,
    .form-select,
    input[type="text"],
    input[type="email"],
    input[type="password"],
    input[type="number"],
    input[type="search"],
    input[type="tel"],
    textarea,
    select {
        font-size: 16px;
    }

    .modal-content {
        border-radius: var(--hx-r-lg);
    }

    .card,
    .sd-panel,
    .cp-panel {
        border-radius: var(--hx-r-md);
    }

    .staff-tools-grid {
        gap: 0.65rem;
    }

    .page-hero h1 {
        font-size: clamp(1.4rem, 1.1rem + 1.6vw, 1.9rem);
    }

    body.student-dashboard .page-hero h1,
    body.parent-dashboard .page-hero h1,
    body.student-dashboard .page-hero p,
    body.parent-dashboard .page-hero p {
        max-width: 100%;
        overflow-wrap: anywhere;
        word-break: break-word;
    }

    .profile-avatar {
        width: 3.5rem;
        height: 3.5rem;
        font-size: 1.35rem;
    }

    .profile-card__identity {
        gap: 0.85rem;
    }
}

/* Tablet: the staff sidebar is off-canvas below 992px (existing behaviour);
   give the content back its full width and keep the topbar reachable. */
@media (max-width: 991.98px) {
    body.staff-portal main {
        padding: 1rem 1rem 2.5rem;
    }

    .staff-sidebar {
        box-shadow: var(--hx-shadow-xl);
    }
}

/* Short viewports: a 1080p laptop at 150% OS scaling reports ~600px of height.
   Bootstrap gives .modal-header/.modal-footer flex-shrink: 0, so every pixel the
   viewport loses comes out of the scrollable .modal-body. Reclaim the dialog
   margin and tighten the chrome so the form keeps a usable amount of room.
   Lives here, not in ui_scale.css, so it outranks the .modal-header/.modal-footer
   padding set in design-components.css. */
@media (max-height: 700px) {
    .modal {
        --bs-modal-margin: 0.5rem;
    }

    .modal-dialog-scrollable,
    .modal-dialog-scrollable .modal-content {
        max-height: calc(100vh - 1rem);
    }

    .modal-header {
        padding-top: 0.6rem;
        padding-bottom: 0.6rem;
    }

    .modal-footer {
        padding-top: 0.55rem;
        padding-bottom: 0.55rem;
    }
}

@media (max-width: 575.98px) {
    body.staff-portal main {
        padding: 0.85rem 0.85rem 2.25rem;
    }

    .card-body,
    .sd-panel__body {
        padding: 0.95rem;
    }

    .modal-header,
    .modal-body,
    .modal-footer {
        padding-left: 1rem;
        padding-right: 1rem;
    }

    .hx-empty,
    .notif-empty,
    .assessment-empty {
        padding: var(--hx-s-8) var(--hx-s-4);
    }

    .profile-avatar {
        width: 3.25rem;
        height: 3.25rem;
        font-size: 1.25rem;
    }
}

/* Very wide screens: cap measure so dashboards do not stretch to 4K width. */
@media (min-width: 1600px) {
    body.staff-portal .container {
        max-width: 1320px;
    }
}

/* Respect the platform setting. Motion that conveys state is reduced to an
   instant change; nothing is left mid-animation and nothing stays hidden. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }

    .hx-reveal,
    .hx-stagger > * {
        opacity: 1 !important;
        transform: none !important;
    }

    .hx-skeleton::after {
        animation: none;
        opacity: 0.5;
    }

    .btn:active:not(:disabled) {
        transform: none;
    }

    .page-hero__bg {
        transform: none;
    }

    /* Product marquees stay on the automatic LEFT → RIGHT loop. Exempt only
       the track so the rest of the page still honours reduced motion. */
    .hx-marquee__track {
        animation-duration: var(--hx-mq-duration, 40s) !important;
        animation-iteration-count: infinite !important;
    }
}

/* Windows High Contrast / forced colours: let the OS palette win, but keep
   the structural borders so layout stays readable. */
@media (forced-colors: active) {
    .btn,
    .card,
    .form-control,
    .form-select,
    .staff-nav-link.active,
    .badge {
        border: 1px solid currentColor;
    }

    .hx-skeleton::after {
        animation: none;
    }
}

/* Keyboard users get a stronger ring on dense surfaces. */
.table tbody tr:focus-within {
    outline: 2px solid var(--hx-primary-ring);
    outline-offset: -2px;
}

/* Skip link — already in the markup, now actually visible when focused. */
.visually-hidden-focusable:focus,
.visually-hidden-focusable:focus-within {
    background: var(--hx-primary-solid);
    color: var(--hx-on-primary);
    padding: 0.6rem 1rem;
    border-radius: 0 0 var(--hx-r-sm) 0;
    font-weight: 600;
    z-index: 2001;
    box-shadow: var(--hx-shadow-lg);
}

/* Student missed-work attention banner (not a lock). */
.bw-missed-work-alert {
    /* Centred dialog, matching the Progress Report modal pattern.
       The element itself is the full-screen overlay; its inner .container is the card.
       position:fixed keeps it out of normal flow, so it never pushes page content down
       and the mount point in auth.js stays irrelevant.
       z-index 1055 matches Bootstrap's modal layer. */
    position: fixed;
    inset: 0;
    z-index: 1055;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    background: rgba(15, 23, 42, 0.55);
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
    color: #FFFFFF;
    overflow-y: auto;
    overscroll-behavior: contain;
}

/* The inner Bootstrap .container becomes the dialog card. */
.bw-missed-work-alert > .container {
    width: 100%;
    max-width: 32rem;
    margin: auto;
    padding: 1.25rem;
    border-radius: 0.75rem;
    border: 1px solid rgba(255, 255, 255, 0.22);
    box-shadow: 0 1.5rem 3rem rgba(0, 0, 0, 0.45);
}

/* Inside a 32rem card, keep icon / text / buttons stacked at every width. */
.bw-missed-work-alert > .container > .d-flex {
    flex-direction: column !important;
    align-items: stretch !important;
    gap: 0.875rem !important;
}

/* Severity colour moves from the overlay to the card. */
.bw-missed-work-alert--warning > .container {
    background: linear-gradient(145deg, #B45309, #78350F);
}

.bw-missed-work-alert--high > .container {
    background: linear-gradient(145deg, #DC2626, #7F1D1D);
}

.bw-missed-work-alert__icon {
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 999px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    background: rgba(255, 255, 255, 0.16);
    font-size: 1.15rem;
}

.bw-missed-work-alert__title {
    font-weight: 700;
    letter-spacing: 0.01em;
    font-size: 1.05rem;
}

.bw-missed-work-alert__body,
.bw-missed-work-alert__count {
    color: rgba(255, 255, 255, 0.94);
}

.bw-missed-work-alert__count {
    font-size: 0.875rem;
}

.bw-missed-work-alert__action,
.bw-missed-work-alert__dismiss {
    color: #FFFFFF;
    border: 1px solid #FFFFFF;
    background: rgba(255, 255, 255, 0.22);
}

.bw-missed-work-alert__action:hover,
.bw-missed-work-alert__action:focus,
.bw-missed-work-alert__dismiss:hover,
.bw-missed-work-alert__dismiss:focus {
    color: #111827;
    background: #FFFFFF;
    border-color: #FFFFFF;
}

.bw-missed-work-alert__action:focus-visible,
.bw-missed-work-alert__dismiss:focus-visible {
    outline: 2px solid #FFFFFF;
    outline-offset: 3px;
}

[data-theme="dark"] .bw-missed-work-alert--warning > .container {
    background: linear-gradient(145deg, #92400E, #422006);
}

[data-theme="dark"] .bw-missed-work-alert--high > .container {
    background: linear-gradient(145deg, #B91C1C, #450A0A);
}

[data-theme="dark"] .bw-missed-work-alert__body,
[data-theme="dark"] .bw-missed-work-alert__count {
    color: #F9FAFB;
}

/* Print: drop the decorative layer, keep the content. */
@media print {
    body {
        background: #FFFFFF !important;
        color: #000000 !important;
    }

    #top,
    .bw-missed-work-alert,
    .staff-sidebar,
    .staff-topbar,
    .site-footer,
    .hx-theme-toggle,
    .hx-route-progress,
    .practice-dock {
        display: none !important;
    }

    body.staff-portal #wrapwrap {
        padding: 0 !important;
    }

    .card,
    .sd-panel,
    .cp-panel {
        box-shadow: none !important;
        border: 1px solid #CCCCCC !important;
        break-inside: avoid;
    }
}
