/* ── Genius Sync design tokens — the product website's identity (paper, indigo ink and the
   toy-block colours of the logo). Theme/SchoolSyncTheme.cs carries the same values for
   MudBlazor; keep the two in step. ─────────────────────────────────────────────────────── */
:root {
    --gs-paper: #FFFAF0;
    --gs-cream: #FFF1D2;
    --gs-ink: #1E2145;
    --gs-ink2: #4A4D70;
    --gs-mute: #6A6C88;
    --gs-line: rgba(30, 33, 69, 0.13);
    --gs-sun: #FFC52E;
    --gs-sun-l: #FFE7A3;
    --gs-berry: #B3245A;
    --gs-berry-l: #FBE1EA;
    --gs-sky: #2F7FE6;
    --gs-sky-d: #1B5DBB;
    --gs-sky-l: #DDEBFF;
    --gs-leaf: #1C7F51;
    --gs-leaf-l: #DBF2E5;
    --gs-amber: #9A5B00;
    --gs-amber-l: #FFEBC2;
    --gs-night: #1C1E44;
    --gs-font-display: 'Fredoka', ui-rounded, 'Segoe UI', system-ui, sans-serif;
    --gs-font-body: 'Figtree', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --gs-shadow: 0 30px 60px -32px rgba(30, 33, 69, 0.45);
}

html, body {
    height: 100%;
    margin: 0;
    font-family: var(--gs-font-body);
    color: var(--gs-ink);
    /* Same paper as the loading splash and every other screen's base — a permanent baseline
       so nothing (a resize, a brief gap between the splash unmounting and the app's own
       layout painting, etc.) can ever show the browser's default white through. The inline
       <style> in index.html duplicates just this line so it's already in effect before this
       stylesheet itself has loaded. */
    background: var(--gs-paper);
    /* Global safety net: no page should ever be horizontally scrollable — this
       only ever kicks in if something further down the tree is wider than the
       viewport, at which point clipping the overflow is safer than a page-wide
       horizontal scrollbar shifting every fixed-position element (appbar, drawer). */
    overflow-x: hidden;
}

/* ── Loading splash — a fixed overlay sibling of #app (index.html), shown from first paint
   until a tiny inline script (index.html, no Blazor/startup code touched) detects Blazor has
   inserted its first real content into #app and fades this out. Same design language as
   SchoolSync.StudentPortal.Web's loading splash (wwwroot/css/app.css there) so both apps read
   as one product family — kept as a separate copy rather than a shared reference for the same
   reason the theme/CSS are already duplicated between the two apps (see StudentPortal.Web's
   own .csproj comment). `html, body`'s inline <style> in index.html covers the sliver of time
   before this stylesheet itself has loaded. ─────────────────────────────────────────────── */
.loading-splash {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    box-sizing: border-box;
    padding: 24px;
    background:
        radial-gradient(ellipse 900px 700px at 50% 30%, #FFFFFF 0%, transparent 60%),
        var(--gs-paper);
    opacity: 0;
    animation: loading-splash-fade-in 0.5s ease forwards;
    transition: opacity 0.45s ease;
}

.loading-splash.loading-splash-hide {
    opacity: 0;
    pointer-events: none;
}

@keyframes loading-splash-fade-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

.loading-splash-glow {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background:
        radial-gradient(ellipse 520px 420px at 50% 42%, rgba(255, 197, 46, 0.22), transparent 65%),
        radial-gradient(ellipse 480px 480px at 60% 60%, rgba(47, 127, 230, 0.10), transparent 65%);
}

/* Handful of small, slow-drifting motes — kept extremely subtle (low opacity, tiny size,
   long duration) so it reads as ambient depth rather than a decorative gimmick. */
.loading-splash-particles {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    overflow: hidden;
}

.loading-splash-particles span {
    position: absolute;
    width: 3px;
    height: 3px;
    border-radius: 50%;
    opacity: 0;
    animation: loading-particle-float 7s ease-in-out infinite;
}

.loading-splash-particles span:nth-child(1) { left: 22%; top: 68%; background: #FFC52E; animation-delay: 0s; }
.loading-splash-particles span:nth-child(2) { left: 78%; top: 32%; background: #2F7FE6; animation-delay: 1.4s; }
.loading-splash-particles span:nth-child(3) { left: 36%; top: 22%; background: #B3245A; animation-delay: 2.8s; }
.loading-splash-particles span:nth-child(4) { left: 66%; top: 74%; background: #FFC52E; animation-delay: 4.2s; }
.loading-splash-particles span:nth-child(5) { left: 50%; top: 48%; background: #2F7FE6; animation-delay: 5.6s; }

@keyframes loading-particle-float {
    0% { opacity: 0; transform: translateY(0); }
    30% { opacity: 0.4; }
    70% { opacity: 0.4; }
    100% { opacity: 0; transform: translateY(-36px); }
}

.loading-splash-content {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    max-width: 320px;
}

/* Icon container — the toy-block logo sits inside a soft rounded surface rather than
   floating bare, with a slow pulsing glow so the whole composition feels alive without being
   distracting. */
.loading-splash-icon-ring {
    display: flex;
    align-items: center;
    justify-content: center;
    width: clamp(84px, 20vw, 104px);
    height: clamp(84px, 20vw, 104px);
    border-radius: 26px;
    margin-bottom: 22px;
    background: #FFFFFF;
    border: 1.5px solid var(--gs-line);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    opacity: 0;
    animation:
        loading-splash-scale-in 0.5s ease 0.1s forwards,
        loading-icon-ring-pulse 3.2s ease-in-out 0.6s infinite;
}

.loading-splash-icon {
    width: 52%;
    height: 52%;
}

@keyframes loading-splash-scale-in {
    from { opacity: 0; transform: scale(0.82); }
    to { opacity: 1; transform: scale(1); }
}

@keyframes loading-icon-ring-pulse {
    0%, 100% { box-shadow: 0 0 0 6px rgba(255, 197, 46, 0.18), 0 18px 36px -18px rgba(30, 33, 69, 0.35); }
    50% { box-shadow: 0 0 0 12px rgba(255, 197, 46, 0.10), 0 18px 36px -18px rgba(30, 33, 69, 0.35); }
}

.loading-splash-wordmark {
    font-family: var(--gs-font-display);
    font-weight: 600;
    font-size: clamp(1.4rem, 5.5vw, 1.75rem);
    letter-spacing: -0.01em;
    color: var(--gs-ink);
    margin-bottom: 10px;
    opacity: 0;
    animation: loading-splash-fade-up 0.5s ease 0.3s forwards;
}

.loading-splash-wordmark .accent-berry { color: var(--gs-berry); }

.loading-splash-message {
    font-family: var(--gs-font-body);
    font-size: 0.9rem;
    color: var(--gs-mute);
    margin-bottom: 22px;
    opacity: 0;
    animation: loading-splash-fade-up 0.5s ease 0.45s forwards;
}

@keyframes loading-splash-fade-up {
    from { opacity: 0; transform: translateY(6px); }
    to { opacity: 1; transform: translateY(0); }
}

/* Modern gradient sweep line, favoured over a spinner or dots — a slim rounded track with a
   short gradient segment sweeping across it. */
.loading-bar {
    position: relative;
    width: 150px;
    height: 3px;
    border-radius: 999px;
    overflow: hidden;
    background: rgba(30, 33, 69, 0.10);
    opacity: 0;
    animation: loading-splash-fade-up 0.5s ease 0.6s forwards;
}

.loading-bar span {
    position: absolute;
    inset: 0;
    width: 42%;
    left: -42%;
    border-radius: 999px;
    background: linear-gradient(90deg, #FFC52E, #B3245A, #2F7FE6);
    animation: loading-bar-sweep 1.6s ease-in-out infinite;
}

@keyframes loading-bar-sweep {
    0% { left: -42%; }
    100% { left: 100%; }
}

#blazor-error-ui {
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.5rem;
}


/* ── Brand mark (BrandMark.razor): the toy-block logo + "Genius Sync" wordmark ──────────── */
.brand-mark {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    color: var(--gs-ink);
    text-decoration: none;
    min-width: 0;
}

.brand-mark-icon {
    display: block;
    flex-shrink: 0;
}

.brand-wordmark {
    font-family: var(--gs-font-display);
    font-weight: 600;
    font-size: 1.3rem;
    line-height: 1;
    letter-spacing: -0.01em;
    white-space: nowrap;
    color: var(--gs-ink);
}

.brand-wordmark b {
    font-weight: 600;
    color: var(--gs-berry);
}

.brand-wordmark small {
    display: block;
    margin-top: 4px;
    font: 700 0.62rem/1 var(--gs-font-body);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--gs-mute);
}

h1, h2, h3, h4, h5, h6 { font-family: var(--gs-font-display); }

/* ── Genius Sync theme — logo, borders, pill status chips ─────────────── */

.app-logo {
    height: 40px;
    width: auto;
    display: block;
}

.app-logo-large {
    width: min(90%, 380px);
    max-width: 100%;
    height: auto;
    display: block;
}

/* The app bar's brand (a BrandMark): the wordmark shrinks on phones below. */
.app-wordmark .brand-wordmark {
    font-size: 1.35rem;
}


/* Phones: the connection chips drop their words where the icon says it (see .app-bar-chip-text), and the wordmark shrinks. */
@media (max-width: 599px) {
    .app-wordmark .brand-wordmark { font-size: 1.1rem; }
    .app-bar-chip-text { display: none; }
}

.login-logo-wrap {
    display: flex;
    justify-content: center;
    margin-top: -16px;
    margin-bottom: 8px;
}

.app-bar-bordered {
    border-bottom: 1px solid var(--mud-palette-divider);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    background-color: rgba(255, 250, 240, 0.88) !important;
}

.auth-layout {
    min-height: 100vh;
    background-color: var(--mud-palette-background);
    display: flex;
    align-items: center;
    justify-content: center;
}

.drawer-bordered {
    border-right: 1px solid var(--mud-palette-divider);
    background: #FFFFFF;
}

/* ── Mobile drawer overlay fix — the actual root cause of content being cut
   off/overlapping on phones ───────────────────────────────────────────────
   MudDrawer's Persistent variant (chosen deliberately in MainLayout.razor to
   avoid MudBlazor's JS-based Responsive variant, which broke MAUI Hybrid) has
   NO built-in breakpoint awareness of its own: MudBlazor's own CSS pushes
   .mud-main-content/.mud-appbar over by the drawer's full width via
   margin-left whenever a Persistent drawer is open, at EVERY screen size —
   there's no small-viewport exception, because "Persistent" means exactly
   that: it always occupies layout space when open. On a ~375px phone, with
   the drawer open by default (MainLayout's _drawerOpen starts true) and a
   ~240px drawer width, that left only ~135px for all page content — enough
   to make every card/table/form look broken, not just wide ones.
   The drawer itself is already position:fixed (MudBlazor's own default), so
   it already visually floats above content regardless of this margin. Below
   960px (the same breakpoint used elsewhere in this file), cancelling that
   margin is the whole fix: content reclaims the full viewport width, and the
   already-fixed-position drawer naturally overlays on top when open, with a
   tap-to-close scrim (added in MainLayout.razor) behind it for usability. */
@media (max-width: 959px) {
    .mud-main-content,
    .mud-appbar {
        margin-left: 0 !important;
        width: 100% !important;
        /* Without this, .mud-main-content — a flex ITEM inside MudLayout's flex
           row — still refuses to shrink below the intrinsic min-content width of
           whatever it contains (the browser default for flex items is
           min-width:auto, which "width:100%" above does NOT override once content
           demands more space). A wide table several levels down can still force
           this element wider than the viewport despite the width:100% rule,
           which silently defeats any overflow-x:auto scroll container further
           down the tree (nothing ever actually became narrower than its content,
           so there was nothing to scroll against). This is what let the Fees
           Invoice page's Line Items table push the whole page wider on real
           phones even after the drawer-margin fix above. */
        min-width: 0 !important;
    }
}

.mobile-drawer-scrim {
    display: none;
}

@media (max-width: 959px) {
    .mobile-drawer-scrim {
        display: block;
        position: fixed;
        inset: 0;
        background: rgba(28, 30, 68, 0.45);
        /* One below the drawer's own z-index, so it sits behind the drawer
           but above everything else (page content, appbar). */
        z-index: calc(var(--mud-zindex-drawer, 1300) - 1);
    }
}

/* Rounded, pill-shaped status chips (matches the reference's "Active" badges) */
.mud-chip.mud-chip-filled {
    border-radius: 999px;
    font-weight: 600;
}

/* ── Ambient gradient mesh, behind the main content only ──────────────────
   Kept out of the sidebar/appbar so nav text stays crisp and easy to read. */
.mud-main-content {
    position: relative;
}

.mud-main-content::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    background:
        radial-gradient(ellipse 800px 500px at 15% -10%, rgba(255, 197, 46, 0.16), transparent 60%),
        radial-gradient(ellipse 900px 600px at 90% 10%, rgba(47, 127, 230, 0.07), transparent 60%),
        radial-gradient(ellipse 700px 700px at 50% 120%, rgba(179, 36, 90, 0.05), transparent 60%);
    animation: mesh-drift 24s ease-in-out infinite alternate;
    pointer-events: none;
}

@keyframes mesh-drift {
    0%   { transform: translate3d(0, 0, 0) scale(1); }
    100% { transform: translate3d(-2%, 2%, 0) scale(1.05); }
}

/* ── Card panels — scoped to cards inside the main content, so the
   solid-color appbar/drawer navigation is never affected. ────────────── */
.mud-main-content .mud-paper {
    border-radius: 16px;
    background-color: #FFFFFF;
    border: 1.5px solid var(--gs-line);
    transition: border-color 0.25s ease, box-shadow 0.25s ease;
}

.mud-main-content .mud-paper:hover {
    border-color: rgba(30, 33, 69, 0.22);
    box-shadow: 0 18px 36px -26px rgba(30, 33, 69, 0.45);
}

/* ── Signature element: the website's ink button sitting on a sunflower "block"
   edge. ─────────────────────────────────────────────────────────────────── */
.mud-button-filled.mud-button-filled-primary {
    background: var(--gs-ink);
    color: #FFFFFF;
    box-shadow: 0 3px 0 var(--gs-sun);
    transition: box-shadow 0.25s ease, transform 0.15s ease;
}

.mud-button-filled.mud-button-filled-primary:hover {
    background: var(--gs-ink);
    box-shadow: 0 5px 0 var(--gs-sun), 0 14px 26px -14px rgba(30, 33, 69, 0.7);
    transform: translateY(-1px);
}

@keyframes gradient-shift {
    0%, 100% { background-position: 0% 50%; }
    50%      { background-position: 100% 50%; }
}

/* ── Hover glow on other interactive elements ──────────────────────────── */
.mud-icon-button:hover {
    background-color: rgba(255, 197, 46, 0.22);
}

.mud-nav-link {
    border-radius: 10px;
    margin: 2px 8px;
    transition: background-color 0.2s ease, box-shadow 0.2s ease, transform 0.15s ease;
}

.mud-nav-link:hover {
    background-color: rgba(255, 197, 46, 0.18);
    transform: translateX(2px);
}

/* MudBlazor 7 marks the current page with .active (older builds used .mud-selected). */
.mud-nav-link.mud-selected,
.mud-nav-link.active:not(.mud-nav-link-disabled) {
    background: linear-gradient(90deg, rgba(255, 197, 46, 0.42), rgba(255, 231, 163, 0.28)) !important;
    border-inline-end-width: 0;
    box-shadow: inset 3px 0 0 0 var(--gs-berry);
    color: var(--gs-ink) !important;
    font-weight: 700;
}

/* ── Sidebar brand header ───────────────────────────────────────────── */
.nav-brand {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 20px 20px 18px;
    border-bottom: 1px solid var(--gs-line);
    margin-bottom: 8px;
}

.nav-brand .brand-wordmark {
    font-size: 1.25rem;
}

/* ── Section grouping labels — purely visual, no interaction change ───── */
.nav-section-label {
    display: block;
    padding: 16px 20px 6px;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 1.2px;
    text-transform: uppercase;
    color: var(--gs-mute);
}

.nav-section-label:first-of-type {
    padding-top: 4px;
}

/* ── Per-item accent colours — resting icon tint + matching hover wash
   wash. Active-state chrome (background/left-bar) stays the single
   consistent sunflower/berry brand treatment above, independent of these. ── */
.mud-nav-link .mud-icon-root {
    width: 22px;
    height: 22px;
    font-size: 22px;
}

.mud-nav-link.mud-selected .mud-icon-root,
.mud-nav-link.active .mud-icon-root {
    transform: scale(1.06);
}

/* Class names are historical; the colours are the logo's blocks. */
.nav-icon-orange .mud-icon-root { color: var(--gs-berry) !important; }
.nav-icon-blue .mud-icon-root { color: var(--gs-sky) !important; }
.nav-icon-green .mud-icon-root { color: var(--gs-leaf) !important; }
.nav-icon-purple .mud-icon-root { color: var(--gs-amber) !important; }
.nav-icon-cyan .mud-icon-root { color: var(--gs-sky-d) !important; }

.nav-icon-orange:hover { background-color: rgba(179, 36, 90, 0.08); }
.nav-icon-blue:hover { background-color: rgba(47, 127, 230, 0.09); }
.nav-icon-green:hover { background-color: rgba(28, 127, 81, 0.09); }
.nav-icon-purple:hover { background-color: rgba(255, 197, 46, 0.20); }
.nav-icon-cyan:hover { background-color: rgba(27, 93, 187, 0.08); }

@media (max-width: 599px) {
    .nav-brand {
        padding: 16px 16px 14px;
    }

    .nav-section-label {
        padding: 12px 16px 6px;
    }

    .mud-nav-link {
        margin: 2px 6px;
    }
}

/* Tables whose rows navigate somewhere on click (e.g. Students list). */
.clickable-table .mud-table-row {
    cursor: pointer;
}

/* Wide tables scroll horizontally within their own card instead of the page.
   Shared by every page with a MudTable/MudSimpleTable — first proven on the
   Fees/Invoice page's Line Items and Payments tables, now the standard
   pattern reused app-wide (see the "Shared responsive patterns" section
   further down for the mobile-only font/padding/min-width companion rules
   that go with this same class). */
.table-scroll {
    overflow-x: auto;
    max-width: 100%;
    box-sizing: border-box;
}

@media (max-width: 600px) {
    /* Dense table cells still don't fit 4-6 columns in ~340px of usable
       width, so shrink type/padding to need less room before the
       horizontal scroll above has to kick in at all.
       !important on both: MudBlazor's own per-cell rules set font-size/
       padding directly on td/th, which would otherwise override a plain
       descendant rule via inheritance regardless of this selector's
       specificity. */
    .table-scroll td,
    .table-scroll th {
        font-size: 0.8rem !important;
        padding: 6px 8px !important;
        white-space: nowrap;
    }

    /* The table itself keeps a sensible minimum width so its columns stay
       readable instead of being crushed to illegibility — on a ~360-412px
       phone that's necessarily wider than the viewport, which is exactly
       what the overflow-x:auto above is for: only the table scrolls
       horizontally within its own card, the page itself never does. */
    .table-scroll table {
        min-width: 412px;
    }
}

/* Covers MudTable, MudSimpleTable, and MudDataGrid row markup */
.mud-table-row:hover,
.mud-tr:hover,
tbody tr:hover {
    box-shadow: inset 3px 0 0 var(--mud-palette-info);
}

/* ── Accessibility floor: visible focus ring, reduced motion honoured ──── */
:focus-visible {
    outline: 3px solid var(--gs-sky);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    .mud-main-content::before,
    .mud-button-filled.mud-button-filled-primary,
    .loading-splash-icon-ring,
    .loading-splash-particles span,
    .loading-bar span {
        animation: none;
    }

    .loading-splash-icon-ring,
    .loading-splash-wordmark,
    .loading-splash-message,
    .loading-bar {
        opacity: 1;
    }

    .loading-bar span {
        left: 0;
    }

    * {
        transition-duration: 0.01ms !important;
        animation-duration: 0.01ms !important;
    }
}

/* ── Login split-screen (LoginPage only — scoped classes, doesn't touch
   .auth-layout/.login-logo-wrap which RegisterPage also relies on). ───── */

.login-shell {
    position: relative;
    display: flex;
    min-height: 100vh;
    width: 100%;
    overflow: hidden;
    /* One continuous background for both sides — the split is structural
       (flex layout) only, never a color/panel boundary: the website's warm
       paper with a faint dot grid, and sunflower/sky orbs (::before/::after)
       for depth. */
    background:
        radial-gradient(circle at 1px 1px, rgba(30, 33, 69, 0.07) 1px, transparent 0) 0 0 / 22px 22px,
        var(--gs-paper);
}

.login-shell::before,
.login-shell::after {
    content: "";
    position: absolute;
    border-radius: 50%;
    filter: blur(90px);
    pointer-events: none;
    z-index: 0;
}

.login-shell::before {
    width: 560px;
    height: 560px;
    top: -180px;
    left: -140px;
    background: radial-gradient(circle, rgba(255, 197, 46, 0.45), transparent 70%);
}

.login-shell::after {
    width: 560px;
    height: 560px;
    bottom: -200px;
    right: -160px;
    background: radial-gradient(circle, rgba(47, 127, 230, 0.16), transparent 70%);
}

/* Root-cause fix for mobile overflow/clipping: every panel and card below
   uses width:100% together with its own padding. Without border-box, that
   padding is added ON TOP of the 100% width (content-box is the CSS
   default), so each element renders wider than its parent by exactly its
   horizontal padding — invisible on a wide desktop viewport where the
   spare space just eats into the gap between panels, but on a 375–414px
   phone that overflow (up to ~80px) pushes the form card past the screen
   edge, causing horizontal scroll and clipped inputs. Scoped to this
   page's subtree only, so it can't shift spacing anywhere else in the app. */
.login-shell,
.login-shell *,
.login-shell *::before,
.login-shell *::after {
    box-sizing: border-box;
}

/* .login-content-row wraps the two panels below so the gap between them is
   a single, explicit, genuinely-controlled value instead of an accidental
   byproduct of two independently flex-grown panels whose actual content
   (branding text, the login card) is separately capped and left/center-
   anchored inside them. That's why bumping the panels' own flex-basis
   percentage (52/48, then 56/44) never closed the gap: growing a panel
   doesn't shrink the *leftover* space inside it once its content hits its
   own max-width — it just relocates where that leftover space sits. Both
   panels are now sized to their content only (flex: 0 1 auto, no grow), so
   the row's rendered width is simply branding-content + gap + login-card,
   centered as one unit within the shell. */
.login-content-row {
    display: flex;
    align-items: stretch;
    justify-content: center;
    width: 100%;
    max-width: 1360px;
    margin: 0 auto;
    gap: clamp(32px, 5vw, 110px);
}

.login-branding-panel {
    position: relative;
    z-index: 1;
    flex: 0 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    padding: 40px 32px;
}

.login-branding-content {
    position: relative;
    z-index: 1;
    max-width: 480px;
}

.login-brand-mark {
    display: inline-flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 48px;
}

.login-brand-mark .brand-wordmark {
    font-size: 1.7rem;
}

.login-heading {
    font-family: var(--gs-font-display);
    font-weight: 600;
    font-size: 2.6rem;
    line-height: 1.08;
    letter-spacing: -0.015em;
    color: var(--gs-ink);
    margin: 0 0 16px;
}

/* The website's berry word with a sunflower highlighter stroke. */
.login-heading .accent {
    color: var(--gs-berry);
    background: linear-gradient(transparent 72%, rgba(255, 197, 46, 0.75) 72%, rgba(255, 197, 46, 0.75) 92%, transparent 92%);
}

.login-subtitle {
    color: var(--gs-ink2);
    font-size: 1.05rem;
    line-height: 1.65;
    margin: 0 0 40px;
}

.login-illustration {
    position: relative;
    height: 96px;
    margin-bottom: 40px;
}

.login-orbit-badge {
    position: absolute;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 20px;
    background: #FFFFFF;
    border: 1.5px solid var(--gs-line);
    box-shadow: 0 4px 0 rgba(30, 33, 69, 0.12), 0 18px 30px -20px rgba(30, 33, 69, 0.45);
    animation: badge-float 5s ease-in-out infinite;
}

.login-orbit-badge.primary {
    width: 96px;
    height: 96px;
    left: 0;
    top: 0;
    background: var(--gs-sun-l);
    color: var(--gs-berry);
    animation-delay: 0s;
}

.login-orbit-badge.secondary {
    width: 60px;
    height: 60px;
    left: 88px;
    top: 36px;
    background: var(--gs-sky-l);
    color: var(--gs-sky-d);
    animation-delay: 0.6s;
}

.login-orbit-badge.tertiary {
    width: 52px;
    height: 52px;
    left: 8px;
    top: 64px;
    background: var(--gs-leaf-l);
    color: var(--gs-leaf);
    animation-delay: 1.2s;
}

@keyframes badge-float {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-8px); }
}

.login-feature-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px;
}

.login-feature-chip {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 16px;
    border-radius: 12px;
    background: #FFFFFF;
    border: 1.5px solid var(--gs-line);
    color: var(--gs-ink2);
    font-weight: 600;
    font-size: 0.88rem;
    backdrop-filter: blur(8px);
    transition: border-color 0.25s ease, transform 0.25s ease, background-color 0.25s ease;
}

.login-feature-chip:hover {
    border-color: var(--gs-sun);
    background: #FFFDF6;
    transform: translateY(-2px);
}

.login-feature-chip .mud-icon-root {
    color: var(--gs-berry);
    flex-shrink: 0;
}

/* ── Premium polish (LoginPage.razor only) ──────────────────────────────
   Additive classes only — .login-feature-chip above is untouched and still
   used as-is by RegisterPage.razor/ForgotPasswordPage.razor, so they render
   exactly as before. These new classes are the login page's own upgraded
   badge/feature-card/trust-row treatment, reusing the same design tokens
   (colours, radii, blur, border pattern) already established above rather
   than inventing new ones. */

.login-badge-pill {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 7px 16px;
    border-radius: 999px;
    background: #FFFFFF;
    border: 1.5px solid var(--gs-line);
    color: var(--gs-berry);
    font-size: 0.74rem;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    margin-bottom: 24px;
}

.login-badge-pill .mud-icon-root {
    color: #E0A100;
    font-size: 1rem;
}

.login-feature-card {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 16px;
    border-radius: 16px;
    background: #FFFFFF;
    border: 1.5px solid var(--gs-line);
    transition: border-color 0.25s ease, transform 0.25s ease, background-color 0.25s ease;
}

.login-feature-card:hover {
    border-color: var(--gs-sun);
    background: #FFFDF6;
    transform: translateY(-2px);
}

.login-feature-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 10px;
    margin-bottom: 6px;
    flex-shrink: 0;
}

.login-feature-icon.tone-orange { background: var(--gs-sun-l); color: var(--gs-ink); }
.login-feature-icon.tone-berry  { background: var(--gs-berry-l); color: var(--gs-berry); }
.login-feature-icon.tone-green  { background: var(--gs-leaf-l); color: var(--gs-leaf); }
.login-feature-icon.tone-blue   { background: var(--gs-sky-l); color: var(--gs-sky-d); }

.login-feature-title {
    font-family: var(--gs-font-display);
    font-weight: 600;
    font-size: 0.98rem;
    color: var(--gs-ink);
}

.login-feature-desc {
    font-size: 0.76rem;
    line-height: 1.4;
    color: var(--gs-mute);
}

.login-trust-row {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
    margin-top: 32px;
    color: var(--gs-mute);
    font-size: 0.8rem;
}

.login-trust-row span {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.login-trust-row .mud-icon-root {
    font-size: 1.05rem;
    color: var(--gs-leaf);
}

.login-form-panel {
    position: relative;
    z-index: 1;
    /* flex: 0 1 auto — mirrors .login-branding-panel above: sized to its own
       content (the ~420px .login-form-card + this panel's own padding), not
       grown to fill leftover row width. justify-content:center below is now
       mostly a no-op in practice (the panel rarely ends up wider than the
       card needs) but stays as a harmless safety net if it ever does. */
    flex: 0 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 40px 24px;
}

.login-form-card {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 420px;
    padding: 48px 40px;
    border-radius: 24px;
    background: #FFFFFF;
    border: 1.5px solid var(--gs-line);
    box-shadow: var(--gs-shadow);
}


.login-form-card .mud-input-outlined-border {
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.login-form-card .mud-input.mud-input-outlined.mud-shrink .mud-input-outlined-border,
.login-form-card .mud-input-outlined:hover .mud-input-outlined-border {
    border-color: rgba(30, 33, 69, 0.45);
}

.login-form-card .mud-button-filled.mud-button-filled-primary {
    padding-top: 14px;
    padding-bottom: 14px;
    font-size: 0.95rem;
    font-weight: 700;
    letter-spacing: 0.2px;
    text-transform: none;
    border-radius: 999px;
}

.login-form-card .mud-button-filled.mud-button-filled-primary .mud-icon-root {
    font-size: 1.15rem;
}

/* Register's form has more fields than Login's — a wider card lets them pair up
   two-per-row (see the MudGrid xs=12/sm=6 layout in RegisterPage.razor) instead of
   stacking into one long column. Login keeps the narrower default width. */
.register-form-card {
    max-width: 640px;
}

.register-tip {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    height: 100%;
    padding-top: 6px;
}

.login-mobile-brand {
    display: none;
}

.auth-copyright {
    position: fixed;
    right: 16px;
    bottom: 12px;
    font-size: 0.72rem;
    color: var(--gs-mute);
    letter-spacing: 0.3px;
    pointer-events: none;
    z-index: 5;
}

/* ── Desktop split-screen: lock the shell to exactly one viewport height so
   the branding panel stays visually fixed while the form panel scrolls
   independently when its content (e.g. the longer Register form) is
   taller than the screen. Below this breakpoint the layout stacks into
   normal document flow instead (see the max-width:959px block), so no
   fixed-height/overflow tricks are needed — or wanted — there. ────────── */
@media (min-width: 960px) {
    .login-shell {
        height: 100vh;
    }

    /* Relays .login-shell's height:100vh down to the two panels — without
       this, .login-content-row (a plain block-level flex container with no
       explicit height) would size to its children's content height instead,
       and the panels' own height:100% below would resolve against that
       instead of the full viewport. */
    .login-content-row {
        height: 100%;
    }

    .login-branding-panel {
        height: 100%;
    }

    .login-form-panel {
        height: 100%;
        overflow-y: auto;
        /* flex-start (not center) is deliberate: centering an overflowing flex
           item clips its top edge in Chrome/Firefox and makes it unreachable
           by scrolling. The card centers itself instead via auto margins
           below, which only take effect when it actually fits. */
        align-items: flex-start;
    }

    .login-form-card {
        margin: auto 0;
    }
}

/* Tablet + mobile share this stacked-column structure. The illustration is
   dropped here (not just on phones) because it's the single biggest chunk of
   branding-panel height (badges + margin ≈ 136px) and purely decorative —
   dropping it is what actually makes "everything fit naturally" once the
   layout is a single column instead of a 52/48 split. */
@media (max-width: 959px) {
    .login-shell {
        flex-direction: column;
        min-height: auto;
    }

    /* display:contents makes the desktop-only row wrapper generate no box of
       its own — .login-branding-panel/.login-form-panel become direct flex
       children of .login-shell again below this breakpoint, so they pick up
       flex-direction:column above exactly as if the wrapper didn't exist.
       This is what keeps the stacked mobile/tablet layout byte-for-byte
       unchanged; none of .login-content-row's desktop-only width/gap/
       centering rules apply here. */
    .login-content-row {
        display: contents;
    }

    .login-branding-panel {
        flex: 0 0 auto;
        padding: 40px 32px 32px;
        justify-content: center;
    }

    .login-branding-content {
        max-width: 560px;
        margin: 0 auto;
        text-align: center;
    }

    .login-brand-mark {
        margin-bottom: 28px;
    }

    .login-heading {
        font-size: 1.9rem;
        margin-bottom: 12px;
    }

    .login-subtitle {
        margin-bottom: 24px;
    }

    .login-illustration {
        display: none;
    }

    .login-badge-pill {
        margin-bottom: 20px;
    }

    /* .login-trust-row is display:flex (a block-level box), so it doesn't
       pick up .login-branding-content's text-align:center above the way the
       inline-flex badge pill and grid-item feature cards do — needs its own
       centering here. */
    .login-trust-row {
        justify-content: center;
        margin-top: 24px;
    }

    .login-form-panel {
        flex: 0 0 auto;
        padding: 32px 24px 48px;
    }

    .login-form-card {
        padding: 40px 32px;
    }
}

@media (max-width: 599px) {
    .login-shell::before,
    .login-shell::after {
        width: 320px;
        height: 320px;
    }

    .login-branding-panel {
        padding: 32px 20px 24px;
    }

    .login-brand-mark {
        margin-bottom: 20px;
    }

    .login-heading {
        font-size: 1.5rem;
        line-height: 1.25;
    }

    .login-subtitle {
        font-size: 0.92rem;
        margin-bottom: 20px;
    }

    .login-feature-grid {
        display: none;
    }

    /* Marketing/decorative content stays minimal on true phone widths — just
       logo, badge pill, headline and subtitle — so the login form is what
       the user actually sees first, per the "form is the primary focus on
       mobile" brief. */
    .login-badge-pill {
        padding: 6px 14px;
        font-size: 0.74rem;
        margin-bottom: 16px;
    }

    .login-trust-row {
        display: none;
    }

    .login-form-panel {
        /* .auth-copyright switches to position:static below (back into normal
           flow) so it can sit under the card instead of floating fixed over
           it. But this panel is display:flex with the default row direction
           (set on the unconditioned base rule for desktop's side-by-side
           card), so without column here, the copyright note would become a
           second flex item in that same row — splitting the row's width
           with the card via flex-shrink and squeezing the whole form down to
           roughly two-thirds of its intended width. Column stacks them. */
        flex-direction: column;
        padding: 0 16px 40px;
    }

    .login-form-card {
        width: 100%;
        max-width: none;
        padding: 28px 20px;
        border-radius: 16px;
    }

    .login-form-card .mud-typography-h4 {
        font-size: 1.4rem;
    }

    .auth-copyright {
        position: static;
        display: block;
        text-align: center;
        margin-top: 16px;
        color: var(--gs-mute);
    }
}

@media (prefers-reduced-motion: reduce) {
    .login-orbit-badge {
        animation: none;
    }
}

/* ── Shared responsive patterns (app-wide) ──────────────────────────────────
   Originally built for the Fees/Invoice detail page, then generalized here
   once the same four broken shapes turned out to repeat across nearly every
   list/detail/dashboard page in the app:
     1. .detail-header / .detail-title-block / .detail-title-row / .detail-heading
        — a page header pairing a name/number with a status MudChip
          (InvoiceDetailPage, ClassDetailPage, PlatformTenantDetailPage, …).
     2. .action-button-group — a row of MudButtons (Edit/Void/Download/etc.)
        next to a header.
     3. .stat-card / .stat-card-row / .stat-card-text / .stat-card-value
        — the icon + big-number "KPI" card used at the top of nearly every
        list/dashboard page (Fees, Students, Staff, Centres, Classes,
        Attendance, Users, Notifications, Lesson Plans, Lesson Insights,
        Assessments, Settings, the main Dashboard, …).
     4. .table-scroll (defined earlier in this file, near .clickable-table)
        — wraps MudTable/MudSimpleTable so wide tables scroll within their
        own card instead of the page.
   Reuse these four instead of writing new page-specific CSS — that's the
   whole point of generalizing them out of the Invoice page fix.

   Breakpoints used below, and how they map onto MudGrid's fixed breakpoint
   set (xs<600, sm 600-959, md 960+ — MudGrid's own breakpoints can't be
   retuned per-instance to arbitrary pixel values):
     - mobile      <=600px               → xs
     - tablet      768px-991px (spec)    → sm (600-959px) — the ~31px sliver
       between 960 and 991 is covered by the desktop tier below instead, an
       intentional, effectively invisible simplification rather than fighting
       MudGrid's fixed breakpoints with extra override CSS for a 31px window.
     - desktop     >=992px (spec)        → md (960px+), left exactly as it
       already was on every page — existing card-grid column counts (2, 3 or
       4 across) are preserved by keeping each page's own sm=/md= values and
       only ever changing xs="6" to xs="12", never touching sm/md.
   Card column counts come entirely from each page's own MudItem xs=/sm=/md=
   values in the .razor files, not from CSS. */

/* Global hardening — applies at every width, not just mobile. These only
   ever change anything when content would otherwise overflow, so they're a
   no-op wherever things already fit (i.e. desktop/tablet are unaffected). */
.detail-header,
.detail-title-block,
.detail-title-row,
.action-button-group,
.stat-card,
.stat-card-row,
.stat-card-text {
    box-sizing: border-box;
    max-width: 100%;
}

/* min-width:0 on every link in these flex chains: browsers default flex
   ITEMS to min-width:auto, which refuses to let them shrink below their
   content's intrinsic width — the exact mechanism that let a long
   invoice/reference number or a wide table force a page wider than the
   viewport despite width:100%/max-width:100% rules elsewhere. Harmless
   where nothing is wide enough to hit this in the first place (i.e.
   desktop/tablet). */
.detail-header,
.detail-title-block,
.detail-title-row,
.stat-card-row,
.stat-card-text {
    min-width: 0;
}

@media (max-width: 600px) {
    /* Name/number heading + status chip: allow the inner row to wrap
       instead of forcing the chip up against the heading, and shrink the
       heading itself so a long value can't force overflow before it even
       gets a chance to wrap. */
    .detail-title-row {
        flex-wrap: wrap;
    }

    /* Compound selectors (not just .detail-heading) are deliberate: app.css
       loads BEFORE MudBlazor.min.css in index.html, so a plain single-class
       selector here would lose a same-specificity tie against MudBlazor's
       own typography classes (which load later and would otherwise win,
       silently no-op'ing these overrides). Covers both Typo.h4 (detail page
       headings) and Typo.h5 (stat card values) — whichever the page uses.
       Matches the .login-form-card .mud-typography-h4 pattern already used
       elsewhere in this file for the same reason. */
    .detail-heading.mud-typography-h4,
    .detail-heading.mud-typography-h3 {
        font-size: 1.4rem;
        word-break: break-word;
        overflow-wrap: anywhere;
    }

    /* Action buttons: stack full-width instead of wrapping into a cramped
       multi-per-row grid — simpler to tap and never crowds under the
       heading regardless of how many buttons a given page/status shows. */
    .action-button-group {
        flex-direction: column;
        align-items: stretch;
        width: 100%;
    }

    .action-button-group .mud-button-root {
        width: 100%;
    }

    /* Stat/KPI card values (e.g. "ZAR 2 500,00", a due date, a count): make
       sure the value text itself shrinks and wraps instead of pushing at or
       past the card's edge, now that the card has full mobile width to work
       with (each page's own MudItem xs="12" already stacks these to one per
       row below 600px — see the breakpoint mapping above). */
    .stat-card-value.mud-typography-h5,
    .stat-card-value.mud-typography-h6 {
        font-size: 1.15rem;
        word-break: break-word;
        overflow-wrap: anywhere;
        line-height: 1.25;
    }
}
