/* Mateen brand font (Inter only - the earlier Cormorant Garamond serif accent was dropped per
   explicit request) - see Theme/MateenTheme.cs's Typography config and the brand section at the
   bottom of this file. Loaded via @import here (rather than a <link> in App.razor's <head>) so this
   restyle stays scoped to app.css/Theme/Home.razor per the brief. */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');

html, body {
    /* 'Tajawal' is a fallback here, not a replacement (2026-10-03, per user request): Inter has no
       Arabic glyphs, so the browser's own per-character glyph-coverage fallback within this single
       font-family list renders any Arabic text in Tajawal automatically - names, chips, dashboard,
       ticket notes, everywhere - while Latin text stays on Inter. Zero per-place markup needed. See
       the Tajawal @font-face block further down this file for the actual font files/weights, and
       Theme/MateenTheme.cs's Typography config for the matching MudBlazor-side stack. */
    font-family: 'Inter', 'Tajawal', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

a, .btn-link {
    color: #006bb7;
}

.btn-primary {
    color: #fff;
    background-color: #1b6ec2;
    border-color: #1861ac;
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

.content {
    padding-top: 1.1rem;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* Reports "Print" support (ARCHITECTURE.md Increment 3, "Print") - shared here rather than
   duplicated per report page. Hides app chrome (app bar, drawer/nav, the print button itself, the
   Blazor reconnect UI) and lets only the report content and MudChart canvases through, full width,
   with no dark-mode background (printers render dark backgrounds as wasted ink/unreadable text on
   paper regardless of which theme the user was viewing on screen). */
@media print {
    .mud-appbar,
    .mud-drawer,
    .mud-drawer-container,
    #blazor-error-ui,
    .no-print {
        display: none !important;
    }

    html, body, .mud-layout, .mud-main-content, .mud-container {
        background: #fff !important;
        color: #000 !important;
        margin: 0 !important;
        padding: 0 !important;
        max-width: 100% !important;
        overflow: visible !important;
    }

    .mud-main-content {
        margin-left: 0 !important;
    }
}

/* ===================================================================================
   Mateen brand identity (ARCHITECTURE.md Increments 6/6b) - applied here to Mateen.UI's public
   landing page ("/", Components/Pages/Home.razor) as the deferred follow-up to
   Mateen.Management's already-shipped rollout. Source of truth: the same v0.app-generated
   reference site (https://mateen-fawn.vercel.app/), snapshotted to
   D:\Ammar\Projects\AI\Mateen\Design\v0-reference\ (landing.html/style.css). See
   Theme/MateenTheme.cs for the exact token values and, importantly, its class-summary comment
   explaining the mode-switching approach used here.

   Mode switching: unlike Mateen.Management (which wraps its layouts in a `.mateen-dark` class and
   reads custom `--mateen-*` variables), every rule below reads MudBlazor's own reactive
   `--mud-palette-*` custom properties directly - these already swap automatically the instant
   MainLayout's dark-mode toggle fires, no wrapper class needed. This also means these classes work
   correctly on any other page that reuses them (e.g. a future login-page restyle) without any
   changes to MainLayout itself.
   =================================================================================== */

/* Name is a holdover from the earlier serif brand accent (Cormorant Garamond), dropped per explicit
   request - this now just applies the same modern Inter stack as the rest of the app. Kept as its
   own class (not deleted) since it's referenced from many .razor files as a heading/wordmark accent. */
.mateen-serif {
    /* 'Tajawal' fallback added 2026-10-03 - see the html,body rule near the top of this file for why. */
    font-family: 'Inter', 'Tajawal', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

/* Dashboard.razor's relocated Students Progress/Leaderboard/Comparison row
   (Components/Shared/DashboardCard.razor; this used to be a longer multi-row "Quick Launch" grid -
   see that file's own top-of-file remarks for the 2026-10-02 removal/relocation). min-height is a
   measured value, not a guess: rendered the full grid in headless Chrome at 1280px wide (three
   cards per row) with the longest real title on the page and read back the tallest card's actual
   box height, then rounded up. Combined with the generous padding below this reads as a
   deliberately-sized panel rather than a thin strip, on every card - including Leaderboard/
   Comparison, which have no Count and, depending on breakpoint, can sit alone in their own row where
   height:100% has nothing to stretch against. */
.dashboard-card {
    min-height: 138px;
}

.dashboard-card-content {
    height: 100%;
    display: flex;
    flex-direction: column;
    padding: 20px 24px !important;
}

/* Icon moved off the bare page background into a tinted circular badge for more visual weight now
   that the card has room for it - same color-mix-against-surface recipe as .mateen-group-card's
   chip fill above, so it tracks both palette and mode with no extra dark-mode rule. */
.dashboard-card-icon-badge {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    flex-shrink: 0;
    border-radius: 50%;
    background-color: color-mix(in srgb, var(--mud-palette-primary) 16%, var(--mud-palette-surface));
}

/* DashboardCard (the row of report-launcher cards directly under the top DashboardStatCard row,
   2026-10-03) matches DashboardStatCard's own icon treatment - a darker 86% primary tint with a
   white icon - rather than this shared class's default light 16% tint + primary-coloured icon.
   Scoped to .dashboard-card specifically (MudCard's own class, see the markup above) so
   ReportSummaryCards.razor's own use of the bare .dashboard-card-icon-badge class is untouched. */
.dashboard-card .dashboard-card-icon-badge {
    background-color: color-mix(in srgb, var(--mud-palette-primary) 86%, var(--mud-palette-surface));
    color: #fff;
}

.dashboard-card-count {
    text-align: right;
    flex-shrink: 0;
}

.dashboard-card-count-number {
    /* 'Tajawal' fallback added 2026-10-03 - see the html,body rule near the top of this file for why. */
    font-family: 'Inter', 'Tajawal', 'Helvetica Neue', Helvetica, Arial, sans-serif;
    font-weight: 700;
    line-height: 1;
}

/* The second, smaller figure under the header row (see DashboardCard.razor's own comment for why
   this replaced the one-line purpose caption, which replaced the original description blurb - "I
   need numbers", not prose). Sits directly under the header row with a fixed gap, same reasoning as
   the description row it replaces: the extra space from min-height above falls below it, matching
   how the Leaderboard/Comparison cards with no Count already behave. */
.dashboard-card-secondary {
    margin-top: 12px;
    display: flex;
    align-items: baseline;
    gap: 6px;
    font-size: .8125rem;
}

.dashboard-card-secondary-value {
    /* 'Tajawal' fallback added 2026-10-03 - see the html,body rule near the top of this file for why. */
    font-family: 'Inter', 'Tajawal', 'Helvetica Neue', Helvetica, Arial, sans-serif;
    font-weight: 700;
    font-size: 1rem;
    color: var(--mud-palette-text-primary);
    font-variant-numeric: tabular-nums;
}

/* Components/Shared/ReportSummaryCards.razor's four-card row (Reports/AllStudents.razor,
   Reports/StudentProgress.razor, and the Student dashboard's "My progress" - GroupProgress.razor and
   TeacherProgress.razor, the other two callers when this was written, were both later removed as
   redundant launcher pages; AllStudents.razor absorbed Group Progress's report directly). Confirmed
   by rendering the row in headless Chrome with real data shapes: MudGrid's row already
   stretches every MudItem cell to the tallest sibling's height (flex row, default align-items:
   stretch), but a plain MudPaper never opts into that stretched height itself, so without this class
   each card was sized purely by its own content - Attendance's 5-figure caption made it visibly the
   tallest, Average grade/Ayahs covered the shortest, with dead space nowhere to go since the paper
   itself just stopped short. height:100% is this card's opt-in, same as .mateen-group-card's own
   "Fills the grid cell" rule; the flex column on top of it is what then puts that reclaimed height to
   use instead of leaving it as a gap - see __figure below. */
.report-summary-card {
    height: 100%;
    display: flex;
    flex-direction: column;
}

.report-summary-card__header {
    display: flex;
    align-items: center;
    gap: 12px;
}

/* Grows to fill whatever space the header (fixed) and an optional trailing detail/caption line
   (fixed) don't use, and centers the headline figure inside it - so the figure sits consistently
   "in the middle of what's left" on every card, whether that's the small gap above Attendance's
   breakdown caption or the full body height on a caption-less card like Average grade. */
.report-summary-card__figure {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
}

.mateen-btn-natural-case {
    /* MudButton applies text-transform:uppercase by default (Material Design convention) - the
       real reference site's buttons use natural case ("Sign in", "Get Started"), not caps. This
       was a deliberate correction made for Mateen.Management's buttons too (see its app.css) -
       carried over here for the same visual language. */
    text-transform: none !important;
    font-weight: 600;
}

/* Legacy-only (HomeLegacy.razor uses it three times; nothing else does). The class name says
   "gold" but it has always been palette-bound, so since 2026-09-26 it paints a violet glyph on a
   12% violet disc. Not renamed because HomeLegacy.razor is a kept-verbatim reference copy and must
   not be edited; if that page is ever retired, this rule goes with it. */
.mateen-gold-badge-icon {
    width: 56px;
    height: 56px;
    border-radius: 999px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    background: color-mix(in srgb, var(--mud-palette-primary) 12%, transparent);
    color: var(--mud-palette-primary);
}

/* --- Legacy landing page (Components/Pages/HomeLegacy.razor, route /landing-old) -------------
   These .mateen-home-* rules styled the ORIGINAL landing page. That page is now preserved verbatim
   at /landing-old as the user's reference copy, and "/" was reimplemented from the identity-board
   mock - so every rule in this sub-section is still live, but only for HomeLegacy.razor. The
   current landing page is styled by the .mateen-landing-* block that follows this one; do not
   merge the two. */

.mateen-home-hero {
    position: relative;
    overflow: hidden;
    border-radius: 20px !important;
    color: #FFFFFF;
    /* Same mauve -> purple/70% -> primary stop pattern as Mateen.Management's closing-CTA-band
       gradient (its most authentic use of all three brand accents together) - reused here for the
       hero since this page has no separate CTA band section. Brand-mauve is mapped onto Mud's
       "Dark" palette role and brand-violet onto "Primary" (see MateenTheme.cs), so this gradient
       recolors correctly and instantly on every dark/light toggle. */
    background: linear-gradient(135deg, var(--mud-palette-dark) 0%, color-mix(in srgb, var(--mud-palette-secondary) 70%, transparent) 55%, var(--mud-palette-primary) 100%);
}

.mateen-home-hero-pattern {
    position: absolute;
    inset: 0;
    background-image: url('branding/pattern-tile-gold.png');
    background-size: 480px;
    background-repeat: repeat;
    opacity: 0.12;
    mix-blend-mode: overlay;
    pointer-events: none;
}

.mateen-home-hero-content {
    position: relative;
    z-index: 1;
}

.mateen-home-hero-logo {
    width: 100%;
    max-width: 220px;
    height: auto;
    filter: drop-shadow(0 12px 28px rgba(0, 0, 0, 0.35));
}

.mateen-home-feature-card {
    border-radius: 16px !important;
    border: 1px solid var(--mud-palette-lines-default) !important;
    background: var(--mud-palette-surface) !important;
    transition: border-color 0.15s ease, transform 0.15s ease;
}

.mateen-home-feature-card:hover {
    border-color: color-mix(in srgb, var(--mud-palette-primary) 50%, transparent) !important;
    transform: translateY(-2px);
}

.mateen-home-security {
    border: 1px solid color-mix(in srgb, var(--mud-palette-primary) 30%, transparent) !important;
    background: color-mix(in srgb, var(--mud-palette-primary) 6%, var(--mud-palette-surface)) !important;
    border-radius: 16px !important;
}

.mateen-home-plan-card {
    border-radius: 16px !important;
    border: 1px solid var(--mud-palette-lines-default) !important;
    background: var(--mud-palette-surface) !important;
}

.mateen-home-plan-highlighted {
    border: 1px solid var(--mud-palette-primary) !important;
    box-shadow: 0 16px 40px -16px color-mix(in srgb, var(--mud-palette-primary) 45%, transparent) !important;
}

.mateen-home-plan-badge {
    background: var(--mud-palette-primary) !important;
    color: var(--mud-palette-primary-text) !important;
    font-weight: 600;
}

/* ===================================================================================
   Public landing page ("/", Components/Pages/Home.razor) - identity-board implementation
   -----------------------------------------------------------------------------------
   Source of truth: D:\Ammar\Projects\AI\Mateen\Design\html\landing.html - a standalone page built
   directly from the identity boards (Design\Dark mode\Mateen Identity.jfif, PAGE LAYOUTS > LANDING
   PAGE, and Design\Light Mode\Folder.jfif for the light palette). Every literal hex/rgba below is
   carried over from that file, where it is recorded as pixel-sampled off those boards. Nothing here
   is an invented colour.

   HOW DARK/LIGHT IS SWITCHED (and why the mock's own toggle was NOT brought in)
   -----------------------------------------------------------------------------------
   The mock ships its own `html[data-theme]` switch plus an inline toggleTheme() button in its nav.
   This app already has one global dark/light mode, owned by MainLayout's MudThemeProvider and
   toggled from the app bar, so a second in-page toggle would be a competing source of truth. The
   mock's toggle is therefore deliberately dropped: this page follows the app's mode like every
   other page.

   Doing that without a wrapper class needed a reactive per-mode signal in CSS. MudBlazor 9.10.0
   does not emit a dark-mode class, but its ThemeProvider DOES emit the custom property
   `--mud-native-html-color-scheme: light | dark` on :root, and re-emits it the instant the mode
   flips (verified in this app's own rendered output). `.mateen-landing` below feeds that straight
   into the real CSS `color-scheme` property; because `color-scheme` inherits, every
   `light-dark(<light value>, <dark value>)` in this block resolves against the app's current mode,
   with no JS, no wrapper class and no MainLayout change - the same "read Mud's reactive variables
   directly" principle the rest of this file follows.

   Two of the mock's per-mode values are NOT colours and so cannot go through light-dark(), which
   only switches colour values:
     * its --pattern-op (.26 light / .30 dark) is folded into the pattern INK colour instead;
     * its --shadow differs in geometry as well as colour (0 22px 56px vs 0 26px 70px), so one
       averaged geometry is used with a light-dark() colour.

   WHICH VALUES COME FROM THE APP PALETTE INSTEAD
   -----------------------------------------------------------------------------------
   Where a sampled colour has a real equivalent in Theme/MateenTheme.cs it is taken from the
   palette, so this page stays in step with the rest of the app:
     body/muted text -> --mud-palette-text-primary / -text-secondary (mock #1C2340/#EDF0F8 and
                        #5B6486/#9FADD0 - the palette's #2A2740/#E9EAEE and #6B667F/#A3A4D1 are the
                        same colours for practical purposes);
     card surface    -> --mud-palette-surface (mock #FDFBFC light is the palette's #FFFFFF; in dark
                        the palette's navy #121A3F replaces the mock's #1F2847 - it keeps the
                        mock's card-lighter-than-page relationship while matching every other card
                        in the app);
     hairlines       -> --mud-palette-lines-default;
     ornament pattern /
     ornament hairline
                     -> color-mix() off --mud-palette-primary (#471671 light / #BB86EA dark).
                        NOTE: the mock sampled these as GOLD (#B8860B / #C9A227) and they were
                        gold here too until 2026-09-26, when the primary palette role was changed
                        from brand-gold to brand-violet (Theme/MateenTheme.cs). They were bound to
                        the palette rather than hardcoded precisely so a palette change carries,
                        and it has: the frame/hairline/pattern ink are now violet. Kept on the
                        palette deliberately - this ornament is the page's "primary colour shows
                        up here" surface, and at 26-32% alpha it reads as a soft lavender tracery
                        in light mode and a lilac one in dark, at the same visual weight the gold
                        had. The METALLIC button ramps below were a different matter and stayed gold
                        through that change (see .mateen-landing-btn--gold) - moved to purple
                        2026-10-02, sourced from the logomark itself rather than the palette (see
                        that rule's own comment).
   Only surfaces with no palette equivalent are stated as sampled literals: the page gradient, the
   nav wash, the headline gradient stops, and the two metallic button ramps (.mateen-landing-btn--bronze,
   unchanged, and .mateen-landing-btn--gold, now a purple ramp - the class name stays as-is since it
   is also the Razor markup's CSS hook, not touched by this CSS-only change).
   =================================================================================== */

.mateen-landing {
    /* The mode signal - see the block comment above. Everything else here is downstream of it. */
    color-scheme: var(--mud-native-html-color-scheme);
    /* Page body behind/inside the frame (mock --page-1/2/3). */
    --mateen-landing-page-1: light-dark(#F4F2F7, #0A1330);
    --mateen-landing-page-2: light-dark(#E6EAF6, #071026);
    --mateen-landing-page-3: light-dark(#DDDEE3, #060D20);
    /* Alpha'd copies of the three stops above, for .mateen-landing-frame::after - the veil that
       sits ON TOP of .mateen-landing-photo-left (2026-10-02, "the gray landing hero should be on
       top of it as transparent"). Same colours, same idea (page-1 is the top highlight glow,
       page-2/3 the linear ramp), just translucent instead of opaque - see that rule's own comment
       for why this has to be a SEPARATE layer rather than just adding alpha to page-1/2/3 in place
       (.mateen-landing-frame's own background must stay opaque: it is still the base page colour
       for every bit of the page the fixed photo+veil pair doesn't reach). Dark mode is weighted
       more opaque than light (70/74/78% vs. 55/58/62%) for the same reason the photo's own
       multiply-tint is asymmetric in .mateen-landing-photo-left's comment: the dark photo crush
       needs a bit more coverage to stay legible, light mode can stay closer to a true thin wash. */
    --mateen-landing-veil-1: light-dark(color-mix(in srgb, #F4F2F7 55%, transparent), color-mix(in srgb, #0A1330 70%, transparent));
    --mateen-landing-veil-2: light-dark(color-mix(in srgb, #E6EAF6 58%, transparent), color-mix(in srgb, #071026 74%, transparent));
    --mateen-landing-veil-3: light-dark(color-mix(in srgb, #DDDEE3 62%, transparent), color-mix(in srgb, #060D20 78%, transparent));
    /* Ghost-button / secondary fill (mock --card-2). */
    --mateen-landing-card-2: light-dark(#EDEAF4, #233355);
    /* Hero headline gradient stops (mock --title-1/2/3), sampled straight across the mock's own
       headline in each mode. */
    --mateen-landing-title-1: light-dark(#8A6320, #BEA088);
    --mateen-landing-title-2: light-dark(#A87B33, #DDC3AC);
    --mateen-landing-title-3: light-dark(#5B4A6E, #F0DCC4);
    /* Text colour that sits on the metallic gold/bronze buttons (mock --btn-text).
       Was a dark brown/navy (#2A1F0B / #16203F), correct back when this ramp's brightest stop was
       pale lavender #D3B6F7. 2026-10-02 deepening pass (see .mateen-landing-btn--gold) pulled every
       stop down to a genuinely saturated identity purple, including the "peak" - dark text no
       longer clears AA (peak #9333EA only manages ~3.9:1 against #2A1F0B, and the darkest stop
       #581C87 collapses to ~1.9:1). White reads 5.4:1+ across the whole ramp instead, so this is one
       flat value now rather than a light/dark-mode pair - the button background never changes by
       mode. */
    --mateen-landing-btn-text: #FFFFFF;
    --mateen-landing-bronze-text: light-dark(#FFF6E8, #F4E7D4);
    /* Frame border + hairline + pattern ink: derived from the palette rather than hardcoded
       (opacity per the mock's own alphas, with --pattern-op folded in here since light-dark()
       can't carry a bare number). Originally because the mock's sampled golds WERE the brand gold;
       since 2026-09-26 the primary is brand-violet and these follow it - see the block comment
       above. */
    --mateen-landing-frame: color-mix(in srgb, var(--mud-palette-primary) 32%, transparent);
    --mateen-landing-line: color-mix(in srgb, var(--mud-palette-primary) 26%, transparent);
    --mateen-landing-pattern-ink: color-mix(in srgb, var(--mud-palette-primary) 28%, transparent);
    /* Mock --shadow: one geometry, per-mode colour (see the block comment). */
    --mateen-landing-shadow-color: light-dark(rgba(40, 50, 90, .16), rgba(0, 0, 0, .5));
    color: var(--mud-palette-text-primary);
}

/* The mock frames the page in a thin rounded border (gold on the board; --mateen-landing-frame is
   palette-bound and so is violet since 2026-09-26) and floats it on the window. That frame is
   deliberately NOT reproduced: the page now runs full-bleed, edge to edge, with its own inner nav
   hoisted into the real app bar (see .mateen-appbar-* below), so a border would draw a second,
   competing edge just under the app bar's own. The gradient backdrop is what's kept - that IS the
   page surface. overflow:hidden stays: the side arches bleed past both edges by design.
   --mateen-landing-frame / --shadow-color are retained above rather than deleted so the framed
   treatment can be restored in one line if it's ever wanted back. */
.mateen-landing-frame {
    position: relative;
    overflow: hidden;
    background: radial-gradient(900px 500px at 50% 0%, var(--mateen-landing-page-1) 0%, transparent 70%), linear-gradient(180deg, var(--mateen-landing-page-2) 0%, var(--mateen-landing-page-3) 100%);
    /* Fill the window even when the content is short, now that nothing frames it. */
    min-height: calc(100vh - var(--mud-appbar-height, 64px));
}

/* The veil that sits ON TOP of .mateen-landing-photo-left (2026-10-02, "I need
   .mateen-landing-photo-left to be 100% to the right, and the gray landing hero should be on top
   of it as transparent"). This is deliberately a SEPARATE generated box, not just "add alpha to
   .mateen-landing-frame's own background" - an element's own background is always the floor of its
   stacking context, it can never paint above its own children (the photo included), no matter what
   z-index the frame itself gets. ::after is used (not ::before) because generated-content order
   puts it AFTER every real child in paint order, so at the same z-index:0 it wins the tie-break
   against .mateen-landing-photo-left and paints above it (confirmed by rendering - see report);
   .mateen-landing-hero's explicit z-index:1 still wins outright regardless of document order, so
   hero content stays on top of the veil same as it was on top of the photo.
   position:fixed + the exact same box (100% x 860px, top:0/left:0) as .mateen-landing-photo-left,
   not position:absolute spanning the whole tall .mateen-landing-frame, so the veil and the photo
   move (or rather, stay pinned) together as one atomic pair through the whole scroll range - see
   .mateen-landing-photo-left's own POSITION comment for why fixed was needed there; the same
   reasoning applies here, otherwise the veil would scroll away from the photo it is meant to sit
   on top of. .mateen-landing-frame's OWN background above is untouched and stays fully opaque: it
   remains the base colour for literally everything else - the rest of this hero band's own
   negative space beyond the fixed box, every section/card/footer below it, and the mobile layout
   where this veil (like the photo) is hidden entirely. mask-image mirrors
   .mateen-landing-photo-left's own fade curve (same plateau/fade shape, slightly softer) so the
   veil dissolves into that still-opaque frame background at the same point the photo does, instead
   of leaving a visible seam where the fixed 860px band ends and the normal in-flow gradient takes
   over. */
.mateen-landing-frame::after {
    content: '';
    position: fixed;
    left: 0;
    top: 0;
    width: 100%;
    height: 860px;
    z-index: 0;
    pointer-events: none;
    background: radial-gradient(900px 500px at 50% 0%, var(--mateen-landing-veil-1) 0%, transparent 70%), linear-gradient(180deg, var(--mateen-landing-veil-2) 0%, var(--mateen-landing-veil-3) 100%);
    -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 70%, transparent 100%);
    mask-image: linear-gradient(180deg, #000 0%, #000 70%, transparent 100%);
}

@media (max-width: 900px) {
    .mateen-landing-frame::after {
        display: none;
    }
}

/* --- ornamental side panels ------------------------------------------------------------------
   The mock has a tall patterned arch column bleeding in from each edge behind the hero. The khatam
   pattern itself is an inline <svg><defs><pattern> in Home.razor (it needs to be real SVG so its
   stroke can read --mateen-landing-pattern-ink and recolour with the mode). */
.mateen-landing-side {
    position: absolute;
    /* was 58px, clearing the page's own inner nav - that nav now lives in the app bar, so the
       arches start at the top of the page surface instead. */
    top: 0;
    width: 190px;
    height: 520px;
    opacity: .9;
    pointer-events: none;
    /* dissolve into the hero instead of ending on a hard horizontal edge */
    -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 62%, transparent 100%);
    mask-image: linear-gradient(180deg, #000 0%, #000 62%, transparent 100%);
}

.mateen-landing-side--left {
    /* POSITION: fixed rather than absolute (same reasoning/precedent as
       .mateen-landing-photo-left's own "POSITION: fixed" comment above - see that block for the
       full containing-block mechanics), so the arch stays pinned to the viewport's left edge
       through the whole scroll instead of scrolling away with the hero after 520px. left/top:0
       still resolve against the viewport, not .mateen-landing-frame, because nothing in between
       sets a transform/filter/perspective/contain. height: 100vh so it spans the full viewport
       instead of the old fixed 520px. z-index:0 (explicit, matching .mateen-landing-photo-left's
       own explicit z-index:0) keeps it sorting behind .mateen-landing-hero/-section/-closing's
       z-index:1 at every scroll position, same layering relationship as the photo panel. The
       62%/100% mask fade is left as-is (percentage-based, not switched to a fixed-pixel zone) for
       the same reason .mateen-landing-photo-left's own mask stayed percentage-based through its
       height change: it keeps the arch fully opaque through the hero content band (now a
       proportionally larger area of a taller viewport) and only dissolving in the last third,
       which is the same intent as before just rescaled - a fixed-pixel fade zone would instead
       make the fade start at an arbitrary, viewport-height-independent point that could land
       mid-arch on short screens or barely dissolve at all on tall ones. .mateen-landing-side--right
       is NOT touched: it stays position: absolute / height: 520px, scrolling away with the hero
       as before. */
    position: fixed;
    height: 100vh;
    z-index: 0;
    left: -26px;
}

.mateen-landing-side--right {
    right: 2px;
    transform: scaleX(-1);
}

@media (max-width: 900px) {
    .mateen-landing-side {
        display: none;
    }
}

/* --- left photographic panel ------------------------------------------------------------------
   Replaces .mateen-landing-side--left outright (the right side above keeps the original abstract
   khatam arch - asymmetric on purpose, matching the user's own "merge it in from the left side"
   request). Source: wwwroot/images/mateen-background-upscaled.png (1752x2048, the upscaled of the
   two copies the user supplied; the half-resolution mateen-background.png is unused). The asset's
   own left ~18% is already a white/gold geometric-tile pattern inside a scalloped, teal-bordered
   arch - i.e. the exact "ornamental panel bleeding in from the edge" idea .mateen-landing-side was
   already reaching for, just real photographic art instead of a small inline SVG, so it reads as
   the designer's own panel rather than a pasted photo once faded in. The photographic ~82% to its
   right (mosque-across-a-lake skyline, arch, lantern, Qur'an on a rehal, Arabic calligraphy) is
   intentionally NOT framed as content here - it is cover-cropped and mostly carried under the mask
   below, so none of it (the calligraphy especially) ends up competing with the real hero heading/
   CTA that sits in front of it.

   background-position: left top anchors that decorative strip to the box's own left edge;
   background-size: cover crops the rest rather than letterboxing at in-between aspect ratios.

   DARK MODE: same background-blend-mode: multiply technique as
   .mateen-dashboard-banner--greeting (see that rule's own comment for the full mechanics) rather
   than a second overlay element, so one mask (below) still governs the whole stack. The tint
   layer's surface weight moves in OPPOSITE directions per mode (same reasoning as that rule's
   FOURTH TUNING PASS comment): light mode stays near-white (96%), close to a no-op on an already
   pale image; dark mode is weighted toward surface HEAVILY (84%, not diluted with much of the
   lifted-bright dark-mode primary) because dark surface (--mud-palette-surface, #121A3F) is itself
   the dark colour here - a first pass at 46% left the photo's pale sky visibly glowing as a lighter
   rectangle against this page's near-black gradient (confirmed by rendering); 84% crushes it down
   to sit quietly against the page instead. Re-verified by rendering both modes (see report).

   MASK: one corner-anchored radial-gradient, not the two-band linear mask the small arches use -
   opaque through the decorative left strip, fading toward the box's bottom-right corner, so the
   panel dissolves into the page gradient on its right edge and toward the bottom in one continuous
   sweep rather than two independent hard-edged fades.

   TUNING PASS (2026-10-02, "the gray covers the image a lot, should be more transparent, like a
   fog on the image"): diagnosed by actually rendering both the full stack and isolated layers in
   headless Chrome rather than guessing from the colour maths, because the maths alone is
   misleading here - background-blend-mode: multiply against the LIGHT-mode tint colour
   (near-white, light-dark()'s light branch above) is a near no-op on the photo (multiplying any
   colour by white returns that colour unchanged, regardless of the tint layer's own alpha), which
   rendering confirmed: the tint layer contributes essentially nothing to the greyed-out look in
   light mode. The actual source, found by rendering with mask-image: none as a control, was the
   MASK's old fade curve (radial-gradient(135% 95% at 0% 0%, #000 0%, #000 42%, transparent 88%)):
   its opaque plateau ended at only 42% of the radial distance, so a large middle-to-bottom swath of
   the panel - including the mosque/lake/mountain part of the photo, not just its true edges - was
   already partially transparent, letting .mateen-landing-frame's own pale grey-lavender page
   gradient (--mateen-landing-page-1/2/3) show through and read as a flat grey wash over the photo,
   not an edge fade. The fix pushes the opaque plateau out to 70% and lengthens the radial size
   (150% 95% -> 150% 100%) and end stop (88% -> 100%), so the photo reads at full clarity
   ("clearly a photo underneath") through nearly the whole panel and only dissolves - genuinely
   like a thin fog, not a grey overlay - in the last stretch toward the panel's own right/bottom
   edges. Re-verified by rendering: light mode now matches the source photo's warm tones/detail
   through most of the panel; dark mode (whose multiply tint is NOT a no-op - see the block comment
   above, dark surface IS the dark colour here) was re-checked after this change and still reads as
   the same intentional, crushed-down treatment, just visible over more of the panel now that the
   mask reveals more of it before fading.

   POSITION: fixed rather than absolute (2026-10-02, "the image should appear also during
   scrolling"), so the panel stays pinned to the viewport's left edge through the whole, much-taller
   page instead of scrolling away with the hero after ~860px. left/top:0 still resolve against the
   viewport (not .mateen-landing-frame) because nothing between this element and the initial
   containing block sets a transform/filter/perspective/contain that would create a different
   containing block for a fixed descendant - confirmed by rendering: the panel stays flush against
   the true left edge at every scroll depth. z-index:0 is explicit (it was implicit/auto before)
   so it sorts behind .mateen-landing-hero/-section/-closing's z-index:1 wherever they're compared,
   keeping the panel behind all real content (headings, cards, the contact form, the footer) at
   every scroll position - re-verified by rendering scrolled screenshots in both modes down to the
   footer, not just the hero. pointer-events: none (already set) still keeps it non-interactive now
   that it can sit under content for the whole page rather than just the hero.

   FULL-WIDTH PASS (2026-10-02, "I need .mateen-landing-photo-left to be 100% to the right, and the
   gray landing hero should be on top of it as transparent"): this was a narrow clamp(240px, 25vw,
   430px) decorative strip pinned to the left edge, with the opaque .mateen-landing-frame page
   gradient as the page's actual base layer beside/under it. Flipped per the request: this panel is
   now the full-width hero backdrop (width: 100%, resolves against the viewport same as left/top
   above, since position:fixed's containing block here is the ICB), and the gray/lavender wash moved
   to a translucent overlay painted ABOVE it instead of being the opaque base colour behind it - see
   .mateen-landing-frame::after, added right above .mateen-landing-frame's own rule, for the
   mechanics and why that had to be a new layer rather than just adding alpha to
   .mateen-landing-frame's existing background in place.
   background-position moved from "left top" to "center center": at the old narrow width,
   background-size: cover only ever showed this image's own left ~20% (the decorative geometric-tile
   strip) regardless of position, so left/top was effectively a no-op dial. At full page width that's
   no longer true - cover is now width-dominated (the box is always far wider than the portrait
   1752x2048 source is tall relative to this 860px box, so the scaled image's width always exactly
   fills the box with zero horizontal crop, confirmed by rendering at multiple widths), which means
   the ENTIRE image width is visible (so the left-edge geometric border still reads as a left accent
   exactly like before, now simply stretched across instead of cropped to it), but the vertical crop
   window varies a lot by viewport width - wider screens scale the image up more, showing a narrower
   vertical slice. "left top" would anchor that slice to the image's very top (sky + the top of the
   geometric arch), pushing the Arabic calligraphy and the mosque/lake skyline - the actual
   photographic subject - out of frame entirely on wide screens. "center center" keeps the crop
   window centred on the image's own vertical midpoint at every viewport width, which is where the
   calligraphy line, the mosque domes/minarets and the lit lantern all sit - confirmed by rendering:
   this is what reads as a coherent photographic hero at both ~900px and ~2500px wide, not just one.
   MASK: swapped the old corner-anchored radial-gradient(150% 100% at 0% 0%, ...) for a vertical-only
   linear fade, matching how .mateen-landing-side's own small arches dissolve (same 62% plateau/100%
   end shape). The radial fade was tuned for a narrow LEFT-anchored panel, fading toward ITS OWN
   bottom-right corner - at full page width that same geometry fades out almost the entire right half
   of the screen for no visual reason (confirmed by rendering), which is wrong now that this is a
   full-width hero background rather than a side accent: there is no "right edge" of a narrow panel
   to dissolve toward any more. The vertical fade instead keeps the photo at full strength across the
   ENTIRE width through the top ~62% of the 860px band (where the hero heading/paragraph/CTA actually
   sit) and only dissolves vertically in the last third, so it reads as one continuous full-width
   photographic backdrop that softens into the page below rather than one that vanishes partway
   across the screen. */
.mateen-landing-photo-left {
    position: fixed;
    right: 0;
    top: 0;
    /* opacity (2026-10-03, hand-edit): a flat value here fades the ELEMENT itself - tint +
       photo together, already composited - against whatever sits behind it in the stacking
       context, which is .mateen-landing-frame's own page-gradient background (z-index:0, see
       the POSITION note above). That backdrop is light-dark()'d too: pale lavender in light
       mode, near-black (#071026/#060D20, --mateen-landing-page-2/3) in dark mode. 0.4 over a
       pale backdrop still reads as a toned-down photo (confirmed by rendering - this is the
       improvement the hand-edit was going for); 0.4 over the near-black dark backdrop crushes
       the ALREADY-darkened dark-mode multiply tint (84% mix, see the block comment above) down
       toward that near-black floor, losing the photo almost entirely (confirmed by rendering -
       this was the regression). Scoped with the same light-dark()/color-scheme mechanism this
       whole file uses rather than a flat number: keeps the light-mode toning as hand-edited,
       restores dark mode to its own already-established "crushed but visible" multiply-tint
       look (full element opacity, no extra fade on top) - re-confirmed by rendering both modes. */
    opacity: 0.4;
    width: 45%;
    height: 100vh;
    pointer-events: none;
    z-index: 0;
    background-image:
        linear-gradient(light-dark(
            color-mix(in srgb, var(--mud-palette-surface) 96%, transparent),
            color-mix(in srgb, var(--mud-palette-surface) 84%, transparent)
        ), light-dark(
            color-mix(in srgb, var(--mud-palette-surface) 96%, transparent),
            color-mix(in srgb, var(--mud-palette-surface) 84%, transparent)
        )),
        url('images/mateen-background-upscaled.png');
    background-blend-mode: multiply, normal;
    background-size: cover, cover;
    background-position: center center, center center;
    background-repeat: no-repeat, no-repeat;
    -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 62%, transparent 100%);
    mask-image: linear-gradient(180deg, #000 0%, #000 62%, transparent 100%);
}

/* DARK MODE (2026-10-03, "in the dark mode have its z-index:1 and width:100%"): wants the photo
   more prominent in dark mode - full width instead of the 45% right-anchored strip, and raised to
   the same stacking level as .mateen-landing-hero/-section/-closing (z-index:1) instead of sitting
   behind them. z-index/width aren't colors so light-dark() above doesn't apply to them; same
   @container style() mechanism used for .mateen-login-bg-image/-mark elsewhere in this file. */
@container style(--mud-native-html-color-scheme: dark) {
    .mateen-landing-photo-left {
        z-index: 1;
        width: 100%;
    }
}

@media (max-width: 900px) {
    .mateen-landing-photo-left {
        display: none;
    }
}


/* --- hero ------------------------------------------------------------------------------------ */
.mateen-landing-hero {
    position: relative;
    z-index: 1;
    padding: 74px 26px 66px;
    text-align: center;
    /* Flipped (2026-10-03) to match .mateen-landing-photo-left moving to the right edge - gray now
       sits over the photo's side (the right) fading to transparent toward the hero's left portion,
       same reasoning as before just mirrored. 270deg = "to left": the gray stop sits at the gradient
       line's start (the right edge), fading out moving left. */
    /*background: linear-gradient(270deg, var(--mateen-landing-veil-2) 0%, transparent 60%);*/
}

    .mateen-landing-hero h1 {
        margin: 0 auto 18px;
        /* breaks "MATEEN: A Foundation / for Your Vision" the way the mock does */
        max-width: 19ch;
        font-weight: 600;
        font-size: clamp(2rem, 6vw, 4.1rem);
        line-height: 1.12;
        letter-spacing: .5px;
        /* REVERSED 2026-10-02, same day: a prior pass today made this all-gold on a literal reading
           of "should have that golden color" - wrong call, corrected on explicit instruction ("no
           gold anywhere"), consistent with every other identity change tonight (login arch, all
           three landing/app-bar buttons) moving away from gold toward the brand purple. Reuses the
           exact purple family (#581C87/#7E22CE/#9333EA, purple-900/700/BrandPurple-500) the button
           gradients already use, rather than inventing a new purple, so the headline and the CTAs
           beneath it read as the same identity. title-1/title-2/title-3 (gold/gold/mauve) are no
           longer used here at all; they're still used elsewhere (nav-link hover, section headings) -
           not touched. */
        background: linear-gradient(100deg, #581C87 0%, #9333EA 38%, #7E22CE 70%, #581C87 100%);
        -webkit-background-clip: text;
        background-clip: text;
        color: transparent;
    }

    .mateen-landing-hero p {
        margin: 0 auto 30px;
        max-width: 54ch;
        color: var(--mud-palette-text-secondary);
        font-size: 1rem;
        line-height: 1.7;
    }

.mateen-landing-cta-row {
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    gap: 16px;
}

/* --- buttons --------------------------------------------------------------------------------- */
.mateen-landing-btn {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    height: 44px;
    padding: 0 28px;
    border: 0;
    border-radius: 999px;
    font-family: inherit;
    font-size: .93rem;
    font-weight: 600;
    letter-spacing: .2px;
    text-decoration: none;
    cursor: pointer;
    transition: filter .15s ease, transform .15s ease;
}

    .mateen-landing-btn:hover {
        filter: brightness(1.07);
    }

    .mateen-landing-btn:active {
        transform: translateY(1px);
    }

/* Same metallic ramp as the login page's "Sign in" button and the app bar's Login link - identical
   in both modes, only its text colour flips (well, flipped - see --mateen-landing-btn-text, it's
   one flat value now). Gold through 2026-10-02 (sampled #C78E57 / #E6C68F / #C6A572 / #94664F);
   moved to purple the same day, initially sampled off the logomark files directly (#7857CA /
   #C7ABE8 / #D3B6F7). That first purple pass read as too light/pastel - #D3B6F7, the peak stop,
   is a near-pastel lavender nowhere near the app's actual identity purple. Re-anchored the same day
   on the SECOND pass to the real brand token, Theme/MateenTheme.cs's BrandPurple #9333EA (hsl 271°,
   81%, 56% - the same purple used for MudBlazor's Secondary role, nav selection and accent bars
   everywhere else in the app), rather than a fresh logo sample. The flanking stops are the matching
   darker rungs of BrandPurple's own hue (271°) off the Tailwind violet scale it already lines up
   with - purple-900 #581C87 and purple-700 #7E22CE - and the peak is BrandPurple itself, #9333EA,
   not a lightened tint of it, so even the "brightest" point of the shine still reads as unmistakably
   the app's purple rather than drifting pastel. Shape unchanged: dark -> light -> lightest -> light
   -> dark, same 0/14/46/68/100 stop positions as the pass this corrects. */
.mateen-landing-btn--gold {
    color: var(--mateen-landing-btn-text);
    background: linear-gradient(90deg, #581C87 0%, #7E22CE 14%, #9333EA 46%, #7E22CE 68%, #581C87 100%);
    box-shadow: 0 10px 26px rgba(88, 28, 135, .4);
}

/* The landing hero's quieter secondary CTA, next to --gold's louder primary one. Gold/bronze
   through 2026-10-02; moved to purple the same day as --gold so "even those on landing page" match
   the identity too, but deliberately NOT the same ramp - a flatter 3-stop shape (vs. gold's fuller
   5-stop shine) in a desaturated blue-violet a notch off BrandPurple's own hue (~260° here vs.
   BrandPurple's 271°, the same hue the login arch/pattern already sit at and close to LightMauve
   #413C58/DarkMauve #7E77A8's family), so next to the vivid, fully-saturated gold button this one
   still reads as the muted secondary weight of the same identity colour rather than a duplicate of
   it. */
.mateen-landing-btn--bronze {
    color: var(--mateen-landing-bronze-text);
    background: linear-gradient(90deg, #4C3B6B 0%, #6E5A96 55%, #4C3B6B 100%);
}

.mateen-landing-btn--ghost {
    color: var(--mud-palette-text-primary);
    background: var(--mateen-landing-card-2);
    border: 1px solid var(--mateen-landing-line);
}

/* --- sections + card grid -------------------------------------------------------------------- */
.mateen-landing-section {
    position: relative;
    z-index: 1;
    padding: 6px 26px 64px;
    /* anchor targets (#mateen-platform etc.) shouldn't land flush under MainLayout's fixed app bar */
    scroll-margin-top: 80px;
}

    .mateen-landing-section h2 {
        margin: 0 0 8px;
        text-align: center;
        font-weight: 600;
        font-size: clamp(1.5rem, 3.4vw, 2.3rem);
        color: var(--mateen-landing-title-3);
    }

.mateen-landing-section-sub {
    margin: 0 auto 30px;
    max-width: 60ch;
    text-align: center;
    color: var(--mud-palette-text-secondary);
    font-size: .95rem;
    line-height: 1.7;
}

/* The mock's "Featured Service" grid is 4-up because it has 4 cards; this page's two grids hold 3
   and 6, so 3-up is the widest step here - same card language, no orphan row. */
.mateen-landing-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
    max-width: 1080px;
    margin: 0 auto;
}

@media (max-width: 980px) {
    .mateen-landing-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 560px) {
    .mateen-landing-grid {
        grid-template-columns: 1fr;
    }
}

.mateen-landing-card {
    padding: 26px 22px;
    text-align: center;
    background: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 14px;
    transition: transform .18s ease, border-color .18s ease;
}

    .mateen-landing-card:hover {
        transform: translateY(-3px);
        border-color: var(--mateen-landing-line);
    }

    .mateen-landing-card svg {
        display: block;
        margin: 0 auto 14px;
    }

    .mateen-landing-card h3 {
        margin: 0 0 8px;
        font-size: 1.02rem;
        font-weight: 600;
    }

    .mateen-landing-card p {
        margin: 0;
        font-size: .82rem;
        line-height: 1.6;
        color: var(--mud-palette-text-secondary);
    }

/* The security note - the mock has no equivalent panel, so it is built out of the same card tokens
   (surface + primary hairline) as a single full-width row rather than inventing a new treatment. */
.mateen-landing-note {
    display: flex;
    align-items: center;
    gap: 20px;
    max-width: 1080px;
    margin: 0 auto;
    padding: 22px 26px;
    background: var(--mud-palette-surface);
    border: 1px solid var(--mateen-landing-line);
    border-radius: 14px;
}

    .mateen-landing-note svg {
        flex: 0 0 auto;
    }

    .mateen-landing-note h3 {
        margin: 0 0 6px;
        font-size: 1.02rem;
        font-weight: 600;
    }

    .mateen-landing-note p {
        margin: 0;
        font-size: .82rem;
        line-height: 1.6;
        color: var(--mud-palette-text-secondary);
    }

@media (max-width: 560px) {
    .mateen-landing-note {
        flex-direction: column;
        text-align: center;
    }
}

/* --- closing block --------------------------------------------------------------------------- */
.mateen-landing-closing {
    position: relative;
    z-index: 1;
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    gap: 16px;
    padding: 0 26px 58px;
}

/* --- narrow widths (mock's own two breakpoints) ---------------------------------------------- */
@media (max-width: 860px) {
    .mateen-landing-hero {
        padding: 48px 18px 44px;
    }

    .mateen-landing-section {
        padding: 6px 16px 48px;
    }

    .mateen-landing-closing {
        padding: 0 16px 44px;
    }
}

/* ===================================================================================
   Login page (Components/Pages/Auth/Login.razor)

   Implements the identity board's login layout, via the standalone reference at
   Design/html/login.html (every colour there is pixel-sampled from the board): a full-viewport
   night-sky backdrop with warm bokeh, an ornamental mihrab arch holding the logomark, and a
   translucent card overlapping the arch's lower half.

   Mode handling is the same mechanism the .mateen-landing block documents: the mock ships its own
   html[data-theme] switch, which is dropped because MainLayout's MudThemeProvider already owns the
   app's single dark/light mode (its toggle stays visible in the app bar on /login - only the Login
   button hides there). --mud-native-html-color-scheme carries the mode into CSS, and light-dark()
   picks per-mode values off it.

   Two deliberate departures from the mock, both because the app's real login is not the mock's
   single-shot form:
     1. The mock shows Email and Password together. The real flow is stepped - email, then a tenant
        picker when the address belongs to several schools, then the password, plus a forced
        change-password step - so the card renders only the current step's fields. The arch, card
        and button treatment are identical across steps; only the contents change.
     2. The mock has a "Forgot password?" link. This product has no self-service reset (it is
        admin-mediated by design - see ARCHITECTURE.md's auth hardening notes), so that link would
        be dead and is omitted rather than shipped pointing nowhere.
   =================================================================================== */
.mateen-login {
    color-scheme: var(--mud-native-html-color-scheme);
    /* Card + arch (mock --card, --card-line, --arch-*).
       These were GOLD from the 2026-09-26 primary change through 2026-10-02, kept that way on
       purpose while branding/mateen-logomark-*.png (gold line art) sat inside the arch, since a
       violet arch stroke would have clashed with it. That logomark was replaced 2026-10-02 with
       branding/logo-*.png, a purple person-reading-book mark - the clash the gold arch was avoiding
       no longer exists, so card-line/arch-stroke/pattern move to purple here too, sourced directly
       from the new mark rather than the app palette: pixel-sampled off branding/logo-light.png
       (deep purple #61439C, light lavender #C7ABE8) and branding/logo-dark.png (brighter deep
       purple #7857CA, brighter lavender #D3B6F7), matched to the light-dark() mode that each file
       is actually displayed in elsewhere on this page. --mateen-login-arch-fill was never gold to
       begin with (it is the arch's own translucent white/near-black fill, not a gold value) so it
       is untouched. --mateen-login-card-line is part of the same card+arch visual unit (the hairline
       border sitting right under the arch) and not literally "the arch" - moved to purple alongside
       the other three so the whole assembly reads as one consistent family rather than three purple
       edges around one gold hairline. */
    --mateen-login-card-bg: light-dark(rgba(252, 254, 251, .95), rgba(24, 42, 80, .93));
    --mateen-login-card-line: light-dark(rgba(97, 67, 156, .28), rgba(120, 87, 202, .22));
    --mateen-login-arch-fill: light-dark(rgba(255, 255, 255, .9), rgba(13, 25, 54, .92));
    --mateen-login-arch-stroke: light-dark(rgba(97, 67, 156, .6), rgba(120, 87, 202, .75));
    --mateen-login-pattern: light-dark(rgba(97, 67, 156, .42), rgba(120, 87, 202, .55));
    --mateen-login-shadow: light-dark(0 24px 60px rgba(40, 50, 90, .18), 0 30px 80px rgba(0, 0, 0, .55));
}
/* Full-bleed backdrop. Fixed rather than absolute so it covers the viewport on a short card and
   doesn't scroll away on the taller change-password step.

   This is the SAME photographic treatment Mateen.Management's login already uses
   (branding/bg-dark-nebula.png, bg-light-dreamy.png, plus a tint layer that settles the photo down
   so the card and arch stay readable on top of it). It replaced a CSS-only night-sky-and-bokeh
   backdrop that approximated the same mood with gradients - the real photographs read better, and
   using one treatment across both apps means the two login pages are recognisably the same product.

   Both images are always in the DOM and the mode cross-fades between them, rather than swapping a
   background-image: that keeps the transition smooth and avoids a flash of unloaded image on the
   first toggle. The per-mode switch is a style container query on --mud-native-html-color-scheme,
   for the reason given on .mateen-login-mark below - this page does not own the dark-mode flag. */
.mateen-login-bg {
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background-color: var(--mud-palette-background);
}

.mateen-login-bg-image {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center;
    opacity: 0;
    transition: opacity .3s ease;
}

/* Light is the default so a browser without style-query support still gets a real backdrop rather
   than a blank one - same fallback reasoning as the logomark pair. */
.mateen-login-bg-image--light {
    background-image: url('branding/bg-light-dreamy.png');
    opacity: 1;
}

.mateen-login-bg-image--dark {
    background-image: url('branding/bg-dark-nebula.png');
}

/* Knocks the photo back so the translucent card and the arch tracery keep their contrast. Heavier
   in dark mode, matching Management's own values. */
.mateen-login-bg-tint {
    position: absolute;
    inset: 0;
    background: var(--mud-palette-background);
    opacity: .4;
}

@container style(--mud-native-html-color-scheme: dark) {
    .mateen-login-bg-image--dark {
        opacity: 1;
    }

    .mateen-login-bg-image--light {
        opacity: 0;
    }

    .mateen-login-bg-tint {
        opacity: .55;
    }
}

.mateen-login-stage {
    position: relative;
    z-index: 1;
    width: min(520px, 94vw);
    margin: 0 auto;
    padding: 40px 0 56px;
    display: flex;
    flex-direction: column;
    align-items: center;
}

/* The mihrab arch, with the card overlapping its lower half exactly as the mock has it. */
.mateen-login-arch {
    position: relative;
    width: 300px;
    /* Card overlaps the arch's lower half. -130px matches Mateen.Management and clears the
       104px mark, whose bottom edge sits ~175px down the 340-unit viewBox. */
    margin-bottom: -130px;
    pointer-events: none;
}

    .mateen-login-arch svg {
        display: block;
        width: 100%;
        height: auto;
        filter: drop-shadow(0 10px 30px rgba(0, 0, 0, .35));
    }

.mateen-login-mark {
    position: absolute;
    left: 50%;
    top: 41%;
    transform: translate(-50%, -50%);
    width: 104px;
    height: auto;
    object-fit: contain;
    /* Opaque, plus a tight halo. The mark sits directly on the khatam grid, and at the mock's
       0.96 opacity the two line-arts read as one texture instead of a mark on a pattern. Same
       treatment as Mateen.Management's login. */
    filter: drop-shadow(0 0 7px rgba(255, 255, 255, .85)) drop-shadow(0 4px 12px rgba(40, 50, 90, .3));
}

/* The board ships a logomark per mode and the backdrop flips with the mode, so the right one has to
   be shown. Swapping an image needs a real selector (display, not a colour), so light-dark() can't
   do it - hence the style container query, the same approach and the same caveat the landing page's
   brand mark used. The default state is the light-mode mark, so a browser without style-query
   support (pre-Chrome 111 / Firefox 128 / Safari 18) shows that one in both modes - gold line art
   that still reads against the dark arch - rather than stacking both. */
.mateen-login-mark--dark {
    display: none;
}

@container style(--mud-native-html-color-scheme: dark) {
    .mateen-login-mark--dark {
        display: block;
    }

    .mateen-login-mark--light {
        display: none;
    }

    /* The halo flips with the mode: a white glow lifts the mark off the pale sky, a near-black one
       lifts it off the nebula. Same pair as Mateen.Management's login. */
    .mateen-login-mark {
        filter: drop-shadow(0 0 10px rgba(6, 12, 32, .95)) drop-shadow(0 4px 14px rgba(0, 0, 0, .55));
    }
}

/* Overrides MudCard's own surface so the card can be the mock's translucent, blurred panel. */
.mateen-login-card {
    position: relative;
    z-index: 1;
    width: min(440px, 92vw);
    border-radius: 18px !important;
    border: 1px solid var(--mateen-login-card-line) !important;
    background: var(--mateen-login-card-bg) !important;
    box-shadow: var(--mateen-login-shadow) !important;
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
}
.mud-tabs-panels
{
    margin-top:10px !important;
}
/* FIXED (2026-10-04): this used to be background-color: #fff !important in BOTH modes - a white
   plate behind the Mateen logo/wordmark that stayed white after switching to dark mode even though
   this header sits in the same visual row as the app bar beside it (same --mud-appbar-height,
   same y-position), reading as "the navbar logo area doesn't go dark" (explicit bug report,
   2026-10-04). Now tracks the SAME variable the app bar itself uses - AppbarBackground is white in
   light, #121A3F in dark (Theme/MateenTheme.cs) - so the header is visually one continuous bar with
   the app bar in both modes instead of a seam.
   The 2026-10-03 note below about needing an explicit colour on content here is still correct and
   is NOT undone by this change - .mud-drawer still sets color: var(--mud-palette-drawer-text) on
   this header (drawer-text, not appbar-text), so anything that merely inherits colour would still
   be wrong now that the backdrop flips. The brand lockup's explicit colours
   (.mateen-appbar-wordmark__arabic's gradient clip, __latin's --mud-palette-secondary) already
   satisfy this - they are the exact same classes the app bar's own MateenBrand fragment uses
   directly on this same AppbarBackground colour in both modes (see .mateen-appbar-logo's
   dark-mode screenshot), so they are proven to read correctly on both #fff and #121A3F. The logo
   source itself is corrected back to the _isDarkMode pick in MainLayout.razor's MudDrawerHeader
   (restoring what this file's old note asked for "if that !important white is ever dropped") -
   logo-dark.png is tuned for exactly this dark backdrop. */
.mud-drawer-header {
    border-bottom: solid 1px var(--mud-palette-lines-default) !important;
    background-color: var(--mud-palette-appbar-background) !important;
    padding-top: 8px !important;
    padding-bottom: 8px !important;
}
/* Mateen.Management's card is a single MudPaper with pa-8 (32px). This one is a MudCard, so the
       same breathing room has to be set on its three parts instead - MudCardContent's own default
       is tighter, and the header/actions carry their own padding. */
.mateen-login-card .mud-card-header {
    padding: 28px 32px 0;
}

    .mateen-login-card .mud-card-content {
        padding: 20px 32px 4px;
    }

    .mateen-login-card .mud-card-actions {
        padding: 0 32px 28px;
    }

/* Pulls the header block tight to the fields below it and centres everything in it (the title, the
   tenant logo, the school name and the email on the password step). Scoped to the login card rather
   than written as a bare .mud-card-header .mud-card-header-content: that selector would also catch
   HomeLegacy.razor, the kept-for-reference copy of the old landing page, which should not drift. */
.mateen-login-card .mud-card-header .mud-card-header-content {
    text-align: center;
    margin-bottom: -10px;
}

/* The mock's serif "Login" heading. */
.mateen-login-title {
    font-family: var(--mateen-serif-font, "Cormorant Garamond", Georgia, serif);
    font-weight: 500 !important;
    font-size: 2.6rem !important;
    line-height: 1.15;
}

/* Occupies the mock's "Forgot password?" slot - under the password field, above the button - but as
   a statement rather than a link: this product has no self-service reset (admin-mediated by design,
   see ARCHITECTURE.md's auth hardening notes), so there is nowhere for a link to go. Left-aligned
   rather than the mock's right, because it reads as a sentence, not an action. */
.mateen-login-hint {
    display: block;
    margin: 10px 2px 0;
    font-size: .78rem;
    line-height: 1.5;
    color: var(--mud-palette-text-secondary);
}

.mateen-login-footer {
    margin-top: 18px;
    text-align: center;
    font-size: .76rem;
    letter-spacing: .3px;
    color: var(--mud-palette-text-secondary);
}

@media (max-width: 560px) {
    .mateen-login-arch {
        width: 240px;
        margin-bottom: -88px;
    }

    .mateen-login-title {
        font-size: 1.9rem !important;
    }

    .mateen-login-wordmark__arabic {
        font-size: 2.1rem;
    }
}

.mateen-login-subtitle {
    color: var(--mud-palette-text-secondary) !important;
}

.mateen-login-tenant-logo {
    display: block;
    max-width: 64px;
    max-height: 64px;
    width: auto;
    height: auto;
    object-fit: contain;
    margin: 8px auto 0;
    border-radius: 8px;
}

.mateen-login-tenant-picker-logo {
    width: 28px;
    height: 28px;
    object-fit: contain;
    border-radius: 4px;
    flex-shrink: 0;
}

/* Signed-in identity block on the LEFT of the app bar: school logo, school name, then who you are.
   It replaces the Mateen brand there once you are in - which school you are looking at matters more
   at that point than the product's own name, and the product is identified on the way in anyway.
   min-width:0 plus the ellipsis below matter because a long school name and a long person name can
   otherwise push the actions cluster off the right edge. */
.mateen-appbar-identity {
    min-width: 0;
    gap: 10px;
    overflow: hidden;
}

.mateen-appbar-school {
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 30vw;
}

.mateen-appbar-tenant-logo {
    width: 32px;
    height: 32px;
    object-fit: contain;
    border-radius: 6px;
    flex-shrink: 0;
    background-color: rgba(255, 255, 255, 0.15);
}

/* Signed-in identity cluster on the RIGHT of the app bar (Components/Layout/MainLayout.razor):
   avatar photo, name + role, chevron - the whole thing is the MudMenu's custom ActivatorContent,
   so the hover/click affordance has to be built here rather than coming from a MudButton/
   MudIconButton root. color-mix(in srgb, currentColor ...) rather than a flat rgba(), the same
   technique .mateen-person-card__avatar's ring uses, so the hover tint reads correctly against
   both the light AppbarBackground and the dark one (#121A3F) without a second dark-mode rule. */
.mateen-appbar-avatar-cluster {
    cursor: pointer;
    gap: 10px;
    padding: 4px 8px 4px 4px;
    border-radius: 999px;
    transition: background-color .15s ease;
}

    .mateen-appbar-avatar-cluster:hover {
        background-color: color-mix(in srgb, currentColor 10%, transparent);
    }

/* A CONTAINER now, not an <img> rule directly: the signed-in person's own avatar is the shared
   UserAvatar component (Components/Shared) so it also honours Preferences > Default User Avatar
   when they have no photo, the same drop-in contract every other avatar slot in this app follows
   (.mateen-person-card__avatar, .mateen-dashboard-list-item__avatar). overflow:hidden clips
   whichever of UserAvatar's own two renderings (photo or initials plate) it is given to this circle. */
.mateen-appbar-avatar {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    overflow: hidden;
    flex-shrink: 0;
}

.mateen-appbar-avatar-text {
    flex-direction: column;
    line-height: 1.2;
    min-width: 0;
}

.mateen-appbar-avatar-name {
    font-weight: 600;
    font-size: .85rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 160px;
}

.mateen-appbar-avatar-role {
    font-size: .72rem;
    opacity: .68;
    white-space: nowrap;
}

.mateen-appbar-avatar-chevron {
    opacity: .6;
    flex-shrink: 0;
}

/* ===================================================================================
   App-bar brand + landing links (Components/Layout/MainLayout.razor)

   The landing page used to carry its own in-frame nav - brand mark, section links and an "Enter
   platform" pill - sitting directly beneath MainLayout's MudAppBar, which meant two stacked bars
   and two different ways in ("Enter platform" vs "Login"). All three parts were hoisted into the
   real app bar instead, and the page's inner nav and its surrounding frame were removed.

   The links render only for an anonymous visitor on "/" (MainLayout gates them): they are same-page
   anchors, so anywhere else they would be dead. The Login button keeps its icon and caption but
   takes the landing pill's metallic gold ramp, so the one control reads as the way in.
   =================================================================================== */
/* 64x48 per explicit request (2026-10-02, after the logo-*.png swap) - object-fit:contain below
   still letterboxes safely inside this box regardless of the asset's own aspect ratio, same as the
   old 37x34 box did for the previous mark. */
.mateen-appbar-logo {
    width: 64px;
    height: 48px;
    object-fit: contain;
    flex-shrink: 0;
}

/* APP BAR + DRAWER-HEADER BRAND LOCKUP (2026-10-03) - compact variant of the same Arabic-over-Latin lockup as
   .mateen-login-wordmark (standalone, centered) and .mateen-landing-footer-wordmark (scaled down),
   and stacked like both of them per explicit request, replacing the one-line horizontal
   arrangement this first shipped as.

   The constraint that drove that first attempt is still real and is what the sizing below is for:
   the app bar is a single fixed-height toolbar row (MudAppBar undefaulted - 64px desktop / 56px
   mobile) that already holds the 48px-tall .mateen-appbar-logo, so a stack at either of the other
   two placements' sizes would clip or push the row taller and drag the hamburger/spacer/mode
   toggle/Login button out of line with it. Rather than refuse the stack, the type is cut to fit:
   19.2px Arabic + 2px gap + ~9.6px Latin = ~31px, which is comfortably shorter than the 48px logo
   beside it, so the toolbar's own content height is still set by the logo and the bar does not
   grow at all. Keep that total under 48px if these values are ever retuned.

   align-items:center matches both sibling lockups - the two lines center against each other, not
   against the left edge (the footer's flex-start is specific to its left-aligned brand column).
   Layout is declared here in full rather than via d-flex/align-center utility classes in
   MainLayout.razor, so direction and alignment are not split across two files. Same
   gradient-text-clip technique and purple stops as the other two placements.

   SECOND CONSUMER (2026-10-03): the signed-in drawer header (MainLayout.razor's MudDrawerHeader)
   reuses these same four classes rather than forking a .mateen-drawer-wordmark set - its
   constraints are identical, not merely similar: the same 64x48 .mateen-appbar-logo sits beside
   it, and .mud-drawer-header's own min-height is var(--mud-appbar-height) = the very 64px budgeted
   for above, so the measured 30.8px stack clears it with the same margin (measured: 65px header
   including its 1px bottom border, unchanged from the one-line version it replaced, 0 overflow).
   A fork would have been a byte-for-byte copy. The drawer does make one markup-level divergence -
   it drops the d-none d-sm-flex on __latin, see that element's comment there - which is why the
   responsive hiding lives in the .razor and not in this rule. If the two placements ever do need
   to diverge in SIZE, fork then, not before. */
/* WORDMARK COLOURS PER MODE (2026-10-04, dark-mode bug: the lockup stayed in the light-mode purples,
   #9333EA being only ~3:1 on the #121A3F app bar). Shared by all four placements (app bar, signed-in
   drawer header, login, landing footer), so declared once on the three wrapper classes.
   color-scheme is fed from MudBlazor's --mud-native-html-color-scheme right here because the app
   bar, drawer header and login page are NOT inside .mateen-landing, so no ancestor resolves it for
   them; with it set on the wrapper, light-dark() in the children resolves against the app mode.
   Light values are the original ones, unchanged. Dark values are sampled from branding/logo-dark.png:
   body of the mark ~#7858C8 (too dim for text on navy), highlight strokes ~#D0B0F8-#D8B8F8. The ramp
   keeps the light ramp's shape (dark -> bright -> mid -> dark at 0/38/70/100) in those tones. */
.mateen-appbar-wordmark,
.mateen-login-wordmark,
.mateen-landing-footer-wordmark {
    color-scheme: var(--mud-native-html-color-scheme);
    --mateen-wm-stop-edge: light-dark(#581C87, #A48AE6);
    --mateen-wm-stop-peak: light-dark(#9333EA, #D8B8F8);
    --mateen-wm-stop-mid: light-dark(#7E22CE, #BEA0F2);
    --mateen-wm-latin: light-dark(var(--mud-palette-secondary), #D0B0F8);
    --mateen-wm-gradient: linear-gradient(100deg, var(--mateen-wm-stop-edge) 0%, var(--mateen-wm-stop-peak) 38%, var(--mateen-wm-stop-mid) 70%, var(--mateen-wm-stop-edge) 100%);
}

.mateen-appbar-wordmark {
    display: flex;
    flex-direction: column;
    align-items: center;
    /* Vertical here, not the horizontal 10px this used while inline - a gap that size between two
       stacked lines would both read as two separate things and blow the height budget above. */
    gap: 2px;
}

/* 1.2rem rather than the login page's 2.75rem or the footer's 1.6rem - see the height budget in
   the block comment above. line-height:1 (instead of those two's 1.3) is deliberate for the same
   reason: at this size Tajawal's own glyph box still clears the word's ascenders and dots, so the
   tighter leading costs nothing visually and buys ~6px of the budget. */
.mateen-appbar-wordmark__arabic {
    font-size: 1.2rem;
    font-weight: 700;
    line-height: 1;
    direction: rtl;
    background: var(--mateen-wm-gradient);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

.mateen-appbar-wordmark__latin {
    gap: 6px;
    color: var(--mateen-wm-latin);
    font-size: .6rem;
    line-height: 1;
    /* Overrides --mateen-wordmark-tracking (see its own remarks further down this file) - smaller
       than even the footer's 5px, proportional to this placement's much smaller "MATEEN" text. */
    --mateen-wordmark-tracking: 3px;
}

.mateen-appbar-wordmark__line {
    width: 12px;
    height: 1px;
    background-color: var(--mateen-wm-latin);
    opacity: .6;
}

.mateen-appbar-links {
    display: flex;
    align-items: center;
    gap: 22px;
    font-size: .9rem;
}

    .mateen-appbar-links a {
        color: inherit;
        text-decoration: none;
        opacity: .82;
        white-space: nowrap;
        transition: opacity .15s ease;
    }

        .mateen-appbar-links a:hover,
        .mateen-appbar-links a:focus-visible {
            opacity: 1;
            text-decoration: underline;
            text-underline-offset: 4px;
        }

/* Same metallic ramp as .mateen-landing-btn--gold - deliberately duplicated rather than shared,
   because that class carries the landing pill's own geometry (44px, its own padding) which would
   fight MudButton's. !important is needed only because MudButton's Filled variant sets its own
   background-color and color at the same specificity. Purple since 2026-10-02, re-anchored to
   BrandPurple #9333EA (not a logo sample) the same day - see .mateen-landing-btn--gold's comment
   for where the stops come from. Text flipped from dark brown #2A1F0B to white alongside that
   deepening for the same contrast reason documented on --mateen-landing-btn-text. */
.mateen-appbar-login {
    background: linear-gradient(90deg, #581C87 0%, #7E22CE 14%, #9333EA 46%, #7E22CE 68%, #581C87 100%) !important;
    color: #FFFFFF !important;
    border-radius: 999px !important;
    font-weight: 600;
    box-shadow: 0 6px 18px rgba(88, 28, 135, .38) !important;
}

/* The mock's "Sign in" button: the same metallic ramp as the landing page's pill and the app bar's
   Login link. It replaces an earlier primary-to-primary-lighten gradient, which belonged to the
   previous login reference. Gold (sampled #94664F / #C78E57 / #E6C68F / #C6A572) through
   2026-10-02; purple from that date, re-anchored to BrandPurple #9333EA (not a logo sample) the
   same day, see .mateen-landing-btn--gold's comment for where the stops come from. Text used to
   flip light-dark(#2A1F0B, #16203F) because the old ramp's background didn't change by mode either
   - now a flat white for the same reason documented on --mateen-landing-btn-text, the deepened
   ramp no longer clears AA against dark text in either mode. !important throughout because
   MudButton's Filled variant sets its own background-color and color at the same specificity. */
.mateen-login-submit-btn {
    background: linear-gradient(90deg, #581C87 0%, #7E22CE 14%, #9333EA 46%, #7E22CE 68%, #581C87 100%) !important;
    color: #FFFFFF !important;
    border-radius: 999px !important;
    font-weight: 600;
    min-width: 132px;
    box-shadow: 0 10px 26px rgba(88, 28, 135, .4) !important;
    transition: filter .15s ease;
}

    .mateen-login-submit-btn:hover {
        filter: brightness(1.07);
    }

/* Step.Email has no Back button, so the primary is alone on its row - stretch it to the mock's
   full-width "Sign in". With a Back button present the two share the row instead. */
.mateen-login-actions--single .mateen-login-submit-btn {
    flex: 1 1 auto;
}

/* ===================================================================================
   Mushaf (Quran) fonts - AYAH TEXT ONLY
   -----------------------------------------------------------------------------------
   These four families are the King Fahd Glorious Quran Printing Complex (KFGQPC) faces
   shipped in wwwroot/fonts/. They are registered here (app.css is global, loaded from
   Components/App.razor) rather than in a .razor.css isolation file because ayah text will
   appear on several pages.

   IMPORTANT: none of these families are applied globally. `html, body`, all headings and
   every MudBlazor component keep the Inter stack defined at the top of this file. A Mushaf
   family reaches the DOM only through the `.mateen-ayah` class below.

   Registered family names (use these exact strings from any future font picker):
     'QPC Hafs V22'      -> UthmanicHafs_V22.ttf            (newest QPC Hafs revision; TTF only,
                                                              no woff2 was supplied)
     'QPC Hafs V18'      -> UthmanicHafs1Ver18.woff2/.ttf
     'QPC Hafs V17'      -> UthmanicHafs1-Ver17.woff2/.ttf
     'KFGQPC Nastaleeq'  -> KFGQPCNastaleeq-Regular.woff2/.ttf

   V22 -> V18 fallback rationale: V22 is the preferred default, but it is the only family here
   without a woff2 and is the newest revision, so it is also the most likely to fail to load or
   to lack a glyph for some codepoint. Listing V18 next in the chain covers both failure modes:
   the browser skips a family whose file fails to download AND falls through per-glyph to the
   next family when a glyph is missing. A generic Arabic serif closes the chain.

   Theming contract: ayah text is styled in exactly ONE place. A future toolbar (on the not-yet-
   built ayat page) changes the Mushaf font and size purely by overriding the two custom
   properties below - e.g. element.style.setProperty('--mateen-ayah-font', "'QPC Hafs V17', serif").
   Nothing else in this file or the app may hardcode a Mushaf family.
   =================================================================================== */

@font-face {
    font-family: 'QPC Hafs V22';
    src: url('fonts/UthmanicHafs_V22.ttf') format('truetype');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'QPC Hafs V18';
    src: url('fonts/UthmanicHafs1Ver18.woff2') format('woff2'),
         url('fonts/UthmanicHafs1Ver18.ttf') format('truetype');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'QPC Hafs V17';
    src: url('fonts/UthmanicHafs1-Ver17.woff2') format('woff2'),
         url('fonts/UthmanicHafs1-Ver17.ttf') format('truetype');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'KFGQPC Nastaleeq';
    src: url('fonts/KFGQPCNastaleeq-Regular.woff2') format('woff2'),
         url('fonts/KFGQPCNastaleeq-Regular.ttf') format('truetype');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

:root {
    /* Default Mushaf face for ayah text: V22 first, V18 as the load/glyph fallback, then a
       generic Arabic serif. Override this variable (not the class) to switch fonts. */
    --mateen-ayah-font: 'QPC Hafs V22', 'QPC Hafs V18', 'Traditional Arabic', serif;
    /* Quran text needs to be read comfortably at a glance; 2rem is the baseline. Override to
       resize. */
    --mateen-ayah-size: 2rem;
}

.mateen-ayah {
    font-family: var(--mateen-ayah-font);
    font-size: var(--mateen-ayah-size);
    /* The Uthmanic faces carry tall stacked diacritics (and low ones) that clip against a normal
       line-height, so this is deliberately generous and scales with font-size. */
    line-height: 2.2;
    direction: rtl;
    /* Verse text is centred rather than justified: the Uthmanic glyph metrics are hand-tuned for
       Mushaf line breaks, and browser justification stretches the inter-word spacing unevenly. */
    text-align: center;
    text-justify: none;
    /* Keeps the tall marks from being cropped by an ancestor's overflow rules. */
    padding-block: 0.15em;
    font-feature-settings: 'liga' 1, 'calt' 1;
    font-variant-ligatures: common-ligatures contextual;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

/* .mateen-ayah as a MudAutocomplete InputClass (sura pickers: StudentSuraProgressPanel, PlanFormDialog,
   TicketFormDialog). MudBlazor 9.10.0 puts InputClass on the .mud-input ROOT - the flex row holding the
   <input>, the clear/dropdown adornments and the floating label - not on the <input>. The class's
   `direction: rtl` therefore mirrored the whole control: the adornments jumped to the LEFT edge and sat
   on the label (only with Arabic sura names, when ArabicNameClass is non-null). The control's chrome
   follows the UI direction, so it is put back to ltr here; the <input> is ltr too, because a
   "113. <arabic name>" value under an rtl base direction renders its "." on the wrong side of the number
   (the Arabic run still shapes/orders itself). The class's padding-block is dropped on the root so the
   field keeps its normal height. Verse text on spans/divs is unaffected: they are not .mud-input. */
.mud-input.mateen-ayah {
    direction: ltr;
    padding-block: 0;
}

    .mud-input.mateen-ayah input {
        direction: ltr;
    }

/* -----------------------------------------------------------------------------------
   End-of-ayah ornament - the rosette/flower medallion with the ayah number inside it,
   printed at the END of every verse in a real Mushaf.
   -----------------------------------------------------------------------------------
   HOW THE MEDALLION IS PRODUCED (read this before changing the markup)

   The canonical Unicode encoding of an ayah marker is U+06DD ARABIC END OF AYAH followed by
   the number in Arabic-Indic digits U+0660-U+0669 - i.e. the four codepoints U+06DD U+0662 U+0668 U+0666 for 286.
   U+06DD is a "prefixed format control": a font is expected to swallow the digits that follow
   it and draw them INSIDE its ornament glyph. That is what a system Arabic face (Segoe UI,
   Arabic Typesetting, Traditional Arabic) does, and it is the sequence you want if you ever
   render ayah text in a non-Mushaf font.

   The four KFGQPC faces shipped above do it the other way round, and this matters: in those
   fonts the DIGIT glyphs themselves are drawn as the ornate rosette (they are Mushaf faces -
   the only numbers that occur in their text are ayah numbers), composed by 'calt'/'rlig', so
   the bare string U+0662 U+0668 U+0666 already renders as one medallion reading 286. Adding
   U+06DD in front of them draws the ornament TWICE: an empty rosette immediately followed by
   the numbered one. Verified by rendering all four files in headless Chrome (HarfBuzz) and in
   WPF/DirectWrite - both engines double it, in every one of V22/V18/V17/Nastaleeq.

   So the ayah number is emitted as Arabic-Indic DIGITS ONLY, with no U+06DD - see
   RewayahDetails.razor's EndOfAyahMark(). Do not "fix" that by prepending U+06DD.

   WHY THERE IS ALSO A CSS MEDALLION

   The digits-only string leans entirely on the Mushaf font, and there are two moments where
   that font is not there: the first paint of a page (every @font-face above is font-display:
   swap, so the fallback face paints first) and an outright load failure of the .ttf/.woff2.
   In those moments the digits would fall back to a plain number with no ornament at all. The
   radial-gradient disc below covers that: it is a soft gilded medallion drawn in CSS, sized so
   that up to three digits sit inside it, which means the marker always reads as an ayah
   medallion and never as a loose number (and never as a tofu box - U+06DD is deliberately not
   in the string, and every one of U+0660-U+0669 is present in all four fonts). When the Mushaf
   face does load, the same disc simply reads as gilding behind the real rosette.

   BIDI

   The digits are Unicode class AN (Arabic Number), so inside the RTL .mateen-ayah paragraph a
   trailing AN run lands at the LEFT end of the rendered line - exactly where a Mushaf puts the
   marker - while the digits themselves keep their own left-to-right order (286 stays 286).
   unicode-bidi: isolate makes that independent of whatever the verse text ends with, so no
   trailing punctuation or number in the source text can reorder or absorb the marker.

   THEMING CONTRACT

   Ink is the brand primary - var(--mud-palette-primary), so #471671 light / #BB86EA dark with no extra
   rule for dark mode, which is also how a Mushaf gilds its medallions - overridable per page by
   setting --mateen-ayah-mark-color on any ancestor. The var() chain is resolved on the marker
   itself rather than declared at :root so it still finds the palette if MudBlazor ever emits its
   variables on a themed wrapper instead of :root, and the disc below is built from currentColor
   so the whole medallion follows that one colour. Size is relative to the inherited
   --mateen-ayah-size, so the font/size toolbar keeps working with no extra rules, and nothing
   here hardcodes a Mushaf family: the marker inherits --mateen-ayah-font from .mateen-ayah on
   purpose, so the medallion is always drawn by the same face as the verse it closes.
   ----------------------------------------------------------------------------------- */
.mateen-ayah-mark {
    display: inline-block;
    unicode-bidi: isolate;
    direction: rtl;
    color: var(--mateen-ayah-mark-color, var(--mud-palette-primary));
    /* Gap between the last word and the marker. Logical, so it stays on the side facing the text
       in RTL, and small because min-width below already leaves air around the glyph. */
    margin-inline-start: 0.1em;
    /* Slightly under 1em keeps a three-digit number inside the disc in the fallback face; in a
       Mushaf face the rosette is a single glyph and is unaffected. */
    font-size: 0.95em;
    /* Square box (min-width == line-height) so border-radius gives a true circle, and small
       enough not to push .mateen-ayah's 2.2 line-height around. */
    min-width: 1.7em;
    line-height: 1.7;
    text-align: center;
    border-radius: 50%;
    /* The CSS medallion. Feathered rather than a hard 1px ring on purpose: a hard outline would
       read as a second circle around the font's own rosette. closest-side keeps the disc round
       even when the box is widened by a long number. */
    background: radial-gradient(closest-side circle,
                                color-mix(in srgb, currentColor 18%, transparent) 0 82%,
                                color-mix(in srgb, currentColor 6%, transparent) 92%,
                                transparent 100%);
}

    /* The component renders the marker span unconditionally and leaves it EMPTY when the stored
       text already carries its own end-of-ayah sequence, so that the medallion is never drawn
       twice. Without this rule an empty span would still paint a blank violet disc. */
    .mateen-ayah-mark:empty {
        display: none;
    }

/* ===================================================================================
   TAJAWAL (2026-10-02) - self-hosted Arabic/Latin UI font, added at the user's request ahead of
   a specific use ("I will let you know later where to use it"). Same pattern as the Mushaf
   @font-face block above: registered here so it is available app-wide, but NOT applied anywhere -
   `html, body` and every MudBlazor component still keep the Inter stack from the top of this file.
   A page reaches Tajawal only through the .mateen-tajawal class below, once something asks for it.

   wwwroot/fonts/Tajawal-*.ttf, TTF only (no woff2 was supplied, same situation as 'QPC Hafs V22'
   above - font-display:swap covers the slower TTF parse on first paint). One family name, seven
   @font-face blocks differentiated by font-weight, so `font-weight` alone selects the right file -
   standard multi-weight @font-face practice, not seven separate families. */
@font-face {
    font-family: 'Tajawal';
    src: url('fonts/Tajawal-ExtraLight.ttf') format('truetype');
    font-weight: 200;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Tajawal';
    src: url('fonts/Tajawal-Light.ttf') format('truetype');
    font-weight: 300;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Tajawal';
    src: url('fonts/Tajawal-Regular.ttf') format('truetype');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Tajawal';
    src: url('fonts/Tajawal-Medium.ttf') format('truetype');
    font-weight: 500;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Tajawal';
    src: url('fonts/Tajawal-Bold.ttf') format('truetype');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Tajawal';
    src: url('fonts/Tajawal-ExtraBold.ttf') format('truetype');
    font-weight: 800;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Tajawal';
    src: url('fonts/Tajawal-Black.ttf') format('truetype');
    font-weight: 900;
    font-style: normal;
    font-display: swap;
}

:root {
    /* Default Tajawal weight when .mateen-tajawal is used with no weight override - Regular, the
       safest default until a specific use picks a different one. Override by setting font-weight
       directly on the element/a modifier class; this variable exists only so the fallback stack
       (not just the weight) can be swapped in one place if Tajawal ever needs a substitute. */
    --mateen-tajawal-font: 'Tajawal', 'Segoe UI', Tahoma, sans-serif;
}

/* The interface: apply this class wherever Tajawal is wanted. Does not set font-weight - the
   @font-face chain above already covers 200/300/400/500/700/800/900, so a caller sets font-weight
   (200-900) same as any web-safe font and gets the matching file for free, no extra class needed
   per weight. */
.mateen-tajawal {
    font-family: var(--mateen-tajawal-font);
}

/* ===================================================================================
   MONTSERRAT LIGHT WORDMARK (2026-10-02) - self-hosted... actually Google-hosted, added for a
   specific stated purpose: setting the word "MATEEN" in a logo/wordmark treatment. Loaded via
   @import (not self-hosted) because it is a plain Google Font with no supplied file, same as Inter
   at the top of this file - only the one weight actually asked for (300/Light) is fetched, not the
   whole family, to keep the request small.

   Spec given: Font Montserrat, Weight 300 (Light), Letter spacing 8-14px. Letter spacing is a
   RANGE, not one value - a wordmark's ideal tracking shifts with how large it is ever used, so
   --mateen-wordmark-tracking is a variable (default 11px, the middle of the given range) rather
   than a fixed property, same theming-contract idiom as --mateen-ayah-size above: override the
   variable per placement instead of forking the class. text-transform:uppercase is baked in since
   the ask was specifically the word MATEEN in caps as a wordmark, not sentence-case text. */
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@300&display=swap');

:root {
    --mateen-wordmark-tracking: 11px;
}

.mateen-wordmark-montserrat {
    font-family: 'Montserrat', 'Inter', 'Helvetica Neue', Helvetica, Arial, sans-serif;
    font-weight: 300;
    letter-spacing: var(--mateen-wordmark-tracking);
    text-transform: uppercase;
}

/* LOGIN PAGE BRAND LOCKUP (2026-10-03) - the Arabic logotype over the Latin wordmark, under the
   login card. First real use of both .mateen-tajawal and .mateen-wordmark-montserrat since they
   were prepared ahead of time on 2026-10-02 - see those blocks' own remarks. */
.mateen-login-wordmark {
    margin-top: 28px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
}

/* The Arabic word itself ("متين") - large and Black (900, Tajawal's heaviest registered weight,
   the closest stand-in this app has to the reference artwork's thick calligraphic logotype) rather
   than a plain body weight, so it reads as a logotype and not a line of UI text. Same gradient-text
   technique (and the same purple stops) as the landing hero's h1 in app.css - see that rule's own
   remarks for why purple, not the reference artwork's gold. */
.mateen-login-wordmark__arabic {
    font-size: 2.75rem;
    font-weight: 900;
    line-height: 1.3;
    direction: rtl;
    background: var(--mateen-wm-gradient);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

.mateen-login-wordmark__latin {
    display: flex;
    align-items: center;
    gap: 10px;
    color: var(--mateen-wm-latin);
}

/* Flanking rules either side of "MATEEN", matching the reference artwork's layout - a flat brand
   purple rather than a gradient, since a gradient would be imperceptible on a 1px-tall line. */
.mateen-login-wordmark__line {
    width: 28px;
    height: 1px;
    background-color: var(--mateen-wm-latin);
    opacity: .6;
}

/* Content-card top accent bar - the shared treatment for every content <MudCard> in the app.
   The bootdey "dashboard border cards" accent (a flat colored border on one edge of an otherwise
   plain rounded card) rotated from the left edge to the top edge, at 7px, in a brand color.

   Three variants, all driven through --mateen-accent-color so each variant states its color in
   exactly one place:
     .mateen-accent-card              -> brand violet, var(--mud-palette-primary). The default for
                                         dashboard/list/detail/report cards.
     .mateen-accent-card--tertiary    -> deeper brand periwinkle,
                                         var(--mud-palette-tertiary-darken). Was reserved for the
                                         people cards (student + educator); since 2026-09-26 those
                                         are the centred Components/Shared/PersonCard.razor, which
                                         carries NO accent bar at all (see the "Person cards" block
                                         at the end of this file for why, and for the per-role
                                         colour that replaced it). What still uses this variant is
                                         TeacherDetails.razor's educator photo frame.
     .mateen-accent-card--secondary   -> brand purple, var(--mud-palette-secondary). Reserved for
                                         memorization plan cards.
   Always a var(--mud-palette-*), never a hex: all three colors differ per mode (primary #471671 light /
   #BB86EA dark; tertiary-darken #4A5DED light / #5C6EF5 dark; secondary #9333EA both modes -
   Theme/MateenTheme.cs), and the variable is what keeps dark mode correct with no extra CSS.

   Why this lives in app.css instead of a per-component .razor.css: these components' root elements
   are <MudCard>s, not plain HTML. Blazor CSS isolation only stamps its b-* scope attribute onto
   elements a component declares itself, never onto a child component's rendered markup, and ::deep
   needs a scoped ancestor element to hang off - so a scoped rule would never reach the rendered
   .mud-card div without wrapping the card in an extra <div> (a layout change). */
.mateen-accent-card {
    /* .mud-paper is already position:relative !important in MudBlazor 9.x; restated so the accent
       stays anchored to the card even if that ever changes. */
    position: relative;
    --mateen-accent-color: var(--mud-palette-primary);
}

    /* Drawn as a pseudo-element bar rather than border-top for two reasons: with border-left/right
       at 0 width a rounded border-top tapers diagonally to a point at both corners instead of
       reading as a flat bar, and a real border would also push the card's content down by its own
       height. This keeps a uniform 7px band across the full card width and adds zero layout.
       Inset to 0 on all three edges rather than width:100% so that on Outlined="true" cards - which
       DO carry their own 1px border - the bar sits inside that border instead of overhanging it. */
    .mateen-accent-card::before {
        content: "";
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        height: 7px;
        background-color: var(--mateen-accent-color);
        /* .mud-card has no overflow:hidden, so the bar has to follow the card's own corner radius
           itself rather than being clipped into shape. */
        border-top-left-radius: inherit;
        border-top-right-radius: inherit;
        /* Keeps the band out of the way of the card's own click handling / text selection. */
        pointer-events: none;
        z-index: 1;
    }

/* Preferences > Cards > "Show the accent bar on cards" (UserPreferences.razor, UserPreferenceState.cs).
   MainLayout.razor puts .mateen-hide-card-accents on the element wrapping every signed-in page's
   @Body when the preference is off, so this single descendant-selector rule reaches every
   .mateen-accent-card - and every variant/override below that repositions the same ::before - in
   all 28+ .razor files that carry the class, with none of them touched. display:none rather than
   visibility/opacity because the bar is a zero-content pseudo-element to begin with - hiding it
   adds no layout shift either way. */
.mateen-hide-card-accents .mateen-accent-card::before {
    display: none;
}

/* People cards (student / educator) - the deeper brand periwinkle instead of the primary violet. Only the
   custom property is overridden; the ::before geometry above is shared. */
.mateen-accent-card--tertiary {
    --mateen-accent-color: var(--mud-palette-tertiary-darken);
}

/* Memorization plan cards - the vivid brand purple instead of the deep primary violet. Only the custom property is
   overridden; the ::before geometry above is shared. */
.mateen-accent-card--secondary {
    --mateen-accent-color: var(--mud-palette-secondary);
}

/* ===================================================================================
   Dialog title bar - the shared treatment for every dialog under Components/Dialogs (paired with
   Components/Shared/DialogTitleBar.razor, which renders the row inside it: title + close X). It
   mirrors the spirit of .mateen-accent-card above (gray header band + a primary-coloured top accent bar drawn
   as a pseudo-element rather than a border, for the same corner-mitre reason) but re-derived for
   MudDialog's own rendered DOM, which .mateen-accent-card's selectors don't reach (MudDialog's root
   is a MudCard-unrelated <div class="mud-dialog">, not a MudCard).

   Applied via <MudDialog TitleClass="mateen-dialog-title"> on each dialog - MudDialog exposes
   TitleClass precisely so a dialog's title div can be targeted without a wrapping element, same
   underlying constraint as .mateen-accent-card's comment: a dialog's root markup is MudBlazor's own
   rendered output, not something Blazor CSS isolation (::deep) can reach without a wrapper. Still
   opt-in per dialog through that one class - deliberately NOT a global .mud-dialog-title restyle,
   so a MudBlazor-provided dialog (MessageBox, or any future third-party one) is left alone.

   Gray background: var(--mud-palette-background-gray), MudBlazor's own stock token for exactly this
   purpose (untouched by Theme/MateenTheme.cs, so it keeps MudBlazor's own gray in both modes -
   verified via the actual compiled Palette defaults: #F5F5F5 in light mode against this theme's
   white Surface/#FFFFFF, and #27272F in dark mode against this theme's navy Surface/#121A3F - a
   clearly distinct neutral gray from the (very different-hued) body surface in both modes, unlike a
   single hardcoded hex which would only read as "gray" in one of the two.

   Accent bar: .mud-dialog-title already carries its own border-top-left/right-radius equal to the
   dialog's own var(--mud-default-borderradius) (see MudBlazor's compiled CSS), so the ::before here
   can inherit that radius directly, no extra wrapper needed - same "adds zero layout, only overlays
   existing padding" approach as .mateen-accent-card::before. */
.mateen-dialog-title {
    position: relative;
    background-color: var(--mud-palette-background-gray);
}

    .mateen-dialog-title::before {
        content: "";
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        height: 7px;
        background-color: var(--mud-palette-primary);
        border-top-left-radius: inherit;
        border-top-right-radius: inherit;
        pointer-events: none;
    }

/* The row inside that band (Components/Shared/DialogTitleBar.razor): title text, then the close X
   pushed to the far end of the same row. A plain flex row rather than MudBlazor's own
   .mud-button-close (which is position:absolute, top/right 8px, and pairs with a
   .mud-dialog-title:has(.mud-button-close) { padding-inline-end: 64px } rule): at Typo.h5 several of
   these titles wrap to two lines on a phone-width dialog, and a flex row makes the text wrap
   *beside* the X, which an out-of-flow absolute button can't guarantee. align-items:flex-start keeps
   the X pinned to the first line when that happens instead of drifting to the vertical middle of a
   two-line title. */
.mateen-dialog-title-bar {
    display: flex;
    align-items: flex-start;
    gap: 8px;
}

/* min-width:0 is what actually lets the title shrink below its longest word's width inside the flex
   row; overflow-wrap:anywhere then breaks that word rather than letting it push the X off the edge
   (.mud-dialog-title sets the same, but it applies to the title div, not to this flex item). */
.mateen-dialog-title-text {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}

    /* One step down from Typo.h5's 1.5rem on phone-width dialogs - the same title still reads as
       "larger than body" there, but a long one (e.g. "Assign/unassign educator - <group>") wraps to
       two lines instead of three or four and doesn't crowd out the X. */
    @media (max-width: 599.95px) {
        .mateen-dialog-title-text {
            font-size: 1.25rem;
        }
    }

/* Pulled into the title div's own 24px inline padding so the X reads as sitting in the dialog's
   corner, without the row (or the gray band) growing to accommodate it. Logical property, not
   margin-right: MudBlazor flips the whole dialog for RTL (.mud-dialog-rtl). */
.mateen-dialog-title-close {
    flex: 0 0 auto;
    margin-inline-end: -8px;
}

/* TicketFormDialog's per-row Sura/From ayah/To ayah/Grade sizing (the MudStack Row inside
   @foreach (var row in _rows)) - flex sizing has to be applied via Class, never Style, on these
   four fields. MudSelect forwards Style to its own outer MudInputControl div (the actual flex item
   inside the row), so Style="flex:...;" works there - but MudNumericField forwards Style one level
   deeper, to the inner MudInput's root <div class="mud-input">, whose own parent
   (.mud-input-control-input-container) is a *column* flex container (it stacks the input over its
   floating label). A flex-basis meant as a width on a row is instead read as a *height* in that
   column's main axis, which is exactly why the two MudNumericFields rendered visibly taller than
   the two MudSelects (verified by hiding MudNumericField's spin-button adornment entirely and
   confirming the height didn't change - the spin buttons were never the cause; the misrouted
   flex-basis was). Class doesn't have this problem: both MudSelect.Classname and
   MudNumericField.Classname forward Class to their outer MudInputControl the same way, so
   class-based flex-basis lands on the correct element for both, in both width and height. */
.mateen-ticket-row-sura {
    flex: 2 1 220px;
}

.mateen-ticket-row-ayah {
    flex: 0 1 70px;
}

.mateen-ticket-row-grade {
    flex: 1 1 200px;
}

/* The "Add mistake" mini-form (Ayah/Ayah text/Type/How many/Note) - same Class-not-Style requirement
   as the Sura row above, for the same reason (MudNumericField/MudTextField vs MudSelect route Style
   to different elements on different flex axes). The Ayah number field reuses .mateen-ticket-row-ayah
   directly so it's guaranteed the exact same width/height as the Sura row's own From/To ayah fields. */
.mateen-mistake-type {
    flex: 1 1 170px;
}

.mateen-mistake-note {
    flex: 1 1 200px;
}

/* The ayah-text display, sitting in this SAME row between Ayah and Type rather than on its own row
   underneath (2026-09-29 - this class existed before that, at a fixed 280px flex-basis, but had zero
   callers anywhere in Components/: a leftover from an earlier iteration of this exact layout that was
   never wired up. It is repurposed here rather than replaced with a new name, because "the ayah text
   is a flex item in the entry row, not a full-width block below it" was already exactly its intent).

   flex-basis:50% is a literal reading of "50% of the row's width": on a flex item, a percentage basis
   resolves against the flex container's own content-box width - the MudStack Row's, here - so it
   tracks the drawer's actual width rather than a guessed pixel figure baked in for one viewport.
   flex-grow:1 lets it claim slack on a wide drawer instead of leaving dead whitespace next to a short
   verse; flex-shrink:1 gives room back to Ayah/Type/How-many/Note before those get crushed. min-width
   is the floor below which a genuinely long ayah stops reading as a paragraph and starts reading as a
   single-word-per-line column - it wins over the 50% basis on a narrow drawer, which is exactly why
   this row is expected to wrap onto more than one visual line at typical widths once Ayah + this +
   Type + How many + Note are all fighting for space (Wrap="Wrap.Wrap" already covers that; "no need
   to split it into a new row" was about not keeping it as a second row in the MARKUP, not about
   fitting the whole form on one visual line).

   This is a plain <div> (see .mistake-ayah below, in each dialog's own scoped stylesheet, for its
   inner label+text layout), not a MudBlazor input - so unlike its .mateen-ticket-row-*/
   .mateen-mistake-type/.mateen-mistake-note neighbours it was never at risk of the Style-routing
   hazard documented above .mateen-ticket-row-sura. Class is used anyway, for the same two reasons
   those fields use it: consistency with the rest of the row, and because this file - not either
   dialog's own scoped stylesheet - is where both dialogs' shared row-item widths live. */
.mateen-mistake-ayah-text {
    flex: 1 1 50%;
    min-width: 260px;
}

/* ===================================================================================
   Recorded-mistake card (MistakeManagerDialog.razor's _mistakes list, TicketFormDialog.razor's staged
   row.Mistakes list) - both dialogs' own near-identical single-line, Outlined MudPaper rows ("Type x
   Count - ayah N (-score)" plus a tooltip-truncated note and edit/delete icons, all on one line),
   replaced with a taller, grey-filled card that leads with the mistake's OWN ayah text - not the
   entry form's currently-pending ayah, since each mistake in the list can carry a different ayah
   number - then stacks type, points and the full note underneath it, with the actions in the header
   row's own top-right corner.

   PROMOTED STRAIGHT TO app.css. MistakeManagerDialog.razor.css's own .mistake-ayah comment already
   flagged the rule to follow here: "if a third dialog ever needs the same label, that is the moment
   to promote it to app.css." This card is used by exactly the two dialogs opened from the same
   ticket row (see both dialogs' own DrawerOptions remarks), so a third near-duplicate written twice
   into two scoped stylesheets is exactly the drift that precedent warned against - one class set,
   shared, is what keeps the two cards from independently going out of step over time.
   =================================================================================== */
.mateen-mistake-card {
    border-radius: 10px;
    padding: 14px 16px;
    border: 1px solid var(--mud-palette-lines-default);
    /* The mode signal every light-dark() below resolves against. MudBlazor 9.10.0 only wires
       --mud-native-html-color-scheme into the real `color-scheme` property on .mud-input itself, so
       anything else calling light-dark() has to set this on the SAME rule or it silently always
       resolves its light-mode value, dark palette or not - this exact bug already happened twice
       tonight in this codebase (see the person-card and pill/panel blocks above for the same guard). */
    color-scheme: var(--mud-native-html-color-scheme);
    /* A palette-derived neutral grey, not a literal hex - the same color-mix-against-surface recipe
       .mateen-pill and .mateen-plans-group use above, at their same 7%/9% ratio: strong enough that a
       list of these cards reads as visually separated blocks (this app has no dark-mode wrapper
       class, so this only works via the light-dark()-over-color-scheme mechanism those blocks
       document), not a wash barely different from the drawer's own surface. */
    background-color: light-dark(color-mix(in srgb, var(--mud-palette-text-primary) 7%, var(--mud-palette-surface)), color-mix(in srgb, #FFFFFF 9%, var(--mud-palette-surface)));
}

/* Ayah text (flex:1, allowed to wrap/shrink) beside the action icons (flex:0, never shrink) - keeping
   the actions in this first row rather than as a fourth stacked line of their own is what puts them
   in the card's top-right corner without resorting to absolute positioning, which would otherwise
   need a hand-guessed padding reservation to keep the ayah text from ever running under them. */
.mateen-mistake-card__header {
    display: flex;
    align-items: flex-start;
    gap: 8px;
}

.mateen-mistake-card__ayah {
    flex: 1 1 auto;
    min-width: 0;
}

.mateen-mistake-card__actions {
    flex: 0 0 auto;
}

/* Type, then points, then the note - stacked in that order beneath the header row. This is the
   height the card was asked for: room for three lines to read clearly instead of being squeezed
   into one, with a small gap between them rather than MudText's own zero margin running them
   together. */
.mateen-mistake-card__body {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-top: 8px;
}

/* No longer truncated-with-tooltip (that was .mateen-ticket-mistake-note-wrap/-note, both removed
   with this change - it was a single-line row's accommodation, and this card has the height to show
   a note in full without one). A note is exactly the kind of detail a teacher opens this list to
   re-read, not something worth hiding behind a hover; overflow-wrap covers a single very long word
   the way the ayah-text label already does, and pre-wrap keeps a note's own line breaks intact. */
.mateen-mistake-card__note {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* ===================================================================================
   School Configuration page (Components/Pages/Admin/SchoolConfiguration.razor)

   A single settings card on an otherwise empty page. Left-aligned at a fixed 600px it sat in the
   corner of a wide window with the rest of the page blank, so it is centred in its container and
   given more room from Md up (960px, MudBlazor's own Md breakpoint) where there is width to spare.
   Below that it stays fluid and simply fills the column, as it did before.
   =================================================================================== */
.mateen-config-card {
    max-width: 600px;
    margin-inline: auto;
}


/* ===================================================================================
   Person cards (Components/Shared/PersonCard.razor + Components/Shared/StudentSummaryCard.razor,
   which wraps it). Used by Admin/Users.razor's grid, Teachers/Teachers.razor, and the four student
   grids/detail cards.

   SOURCE
   -----------------------------------------------------------------------------------
   Design/"User card design.png": a white card on a pale page, ~18px corner radius, a soft diffuse
   drop shadow, and everything centred in one column - circular avatar, bold name, muted subtitle,
   a stat row with a small label above a large value, then a tinted footer band separated by a
   hairline. The reference's own content (three stats, a productivity progress bar) is NOT this
   app's data: the stat row here carries the Age (plus, on the Educators page only, that educator's
   group/student counts) and the footer band carries the Active/Inactive chip, with the rest of the
   band left free for the student progress treatment that is still to be specified.

   NO ACCENT BAR HERE, ON PURPOSE
   -----------------------------------------------------------------------------------
   These cards deliberately do NOT use .mateen-accent-card / --tertiary above. The reference has no
   top bar; a flat 7px band across the top of an otherwise symmetric centred stack reads as a
   leftover; and the role signal it used to carry is now carried much more legibly by the avatar
   ring + subtitle colour below. Every non-people card in the app keeps its accent bar unchanged.

   ROLE COLOURS
   -----------------------------------------------------------------------------------
   Admin/Users.razor no longer groups its cards under "Admin (n)" / "Educator (n)" / "Student (n)"
   headings - it is one continuous grid - so colour is what separates the three populations. The
   role word is still printed in the card's subtitle, so colour is a redundant second encoding, not
   the only one. Three widely separated hues were picked (38 / 195 / 272 deg) so that no two are
   confusable at ring size, and so that none of them lands near the Active chip's green (~122 deg)
   or the Inactive chip's grey:

     Admin    -> brand violet, var(--mud-palette-primary) - #471671 light / #BB86EA dark. Taken
                 from the palette rather than restated so it tracks Theme/MateenTheme.cs.
                 12.95:1 on the light card, 6.19:1 on the dark card.
     Educator -> bronze/antique gold, hue 38. Gold was part of the identity (the logomark, the
                 login arch, the metallic button ramps) through 2026-10-02; all three have since
                 moved to purple (the logomark itself was replaced with a purple mark, and the arch
                 + button ramps followed it), so this role swatch is now the one deliberate
                 remaining gold accent, kept for role-colour variety rather than as a brand callback.
                 It is darkened well past the identity board's #B8860B (which is only 3.25:1 on white)
                 so it is legible as subtitle text. #8F6100 light: 5.42:1 on the white card,
                 4.83:1 on the #EDF2FE page. #E8B65C dark: 9.33:1 on the #121A3F card.
     Student  -> deep cyan/teal, hue 195. Far enough from the Active chip's green that a filled
                 green pill and a teal ring never trade places. #0F6E8C light: 5.79:1 on the white
                 card, 5.16:1 on the page. #4FB8D4 dark: 7.56:1 on the dark card.
     Neutral  -> the theme's mauve (var(--mud-palette-dark)) for an unrecognised role.

   PER-MODE VALUES WITHOUT A WRAPPER CLASS
   -----------------------------------------------------------------------------------
   Same mechanism the .mateen-landing / .mateen-login blocks above document and for the same
   reason (this app has no dark-mode wrapper class): MudBlazor's ThemeProvider emits the reactive
   custom property --mud-native-html-color-scheme on :root, this block feeds it into the real
   inherited `color-scheme` property, and light-dark() then resolves every per-mode pair against
   the app's current mode with no JS and no MainLayout change. Everything that has a palette
   equivalent (surface, text, hairlines, the Admin accent) uses var(--mud-palette-*) instead and
   needs no light-dark() at all.

   EQUAL HEIGHTS IN A GRID
   -----------------------------------------------------------------------------------
   Guaranteed three ways, since these grids mix one-word and very long names and 0..n groups:
   height:100% + column flex on the card (MudCard is already display:flex/column), margin-top:auto
   on the footer so it is pinned to the bottom edge however short the body is, and a hard one-line
   ellipsis on both the name and the subtitle. The body's max-width keeps the same markup from
   looking absurd when the card is NOT in a grid cell - StudentDetails.razor's copy is full page
   width - without needing a second variant.
   =================================================================================== */
.mateen-person-card {
    /* The mode signal every light-dark() below resolves against. */
    color-scheme: var(--mud-native-html-color-scheme);
    --mateen-person-accent: var(--mud-palette-dark);
    --mateen-person-shadow: light-dark(rgba(42, 39, 64, .10), rgba(0, 0, 0, .45));
    --mateen-person-shadow-hover: light-dark(rgba(42, 39, 64, .18), rgba(0, 0, 0, .60));
    /* Positioning context for the bottom accent bar on the admin/educator variants below. */
    position: relative;
    height: 100%;
    /* The design reference is a 254px card, and its proportions - avatar, centred stack, the
       three-slot footer row - only read correctly at roughly that width. The grid cell is wider
       than that at most breakpoints (lg="3" of the Large container is ~312px), so the card is
       capped and centred inside its cell rather than stretched to fill it. Below Sm the cell is
       narrower than 254px and this cap does nothing, so phone layout is unaffected. */
    max-width: 254px;
    margin-inline: auto;
    /* The footer band is painted edge-to-edge, so it has to be clipped into the card's own corner
       radius - .mud-card sets no overflow of its own. Safe here: nothing in this card is drawn
       outside its box (MudTooltip/MudChip popovers are portalled to the body, not nested). */
    overflow: hidden;
    border-radius: 18px;
    border: 1px solid var(--mud-palette-lines-default);
    box-shadow: 0 10px 28px var(--mateen-person-shadow);
    /* Rendered with Elevation="0" so MudBlazor's own .mud-elevation-0 box-shadow doesn't have to
       be fought; only the border/shadow transition below is ours. */
    transition: box-shadow 200ms cubic-bezier(.4, 0, .2, 1), border-color 200ms cubic-bezier(.4, 0, .2, 1), transform 200ms cubic-bezier(.4, 0, .2, 1);
}

.mateen-person-card--admin {
    --mateen-person-accent: var(--mud-palette-primary);
}

.mateen-person-card--educator {
    --mateen-person-accent: light-dark(#e2960e, #c98c0b);
    /*--mateen-person-accent: light-dark(#c98c0b, #E8B65C);*/
}

.mateen-person-card--student {
    --mateen-person-accent: light-dark(#0F6E8C, #4FB8D4);
    /* The bottom bar only - a lighter cyan than the accent above, since it sits on the footer
       band rather than on the card surface. Same value in both modes, as specified. */
    --mateen-person-bar: rgb(87 167 191);
}

/* The one card that is not part of a 254px grid: the student details page shows a single card
   across the full page width, with the progress bars in its footer. */
.mateen-person-card--full {
    max-width: none;
}

/* A 7px accent bar along the BOTTOM edge of every person card - the same device
   .mateen-accent-card draws on the top edge of content cards, moved to the foot so it reinforces
   the footer band rather than competing with the avatar at the top.

   Drawn as a pseudo-element rather than a border-bottom for the same reason .mateen-accent-card
   does: a real border would be mitred into the card's 18px corner radius and taper at both ends,
   and it would add to the card's box and break the equal-height guarantee. This overlays the
   existing footer padding and adds no layout. The card already sets overflow:hidden, so the bar is
   clipped into the rounded corners for free.

   The bar's colour is its own variable rather than --mateen-person-accent directly: the student
   card's bar is a lighter cyan than the accent its avatar ring and subtitle use, because it sits on
   the footer band rather than on the card surface. When the memorization progress lands in that
   footer, check the two still read as separate things and not as one bar. */
.mateen-person-card--admin::after,
.mateen-person-card--educator::after,
.mateen-person-card--student::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 7px;
    background-color: var(--mateen-person-bar, var(--mateen-person-accent));
    pointer-events: none;
}

/* Only the cards that actually navigate/open a dialog react to the pointer - the detail pages'
   static copies leave OnCardClick unbound and get neither the cursor nor this. */
.mateen-person-card--clickable:hover {
    border-color: color-mix(in srgb, var(--mateen-person-accent) 45%, transparent);
    box-shadow: 0 14px 34px var(--mateen-person-shadow-hover);
    transform: translateY(-2px);
}

@media (prefers-reduced-motion: reduce) {
    .mateen-person-card,
    .mateen-person-card--clickable:hover {
        transition: none;
        transform: none;
    }
}

.mateen-person-card__body {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    width: 100%;
    /* Only ever bites on the full-page-width detail card; inside a grid cell the card is narrower
       than this anyway. */
    max-width: 560px;
    margin-inline: auto;
    padding: 24px 20px 20px;
}

/* The ring is a double box-shadow rather than a border so it sits OUTSIDE the 72px circle and the
   photo keeps its full diameter: an inner ring in the card's own surface colour separates the
   photo from the coloured ring, exactly as the reference's avatar sits inside a halo. */
.mateen-person-card__avatar {
    flex: 0 0 auto;
    width: 72px;
    height: 72px;
    border-radius: 50%;
    overflow: hidden;
    background-color: color-mix(in srgb, var(--mateen-person-accent) 14%, var(--mud-palette-surface));
    box-shadow: 0 0 0 3px var(--mud-palette-surface), 0 0 0 5px color-mix(in srgb, var(--mateen-person-accent) 65%, transparent);
    /* Keeps the outer ring clear of the body's own padding box. */
    margin: 5px 5px 0;
}

    .mateen-person-card__avatar img {
        display: block;
        width: 100%;
        height: 100%;
        object-fit: cover;
    }

/* white-space:nowrap + ellipsis rather than a 2-line clamp: a hard single line is what keeps every
   card in a grid row exactly the same height. The component puts the untruncated name in a title
   attribute so the full text is still reachable.

   TYPE IS NOT SET HERE (2026-10-03): both name elements in PersonCard.razor now also carry
   .Tajawal-Title-Bold (wwwroot/tajawal-typography.css), which supplies family + weight 700 + size
   1.25rem + line-height 1.4 as one role. That file is linked AFTER this one in Components/App.razor,
   so at equal specificity (one class each) its declarations won anyway - the font-size: 1.0625rem /
   font-weight: 700 / line-height: 1.35 that used to live here were dead, and keeping them would
   have read as if this rule still controlled the name's type. What remains here is layout and
   colour only, which the typography class deliberately does not touch. The horizontal variant's
   own font-size further down is NOT dead - see its comment. */
.mateen-person-card__name {
    margin-top: 16px;
    max-width: 100%;
    color: var(--mud-palette-text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The role (Users/Educators) or the group (students), in the role colour - this plus the avatar
   ring is the whole role signal now that the per-role section headings are gone. */
.mateen-person-card__subtitle {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 5px;
    margin-top: 4px;
    max-width: 100%;
    min-width: 0;
    font-size: .8125rem;
    font-weight: 600;
    line-height: 1.4;
    color: var(--mateen-person-accent);
}

    /* The ellipsis has to live on the text node, not on the flex container that also holds the
       icon - a flex container never truncates its own children. */
    .mateen-person-card__subtitle-text {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /* "No groups" - falls back to the normal muted text colour so an empty state never reads as a
       group name rendered in the accent. */
    .mateen-person-card__subtitle--muted {
        color: var(--mud-palette-text-secondary);
        font-weight: 400;
    }

    .mateen-person-card__subtitle .mud-icon-root {
        flex: 0 0 auto;
        font-size: 1rem;
    }

/* space-around, not space-between: with the single Age column this row usually has, it centres;
   with the Educators page's three columns it spaces them evenly, as in the reference. */
.mateen-person-card__stats {
    display: flex;
    justify-content: space-around;
    align-items: flex-start;
    gap: 15px;
    width: 100%;
    margin-top: 18px;
}

/* BUG FIX 2026-09-30: the student grid card's Hifd ring (StudentSummaryCard.razor) used to sit
   wherever .mateen-person-card__body's plain top-down stack left it, which read as floating
   mid-card whenever a MudGrid row stretched the card taller than its own content (e.g. a neighbour
   with more spoken-language pills gave the row extra height this card's shorter identity block did
   not need). Pin the stats row that holds the ring to the BOTTOM of body's flexible space instead,
   flush above the footer band, regardless of how much slack space body ends up with.

   :has() scopes this to only the stats row that actually holds that ring, so the Users and
   Educators grids' Age stat - the same .mateen-person-card__stats class, unconditionally - never
   moves. This has to live here, unscoped, rather than in StudentSummaryCard.razor.css: Blazor's CSS
   isolation stamps a component's scope attribute onto elements THAT component's own markup renders,
   and .mateen-person-card__stats is rendered by PersonCard.razor, not StudentSummaryCard.razor - the
   only element with StudentSummaryCard's scope attribute here is the ring div itself, a DESCENDANT
   of .mateen-person-card__stats, not an ancestor of it. ::deep only ever extends a scoped selector
   further INTO descendants; there is no Blazor syntax that lets a scoped descendant reach back up to
   style its own ancestor, so a scoped .razor.css rule for this can never match no matter how it is
   written - confirmed by inspecting the actually-compiled/served CSS, not assumed. Plain CSS has no
   such restriction, and the :has() guard keeps the effect exactly as narrowly targeted as a scoped
   rule would have been. */
.mateen-person-card__stats:has(> .mateen-student-summary-card__ring) {
    margin-top: auto;
}

.mateen-person-card__stat {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

/* Label above, value below - the reference's own stat ordering, which is the reverse of the usual
   "big number then caption". */
.mateen-person-card__stat-label {
    font-size: .75rem;
    line-height: 1.2;
    color: var(--mud-palette-text-secondary);
}

.mateen-person-card__stat-value {
    font-size: 1.25rem;
    font-weight: 700;
    line-height: 1.25;
    color: var(--mud-palette-text-primary);
}

/* The reference's footer band. margin-top:auto is what pins it to the bottom edge of a card whose
   body is shorter than its neighbours' - the other half of the equal-height guarantee. The tint is
   a color-mix against the palette surface rather than a light-dark() pair, so it is automatically
   a pale wash of the role colour on white and a dark one on the navy card. */
.mateen-person-card__footer {
    flex: 0 0 auto;
    margin-top: auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    padding: 12px 20px;
    background-color: color-mix(in srgb, var(--mateen-person-accent) 10%, var(--mud-palette-surface));
    border-top: 1px solid color-mix(in srgb, var(--mateen-person-accent) 22%, transparent);
}

/* Bottom row of the card: an optional action at each end, status chip centred. The side slots are
   a fixed 36px whether or not they hold a button, which is what keeps the chip on the centre line
   across a grid of cards where some have one action, some two and some none. */
    .mateen-person-card__footer-row {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 8px;
        width: 100%;
    }

    .mateen-person-card__footer-slot {
        flex: 0 0 36px;
        display: flex;
        align-items: center;
        justify-content: flex-start;
        min-height: 30px;
    }

    .mateen-person-card__footer-slot--end {
        justify-content: flex-end;
    }

    /* ...but a row that ends up holding NOTHING AT ALL collapses, rather than reserving those 30px
       plus the band's 10px column gap for a chip that is not coming. Two cards are in that state
       since 2026-09-27: the student GRID card (ShowStatusChip="false" - the whole-Quran bars take
       the chip's place in the band, and the user asked for no dead space left behind under them)
       and the student DETAIL card (StatusChipInline moved the chip into the email row, NameAction
       moved the assign button up beside the name).

       The test cannot be :not(:has(*)) the way __footer-extra's is: PersonCard renders both slot
       divs unconditionally, so this row ALWAYS has element children and that selector could never
       match. The question that actually distinguishes the two states is whether the row holds any
       element that is not one of those two reserved slots - the status chip is such an element
       (a direct non-slot child), and so is a FooterStart/FooterEnd button (a non-slot descendant
       inside a slot). So every card that still puts something in this row keeps it, untouched, at
       the same min-height it always had. */
    .mateen-person-card__footer-row:not(:has(*:not(.mateen-person-card__footer-slot))) {
        display: none;
    }

/* Where per-page footer content goes (today: StudentDetails.razor's Hifd/Morajaah bars; later: the
   student progress treatment the user will specify). Capped and centred for the same reason the
   body is - this card is full page width on the detail pages. */
.mateen-person-card__footer-extra {
    width: 100%;
    max-width: 720px;
    margin-inline: auto;
}

    /* A <Footer> fragment whose whole content is conditional - StudentSummaryCard's is: the bars
       only on the detail page, the Assign button only for an admin - still renders this wrapper,
       and still renders the whitespace text nodes around those @if blocks, so :empty never matches
       it. :not(:has(*)) does: it asks whether the wrapper has any ELEMENT child. Collapsing it
       matters because the footer is a gap:10px flex column - an "empty" wrapper is still a flex
       item, and it was opening a 10px band of dead space under the status chip on every grid card
       (which, since the cards are all forced to the same height, pushed the chip up off the
       footer's centre relative to its neighbours). */
    .mateen-person-card__footer-extra:not(:has(*)) {
        display: none;
    }

/* -----------------------------------------------------------------------------------
   HORIZONTAL VARIANT (.mateen-person-card--horizontal <- PersonCard's Horizontal="true")

   The educator details page (Teachers/TeacherDetails.razor) opens with the SAME card as the
   Educators grid it was reached from, laid out as one row across the full page width - so the
   drill-in reads as "the card you clicked, opened", not as an unrelated header block. Three
   columns:

       avatar | name + email (muted) + role + detail line | groups

   The 7px accent bar moves to the LEFT edge here (the ::after override below): along the bottom of
   a 1400px band it is a stripe across the whole page, while on the left edge it reads as the
   role-coloured spine of a wide card. Every rule in this sub-block is scoped to --horizontal, so
   the vertical grid cards (Users, Educators, the four student grids, StudentDetails' full-width
   card) are untouched.

   COLLAPSE ORDER
   -----------------------------------------------------------------------------------
   Three columns cannot hold at phone width, so they give way one at a time:
     >= 960px     three columns, the groups column separated by a vertical hairline;
     600-959px    the groups column drops to a full-width row beneath the avatar + identity pair
                  (the two that must stay side by side for the card to still read as a person) and
                  its hairline becomes a top rule;
     < 600px      one column - avatar, identity, groups - still left-aligned rather than re-centred
                  (this is the horizontal card at a narrow width, not the vertical card), the avatar
                  back down to its 72px grid size, and the footer's actions wrapping under the
                  status chip.

   LONG VALUES
   -----------------------------------------------------------------------------------
   This card is alone on the page with no equal-height neighbours, so unlike the grid cards the name
   is allowed to wrap onto a second line instead of being ellipsised, and a long email wraps on any
   character (overflow-wrap:anywhere - an address offers no spaces to break at). Both text columns
   are minmax(0, ...) grid tracks, so neither can push the other past the card edge, and a long
   group name wraps inside its chip rather than overflowing the column.
   ----------------------------------------------------------------------------------- */
.mateen-person-card--horizontal {
    /* Implies --full: this variant only exists at full page width. */
    max-width: none;
}

/* Overrides the bottom-bar ::after above - same specificity, later in the file - into a full-height
   bar on the left edge. It sets no `content`, so only the accented variants (which do) emit the
   pseudo-element at all; a Neutral horizontal card still has no bar, exactly as vertically. */
.mateen-person-card--horizontal::after {
    top: 0;
    right: auto;
    bottom: 0;
    width: 7px;
    height: auto;
}

.mateen-person-card__body--horizontal {
    display: grid;
    /* avatar | identity | stats | groups - the stats sit in their own middle column, not under
       the identity text. */
    grid-template-columns: auto minmax(0, 1fr) auto minmax(0, 17rem);
    align-items: start;
    gap: 16px 28px;
    text-align: left;
    max-width: none;
    margin-inline: 0;
    /* The left padding carries the 7px accent bar plus the body's normal 24px, so the bar never
       touches the avatar's outer ring. */
    padding: 24px 28px 20px 31px;
}

/* Bigger than the 72px grid avatar - at full page width the 72px circle looks lost next to an
   h4-sized name. The ring construction (double box-shadow) scales with it for free. */
.mateen-person-card--horizontal .mateen-person-card__avatar {
    width: 104px;
    height: 104px;
}

/* Column 1 of the horizontal body: the photo with the student's level chip under it (PersonCard's
   LevelChip). A wrapper rather than two grid siblings, so the identity column stays in grid column
   2 and every per-variant grid-template in this file keeps working as written. Capped to the
   avatar's own footprint (104px + its 2x5px ring margin) so a chip never widens the column past
   the photo - "Intermediate" at the chip's 12px is the widest of the three and still fits. */
.mateen-person-card__avatar-col {
    display: flex;
    flex-direction: column;
    align-items: center;
    max-width: 114px;
}

/* The level chip itself. Geometry and type are MudChip's own Size.Small; this rule only replaces
   its 4px all-round margin with the gap under the photo (two classes, and later in the cascade
   than MudBlazor.min.css, so it wins without !important).

   COLOUR - the user's three names mapped onto real palette roles, never literals, so all three
   flip with the mode like everything else in this file:
     Advanced     -> Primary itself          #471671 / #BB86EA
     Intermediate -> TertiaryDarken, deepened (see below)
     Beginner     -> PrimaryLighten          #8854B6 / #CFA6F2
   The LABEL is not set here at all: the component passes Color.Primary, so MudBlazor paints the
   text with --mud-palette-primary-text (#F6F1FB light / #1A1330 dark), which is the theme's own
   "text on a brand fill" token and already flips. Measured on the rendered chips, light / dark:
   Advanced 11.65:1 / 6.54:1, Intermediate 5.25:1 / 4.99:1, Beginner 4.73:1 / 8.81:1 - all six
   clear AA for the chip's 12px label. */
.mateen-person-card__level.mud-chip {
    margin: 12px 0 0;
    font-weight: 600;
}

    /* "Dark blue": TertiaryDarken (#4A5DED / #5C6EF5) is the one genuinely blue role in this
       palette that is dark in light mode AND still separates from the navy card in dark mode - the
       dark card colour #121A3F itself would be an invisible chip there, and plain Tertiary
       (#6879F9) is too light to hold white text (3.69:1).
       The 12% of text-primary is what makes the dark side legal: it deepens the fill in light mode
       and lifts it in dark - exactly the direction each mode's contrast text needs - because the
       raw dark token reaches only 4.20:1 under a 12px label, just short of AA. Both halves of the
       mix are palette tokens, so a palette change still carries through. */
.mud-chip-filled.mateen-person-card__level--beginner {
    background-color: color-mix(in srgb, var(--mud-palette-tertiary-darken) 88%, var(--mud-palette-text-primary));
}

    /* "Light purple": the primary's own Lighten role - the same brand violet as Advanced, one step
       lighter, which is what makes the three chips read as one scale (deeper = further along)
       rather than three unrelated colours. */
.mud-chip-filled.mateen-person-card__level--intermediate {
    background-color: var(--mud-palette-primary-lighten);
}

/* Column 2: everything that identifies the person, stacked and left-aligned. */
.mateen-person-card__identity {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    min-width: 0;
}

/* Name row: the name and, only when NameAction is set (the student detail card's "Move to group"
   button), an action pinned to the row's right edge - space-between is what puts the button at the
   COLUMN's right edge rather than hugging the name text, and center rather than baseline is what
   lines the small round icon button up with the visual middle of the name line, not its text
   baseline. Every other horizontal card renders no NameAction, so this row is indistinguishable
   from a plain name line - see .mateen-person-card__name below, unchanged. */
.mateen-person-card__name-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    width: 100%;
}

    .mateen-person-card__name-row .mateen-person-card__name {
        /* Without this the name, as a flex item, refuses to shrink below its own content width and
           pushes NameAction past the column's edge instead of wrapping/ellipsising against it. */
        min-width: 0;
    }

    /* The name plus its note icon, as ONE flex item of the row - otherwise the row's
       space-between would spread name / icon / NameAction evenly and leave the icon floating in
       the middle instead of sitting against the name. This is the item that absorbs the row's
       spare width and the one that shrinks, so a long name wraps (the horizontal card's name has
       white-space:normal, see below) against a fixed icon rather than shoving it out of view. */
    .mateen-person-card__name-group {
        display: flex;
        align-items: center;
        gap: 6px;
        min-width: 0;
        flex: 1 1 auto;
    }

    /* Never shrinks, never wraps away from the name, and sits on the name's visual middle. The
       tooltip itself is MudBlazor's, so only the trigger needs styling here.

       Since 2026-10-04 the trigger is a FILLED Info-coloured disc rather than a bare glyph (user
       request: "the info button should be with info background color"), i.e. the Info palette role
       as the background and that role's own contrast text as the glyph - never a hardcoded white,
       which is how the drawer header went white-on-white earlier today.

       It costs the name row nothing: 22px is well inside the 22px/1.4 = ~31px line box the
       Tajawal-Title-Bold name already gives this row, so the row's height is unchanged and the
       glyph only gains 2px of width (20px -> 22px) over the bare icon it replaces. The glyph is
       stepped down to 1rem so the disc reads as a badge around it instead of clipping it.

       Measured on the rendered disc: white glyph on #2196F3 = 3.12:1, over the 3:1 WCAG asks of a
       graphical object; the disc itself also sits at 3.12:1 against the white card. Dark mode needs
       one step down the same role - see the style query below. */
    .mateen-person-card__note-icon {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        flex: 0 0 auto;
        width: 22px;
        height: 22px;
        border-radius: 50%;
        background-color: var(--mud-palette-info);
        color: var(--mud-palette-info-text);
        cursor: help;
        transition: background-color .15s ease-in-out;
    }

        .mateen-person-card__note-icon:hover {
            background-color: var(--mud-palette-info-darken);
        }

        /* MudTooltip wraps the glyph in its own inline-flex root; it has to fill the disc so the
           whole disc is the hover target, not just the 16px glyph inside it. */
        .mateen-person-card__note-icon .mud-tooltip-root {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            width: 100%;
            height: 100%;
        }

        .mateen-person-card__note-icon .mud-icon-root {
            font-size: 1rem;
        }

/* MudBlazor's stock DARK Info (#3299FF) is BRIGHTER than its light one (#2196F3), so the white
   --mud-palette-info-text glyph only reaches 2.94:1 on it - under the 3:1 a graphical object needs,
   and exactly the "a colour that doesn't flip with the theme goes unreadable" trap this card's
   drawer header fell into earlier today. One step down the same role (info-darken, #0A85FF) takes
   it to 3.61:1 and still reads as the Info blue; light mode is left on --mud-palette-info itself.
   The mode signal is MudBlazor's own reactive --mud-native-html-color-scheme custom property
   queried as a style container - 9.10.0 emits no dark-mode class - the same mechanism the landing
   and login blocks in this file already use. Hover keeps its "one step the other way" feedback. */
@container style(--mud-native-html-color-scheme: dark) {
    .mateen-person-card__note-icon {
        background-color: var(--mud-palette-info-darken);
    }

        .mateen-person-card__note-icon:hover {
            background-color: var(--mud-palette-info);
        }
}

    .mateen-person-card__name-action {
        flex: 0 0 auto;
    }

/* The detail-page card's name is a page-level heading, not a grid-card label, so it keeps its own
   larger size on top of .Tajawal-Title-Bold's family/weight (2026-10-03). This selector is two
   classes to the typography class's one, so this font-size wins on purpose - the Tajawal family and
   the 700 weight still come through unchanged (the class and the old base rule both said 700, so
   nothing about the weight changed here), only the SIZE is variant-scaled. */
.mateen-person-card--horizontal .mateen-person-card__name {
    margin-top: 0;
    font-size: 1.375rem;
    /* Wraps here instead of the grid cards' hard one-line ellipsis - see LONG VALUES above. */
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    overflow-wrap: anywhere;
}

/* Email row: the email and, only when StatusChipInline is set (the student detail card), the
   Active/Inactive chip beside it - gap only matters once there are two children, so every other
   horizontal card's single email span sits exactly where the old .mateen-person-card__email div
   used to, and the margin-top that used to live on that div now lives on the row instead. */
.mateen-person-card__email-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 2px;
    max-width: 100%;
    min-width: 0;
}

/* The email, muted - the horizontal layout's own line, between the name and the role. */
.mateen-person-card__email {
    min-width: 0;
    max-width: 100%;
    font-size: .8125rem;
    line-height: 1.5;
    color: var(--mud-palette-text-secondary);
    overflow-wrap: anywhere;
}

.mateen-person-card--horizontal .mateen-person-card__subtitle {
    justify-content: flex-start;
    margin-top: 6px;
}

/* The host page's detail line (gender · age · phone · WhatsApp on TeacherDetails). */
.mateen-person-card__details {
    margin-top: 10px;
    max-width: 100%;
    font-size: .8125rem;
    line-height: 1.6;
    color: var(--mud-palette-text-secondary);
    overflow-wrap: anywhere;
}
/* Column 3: the groups. The hairline is what makes it read as a separate column rather than as
   text that ran on from the identity column. */
.mateen-person-card__aside {
    align-self: center;
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-width: 0;
    margin-top: 18px;
    padding-left: 24px;
    border-left: 1px solid var(--mud-palette-lines-default);
}

.mateen-person-card__aside-label {
    font-size: .75rem;
    line-height: 1.2;
    color: var(--mud-palette-text-secondary);
}

.mateen-person-card__aside-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}

    /* A chip is normally a fixed-height nowrap pill, which a long group name would push out of the
       column; capped to the column width with a wrapping label and an auto height instead, so the
       chip grows downwards and its close "x" stays reachable. */
    .mateen-person-card__aside-chips .mud-chip {
        max-width: 100%;
        height: auto;
        min-height: 26px;
        margin: 0;
    }

        .mateen-person-card__aside-chips .mud-chip .mud-chip-content {
            min-width: 0;
            white-space: normal;
            overflow-wrap: anywhere;
        }

/* Footer band, full page width: status chip at the left, the host page's actions (which are text
   buttons here, not the grid cards' icon buttons) grouped at the right. */
.mateen-person-card--horizontal .mateen-person-card__footer {
    align-items: stretch;
    padding: 12px 20px 12px 31px;
}

.mateen-person-card--horizontal .mateen-person-card__footer-row {
    flex-wrap: wrap;
    justify-content: flex-start;
    gap: 12px;
}

/* Content-sized, not the grid cards' fixed 36px icon slots. */
.mateen-person-card--horizontal .mateen-person-card__footer-slot {
    flex: 0 0 auto;
    min-height: 0;
}

    /* An unused slot would still be a flex item and still take a 12px gap, pushing the status chip
       off the card's left edge - same :not(:has(*)) test as .mateen-person-card__footer-extra
       above, and for the same reason (the whitespace text nodes around a slot's @if mean :empty
       never matches). */
    .mateen-person-card--horizontal .mateen-person-card__footer-slot:not(:has(*)) {
        display: none;
    }

.mateen-person-card--horizontal .mateen-person-card__footer-slot--end {
    margin-left: auto;
}

/* Tablet: groups drop under the avatar + identity pair, hairline becomes a top rule. */
@media (max-width: 1200px) {
    .mateen-person-card__body--horizontal {
        grid-template-columns: auto minmax(0, 1fr);
    }

    .mateen-person-card--horizontal .mateen-person-card__aside {
        grid-column: 1 / -1;
        margin-top: 0;
        padding-left: 0;
        padding-top: 14px;
        border-left: 0;
        border-top: 1px solid var(--mud-palette-lines-default);
    }
}

/* Phone: one column, smaller avatar and name, footer actions on their own line. */
@media (max-width: 599.98px) {
    .mateen-person-card__body--horizontal {
        grid-template-columns: minmax(0, 1fr);
        gap: 12px;
        padding: 20px 16px 16px 23px;
    }

    .mateen-person-card--horizontal .mateen-person-card__avatar {
        width: 72px;
        height: 72px;
    }

    .mateen-person-card--horizontal .mateen-person-card__name {
        font-size: 1.1875rem;
    }

    .mateen-person-card--horizontal .mateen-person-card__footer {
        padding-left: 23px;
    }

    /* The actions wrap onto their own line under the status chip, left-aligned with everything
       else on the card rather than kept hard right against the edge. */
    .mateen-person-card--horizontal .mateen-person-card__footer-slot--end {
        justify-content: flex-start;
        margin-left: 0;
        width: 100%;
    }
}

/* ===================================================================================
   Form dialogs presented as right-hand drawers (Components/Dialogs/UserFormDialog.razor,
   TicketFormDialog.razor, MistakeManagerDialog.razor)

   These forms slide in from the right edge instead of appearing as centred modals. They are still
   MudDialogs, not MudDrawers: MudDrawer belongs to MudLayout and cannot be opened per-page with
   parameters and an awaited result, while DialogService can - so each form keeps its awaited
   DialogResult and its cascaded IMudDialogInstance (which the shared DialogTitleBar cancels
   through), and only its position and chrome change here.

   Every drawer gets .mateen-drawer-dialog; the two ticket/mistake forms add
   .mateen-drawer-dialog--wide on top of it because their field rows need more width than the user
   form's - see that rule below.

   MudBlazor's own DialogPosition.CenterRight leaves a 32px gutter against the viewport
   (.mud-dialog-centerright { padding-right: 32px }); a drawer has to sit flush, so that gutter is
   removed for these dialogs only, matched on the container that actually holds them rather than
   globally.
   =================================================================================== */
.mud-dialog-container.mud-dialog-centerright:has(.mateen-drawer-dialog) {
    padding-right: 0;
    align-items: stretch;
}

.mateen-drawer-dialog {
    /* dvh, not vh: on mobile browsers vh includes the collapsing URL bar, which would push the
       action buttons below the fold. */
    height: 100dvh;
    max-height: 100dvh;
    margin: 0;
    border-radius: 0;
    display: flex;
    flex-direction: column;
    animation: mateen-drawer-slide-in 220ms cubic-bezier(.2, .8, .2, 1);
}

    /* These forms are long (the user form is 8+ fields plus a photo square; the ticket form can grow
       a whole extra Sura row group, each with its own staged-mistake list, per "Add another Sura"),
       so the body scrolls while the title bar and the action buttons stay put - the main practical
       reason a drawer beats a tall modal here. */
    .mateen-drawer-dialog .mud-dialog-content {
        flex: 1 1 auto;
        overflow-y: auto;
    }

    .mateen-drawer-dialog .mud-dialog-actions {
        flex: 0 0 auto;
        border-top: 1px solid var(--mud-palette-lines-default);
    }

/* WIDE VARIANT - TicketFormDialog and MistakeManagerDialog (Components/Dialogs).
   Both of those forms carry a 4-wide field row (Sura/From ayah/To ayah/Grade, and the mistake
   Ayah/Type/How many/Note row) that needs roughly 630px of content width before it wraps, so they
   open at MaxWidth.Medium (960px) instead of the 600px UserFormDialog uses - see
   TicketFormDialog.DrawerOptions for the full reasoning. 960px is exactly the width the ticket form
   already had as a centred modal, so no field row lays out differently than before.

   The clamp is what this class adds on top of MudBlazor's own .mud-dialog-width-md: at 960px a
   right-anchored panel would cover practically the whole viewport on a small laptop or a landscape
   tablet (1000-1100px), at which point it stops reading as a drawer over the records table and just
   looks like a broken full-screen page. min() keeps the full 960px wherever there is room for it and
   otherwise leaves a sliver of the page visible behind the panel. Deliberately placed BEFORE the
   phone media query below: that rule and this one have the same specificity, so source order is what
   lets the phone rule still take the panel full-bleed under 600px. */
.mateen-drawer-dialog--wide {
    max-width: min(960px, 82vw);
}

@keyframes mateen-drawer-slide-in {
    from {
        transform: translateX(100%);
    }
}

/* Respect a reduced-motion preference: the panel still appears, it just doesn't travel. */
@media (prefers-reduced-motion: reduce) {
    .mateen-drawer-dialog {
        animation: none;
    }
}

/* Below Sm there is no room for a side panel beside the page - take the full width. */
@media (max-width: 599.95px) {
    .mateen-drawer-dialog {
        max-width: 100%;
        width: 100%;
    }
}

/* user updates*/
/*.mud-grid-item {
    margin: 0 auto !important;
}*/
/* ===================================================================================
   GROUPS LIST CARD (Components/Pages/Groups/Groups.razor)
   ===================================================================================
   Implements Design/groupsview.jfif: a two-per-row grid (MudItem xs=12 sm=6) of cards, each one a
   left-hand body - group name + muted detail note, an "Educator:" meta line, a "Students" label,
   then the student names as pale pill chips (or "0 Students Enrolled") - beside a narrow right-hand
   ACTIONS RAIL holding the edit and delete buttons stacked vertically behind a vertical hairline.

   WHAT THE REFERENCE ASKS FOR THAT IS NOT BUILT HERE
   -----------------------------------------------------------------------------------
   The mock's meta row leads with a "Category: Masjid" chip. There is no Category on
   Models/Groups/GroupSummaryDto, on the group model, or on the API, so that chip is simply absent
   rather than hardcoded; the meta row is a plain flex row that will take it as a first child the
   day a real Category field exists. The mock also says "Teacher:"; this app relabels that role as
   "Educator" everywhere in user-facing text (RoleDisplay.GetDisplayName), so the label here stays
   "Educator:" deliberately.

   THE ACCENT BAR IS KEPT
   -----------------------------------------------------------------------------------
   The card still carries .mateen-accent-card, so the 7px brand-violet pseudo-element bar is drawn
   across its top edge exactly as on every other content card in the app (the mock has no such bar
   - keeping it was an explicit instruction). Nothing here overlaps it: this card replaces
   MudCardContent with its own flex layout, whose padding-top clears the 7px band, and the actions
   rail's hairline runs under it (the bar is z-index 1 and pointer-events:none).

   WHY NOT MudCardContent / MudStack / MudChip
   -----------------------------------------------------------------------------------
   Three requirements pull against MudBlazor's own containers here:
     - the rail's hairline has to run the FULL height of the card, which means the body and the
       rail must be siblings in one stretch-aligned flex row, not content inside one padded box;
     - cards in the same row must be equal height however many chips they carry (see below);
     - the student pills are decorative labels inside an already-clickable card - a MudChip brings
       its own hover/ripple/close affordances and a much heavier override surface than a span.
   The card itself is still a MudCard, so elevation/outline/radius stay the app's.

   EQUAL HEIGHTS IN A GRID
   -----------------------------------------------------------------------------------
   MudGrid rows are flex with the default align-items:stretch, so the chain height:100% on the card
   -> flex:1 on the layout row -> stretch on both the body and the rail is enough: a card listing
   twelve students sets the row height and the empty card beside it is drawn to match, with the
   spare space falling below its content (matching the mock, where the "Sat" card's chip area is
   simply blank). Long group/educator names wrap with overflow-wrap:anywhere instead of being
   ellipsised - a group's name is the only thing identifying it in this list - and min-width:0 on
   the body keeps a long unbroken name from pushing the rail off the card.

   COLOURS
   -----------------------------------------------------------------------------------
   Everything is derived from the palette (Theme/MateenTheme.cs), so both modes track the theme:
     chip fill   -> the primary violet mixed into the card surface, 20% light / 24% dark. On white
                    that lands on ~#DAD2E4, within a shade of the mock's own #DCD2EB pill; the dark
                    value is the same recipe against the navy card instead.
     chip text   -> the primary itself in light mode (#471671, ~9:1 on that fill) and the lighter
                    primary tint in dark mode (#CFA6F2 - the deep violet is unreadable on a dark
                    pill).
     detail note -> var(--mud-palette-primary-lighten) in both modes (#8854B6 light / #CFA6F2
                    dark), which is the mock's muted purple "sunday" note; 5.26:1 on the light card.
   The per-mode pair uses the same light-dark() mechanism the person-card block above documents
   (this app has no dark-mode wrapper class - the mode signal comes from MudBlazor's reactive
   --mud-native-html-color-scheme). =================================================== */
.mateen-group-card {
    /* The mode signal the light-dark() pairs below resolve against. */
    color-scheme: var(--mud-native-html-color-scheme);
    --mateen-group-chip-bg: light-dark(color-mix(in srgb, var(--mud-palette-primary) 20%, var(--mud-palette-surface)), color-mix(in srgb, var(--mud-palette-primary) 24%, var(--mud-palette-surface)));
    --mateen-group-chip-text: light-dark(var(--mud-palette-primary), var(--mud-palette-primary-lighten));
    /* Fills the grid cell so neighbouring cards match height. */
    height: 100%;
    display: flex;
    flex-direction: column;
    transition: box-shadow 200ms cubic-bezier(.4, 0, .2, 1), border-color 200ms cubic-bezier(.4, 0, .2, 1);
}

    /* Only the list page's cards navigate; the detail page's single card is static, so it must not
       offer a hover lift (GroupCard adds --clickable only when OnCardClick is bound). */
    .mateen-group-card--clickable:hover {
        border-color: color-mix(in srgb, var(--mud-palette-primary) 45%, var(--mud-palette-lines-default));
        box-shadow: 0 6px 18px light-dark(rgba(42, 39, 64, .12), rgba(0, 0, 0, .45));
    }

@media (prefers-reduced-motion: reduce) {
    .mateen-group-card {
        transition: none;
    }
}

/* Body + rail, side by side, both stretched to the card's full height. */
.mateen-group-card__layout {
    flex: 1 1 auto;
    display: flex;
    align-items: stretch;
}

.mateen-group-card__body {
    flex: 1 1 auto;
    /* Without this a single long unbroken name would widen the body past the card and shove the
       actions rail out of view. */
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
    /* Top padding clears .mateen-accent-card::before's 7px band. */
    padding: 18px 20px;
}

/* Name + the muted detail note beside it, baseline-aligned so the smaller note sits on the name's
   own baseline the way the mock draws it; wraps to a second line on a narrow card. */
.mateen-group-card__heading {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 10px;
}

.mateen-group-card__name {
    font-size: 1.125rem;
    font-weight: 700;
    line-height: 1.35;
    color: var(--mud-palette-text-primary);
    overflow-wrap: anywhere;
}

.mateen-group-card__details {
    font-size: .875rem;
    font-weight: 500;
    color: var(--mud-palette-primary-lighten);
    overflow-wrap: anywhere;
}

/* The "Educator: <name>" line plus the assign/unassign icon button. A Category chip would be the
   first child of this row if the DTO ever gains one. */
.mateen-group-card__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 2px 6px;
    font-size: .875rem;
    line-height: 1.4;
    /* The whole row stops propagation (it holds a real action), so it must not read as clickable
       card surface. */
    cursor: default;
}

.mateen-group-card__meta-label {
    font-weight: 600;
    color: var(--mud-palette-text-primary);
}

.mateen-group-card__meta-value {
    color: var(--mud-palette-text-primary);
    overflow-wrap: anywhere;
}

    /* The assign icon button sits tight against the educator name, as the mock's trailing person
       glyph does - MudIconButton's own padding would otherwise float it away from the text. */
    .mateen-group-card__meta .mud-icon-button {
        padding: 2px;
        margin-inline-start: 2px;
    }

.mateen-group-card__students {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-top: 2px;
}

.mateen-group-card__section-label {
    font-size: .875rem;
    font-weight: 600;
    color: var(--mud-palette-text-primary);
}

.mateen-group-card__chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.mateen-group-card__student {
    display: inline-flex;
    align-items: center;
    padding: 3px 12px;
    border-radius: 999px;
    font-size: .75rem;
    line-height: 1.5;
    background-color: var(--mateen-group-chip-bg);
    color: var(--mateen-group-chip-text);
    overflow-wrap: anywhere;
}

.mateen-group-card__empty {
    font-size: .875rem;
    color: var(--mud-palette-text-secondary);
}

/* The right-hand rail: a vertical hairline, then edit over delete. Fixed width so every card's
   rail lines up down the page regardless of its body content. */
.mateen-group-card__actions {
    flex: 0 0 auto;
    width: 56px;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    padding: 12px 6px;
    border-inline-start: 1px solid var(--mud-palette-lines-default);
    /* The rail holds real actions, not navigation. */
    cursor: default;
}

    .mateen-group-card__actions > * {
        display: flex;
        justify-content: center;
    }

        /* The short separator the mock draws between the two buttons. Applied to the second child
           - a MudTooltip wrapper around the delete button, not the button itself - so it works
           whether that child is a tooltip root or a bare button. */
        .mateen-group-card__actions > * + * {
            margin-top: 8px;
            padding-top: 8px;
            border-top: 1px solid var(--mud-palette-lines-default);
        }

/* Phone: the card is full width, so the rail is narrowed a touch to give the chips more room. */
@media (max-width: 599.95px) {
    .mateen-group-card__body {
        padding: 16px 14px;
    }

    .mateen-group-card__actions {
        width: 48px;
        padding: 10px 4px;
    }
}


/*user override*/
.mud-icon-button-size-small {
    padding: 10px !important;
}
/* --- Group card, detail-page variant ------------------------------------------------------
   GroupDetail.razor renders the SAME Components/Shared/GroupCard.razor at the top of the page,
   one card across the full width instead of one per half-width grid cell, so it reads as the
   page's header: roomier padding and a page-title-sized name. It also fills the card's Footer
   slot with the weekly study days, which GroupCard puts under a MudDivider spanning the card's
   whole width (body AND actions rail). */
.mateen-group-card--full .mateen-group-card__body {
    /* Roomier than the list card, and the left side still carries the 7px bar. */
    padding: 22px 26px 22px 33px;
    gap: 12px;
}

.mateen-group-card--full .mateen-group-card__name {
    font-size: 1.375rem;
}

.mateen-group-card--full .mateen-group-card__actions {
    width: 64px;
}

/* MudDivider's own margin is 0; the gap is owned by the footer's padding so the rule sits flush
   against the card's side edges the way the actions rail's hairline does. */
.mateen-group-card__footer {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 16px 26px 20px;
}

/* --- Shared pill ------------------------------------------------------------------------------
   The bordered, light-filled 999px pill first drawn for the group detail page's "Weekly study
   days" items and since asked for on the student card's group line too (2026-09-26). Factored out
   of .mateen-group-card__day rather than copied: both now wear .mateen-pill for the geometry and
   the fill, and keep only their own colour/typography rules below.

   The fill is a light-dark() pair, not a color-mix against the accent, so the pill stays neutral -
   a schedule (or a group name) is never mistaken for a status colour. Anything using it must sit
   inside an element that resolves color-scheme (every card in this app does, via
   --mud-native-html-color-scheme). */
.mateen-pill {
    box-sizing: border-box;
    padding: 5px 14px;
    border-radius: 999px;
    background-color: light-dark(color-mix(in srgb, var(--mud-palette-text-primary) 7%, var(--mud-palette-surface)), color-mix(in srgb, #FFFFFF 9%, var(--mud-palette-surface)));
    border: 1px solid var(--mud-palette-lines-default);
}

/* One pill per weekly slot: the day in the card's text colour, the time muted after it. Geometry
   and fill come from .mateen-pill, which the span also carries. */
.mateen-group-card__days {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.mateen-group-card__day {
    display: inline-flex;
    align-items: baseline;
    gap: 8px;
    font-size: .8125rem;
    line-height: 1.5;
}

.mateen-group-card__day-name {
    font-weight: 600;
    color: var(--mud-palette-text-primary);
}

.mateen-group-card__day-time {
    color: var(--mud-palette-text-secondary);
    font-variant-numeric: tabular-nums;
}

/* Phone: the full-width variant's roomier padding is desktop-only - these selectors are more
   specific than the phone rule in the block above, so they have to restate it. */
@media (max-width: 599.95px) {
    .mateen-group-card--full .mateen-group-card__body {
        padding: 16px 14px;
        gap: 10px;
    }

    .mateen-group-card--full .mateen-group-card__name {
        font-size: 1.125rem;
    }

    .mateen-group-card--full .mateen-group-card__actions {
        width: 48px;
    }

    .mateen-group-card__footer {
        padding: 14px 14px 16px;
    }
}

/* Safety net: GroupCard only renders the rail when an Actions fragment was supplied, but both
   callers' fragments are themselves wrapped in an `@if (_isAdmin)`, so a non-admin would
   otherwise get an empty 56px column with a hairline down it. (Blazor trims whitespace-only
   nodes, so the div really is :empty in that case.) */
.mateen-group-card__actions:empty {
    display: none;
}

/* ===================================================================================
   SECTION SEPARATOR (.mateen-section-separator)
   ===================================================================================
   The "attractive separator" that introduces GroupDetail.razor's student roster: a label, an
   optional count bubble, a brand-violet rule that fades out to nothing across the remaining
   width, and an optional action (the Add students button) parked at the end of it.

   The fade is a linear-gradient on a 2px block rather than a border, so it can go from the
   primary at full strength next to the label to fully transparent at the far edge - a flat
   border-bottom across a wide page reads as a table rule, which is what this is trying not to
   look like. Generic on purpose (nothing group-specific in here): any page that needs to break a
   long scroll into named sections can use it. =================================== */
.mateen-section-separator {
    display: flex;
    align-items: center;
    gap: 12px;
}

.mateen-section-separator__label {
    font-size: 1.125rem;
    font-weight: 600;
    color: var(--mud-palette-text-primary);
    white-space: nowrap;
}

/* The count bubble - deliberately quiet: the same violet-tinted pill as the student chips, so the
   two read as one family. */
.mateen-section-separator__count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 24px;
    height: 24px;
    padding: 0 8px;
    border-radius: 999px;
    font-size: .75rem;
    font-weight: 600;
    color: light-dark(var(--mud-palette-primary), var(--mud-palette-primary-lighten));
    background-color: light-dark(color-mix(in srgb, var(--mud-palette-primary) 20%, var(--mud-palette-surface)), color-mix(in srgb, var(--mud-palette-primary) 24%, var(--mud-palette-surface)));
}

.mateen-section-separator__rule {
    flex: 1 1 auto;
    height: 2px;
    border-radius: 2px;
    background: linear-gradient(to right, color-mix(in srgb, var(--mud-palette-primary) 55%, transparent), transparent);
}

/* ===================================================================================
   GROUP MEMBER CARD (Components/Shared/GroupMemberCard.razor)
   ===================================================================================
   GroupDetail.razor's student roster. Visually a smaller sibling of the person cards above -
   same rounded surface, soft shadow, centred column and the student-teal accent - but built on
   UserSummaryDto, which is only Id/FirstName/LastName/Email. See the component's own header
   comment for why it is not a PersonCard (short version: no photo, no date of birth, and an
   IsActive that would be invented rather than empty).

   AVATAR
   -----------------------------------------------------------------------------------
   Initials on a teal-tinted disc with the same ring the student person-cards wear, rather than
   PhotoUrlHelper.PlaceholderUrl - the placeholder is one identical silhouette, so a grid of it
   carries no information at all, while initials at least distinguish one student from the next.

   REMOVE (X)
   -----------------------------------------------------------------------------------
   Absolutely positioned in the top-right corner, as asked. It is only rendered when an OnRemove
   callback was supplied (Admin only), and it calls GroupDetail's existing RemoveStudentAsync,
   which keeps its own confirmation dialog. The card itself is not clickable, so there is no
   navigation for the X to propagate into.

   EQUAL HEIGHTS
   -----------------------------------------------------------------------------------
   height:100% + the same 220px cap/centring the person cards use: the grid cell is wider than the
   card at most breakpoints, and a two-line name next to a one-line name must not produce two
   different card heights in the same row. Names get one-line ellipsis (with a title tooltip);
   emails are allowed to wrap to two lines and then ellipsise, since an email is long and
   truncating it at one line hides the part that distinguishes two students at the same domain.
   =================================================================================== */
.mateen-member-card {
    color-scheme: var(--mud-native-html-color-scheme);
    --mateen-member-accent: light-dark(#0F6E8C, #4FB8D4);
    position: relative;
    height: 100%;
    max-width: 220px;
    margin-inline: auto;
    border-radius: 18px;
    border: 1px solid var(--mud-palette-lines-default);
    box-shadow: 0 10px 28px light-dark(rgba(42, 39, 64, .10), rgba(0, 0, 0, .45));
    overflow: hidden;
}

.mateen-member-card__remove {
    position: absolute;
    top: 6px;
    right: 6px;
    z-index: 1;
}

.mateen-member-card__body {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 6px;
    /* Extra top padding so a long name never collides with the X in the corner. */
    padding: 20px 14px 18px;
}

.mateen-member-card__avatar {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    border-radius: 50%;
    font-size: 1.125rem;
    font-weight: 600;
    letter-spacing: .5px;
    color: var(--mateen-member-accent);
    background-color: color-mix(in srgb, var(--mateen-member-accent) 16%, var(--mud-palette-surface));
    box-shadow: 0 0 0 2px var(--mud-palette-surface), 0 0 0 4px color-mix(in srgb, var(--mateen-member-accent) 55%, transparent);
    /* Clears the ring's 4px so it is not clipped by the card's own overflow:hidden. */
    margin: 4px 0 2px;
}

.mateen-member-card__name {
    width: 100%;
    font-size: .9375rem;
    font-weight: 600;
    line-height: 1.3;
    color: var(--mud-palette-text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.mateen-member-card__email {
    width: 100%;
    font-size: .75rem;
    line-height: 1.4;
    color: var(--mud-palette-text-secondary);
    /* Two lines, then ellipsis - an email is the only other identifying field on this card. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    overflow-wrap: anywhere;
}

/* ===================================================================================
   Card action buttons (Components/Pages/Teachers/TeacherDetails.razor footer)

   Icon-only actions that name themselves on hover via MudTooltip, rather than carrying a visible
   label. They get a soft tinted disc instead of MudBlazor's outlined-button border so they read as
   a pair of quiet affordances sitting in the card's footer band, not as two competing buttons.

   Colour comes from currentColor, which each --primary / --secondary modifier sets: one property
   drives the glyph, the disc and the hover ring together, so a new variant is one line.

   The tooltip is the ONLY place the label appears, so every button using this class must also
   carry an aria-label with the same words - an icon-only button with no accessible name is
   invisible to a screen reader.
   =================================================================================== */
.mateen-card-action {
    border-radius: 50%;
    background-color: color-mix(in srgb, currentColor 12%, transparent);
    transition: background-color 160ms cubic-bezier(.4, 0, .2, 1), transform 160ms cubic-bezier(.4, 0, .2, 1), box-shadow 160ms cubic-bezier(.4, 0, .2, 1);
}

    .mateen-card-action:hover {
        background-color: color-mix(in srgb, currentColor 22%, transparent);
        transform: translateY(-2px);
        box-shadow: 0 4px 12px color-mix(in srgb, currentColor 30%, transparent);
    }

    .mateen-card-action:active {
        transform: translateY(0);
    }

    /* A visible ring for keyboard users - the hover lift alone is not a focus indicator. */
    .mateen-card-action:focus-visible {
        outline: 2px solid currentColor;
        outline-offset: 2px;
    }

.mateen-card-action--primary {
    color: var(--mud-palette-primary);
}

.mateen-card-action--secondary {
    color: var(--mud-palette-secondary);
}

@media (prefers-reduced-motion: reduce) {
    .mateen-card-action {
        transition: background-color 160ms ease;
    }

        .mateen-card-action:hover {
            transform: none;
        }
}

/* --- Group card, DETAIL page only: accent bar down the LEFT edge ------------------------------
   The detail card is one wide full-width card acting as the page header, where a 7px band across
   the whole top reads as a rule under the page title rather than as the card's own accent.

   The LIST cards keep the top bar. Moving it to their left edge was tried and reverted: at narrow
   widths the bar eats into an already-tight card and the group name is pushed into wrapping
   awkwardly. Don't re-apply this to .mateen-group-card without checking the phone layout.

   Overrides .mateen-accent-card::before rather than replacing it: same pseudo-element, repositioned. */
.mateen-group-card--full::before {
    top: 0;
    bottom: 0;
    right: auto;
    width: 7px;
    height: auto;
    border-top-right-radius: 0;
    border-bottom-left-radius: inherit;
}

/* --- Group member card: accent bar along the BOTTOM edge ---------------------------------------
   The same bar the student cards carry in the Students grid, in the same colour
   (rgb(87 167 191)) - these are student cards, so they should be recognisable as such wherever
   they appear. Deliberately NOT --mateen-member-accent: that is the deeper teal used for the
   avatar ring and sits on the card surface, whereas the bar is the lighter value the person
   cards use. See .mateen-person-card--student's own --mateen-person-bar for the pair.

   Pseudo-element, not border-bottom, for the reason the person-card block records: a real border
   would be mitred into the 18px corner radius and would add to the box, and these cards are
   height-matched in a grid. The card sets overflow:hidden, so the bar is clipped to the corners. */
.mateen-member-card::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 7px;
    background-color: rgb(87 167 191);
    pointer-events: none;
}

/* The horizontal card's stat trio as a middle column: a centred group of stats with a hairline on
   each side, so it reads as its own band between the identity and the groups rather than as a
   footnote to either. --start (left-aligned, inline under the identity) is no longer used by the
   horizontal layout; the vertical card's own .mateen-person-card__stats is untouched. */
.mateen-person-card__stats--column {
    align-self: center;
    display: flex;
    gap: 65px;
    padding: 0 26px;
    border-left: 1px solid var(--mud-palette-lines-default);
    border-right: 1px solid var(--mud-palette-lines-default);
}

/* Tablet and below the card is no longer four columns wide, so the stats drop to their own full
   width row and the side hairlines become a single top rule - same treatment the groups column
   already gets one breakpoint up. */
@media (max-width: 959.98px) {
    .mateen-person-card--horizontal .mateen-person-card__stats--column {
        grid-column: 1 / -1;
        justify-content: flex-start;
        padding: 14px 0 0;
        border-left: 0;
        border-right: 0;
        border-top: 1px solid var(--mud-palette-lines-default);
    }
}

/* ===================================================================================
   Student card additions (2026-09-26)

   Two things: the group line on every student card becomes a pill, and the student DETAIL card
   becomes the horizontal card the educator detail page already uses, with a bars row in its
   footer band and a dual progress dial as its third column.
   =================================================================================== */

/* The group line as a pill. .mateen-pill (see the group-card block above) carries the border, the
   999px radius and the neutral fill; this modifier only re-spaces the line now that it is a box
   rather than bare text - 4px under the name was fine for text, too tight for a bordered pill.
   The pill hugs its content in both layouts for free: the vertical card's body is a centred flex
   column (so the pill centres), the horizontal card's identity column is align-items:flex-start
   (so it sits left). Everything else - the accent colour, the icon, the one-line ellipsis on
   .mateen-person-card__subtitle-text, the muted "No groups" variant - is unchanged. */
.mateen-person-card__subtitle--pill {
    margin-top: 8px;
}

/* --- Footer band, lead row (PersonCard's FooterLead) ------------------------------------------
   A full-width row inside the footer band ABOVE the status-chip row - the student detail card's
   Hifd/Morajaah bars. The hairline under it is the same accent mix as the band's own top border,
   so the bars read as their own row rather than as something attached to the chip.

   Collapsed when the fragment renders no elements (a grid card, or a detail card without bars):
   :not(:has(*)) rather than :empty for the reason __footer-extra records - the whitespace text
   nodes around a slot's @if mean :empty never matches. */
.mateen-person-card__footer-lead {
    width: 100%;
}

    .mateen-person-card__footer-lead:not(:has(*)) {
        display: none;
    }

    .mateen-person-card__footer-lead:has(*) {
        padding-bottom: 10px;
        border-bottom: 1px solid color-mix(in srgb, var(--mateen-person-accent) 22%, transparent);
    }

/* --- The two whole-Quran bars, side by side --------------------------------------------------
   Full page width means the bars no longer have to stack: two equal columns, each with its own
   label/value head. A percentage reads "-" (never "0%") when the student has no plan, and its bar
   is then simply an empty track - see StudentSummaryCard's NO PLAN YET note. */
.mateen-student-progress {
    display: flex;
    flex-direction: column;
    gap: 10px;
    width: 100%;
}

.mateen-student-progress__bars {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px 32px;
}

.mateen-student-progress__bar {
    min-width: 0;
}

.mateen-student-progress__bar-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 5px;
    font-size: .75rem;
    line-height: 1.4;
    color: var(--mud-palette-text-primary);
}

.mateen-student-progress__bar-value {
    font-weight: 700;
    /* Tabular figures so the two values line up and neither jumps as it ticks over. */
    font-variant-numeric: tabular-nums;
}

/* The context line under the bars: the totals the three stat columns leave out, plus the rewayah
   name when the student has more than one. Muted, wrapping, and the two parts separated by the
   column gap rather than punctuation. */
.mateen-student-progress__caption {
    display: flex;
    flex-wrap: wrap;
    gap: 2px 16px;
    font-size: .75rem;
    line-height: 1.5;
}

/* --- Hifd completion bars on the decile scale (.mateen-progress-band) -------------------------
   Design/progress.jpg, by way of Models/Plans/ProgressBands - the same scale, out of the same
   switch, that colours the Progress tab's rings, so a bar and a ring showing one student's one
   figure cannot end up different colours. MudProgressLinear.Color only accepts theme colours, so
   the band cannot travel as a parameter: the .razor wraps the bar in a plain div carrying
   class="mateen-progress-band" plus an inline --mateen-progress-band custom property, and these two
   rules paint the bar out of it.

   WHY app.css RATHER THAN A SCOPED STYLESHEET
   These bars live in two different shared components (StudentPlansPanel, and StudentSummaryCard
   which renders the pair twice itself), so CSS isolation would mean the same override duplicated in
   two scoped files that then have to be kept identical by hand - exactly the drift the shared C#
   scale was extracted to prevent. Containment is the wrapper class's job instead: nothing is banded
   that a .razor did not explicitly opt in by wrapping it. (The ring's override stays scoped for the
   opposite reason: there it must NOT reach the same DualProgressRing used as the summary card's
   headline dial, and a class on a wrapper could not stop a global rule from finding it.)

   ONLY THE HIFD BARS WEAR THIS. The Morajaah bars, the plan card's time-elapsed bar and the
   bulk-import bar on RewayahDetails deliberately keep their theme colours - see ProgressBands for
   why each of them would be actively misleading on a completion ramp.

   SPECIFICITY, NOT !important: MudBlazor's own
   .mud-progress-linear.mud-progress-linear-color-primary:not(.mud-progress-linear-buffer)
   .mud-progress-linear-bar is four classes, so the wrapper class in front makes this five and it
   wins outright. The colour class is kept in the selector on purpose - the bars still pass
   Color="Color.Primary", which is both the fallback colour when no band is set and the guarantee
   that a Secondary (Morajaah) bar sitting inside a banded wrapper could never be repainted. */
.mateen-progress-band .mud-progress-linear.mud-progress-linear-color-primary:not(.mud-progress-linear-buffer) .mud-progress-linear-bar {
    background-color: var(--mateen-progress-band, var(--mud-palette-primary));
}

/* The track follows its own fill, the same way each ring tints its track from its arc: a bar at 15%
   must not read as an orange sliver dropped onto an unrelated violet track. MudBlazor already draws
   this track as the bar's colour at .2 opacity, so re-pointing that one colour is the whole change -
   which is also why there is no light-dark() anywhere in this block and therefore nothing for dark
   mode to get wrong: the band is a literal hex from the design and the track is derived from it.
   At a flat 0% (and with no plan at all) ProgressBands sets no property, both declarations fall back
   to the primary, and the bar is pixel-for-pixel the empty violet track it has always been - a
   student who has not started does not get a red bar. */
.mateen-progress-band .mud-progress-linear.mud-progress-linear-color-primary:not(.mud-progress-linear-buffer)::before {
    background-color: var(--mateen-progress-band, var(--mud-palette-primary));
}

/* --- Dual progress rings (Components/Shared/DualProgressRing.razor) ---------------------------
   Design/"dual progress.png": two concentric rings, the headline percentage in the middle, a dot
   legend underneath. Colours are the app's own - outer ring the primary violet (Hifd), inner ring
   the secondary purple (Morajaah) - taken straight from the palette variables, so both modes are
   handled without a second set of values. Each ring's track is its own colour mixed 16% into the
   card surface: visible enough to show the full circle at 0%, quiet enough not to compete with
   the arc. */
.mateen-dual-ring {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
}

.mateen-dual-ring__dial {
    position: relative;
    flex: 0 0 auto;
    width: 132px;
    height: 132px;
}

.mateen-dual-ring__svg {
    display: block;
    width: 100%;
    height: 100%;
}

.mateen-dual-ring__track,
.mateen-dual-ring__arc {
    fill: none;
    /* User units in the 120x120 viewBox - 11 is ~12px at the 132px rendered size. The value comes
       from the component's StrokeWidth parameter as an inline custom property on the root, so a
       track and its arc can never disagree; the fallback is the same 11 the summary card's dial has
       always used, for the (impossible) case of the property going missing. */
    stroke-width: var(--mateen-dual-ring-stroke, 11);
}

.mateen-dual-ring__arc {
    stroke-linecap: round;
    transition: stroke-dashoffset 600ms cubic-bezier(.4, 0, .2, 1);
}

.mateen-dual-ring__track--outer {
    stroke: color-mix(in srgb, var(--mud-palette-primary) 16%, var(--mud-palette-surface));
}

.mateen-dual-ring__track--inner {
    stroke: color-mix(in srgb, var(--mud-palette-secondary) 16%, var(--mud-palette-surface));
}

.mateen-dual-ring__arc--outer {
    stroke: var(--mud-palette-primary);
}

.mateen-dual-ring__arc--inner {
    stroke: var(--mud-palette-secondary);
}

/* The centre figure sits in an HTML overlay rather than an SVG <text>, so it inherits the app's
   font stack and palette colours instead of needing its own. pointer-events:none keeps the title
   tooltips of anything behind it reachable. */
.mateen-dual-ring__center {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1px;
    pointer-events: none;
    text-align: center;
}

/* BUG FIX 2026-09-30: at 1.375rem/700, a worst-case value like "100%" overran the inner ring's own
   hole (~67px across, per the empty-state caption's comment two rules down - the inner track's
   stroke eats into the 36-user-unit inner radius from both sides) and visibly crossed the inner
   ring itself on the student detail card's dual dial. This only ever showed up there: the grid
   card's dial already overrides this smaller via .mateen-dual-ring--compact below, which no caller
   of THIS rule (the default/detail-card size) had an equivalent for. */
.mateen-dual-ring__value {
    font-size: 1.125rem;
    font-weight: 700;
    line-height: 1.1;
    color: var(--mud-palette-text-primary);
    font-variant-numeric: tabular-nums;
}

/* Single-ring mode only (DualProgressRing's ShowInnerRing="false") - the caption word printed ABOVE
   the value, doing the naming job the two-row legend used to do now that there is no legend at all.
   Unlike .mateen-dual-ring__center-caption below, this one is not empty-state-only: it shows
   whenever there is no inner ring, figure or no figure, which is why it is a separate class rather
   than a second use of that one. */
.mateen-dual-ring__center-label {
    font-size: .6875rem;
    line-height: 1.2;
    color: var(--mud-palette-text-secondary);
}

/* Only rendered in the empty state, under the "-": the inner ring's hole is ~67px across, so this
   is sized to wrap onto two lines inside it rather than to fit on one. */
.mateen-dual-ring__center-caption {
    max-width: 66px;
    font-size: .625rem;
    line-height: 1.2;
    color: var(--mud-palette-text-secondary);
}

.mateen-dual-ring__legend {
    display: flex;
    flex-wrap: wrap;
    gap: 2px 14px;
}

.mateen-dual-ring__legend-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: .75rem;
    line-height: 1.6;
    color: var(--mud-palette-text-secondary);
}

.mateen-dual-ring__dot {
    flex: 0 0 auto;
    width: 10px;
    height: 10px;
    border-radius: 50%;
}

.mateen-dual-ring__dot--outer {
    background-color: var(--mud-palette-primary);
}

.mateen-dual-ring__dot--inner {
    background-color: var(--mud-palette-secondary);
}

.mateen-dual-ring__legend-value {
    font-weight: 700;
    color: var(--mud-palette-text-primary);
    font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
    .mateen-dual-ring__arc {
        transition: none;
    }
}

/* Phone: the two bars stack, and the dial shrinks a little now that it is on a full-width row
   under the identity rather than in its own column. */
@media (max-width: 599.98px) {
    .mateen-student-progress__bars {
        grid-template-columns: minmax(0, 1fr);
    }

    .mateen-dual-ring__dial {
        width: 116px;
        height: 116px;
    }

    /* The detail card's three stat labels ("Ayat memorized", "Suras completed") are much longer
       than the educator card's Age / Groups / Students, and with the 65px column gap they inherit
       the row - and with it the whole card - is wider than a phone viewport, which clipped the
       bars' percentages off the right edge. Scoped to the student detail card so the educator
       card's stat row is untouched; min-width:0 is what lets the row shrink below its content
       width now that it is allowed to wrap. */
    .mateen-person-card--student-detail .mateen-person-card__stats--column {
        flex-wrap: wrap;
        gap: 12px 26px;
        min-width: 0;
    }
}

/* Per-bar caption on the student detail card: each progress bar carries the figures that belong
   to it (Hifd: what the count is out of and its Sura reach; Morajaah: its own Sura/ayah coverage
   and ticket count), instead of one caption under both trying to speak for two activities. */
.mateen-student-progress__bar-note {
    margin-top: 6px;
    font-size: .75rem;
    line-height: 1.5;
    /* Units quiet, figures loud - the same emphasis split the plan cards' metric rows use. */
    color: var(--mud-palette-text-secondary);
    font-variant-numeric: tabular-nums;
}

    .mateen-student-progress__bar-note b {
        color: var(--mud-palette-text-primary);
        font-weight: 700;
    }

/* A SUBORDINATE bar in the pair (.mateen-student-progress__bar--sub) - today the Morajaah bar of the
   student grid card's pair. What makes it read as the quieter of the two is its head: one step down
   in size and in the secondary text colour, so Hifd stays the headline even though both bars carry
   the same kind of label/percentage/figure.
   Deliberately not scoped to --compact: any future pairing of a main bar with a supporting one gets
   the same treatment by adding this modifier. Note that the SIZE half of that treatment is
   deliberately neutralised inside --compact, where the pair sits in two side-by-side columns and a
   smaller type size would push that column's track and caption out of line with its neighbour's -
   see the --compact block below. */
.mateen-student-progress__bar--sub .mateen-student-progress__bar-head {
    font-size: .6875rem;
    color: var(--mud-palette-text-secondary);
}

    /* The percentage stays bold enough to read as a figure, just not as loud as the Hifd one. */
    .mateen-student-progress__bar--sub .mateen-student-progress__bar-value {
        font-weight: 600;
    }

/* --- COMPACT modifier: the same pair of bars inside a 254px grid card -------------------------
   The student grid card's footer band renders exactly the markup the detail card's FooterLead does
   (that is the point - the two copies of this pair cannot drift), but the detail card's treatment is
   sized for a full-page-width row: .75rem heads, 8px tracks, 10px/32px gaps. None of that survives
   at 254px, so every step tightens here instead of the grid card getting markup of its own.

   Nothing in this block adds outer spacing. The pair is the last thing in the card, so its bottom
   edge is the footer band's 12px padding and nothing else - the user asked for the bottom of these
   cards to be used, not padded ("don't add unnecessary space"), and the now content-less footer row
   above it collapses (see the shared __footer-row rule) so there is no leftover strip where the
   Active chip used to be either. */
.mateen-student-progress--compact {
    gap: 6px;
}

    /* TWO COLUMNS, ONE ROW (2026-09-27) - the user asked for the two bars side by side rather than
       stacked, because stacking them is what made these grid cards tall ("I don't want the card to
       be too long, I want to keep space as much as possible"). Each column is minmax(0, 1fr) so the
       pair splits the band's content width evenly and neither column can be pushed wider than its
       share by its own text: the card is 254px including its 1px borders and the band has 20px of
       padding a side, so the two columns come out at (212 - 10) / 2 = 101px each (measured, not
       estimated). Everything below is sized against that 101px.

       Row gap is zeroed - with exactly two items in two columns there is never a second row, and a
       leftover row gap would be dead height at the bottom of the band, which is the opposite of
       what this change is for.

       This also overrides the phone rule further up that re-stacks .mateen-student-progress__bars
       below 600px: that selector is a single class and this one is two, so the grid card keeps its
       two columns on a phone - it is a fixed 254px card either way, so it has the same room there
       as anywhere else. Only the DETAIL card's full-width pair stacks on a phone. */
    .mateen-student-progress--compact .mateen-student-progress__bars {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 0 10px;
    }

    /* The head row has to hold the longest label against the widest percentage the "0.#" format can
       produce - "Morajaah" + "99.5%" measures 81px at this size, inside the 101px column with 20px
       to spare. nowrap is the safety net: if a user's font stack (the app asks for Inter and falls
       back to Arial, which is the narrower of the two) or zoom ever pushes it over, the label
       ellipsises rather than wrapping to a second line, which would make this column taller than
       its neighbour and un-align the pair. */
    .mateen-student-progress--compact .mateen-student-progress__bar-head {
        margin-bottom: 2px;
        gap: 4px;
        font-size: .6875rem;
        white-space: nowrap;
    }

        /* The label, not the percentage, is what gives way in that worst case - a clipped "Moraja..."
           is still readable, a clipped percentage is a wrong number. Which one yields is decided by
           these two rules, not left to the browser's proportional shrinking of two flex items:
           the label takes the slack (and overflow:hidden is also what lets a flex item shrink below
           its text width at all), the percentage never shrinks. */
        .mateen-student-progress--compact .mateen-student-progress__bar-head > span:first-child {
            flex: 1 1 auto;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        .mateen-student-progress--compact .mateen-student-progress__bar-value {
            flex: 0 0 auto;
        }

    /* Side by side, the pair MUST share one type size: the head, the track and the caption of each
       column are stacked in their own box, so a smaller Morajaah head would sit its track and its
       caption a couple of pixels above Hifd's and the two columns would visibly fail to line up.
       So --sub's size step-down is cancelled here (this rule restates the main head's size rather
       than dropping the selector, so the intent is at the place anyone would look for it), and
       Morajaah's de-emphasis inside this card is carried entirely by what does NOT affect layout:
       the secondary text colour and the lighter weight from the --sub rules above, plus its quieter
       secondary bar colour. The --sub modifier keeps its full step-down treatment outside --compact,
       where a subordinate bar sits beneath its main one and has no neighbour to line up with. */
    .mateen-student-progress--compact .mateen-student-progress__bar--sub .mateen-student-progress__bar-head {
        font-size: .6875rem;
    }

    /* The per-bar sura figure ("6 / 114 suras"), same block as the detail card's notes - bold figure,
       quiet unit, tabular numerals - only smaller and closer to its bar. tabular-nums matters more
       here than there: the figures sit in two columns across a grid of cards, so the slashes and
       totals line up both down the column and across the pair.

       Worst case "114 / 114 suras" measures 76px, inside the 101px column. nowrap + ellipsis for
       the same reason as the head above: a wrapped caption would make one column taller than the
       other, and one tall column is what this whole change exists to remove. */
    .mateen-student-progress--compact .mateen-student-progress__bar-note {
        margin-top: 2px;
        font-size: .6875rem;
        line-height: 1.3;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    /* Same size parity as the heads, and for the same reason - see that rule. */
    .mateen-student-progress--compact .mateen-student-progress__bar--sub .mateen-student-progress__bar-note {
        font-size: .6875rem;
    }

    /* The no-plan state's single muted line, which stands in for the whole pair. It is a sibling of
       __bars rather than a cell inside it, so it never inherits the two-column grid above - it stays
       one centred line across the whole band. Centred, because it is the only thing in the band and
       the card's stack above it is centred too - left-aligned it read as a bar's caption with its
       bar missing. */
    .mateen-student-progress--compact .mateen-student-progress__caption {
        justify-content: center;
        gap: 0;
        font-size: .6875rem;
        line-height: 1.4;
        text-align: center;
    }

/* ===================================================================================
   Plan card (Components/Shared/StudentPlansPanel.razor)

   Same shape as the group cards: body on the left, a narrow rail of actions down the right behind
   a vertical hairline, and the accent bar moved from the top edge to the left. Keeps its
   .mateen-accent-card--secondary colour (brand purple) - only the bar's position changes.
   =================================================================================== */
.mateen-plan-card__layout {
    display: flex;
    align-items: stretch;
}

.mateen-plan-card__body {
    flex: 1 1 auto;
    /* Without this a long unbroken range label widens the body and pushes the rail out of view. */
    min-width: 0;
    /* The left padding carries the 7px accent bar plus the card's normal padding. */
    padding-left: 23px !important;
}

.mateen-plan-card__actions {
    flex: 0 0 auto;
    width: 56px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    gap: 4px;
    padding: 12px 6px;
    border-inline-start: 1px solid var(--mud-palette-lines-default);
}

/* The accent bar down the LEFT edge instead of across the top - overrides
   .mateen-accent-card::before, same pseudo-element repositioned, so other accent cards are
   unaffected. */
.mateen-plan-card::before {
    top: 0;
    bottom: 0;
    right: auto;
    width: 7px;
    height: auto;
    border-top-right-radius: 0;
    border-bottom-left-radius: inherit;
}

@media (max-width: 599.95px) {
    .mateen-plan-card__actions {
        width: 48px;
    }
}
/* ===================================================================================
   Student detail card: stats inside the identity column

   The stats are rendered INSIDE the identity column (PersonCard's StatsInline), under the
   person's details and behind a separator - not as a grid item of their own. So there is no grid
   placement here at all; the only job left is to undo the middle-column styling the shared
   .mateen-person-card__stats--column carries (side hairlines, vertical centring) and replace it
   with a top rule.

   Why not the shared middle column: these labels ("Morajaah suras covered", "Ayat memorized") are
   several times longer than the educator card's Age / Groups / Students, and a middle column wide
   enough for them squeezed the identity to nothing.
   =================================================================================== */
.mateen-person-card--student-detail .mateen-person-card__body--horizontal {
    /* Two columns now: [avatar] [identity + stats] | [rings]. */
    grid-template-columns: auto minmax(0, 1fr) minmax(0, 17rem);
}

.mateen-person-card--student-detail .mateen-person-card__stats--column {
    align-self: auto;
    justify-content: flex-start;
    flex-wrap: wrap;
    gap: 12px 32px;
    margin-top: 14px;
    padding: 14px 0 0;
    border-left: 0;
    border-right: 0;
    /* The separator the stats sit behind. */
    border-top: 1px solid var(--mud-palette-lines-default);
}

/* The rings centre against the whole left-hand block, which is now taller than the identity text
   alone. */
.mateen-person-card--student-detail .mateen-person-card__aside {
    align-self: center;
}

/* The status chip (StatusChipInline) and the assign button (NameAction) both moved out of the
   footer band and onto the identity column, so this card's footer-row - PersonCard's shared
   chip/FooterStart/FooterEnd row - always renders content-less on it. Collapsing that row is no
   longer this card's business: the rule that used to sit here tested :not(:has(*)), which can never
   match a row whose two slot divs are rendered unconditionally, so it did nothing. The shared
   .mateen-person-card__footer-row rule up in the person-card block does the job properly now, for
   this card and for the student grid card alike - see the comment there.

   Safety net for a future caller that sets StatusChipInline/NameAction without ShowProgressBars:
   with that row collapsed, the footer-lead is this card's only possible content, and when IT has
   nothing either (see the shared :not(:has(*)) rule on footer-lead) the band would still paint as a
   padded, bordered strip holding nothing. Tested structurally rather than by reading the other
   rules' computed display, since :has() matches the DOM tree, not the paint tree - hence the same
   "no element that isn't a reserved slot" test the shared row rule uses, not footer-row:has(*). */
.mateen-person-card--student-detail .mateen-person-card__footer:not(:has(.mateen-person-card__footer-lead:has(*))):not(:has(.mateen-person-card__footer-row *:not(.mateen-person-card__footer-slot))) {
    display: none;
}

/* --- Student detail card: keep it TWO blocks at every width above phone ------------------------
   The shared horizontal layout drops the aside onto its own row below 1200px. That is wrong for
   this card and produced the bug it was reported as: the base rule above declares three columns
   with higher specificity than the shared 1200px rule, so the template stayed three-wide while
   the rings moved to row 2 - leaving a dead ~272px column hanging off the right of row 1.

   The person block beside the rings IS this card's layout, so the rings stay in column 3 instead
   of dropping. The column narrows once there is less room, and only the phone breakpoint stacks. */
@media (max-width: 1200px) {
    .mateen-person-card--student-detail .mateen-person-card__aside {
        grid-column: 3;
        grid-row: auto;
        margin-top: 0;
        padding-top: 0;
        padding-left: 24px;
        border-top: 0;
        border-left: 1px solid var(--mud-palette-lines-default);
    }
}

@media (max-width: 899.98px) {
    .mateen-person-card--student-detail .mateen-person-card__body--horizontal {
        grid-template-columns: auto minmax(0, 1fr) minmax(0, 11rem);
        column-gap: 18px;
    }
}

/* Phone: one column. The base rule's three-column template is more specific than the shared
   phone rule, so it has to be restated here, and the aside's hairline goes back to a top rule. */
@media (max-width: 599.98px) {
    .mateen-person-card--student-detail .mateen-person-card__body--horizontal {
        grid-template-columns: minmax(0, 1fr);
    }

    .mateen-person-card--student-detail .mateen-person-card__aside {
        grid-column: 1 / -1;
        padding-left: 0;
        padding-top: 14px;
        border-left: 0;
        border-top: 1px solid var(--mud-palette-lines-default);
    }
}

/* ===================================================================================
   Plans group panel (Components/Shared/StudentPlansPanel.razor)

   One light panel per rewayah, holding that rewayah's two columns - the default whole-Quran plan
   on the left and its own plans on the right. A student can have plans in more than one rewayah,
   and without this the sections ran together into one undifferentiated column of cards.

   Deliberately a flat tinted surface, not another elevated card: it is a container for cards, and
   giving it its own shadow would make the cards inside look sunken. The tint is the same neutral
   the pills use, so in light mode it sits just off the white cards it holds, and in dark mode it
   lifts slightly off the surface instead - a darker panel there would read as a hole.
   =================================================================================== */
.mateen-plans-group {
    padding: 16px;
    border-radius: 18px;
    border: 1px solid var(--mud-palette-lines-default);
    background-color: light-dark(color-mix(in srgb, var(--mud-palette-text-primary) 5%, var(--mud-palette-surface)), color-mix(in srgb, #FFFFFF 5%, var(--mud-palette-surface))) !important;
}

@media (max-width: 599.98px) {
    .mateen-plans-group {
        padding: 12px;
    }
}

/* ===================================================================================
   Plan metrics (Components/Shared/StudentPlansPanel.razor)

   The two figures a plan card reports, as one labelled row each rather than a single run-on
   sentence ("74 of 6,236 ayahs memorized · Morajaah covered 86 ayahs"), which buried both numbers
   in prose and made the two activities hard to tell apart at a glance.

   Each label takes its series' own colour - the same violet and purple as the Hifd and Morajaah
   bars directly above - so the rows read as a key to those bars. The labels share a fixed minimum
   width so the values line up into a column, and the numerals are tabular so they stay aligned
   whatever the digit count.
   =================================================================================== */
.mateen-plan-metrics {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-top: 10px;
    font-size: .8125rem;
    line-height: 1.55;
}

.mateen-plan-metric {
    display: flex;
    align-items: baseline;
    gap: 10px;
}

.mateen-plan-metric__label {
    flex: 0 0 auto;
    min-width: 68px;
    font-weight: 700;
    letter-spacing: .2px;
}

.mateen-plan-metric__label--hifd {
    color: var(--mud-palette-primary);
}

.mateen-plan-metric__label--morajaah {
    color: var(--mud-palette-secondary);
}

.mateen-plan-metric__value {
    min-width: 0;
    color: var(--mud-palette-text-secondary);
    font-variant-numeric: tabular-nums;
}

    /* The figure itself carries the emphasis; the units around it stay quiet. */
    .mateen-plan-metric__value b {
        color: var(--mud-palette-text-primary);
        font-weight: 700;
    }

.mateen-plan-metrics__empty {
    color: var(--mud-palette-text-secondary);
    font-style: italic;
}

/* Separator between the two figures on a metric row (ayahs · suras). Quieter than the numbers it
   divides, so the row reads as two facts rather than a sentence. */
.mateen-plan-metric__sep {
    margin: 0 2px;
    opacity: .45;
}

/* ===================================================================================
   Per-sura progress cards (Components/Shared/StudentSuraProgressPanel.razor - the Progress tab)

   One card per Sura in the selected plan, so the list is long (113 cards for a whole-Quran plan) and
   every card is small: identity and ayah total on the left, the dual ring on the right, the Hifd and
   Morajaah ayat along the bottom. An auto-fill grid rather than MudGrid breakpoints - the cards have
   a natural minimum width (the ring plus a readable Sura name), and letting the column count follow
   the available width fills a wide screen without a stack of xs/sm/md/lg guesses.

   The cards reuse .mateen-plan-metrics / .mateen-plan-metric for the bottom rows, so the figures
   here are styled by exactly the same rules as the plan cards one tab over.
   =================================================================================== */
.mateen-sura-progress {
    display: grid;
    /* min() so a card can never be wider than a phone viewport and force a horizontal scroll. */
    grid-template-columns: repeat(auto-fill, minmax(min(340px, 100%), 1fr));
    gap: 16px;
}

/* Column layout so the metric rows can be pushed to the bottom edge (margin-top:auto below) and
   every card in a grid row ends up the same height whatever its Sura name wraps to. */
.mateen-sura-card {
    display: flex;
    flex-direction: column;
}

.mateen-sura-card__body {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    /* Clears the 7px accent bar across the top edge, which would otherwise sit tight against the
       Sura name rather than above it. */
    padding-top: 20px !important;
}

.mateen-sura-card__top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
}

.mateen-sura-card__ident {
    display: flex;
    flex-direction: column;
    gap: 2px;
    /* Without this a long transliterated name widens the column and squeezes the ring. */
    min-width: 0;
}

.mateen-sura-card__arabic {
    font-size: .9375rem;
    line-height: 1.6;
    color: var(--mud-palette-text-secondary);
}

/* The Sura's own ayah total for this rewayah, plus the slice a small plan's boundary Sura actually
   asks for. Same emphasis split as the metric rows: figures loud, units quiet. */
.mateen-sura-card__ayat {
    margin-top: 4px;
    font-size: .75rem;
    line-height: 1.5;
    color: var(--mud-palette-text-secondary);
    font-variant-numeric: tabular-nums;
}

    .mateen-sura-card__ayat b {
        color: var(--mud-palette-text-primary);
        font-weight: 700;
    }

.mateen-sura-card__ring {
    flex: 0 0 auto;
}

/* Pinned to the bottom of the card, so the two figures line up across a row of cards whose Sura
   names wrapped to different heights. */
.mateen-sura-card__metrics {
    margin-top: auto;
    padding-top: 10px;
}

/* The ring in a list instead of as a headline: a smaller dial, a centred legend stacked under it so
   it stays inside the card's narrow right-hand column, and a centre figure scaled to the smaller
   hole. The stroke itself is not set here - it comes from DualProgressRing's StrokeWidth parameter
   (this panel passes 5.5 against the default 11), so the thickness stays a property of the caller
   rather than of this modifier. */
.mateen-dual-ring--compact {
    align-items: center;
    gap: 6px;
}

    .mateen-dual-ring--compact .mateen-dual-ring__dial {
        width: 104px;
        height: 104px;
    }

    .mateen-dual-ring--compact .mateen-dual-ring__value {
        font-size: 1rem;
    }

    /* Stacked, not side by side: two labels with percentages on one line would be wider than the
       dial above them and stretch the card. */
    .mateen-dual-ring--compact .mateen-dual-ring__legend {
        flex-direction: column;
        gap: 0;
    }

    .mateen-dual-ring--compact .mateen-dual-ring__legend-item {
        font-size: .6875rem;
        line-height: 1.5;
        gap: 5px;
    }

    .mateen-dual-ring--compact .mateen-dual-ring__dot {
        width: 8px;
        height: 8px;
    }

@media (max-width: 599.98px) {
    /* One card per row on a phone anyway, so the dial gives back width to the Sura name. */
    .mateen-dual-ring--compact .mateen-dual-ring__dial {
        width: 92px;
        height: 92px;
    }
}

/* Spoken languages on a person card, in both variants (see PersonCard.SpokenLanguagesRow). The pills
   reuse .mateen-pill for the border, radius and light fill, exactly as the group subtitle does, so a
   language reads like the other bordered tokens on the card rather than inventing a third chip style.
   Only the size is scaled down here: on a 254px grid card several languages have to wrap without
   turning the card into a list. */
.mateen-person-card__languages {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px;
    margin-top: 6px;
}

    /* The leading translate icon, sized to sit with the pills rather than above them. */
    .mateen-person-card__languages .mud-icon-root {
        font-size: 1rem;
        color: var(--mud-palette-text-secondary);
    }

    .mateen-person-card__languages .mateen-person-card__language {
        font-size: .7rem;
        padding: 1px 8px;
        line-height: 1.6;
    }

/* Centred in the grid card, where everything else in the body is centred; left-aligned in the
   horizontal detail card, where the identity column is. */
.mateen-person-card:not(.mateen-person-card--horizontal) .mateen-person-card__languages {
    justify-content: center;
}

/* ===================================================================================
   LANDING PAGE - CONTACT FORM + FOOTER (Components/Pages/Home.razor)
   ===================================================================================
   An extension of the "Public landing page" block far above, not a new design language: every colour
   here is one of that block's own tokens (--mateen-landing-line, --mateen-landing-card-2,
   --mateen-landing-title-3) or a palette variable it already draws on (--mud-palette-surface,
   -text-primary, -text-secondary, -lines-default, -error). Nothing is sampled, invented or hardcoded
   - which also means both of these follow the app's dark/light mode automatically, through the same
   color-scheme signal .mateen-landing sets up (see that block's header comment).

   These live in app.css rather than a scoped Home.razor.css because the landing page's entire style
   surface is already here, and splitting half of one page's look across two files is how the
   .mateen-landing-* block stops being readable.

   The inputs are plain <input>/<textarea>, not MudTextField, per Home.razor's own header comment on
   why this page's body is bespoke HTML - so they need real styling rather than a class hook, and this
   is it: the card surface and hairline the cards above use, a 10px radius in the same family as their
   14px, and the primary colour only on focus.
   =================================================================================== */

/* Two columns for the short fields; the email, message, error and submit span both via --full. Capped
   at the same 1080px max-width as .mateen-landing-grid and .mateen-landing-note so the form lines up
   with the card grids above it rather than running wider. */
.mateen-landing-contact {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 16px 20px;
    max-width: 1080px;
    margin: 0 auto;
    padding: 26px;
    background: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 14px;
}

.mateen-landing-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
    /* min-width:0 is what actually lets an input shrink below its default intrinsic width inside a
       grid column, instead of forcing the whole form wider than its track. */
    min-width: 0;
}

.mateen-landing-field--full {
    grid-column: 1 / -1;
}

.mateen-landing-field label {
    font-size: .8rem;
    font-weight: 600;
    letter-spacing: .2px;
    color: var(--mud-palette-text-secondary);
}

/* One rule for both control types - they differ only in height, which comes from the textarea's own
   rows attribute. inherit on font-family/size because a bare input would otherwise fall back to the
   browser's default form font and read as foreign next to everything else on this page. */
.mateen-landing-field input,
.mateen-landing-field textarea {
    width: 100%;
    padding: 11px 13px;
    font-family: inherit;
    font-size: .92rem;
    line-height: 1.5;
    color: var(--mud-palette-text-primary);
    background: var(--mateen-landing-card-2);
    border: 1px solid var(--mateen-landing-line);
    border-radius: 10px;
    transition: border-color .15s ease, box-shadow .15s ease;
}

    /* Vertical only: a horizontally resizable textarea can be dragged outside the panel. */
    .mateen-landing-field textarea {
        resize: vertical;
        min-height: 120px;
    }

    /* The focus ring is the primary colour at low alpha rather than the browser's default outline -
       the same color-mix-off-the-palette device the ornament tokens above use, so it follows a palette
       change. outline:none is safe here only because this ring replaces it visibly. */
    .mateen-landing-field input:focus,
    .mateen-landing-field textarea:focus {
        outline: none;
        border-color: var(--mud-palette-primary);
        box-shadow: 0 0 0 3px color-mix(in srgb, var(--mud-palette-primary) 22%, transparent);
    }

    .mateen-landing-field input:disabled,
    .mateen-landing-field textarea:disabled {
        opacity: .6;
    }

/* Per-field message, under the control it belongs to (see Home.razor's ValidateContactForm). */
.mateen-landing-field-error {
    font-size: .76rem;
    line-height: 1.4;
    color: var(--mud-palette-error);
}

/* Whole-form failure - a rejected POST or a network error - sitting just above the submit button, so
   it cannot be missed by someone whose eye is already on the button they just pressed. */
.mateen-landing-contact-status {
    margin: 0;
    padding: 10px 14px;
    font-size: .84rem;
    line-height: 1.5;
    color: var(--mud-palette-error);
    background: color-mix(in srgb, var(--mud-palette-error) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--mud-palette-error) 35%, transparent);
    border-radius: 10px;
}

.mateen-landing-contact-actions {
    display: flex;
    justify-content: flex-end;
}

/* The confirmation state that replaces the form once the API has accepted the message. Same panel
   geometry as the form itself, but hairlined in the primary colour (like .mateen-landing-note) so the
   swap reads as "something changed here" rather than as the form having simply emptied. */
.mateen-landing-thanks {
    max-width: 1080px;
    margin: 0 auto;
    padding: 30px 26px;
    text-align: center;
    background: var(--mud-palette-surface);
    border: 1px solid var(--mateen-landing-line);
    border-radius: 14px;
}

    .mateen-landing-thanks h3 {
        margin: 0 0 8px;
        font-size: 1.1rem;
        font-weight: 600;
        color: var(--mateen-landing-title-3);
    }

    .mateen-landing-thanks p {
        margin: 0 0 18px;
        font-size: .86rem;
        line-height: 1.6;
        color: var(--mud-palette-text-secondary);
    }

/* --- footer ----------------------------------------------------------------------------------- */
/* Sits inside .mateen-landing-frame, so the page gradient shows through rather than a fill of its
   own; the top hairline is what separates it from the closing CTA block above. z-index:1 for the same
   reason every other section here has it: the ornamental side arches are painted behind. */
.mateen-landing-footer {
    position: relative;
    z-index: 1;
    border-top: 1px solid var(--mateen-landing-line);
    padding: 44px 26px 0;
}

/* Five tracks on desktop (2026-10-03, by request): the wordmark lockup owns the leftmost column, the
   tagline sentence sits BESIDE it rather than underneath, then the three link columns.

   Track 1 is `auto` so it shrink-wraps the lockup instead of being handed a share of the row - the
   widest thing in it is the "- MATEEN -" rule-flanked line, which measures 117px, and a fr share would
   either starve it or leave dead space beside a 64px logo. The remaining ~850px is split 2fr for the
   tagline against 1fr per link column; measured at 1080px that is 340px of tagline and 170px per link
   column. Both numbers were chosen off the rendered result rather than guessed: at 340px the sentence
   sets in three full lines with no orphaned last word (it broke "school." onto a fourth at the 1.5fr
   /284px I tried first), and 170px still clears the longest link, "Why memorization" (~115px).
   minmax(0, …) on every fr track because a long word in any of them must wrap rather than push the
   grid past 1080px. */
.mateen-landing-footer-inner {
    display: grid;
    grid-template-columns: auto minmax(0, 2fr) repeat(3, minmax(0, 1fr));
    gap: 28px;
    max-width: 1080px;
    margin: 0 auto;
}

/* The wordmark and the tagline are two separate columns of the footer grid, but they stay wrapped in
   one element in the markup because they are one brand block semantically - and because the narrow
   breakpoints below re-join them into a real flex box. display:contents lifts both children into the
   parent's tracks meanwhile, the same trick .mateen-landing-footer-links uses for its three columns. */
.mateen-landing-footer-brand {
    display: contents;
}

    /* No top margin now that this is a neighbouring column rather than a block under the lockup; the
       2px nudge instead aligns its first line with the "Platform"/"Get in touch" headings across the
       row (h4 is .82rem/1.2-ish against this paragraph's .82rem/1.7, so their first baselines differ by
       about that much). Re-stacked under the wordmark at <=560px, where the margin comes back.

       44ch does not bite on desktop (the 2fr track is narrower than it); it exists for the <=980px
       row, where the brand is a flex box across the full footer width and the sentence would otherwise
       set in one ~700px line - so it still reads as a three-line block beside the lockup. */
    .mateen-landing-footer-brand p {
        margin: 2px 0 0;
        max-width: 44ch;
        font-size: .82rem;
        line-height: 1.7;
        color: var(--mud-palette-text-secondary);
    }

/* Footer-scoped variant of the login page's brand lockup (.mateen-login-wordmark and its __arabic/
   __latin/__line children, 2026-10-03) - same Arabic-over-Latin lockup, same gradient-text-clip and
   flanking-rule treatment, just scaled down: it now owns the footer grid's first, content-sized column
   (~124px) with the tagline and three link columns to its right, rather than standing alone centered
   under a card, so the login page's 2.75rem Arabic/11px tracking would dominate the footer.

   align-items:center is deliberate and keeps the login page's behaviour: the Arabic word and the
   rule-flanked "MATEEN" line are centred against EACH OTHER, which is how the lockup is drawn on the
   identity board. The column itself is the left-most thing in the footer row, so centring inside a
   shrink-wrapped track costs nothing - it only balances the two lines. */
.mateen-landing-footer-wordmark {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
}

/* Logomark above the lockup, first child of .mateen-landing-footer-wordmark so it inherits that
   column's centering. 64px wide is the same box .mateen-appbar-logo uses (and the asset's own ~1.37:1
   aspect makes that ~47px tall), which is about twice the 1.6rem Arabic word below it - enough to read
   as the brand mark heading this column without out-weighing the three link columns beside it. The
   login page's 104px would. height:auto + object-fit:contain so a future asset of another aspect ratio
   letterboxes instead of stretching, same as the other two placements. */
.mateen-landing-footer-logo {
    width: 64px;
    height: auto;
    object-fit: contain;
}

/* One mark per mode, and this page does not own the dark-mode flag (see the footer comment in
   Home.razor), so the pick is made in CSS. Swapping an image needs a real selector - display, not a
   colour - which light-dark() cannot express, hence the style container query, exactly as
   .mateen-login-mark does it. Light is the default state, so a browser without style-query support
   (pre-Chrome 111 / Firefox 128 / Safari 18) shows the light mark in both modes - purple line art that
   still reads on the dark footer - rather than stacking both. */
.mateen-landing-footer-logo--dark {
    display: none;
}

@container style(--mud-native-html-color-scheme: dark) {
    .mateen-landing-footer-logo--dark {
        display: block;
    }

    .mateen-landing-footer-logo--light {
        display: none;
    }
}

.mateen-landing-footer-wordmark__arabic {
    font-size: 1.6rem;
    font-weight: 900;
    line-height: 1.3;
    direction: rtl;
    background: var(--mateen-wm-gradient);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

.mateen-landing-footer-wordmark__latin {
    display: flex;
    align-items: center;
    gap: 6px;
    color: var(--mateen-wm-latin);
    font-size: .72rem;
    /* Overrides --mateen-wordmark-tracking for just this placement, the variable's whole point
       (see its own remarks above) - 5px instead of the login page's 11px default, proportional to
       the smaller Arabic size above. */
    --mateen-wordmark-tracking: 5px;
}

.mateen-landing-footer-wordmark__line {
    width: 14px;
    height: 1px;
    background-color: var(--mateen-wm-latin);
    opacity: .6;
}

/* The three link columns share the footer grid's own tracks, so this wrapper must not introduce a
   second grid of its own - display:contents lets its children sit directly in the parent's tracks. */
.mateen-landing-footer-links {
    display: contents;
}

.mateen-landing-footer-col {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

    .mateen-landing-footer-col h4 {
        margin: 0 0 2px;
        font-size: .82rem;
        font-weight: 600;
        letter-spacing: .3px;
        color: var(--mud-palette-text-primary);
    }

    /* Muted until hovered, where they take the primary colour - the same restraint the card body copy
       uses, so a column of links does not out-shout the brand block beside it. */
    .mateen-landing-footer-col a {
        font-size: .82rem;
        line-height: 1.5;
        color: var(--mud-palette-text-secondary);
        text-decoration: none;
        transition: color .15s ease;
    }

        .mateen-landing-footer-col a:hover {
            color: var(--mud-palette-primary);
        }

/* The legal line, behind its own hairline, at the true bottom of the page. */
.mateen-landing-footer-bottom {
    max-width: 1080px;
    margin: 32px auto 0;
    padding: 16px 0 26px;
    border-top: 1px solid var(--mud-palette-lines-default);
    text-align: center;
    font-size: .76rem;
    color: var(--mud-palette-text-secondary);
}

/* Tablet: five tracks no longer fit, so the brand block takes a full row of its own and the three link
   columns share the next one - which keeps each column wider than its longest link instead of wrapping
   "Why memorization". The wordmark still sits to the LEFT of the tagline rather than above it: the
   brand drops out of display:contents and becomes a real two-item flex row spanning the full grid
   width, which at this width is ~930px - plenty for the ~124px lockup plus the sentence. */
@media (max-width: 980px) {
    .mateen-landing-footer-inner {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    .mateen-landing-footer-brand {
        display: flex;
        align-items: flex-start;
        gap: 26px;
        grid-column: 1 / -1;
    }
}

/* Phone: one column throughout, and the form's two short-field pairs stack. Matches the 560px step
   .mateen-landing-grid and .mateen-landing-note already break at, so the whole page changes shape at
   one width rather than at three. */
@media (max-width: 560px) {
    .mateen-landing-contact {
        grid-template-columns: 1fr;
        padding: 20px 18px;
    }

    .mateen-landing-contact-actions {
        justify-content: stretch;
    }

        /* A full-width submit is the one place this page's fixed-height pill stretches - on a phone a
           right-aligned 170px button under a full-width textarea reads as an afterthought. */
        .mateen-landing-contact-actions .mateen-landing-btn {
            flex: 1 1 auto;
            justify-content: center;
        }

    .mateen-landing-footer {
        padding: 36px 16px 0;
    }

    .mateen-landing-footer-inner {
        grid-template-columns: 1fr;
        gap: 22px;
    }

    /* Phone: side-by-side would leave the sentence a ~240px measure next to the lockup, so the brand
       block re-stacks - wordmark above tagline, as it was before this row existed - and the paragraph
       takes its separating margin back. */
    .mateen-landing-footer-brand {
        flex-direction: column;
        gap: 0;
    }

        .mateen-landing-footer-brand p {
            margin: 10px 0 0;
        }
}

/* ===================================================================================
   ADMIN/TEACHER AGGREGATE DASHBOARD (2026-10-02) - Components/Shared/Dashboard*.razor
   -----------------------------------------------------------------------------------
   New section added to the TOP of Dashboard.razor for Admin/Teacher only, matching
   "Design/Mateen Quran Learning Dashboard.png" - the existing launcher-card grid (Users/Groups/
   Educators/.../Leaderboard/Comparison) is untouched further down the same page. See
   Dashboard.razor's own top-of-file remarks for exactly how the new section and the old grid are
   now split. Everything below is scoped to classes only this new section uses, so none of it can
   affect the untouched launcher grid or the Student block above it.
   =================================================================================== */

/* The two silhouette banners (greeting + bottom quote) share this shell: relative positioning so
   wwwroot/images/mosque-skyline.svg can be layered in as an absolutely-positioned background piece
   behind the real content, clipped to the card's own rounded corners. */
.mateen-dashboard-banner {
    position: relative;
    overflow: hidden;
    border-radius: var(--mud-default-borderradius, 10px);
    isolation: isolate;
}

/* A plain <div>, not an <img> - an <img src="*.svg"> renders the file in its own independent image
   context, where the SVG's own fill="currentColor" resolves against ITS OWN document (no CSS color
   inheritance crosses that boundary, confirmed by rendering: the art painted solid black regardless
   of this class's `color`). A CSS mask is the correct way to recolour an external SVG from the host
   page: wwwroot/images/mosque-skyline.svg becomes an alpha mask, and background-color below is what
   actually paints - that DOES follow this app's --mud-palette-* variables and re-resolves in both
   palette modes with no second copy of the art file.

   This base rule still applies as-is to the bottom quote banner (.mateen-dashboard-banner--quote,
   below). The greeting banner (.mateen-dashboard-banner--greeting, further below) overrides EVERY
   property here instead - it uses a real photo, not this mask, see that override's own comment. */
.mateen-dashboard-banner__art {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: 0;
    pointer-events: none;
    background-color: color-mix(in srgb, var(--mud-palette-primary) 22%, transparent);
    -webkit-mask-image: url('images/mosque-skyline.svg');
    mask-image: url('images/mosque-skyline.svg');
    -webkit-mask-size: cover;
    mask-size: cover;
    -webkit-mask-position: center bottom;
    mask-position: center bottom;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
}

.mateen-dashboard-banner__content {
    position: relative;
    z-index: 1;
}

/* Greeting banner - real photo (wwwroot/images/MosqueLandscape.png, user-supplied 2026-10-02,
   "to match the design") instead of the flat tinted silhouette mask every other banner on this
   page still uses. background-color here is only the flash-of-unstyled-content fallback shown
   before the image paints / if it ever fails to load - once it's up, .__art below (opaque) covers
   this completely. */
.mateen-dashboard-banner--greeting {
    /* Needed for the .__art override below's light-dark() calls (fourth tuning pass) to resolve
       against THIS app's reactive mode rather than the browser's default light-only resolution -
       same mechanism .mateen-landing/.mateen-login/.mateen-pill already rely on elsewhere in this
       file; see .mateen-landing's own block comment for the full explanation of
       --mud-native-html-color-scheme. */
    color-scheme: var(--mud-native-html-color-scheme);
    background-color: var(--mud-palette-surface);
    padding: 22px 28px;
}

/* The greeting banner's own override of the shared .__art hook above: a real photo instead of the
   SVG mask (no mask-image/background-color from the shared rule survives here - both reset), with
   a two-layer wash on top of it for legibility, since the banner's text uses plain default
   MudBlazor text colours (no explicit white/Color.Inherit override - see DashboardGreetingBanner's
   own markup) and therefore NEEDS the backdrop to resolve light under dark (light-mode) text and
   dark under light (dark-mode) text, same as the flat gradient this replaces used to.

   Layer 1 (bottom): the photo itself, cover/center-bottom so the mosque silhouette + foliage along
   its bottom edge - the only genuinely dark detail in an otherwise very pale image - stays the part
   actually visible once this short, wide card crops most of the photo's height away.

   Layer 2 (middle), background-blend-mode: multiply: an OPAQUE gradient mixed from
   --mud-palette-surface, not a transparent tint. Multiplying by near-white (light mode's surface)
   leaves the pale photo essentially untouched; multiplying by near-black (dark mode's surface,
   e.g. #0A0E26) crushes it substantially darker. This is what gives the photo a real per-mode
   treatment with no hand-rolled dark-mode selector anywhere in this file (matching this file's own
   convention of letting --mud-palette-* resolve the mode, not a CSS class/attribute toggle).

   The surface weight (84%/80%) is high, not the 55-60% a first pass tried: --mud-palette-primary
   is brand-violet in BOTH modes but at opposite ends of the lightness scale on purpose (dark
   #471671 in light mode for text-on-primary contrast, lifted to a bright #BB86EA in dark mode for
   the same reason against a dark page - see MateenTheme.cs). At a lower surface weight, light
   mode's multiply colour was dragged down by that DARK light-mode primary enough to visibly
   over-tint the photo and drop the secondary/caption text below comfortable contrast (confirmed by
   rendering - .mud-text-secondary over the lake/mountain band was the weak point). Weighting
   surface this much higher keeps light mode's multiply colour close to white (near a no-op on the
   photo, as intended) while still resolving dark enough in dark mode (surface itself is the thing
   that's actually dark there) to crush the photo substantially - re-verified by rendering both
   modes again after this change.

   Layer 3 (top), normal blend: a translucent brand-tinted SCRIM (not just a tint - it mixes in
   --mud-palette-surface too, nested inside the alpha mix: surface 55% + tertiary/primary 45%,
   THEN that opaque colour is cut to ~35-40% alpha over transparent). Multiply alone (Layer 2)
   wasn't enough on its own, confirmed by rendering: it only ever flattens the photo TOWARD surface,
   it can never go past it, and the photo's own internal detail (mosque/mountain/lake outlines) kept
   .mud-text-secondary's already-low-contrast grey right at the edge of comfortable over the busiest
   part of the image. A real alpha-blended scrim doesn't have that ceiling - because it carries
   surface's own lightness directly (not just as a multiply ceiling), it genuinely lightens in light
   mode and genuinely darkens in dark mode on top of Layer 2, which is what finally settled the
   secondary text. */
/* FOURTH TUNING PASS (2026-10-02): "more transparent and brighter, same as in design reference
   image" - the reference's own greeting banner is a very pale, airy lavender/blue wash with only a
   faint, barely-there skyline watermark, lighter than the "soft photo" treatment the third pass
   above arrived at. Both layers' strength drops, in both modes - but NOT via one shared weight: this
   pass switches every weight/alpha below to a light-dark() pair, because light mode and dark mode
   need to move in OPPOSITE directions on the multiply layer's surface weight to both get brighter,
   and treating them as one shared number (as every earlier pass did) silently fought one mode
   against the other. Concretely, confirmed by rendering (see the brightness regression below):

   Layer 2 (multiply) surface weight - LIGHT raised 84%->96%/80%->92% (same direction the third pass
   already used, just taken further): light-mode surface is near-white, so weighting it even higher
   dilutes the dark light-mode primary (#471671) further, pushing the multiply colour closer to a
   true no-op on the photo.
   DARK dropped 84%->50%/80%->45% - the OPPOSITE direction, and this is the actual bug this pass
   fixes: dark-mode surface (#121A3F) is itself the dark thing here, not primary (primary is LIFTED
   bright, #BB86EA, in dark mode - see MateenTheme.cs). Raising surface weight in dark mode (which an
   earlier draft of this pass tried, by reusing the light-mode direction with one shared value)
   dilutes the BRIGHT primary and concentrates the DARK surface, making the multiply colour darker,
   not lighter - rendering it measured dark-mode's average pixel brightness dropping from 73.6/255
   (third pass) to 53.8/255, a visible regression in the wrong direction. Dropping dark mode's
   surface weight instead (more of the bright, lifted primary blended in) lifts the multiply colour
   to a genuine medium lavender, re-measured at 96/255 after the fix - brighter than the third pass,
   not just brighter than the broken intermediate value.

   Layer 1 (scrim) alpha - LIGHT cut hard, 35%/40% -> 14%/20%: this was the layer actually reading as
   "tinted haze" on top of the photo; cutting its alpha is the literal "more transparent" half of the
   request, letting more of the actual photo through.
   DARK only trimmed, 35%/40% -> 28%/32%: dark mode's scrim base colour (surface 55%/primary 45%,
   both dark-mode values) is itself a medium lavender here too (primary dominates it the same way),
   so unlike light mode this layer isn't pure "haze on top of a fine photo" in dark mode - it's doing
   real work holding the secondary/caption text legible over the mosque-silhouette band once Layer
   2's crush was relaxed this much. Taking it down as far as light mode's 14-20% dropped that text
   below comfortable contrast on re-render; 28/32% was the lowest that held.

   Both final values were reached by rendering several candidates (not guessed) in headless Chrome
   with the real card content over the busiest (silhouette/mountain-line) part of the photo, in both
   modes, the same verification method every earlier pass here used. */
/* FIFTH TUNING PASS (2026-10-02, "make the art background image more to the back and a layer like a
   fog on it"): two related but separate asks, both handled by adding ONE property - filter: blur() -
   on top of everything above rather than retuning the wash layers, which were left as the fourth
   pass tuned them:

   "MORE TO THE BACK": a sharp photo sits at the same optical plane as the greeting text in front of
   it; blurring it is the standard way to push a background layer into depth without touching its
   stacking order. Safe to blur the WHOLE element (not just the photo in isolation) because
   .mateen-dashboard-banner__art is a plain empty <div> with no text content of its own (confirmed
   from DashboardGreetingBanner.razor) - the real greeting/date/verse text lives in the sibling
   .mateen-dashboard-banner__content, a separate element in its own stacking context per the shared
   shell's `isolation: isolate` above, so filter: blur() here cannot reach it.

   "A FOG LAYER": blur doesn't just recede the photo, it diffuses its internal detail (mosque
   outline, mountain line, foliage) the same way atmospheric haze would, which is exactly what
   "fog" reads as - and it does so on top of the fourth pass's existing translucent wash (Layers 2/3
   above), which is the same "translucent tint layer, not a flat cover-up" technique
   .mateen-landing-photo-left's own fog correction established elsewhere in this app tonight. Kept
   that existing wash's values untouched rather than adding a second haze layer: re-rendering below
   showed the blur alone gives a genuinely foggy result without needing more opacity on top of it,
   and the brief's own warning against re-breaking the "too gray" fix argues for changing the fewest
   knobs that get there.

   OVERSIZE (inset -20px / size +40px instead of the base rule's inset: 0 / 100%): blurring an
   element whose edges sit exactly at its clipping parent's edges samples transparent pixels just
   outside its own box for those edge pixels, which reads as a faded/undersaturated rim once
   .mateen-dashboard-banner's `overflow: hidden` crops it - confirmed by rendering an inset:0 draft
   first. Oversizing the box by 20px past every edge (self-consistent: top/left/right/bottom:-20px
   with width/height both `calc(100% + 40px)`, not left to the inset shorthand alone, since the base
   rule's own width/height:100% would otherwise still cascade in alongside it and over-constrain the
   box) gives the blur room to sample real photo pixels at what will become the visible edge, and
   overflow: hidden on the parent then crops the blurred-and-now-soft true edge away instead of ever
   painting it - the composited result has no rim, confirmed by rendering.

   BLUR RADIUS: 2px, one shared value for both modes (unlike every wash layer above, blur is a pixel
   operation, not a colour one - there is no light/dark asymmetry to resolve, light-dark() doesn't
   apply here). An initial render pass tried 7px, which read as receded/hazy but softened the photo
   enough to start feeling out of focus against the still-sharp text in front; dialled back to 2px
   (user's own call after comparing both live) - enough to take the edge off the photo's crispness
   and read as pushed back a step, without the detail loss further blur introduced. */
.mateen-dashboard-banner--greeting .mateen-dashboard-banner__art {
    background-color: transparent;
    -webkit-mask-image: none;
    mask-image: none;
    top: -20px;
    left: -20px;
    right: -20px;
    bottom: -20px;
    width: calc(100% + 40px);
    height: calc(100% + 40px);
    filter: blur(2px);
    background-image:
        linear-gradient(135deg,
            light-dark(
                color-mix(in srgb, color-mix(in srgb, var(--mud-palette-surface) 55%, var(--mud-palette-tertiary) 45%) 14%, transparent),
                color-mix(in srgb, color-mix(in srgb, var(--mud-palette-surface) 55%, var(--mud-palette-tertiary) 45%) 28%, transparent)
            ) 0%,
            light-dark(
                color-mix(in srgb, color-mix(in srgb, var(--mud-palette-surface) 55%, var(--mud-palette-primary) 45%) 20%, transparent),
                color-mix(in srgb, color-mix(in srgb, var(--mud-palette-surface) 55%, var(--mud-palette-primary) 45%) 32%, transparent)
            ) 100%),
        linear-gradient(135deg,
            light-dark(
                color-mix(in srgb, var(--mud-palette-surface) 96%, var(--mud-palette-tertiary)),
                color-mix(in srgb, var(--mud-palette-surface) 50%, var(--mud-palette-tertiary))
            ) 0%,
            light-dark(
                color-mix(in srgb, var(--mud-palette-surface) 92%, var(--mud-palette-primary)),
                color-mix(in srgb, var(--mud-palette-surface) 45%, var(--mud-palette-primary))
            ) 100%),
        url('images/MosqueLandscape.png');
    background-blend-mode: normal, multiply, normal;
    background-size: cover, cover, cover;
    background-position: center, center, center bottom;
    background-repeat: no-repeat, no-repeat, no-repeat;
}

/* Bottom quote banner - the reference's own version of this is visibly darker/moodier than the
   greeting banner (a dusk-toned photo there; here, a deeper wash of the same brand gradient), which
   is what separates "today" (greeting, bright) from "a closing thought" (this one, quieter). */
.mateen-dashboard-banner--quote {
    background: linear-gradient(120deg,
        color-mix(in srgb, var(--mud-palette-dark) 70%, var(--mud-palette-surface)) 0%,
        color-mix(in srgb, var(--mud-palette-primary) 55%, var(--mud-palette-dark)) 100%);
    padding: 26px 32px;
    min-height: 120px;
    display: flex;
    align-items: center;
}

.mateen-dashboard-banner--quote .mateen-dashboard-banner__art {
    background-color: color-mix(in srgb, white 30%, transparent);
}

/* The large decorative Arabic calligraphy on the right of the bottom banner (KFGQPC Nastaleeq - the
   one shipped font with a genuinely cursive/calligraphic skeleton, see app.css's Mushaf font-face
   block above - the Uthmanic Mushaf faces are built for single ayah lines, not a big free-standing
   wordmark). Purely decorative/static, same spirit as the reference's own banner calligraphy. */
.mateen-dashboard-banner__calligraphy {
    font-family: 'KFGQPC Nastaleeq', 'Traditional Arabic', serif;
    font-size: 2.75rem;
    line-height: 1;
    color: color-mix(in srgb, white 55%, transparent);
    direction: rtl;
    white-space: nowrap;
    flex-shrink: 0;
}

/* One of the 4 top stat cards (Students/Groups/Educators/Attention Needed) - an icon circle with the
   title and headline number stacked beside it on one line (icon-left/title+number-inline, matching
   the reference image's own stat row - corrected 2026-10-02, this used to stack the title below the
   icon instead), then a small caption line underneath that whole row. Still a deliberately different
   shape from .dashboard-card above (that one is icon-left/count-right with no stacked title+number
   and no caption row), not a reuse of it. */
.mateen-dashboard-stat-card {
    height: 90%;
    display: flex;
    flex-direction: column;
    gap: 5px;
    padding: 18px 12px !important;
}

.mateen-dashboard-stat-card__icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    border-radius: 50%;
    flex-shrink: 0;
}

.mateen-dashboard-stat-card__number {
    /* 'Tajawal' fallback added 2026-10-03 - see the html,body rule near the top of this file for why. */
    font-family: 'Inter', 'Tajawal', 'Helvetica Neue', Helvetica, Arial, sans-serif;
    font-weight: 700;
    line-height: 1.1;
}

/* Variant backgrounds/icon colours for the stat-card icon circle and the "Attention Needed" card's
   own tinted surface - color-mix against --mud-palette-surface so every variant tracks dark mode
   automatically, same recipe .dashboard-card-icon-badge already uses for its one (primary-only)
   case. */
.mateen-dashboard-stat-card__icon--primary {
    background-color: color-mix(in srgb, var(--mud-palette-primary) 86%, var(--mud-palette-surface));
    color: #fff;
}

.mateen-dashboard-stat-card__icon--tertiary {
    background-color: color-mix(in srgb, var(--mud-palette-tertiary) 86%, var(--mud-palette-surface));
    color: #fff;
}

.mateen-dashboard-stat-card__icon--secondary {
    background-color: color-mix(in srgb, var(--mud-palette-secondary) 86%, var(--mud-palette-surface));
    color: #fff;
}

.mateen-dashboard-stat-card__icon--error {
    background-color: color-mix(in srgb, var(--mud-palette-error) 86%, var(--mud-palette-surface));
    color: #fff;
}

/* The "Attention Needed" card gets the reference's own red-tinted card surface (every other stat
   card stays on the plain MudPaper surface - a wash on all four would bury the one that is
   genuinely meant to stand out as a warning). */
.mateen-dashboard-stat-card--error {
    background-color: color-mix(in srgb, var(--mud-palette-error) 6%, var(--mud-palette-surface));
    border: 1px solid color-mix(in srgb, var(--mud-palette-error) 24%, var(--mud-palette-lines-default));
}

/* Quran Progress card's small icon+figure rows ("28 Juz memorized", "17 Juz reviewed", "41
   Students") beside the ring - icon, bold number, muted caption, all on one baseline. */
.mateen-dashboard-progress-figure {
    display: flex;
    align-items: center;
    gap: 10px;
}

.mateen-dashboard-progress-figure__number {
    font-weight: 700;
    font-size: 1.05rem;
    font-variant-numeric: tabular-nums;
}

/* Two-line figure text (2026-10-03) - "0 Juz" on its own row, the describing word ("memorized" /
   "reviewed" / "Students") beneath it in a smaller muted caption, rather than one run-on line of
   "0 Juz memorized". __number above still carries the bold/tabular-nums styling; this wrapper only
   stacks it over its own label. */
.mateen-dashboard-progress-figure__text {
    display: flex;
    flex-direction: column;
    line-height: 1.25;
}

.mateen-dashboard-progress-figure__label {
    font-size: 0.75rem;
}

/* One row of the "Quran Progress Overview" per-Juz list and the "Student Progress Health" metric
   list - a label, a MudProgressLinear bar, and a trailing percentage, all on ONE row: label LEFT of
   the bar, percentage RIGHT of it. (2026-10-03 design-fidelity pass: this used to stack a label+
   percent line ABOVE a full-width bar on the line below - the reference design puts all three
   side by side on one row instead, so this was restructured to match.) The Juz list additionally
   scrolls (max-height) rather than pushing the whole dashboard row taller than its siblings - 30
   rows at full height would roughly triple this card's height next to Quran Progress/Memorization
   vs Revision. */
.mateen-dashboard-bar-row {
    display: flex;
    align-items: center;
    gap: 12px;
}

.mateen-dashboard-bar-row__label {
    flex: 0 0 auto;
    font-size: .8125rem;
    color: var(--mud-palette-text-primary);
}

.mateen-dashboard-bar-row__bar {
    flex: 1 1 auto;
    min-width: 0;
}

.mateen-dashboard-bar-row__value {
    flex: 0 0 auto;
    font-size: .8125rem;
    font-variant-numeric: tabular-nums;
    text-align: right;
}

/* Label column width per card - fixed so every row's bar starts at the same x regardless of that
   row's own label length ("Juz 30" vs "Juz 1"; "Memorization" vs "Revision"), matching the
   reference's consistently-aligned bar columns. Two different widths because the two callers' label
   text is a very different length - a single shared width would either clip "Memorization" or leave
   a large dead gap after "Juz 30". */
.mateen-dashboard-juz-list .mateen-dashboard-bar-row__label {
    min-width: 54px;
}

.mateen-dashboard-health-list .mateen-dashboard-bar-row__label {
    min-width: 108px;
}

/* Vertical breathing room between the 4 health bars - scoped to this card only (not the shared
   .mateen-dashboard-bar-row class Quran Progress Overview also uses) since that card's own Juz
   list spacing was never reported as an issue. */
.mateen-dashboard-health-list .mateen-dashboard-bar-row + .mateen-dashboard-bar-row {
    margin-top: 10px;
}

.mateen-dashboard-juz-list {
    max-height: 320px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding-right: 4px;
}

/* Quran Progress Overview's bar fill - a fixed solid colour sampled from the reference image (its
   Juz 30/100% bar - the longest, most reliable flat sample, pixel-sampled at #05BA85), applied
   IDENTICALLY to every bar regardless of that bar's own percentage. Deliberately NOT
   .mateen-progress-band (this app's red-to-blue Hifd-completion decile scale, used on plan/summary
   cards elsewhere): confirmed by pixel-sampling several bars at different percentages in the source
   image that this card's reference does not vary bar hue by value - every Juz row, from 100% down
   to 15%, reads the same solid teal/green. */
.mateen-dashboard-juz-bar .mud-progress-linear-bar {
    background-color: #05BA85 !important;
}

/* Student Progress Health's "Revision" bar - the same teal this app already uses for "Revision"
   everywhere else it appears (the Memorization vs Revision chart's own legend/series colour, and
   the Quran Progress donut's ring - both #00B8A9, Theme/ChartTheming.cs's palette). Previously
   Color.Tertiary (brand periwinkle/blue-violet), which neither matched the reference's teal/green
   Revision bar nor this app's own "Revision = teal" convention used two cards to the left. Same
   override technique as the Juz bar above. */
.mateen-dashboard-health-bar--revision .mud-progress-linear-bar {
    background-color: #00B8A9 !important;
}

/* One row of Today's Activity / Students Needing Attention / Quick Actions / Notifications - an
   avatar or icon, a two-line label/caption, and a trailing chip or chevron, separated by a hairline
   from the row below (not the last one). */
.mateen-dashboard-list-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 0;
}

.mateen-dashboard-list-item + .mateen-dashboard-list-item {
    border-top: 1px solid var(--mud-palette-lines-default);
}

.mateen-dashboard-list-item__body {
    flex: 1 1 auto;
    min-width: 0;
}

/* Quick Actions panel's rows are individually clickable (navigate on click, like .dashboard-card
   already does for the launcher grid) - the hover/cursor affordance is scoped to this modifier so
   the read-only rows elsewhere (Today's Activity, Notifications) are not accidentally made to look
   clickable. */
.mateen-dashboard-list-item--clickable {
    cursor: pointer;
    border-radius: 8px;
    margin: 0 -8px;
    padding: 10px 8px;
    transition: background-color .15s ease-in-out;
}

.mateen-dashboard-list-item--clickable:hover {
    background-color: color-mix(in srgb, var(--mud-palette-primary) 6%, transparent);
}

@media (prefers-reduced-motion: reduce) {
    .mateen-dashboard-list-item--clickable {
        transition: none;
    }
}

/* Student Progress Health's "Overall Status" footer pill - a soft tinted banner the same shape for
   all three StudentHealthStatus values, coloured by modifier class below. */
.mateen-dashboard-status-banner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 12px 16px;
    border-radius: 8px;
    margin-top: 4px;
}

.mateen-dashboard-status-banner--healthy {
    background-color: color-mix(in srgb, var(--mud-palette-success) 14%, var(--mud-palette-surface));
    color: var(--mud-palette-success-darken);
}

.mateen-dashboard-status-banner--warning {
    background-color: color-mix(in srgb, var(--mud-palette-warning) 16%, var(--mud-palette-surface));
    color: var(--mud-palette-warning-darken);
}

.mateen-dashboard-status-banner--needsattention {
    background-color: color-mix(in srgb, var(--mud-palette-error) 14%, var(--mud-palette-surface));
    color: var(--mud-palette-error-darken);
}

/* ---------------------------------------------------------------------------------------------
   2026-10-03 design-fidelity pass - the 6 dashboard content cards (Quran Progress, Memorization vs
   Revision, Quran Progress Overview, Today's Activity, Students Needing Attention, Student
   Progress Health). Source: Design/Mateen Quran Learning Dashboard.png, compared crop-by-crop
   against each card's own region (see each .razor file's own remarks for the specific findings on
   that card).
   --------------------------------------------------------------------------------------------- */

/* Every one of the 6 cards' header title, replacing Typo.h6 (1.25rem/20px, weight 600 per
   Theme/MateenTheme.cs's H6Typography override) - measurably larger/heavier than the reference's
   own header titles, which read closer to body-text size but bold. Typo.subtitle1 (1rem/16px) is
   the closest built-in MudBlazor typo step below h6 that still reads as a label rather than a
   caption; its own default weight (400) is bumped to 700 here since the reference title is clearly
   bold, not regular weight. Paired everywhere with MudIcon Size="Size.Small" (1.25rem/20px,
   previously the un-sized default Medium/1.5rem/24px) - together these are the one shared header
   treatment every card below now uses, so all 6 read as one coherent header system instead of 6
   independent guesses. */
.mateen-dashboard-card-title {
    font-weight: 700;
    line-height: 1.3;
}

/* Today's Activity / Students Needing Attention row avatar - a real photo (Students Needing
   Attention) sized for a list row, not PersonCard.razor's own 72px ringed centred-card avatar.
   Plain circle, no ring/shadow - matches the reference's own clean row photo treatment. */
.mateen-dashboard-list-item__avatar {
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    overflow: hidden;
    background-color: var(--mud-palette-background-gray);
}

    .mateen-dashboard-list-item__avatar img {
        display: block;
        width: 100%;
        height: 100%;
        object-fit: cover;
    }

/* Today's Activity's per-row group icon badge - the reference shows a small tinted circle with a
   people glyph between the time and the group name; this card had no such element at all (time ran
   straight into the group name). Cycles through 4 of this app's own palette roles via color-mix
   (not 4 invented hex values) so each visible row reads as a distinct soft tint, matching the
   reference's own 4 different pastel badges across its 4 example rows, while staying tied to the
   real theme palette in both light and dark mode. */
.mateen-dashboard-activity-avatar {
    flex: 0 0 auto;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: color-mix(in srgb, var(--mud-palette-primary) 14%, var(--mud-palette-surface));
    color: var(--mud-palette-primary);
}

.mateen-dashboard-list-item:nth-of-type(4n+2) .mateen-dashboard-activity-avatar {
    background-color: color-mix(in srgb, var(--mud-palette-info) 16%, var(--mud-palette-surface));
    color: var(--mud-palette-info);
}

.mateen-dashboard-list-item:nth-of-type(4n+3) .mateen-dashboard-activity-avatar {
    background-color: color-mix(in srgb, var(--mud-palette-tertiary) 16%, var(--mud-palette-surface));
    color: var(--mud-palette-tertiary);
}

.mateen-dashboard-list-item:nth-of-type(4n+4) .mateen-dashboard-activity-avatar {
    background-color: color-mix(in srgb, var(--mud-palette-secondary) 14%, var(--mud-palette-surface));
    color: var(--mud-palette-secondary);
}

/* Memorization vs Revision's Week/Month/3 Months toggle - the reference shows a soft pill-shaped
   segmented control (light tinted track, the active option a solid rounded pill, the other two
   plain text with no borders), not MudButtonGroup's default joined/outlined buttons (a visible
   border around each segment, square-ish shared edges). Scoped wrapper rather than a global
   MudButtonGroup restyle, since every other button group in the app should keep MudBlazor's
   standard joined look. */
/* .mateen-dashboard-period-toggle lands on the SAME element MudButtonGroup already puts
   .mud-button-group-root on (role="group"), not a wrapper around it - confirmed by reading the
   rendered DOM - so the group-level rule has no combinator, while the per-button rule below is a
   real descendant selector (the <button>s genuinely nest inside that div). */
.mateen-dashboard-period-toggle.mud-button-group-root {
    background-color: var(--mud-palette-background-gray);
    border-radius: 999px;
    padding: 3px;
    gap: 2px;
    border: none !important;
}

.mateen-dashboard-period-toggle .mud-button-root {
    border: none !important;
    border-radius: 999px !important;
    text-transform: none;
    min-width: 0;
}

/* The active button's own Filled+Color.Primary already paints the primary background, but its text
   comes from the theme's PrimaryContrastText (Theme/MateenTheme.cs) - #F6F1FB in light mode and a
   dark purple (#1A1330) in dark mode, neither of which is actually white. Forced to literal white
   here since that is what was explicitly asked for on this one control, overriding the theme's
   contrast-text choice in both modes. */
.mateen-dashboard-period-toggle .mud-button-root.mud-button-filled.mud-button-filled-primary {
    color: #fff;
}

/* Narrow phone widths: the greeting banner's date/quote column and the bottom banner's calligraphy
   both assume horizontal room they don't have under ~700px - stack instead of truncating. */
@media (max-width: 700px) {
    .mateen-dashboard-banner--greeting .mateen-dashboard-banner__content {
        flex-direction: column;
        align-items: flex-start !important;
    }

    .mateen-dashboard-banner__calligraphy {
        display: none;
    }
}

/* ===== DASHBOARD SKELETONS (DashboardCardSkeleton.razor, LazyRender) =====
   Every dashboard loading placeholder carries .mateen-skel: tinted from the palette so it reads in
   both modes, radius matches the cards, wave sweep recoloured from the text colour (MudBlazor's
   built-in rgba(0,0,0,.04) sweep is invisible on dark surfaces). */
.mateen-skel .mud-skeleton {
    background-color: color-mix(in srgb, var(--mud-palette-primary) 9%, var(--mud-palette-action-disabled-background));
}

.mateen-skel .mateen-skel__bar {
    border-radius: 6px;
}

.mateen-skel .mateen-skel__pill {
    border-radius: 999px;
}

.mateen-skel .mud-skeleton-wave::after {
    background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--mud-palette-text-primary) 9%, transparent), transparent);
}

.mateen-skel__row {
    display: flex;
    align-items: center;
    gap: 12px;
}

.mateen-skel__row--between {
    justify-content: space-between;
}

.mateen-skel--banner {
    border-radius: var(--mud-default-borderradius, 10px);
    overflow: hidden;
    background-color: var(--mud-palette-surface);
}

@media (prefers-reduced-motion: reduce) {
    .mateen-skel .mud-skeleton-wave::after {
        animation: none;
        display: none;
    }
}

/* Landing sections are z-index:1 stacking contexts; lift the one hosting an enlarged role screenshot
   (RoleShowcase) above the later sections so its fixed overlay is not painted underneath them. */
.mateen-landing-section:has(.role-frame:hover, .role-frame:focus-visible, .role-card.is-zoomed) { z-index: 40; }
