﻿/* Global styles. Nearly everything is MudBlazor or a component's own .razor.css, so keep
   this file to rules that genuinely have nowhere else to live.

   The Blazor template's defaults were removed: .valid.modified / .invalid /
   .validation-message belong to EditForm, .blazor-error-boundary to <ErrorBoundary>, and
   .form-floating / .form-check-input to Bootstrap. This app uses none of them, and their
   hardcoded #e50000 and #26b050 ignored the theme palette. */

/* FocusOnNavigate (Routes.razor) and the skip link move focus to a heading and to the
   main-content container. Neither is natively focusable, so both carry tabindex="-1" and
   are only ever focused programmatically — the ring there is noise rather than a cue.
   Focus indication on genuinely interactive elements is untouched. */
[tabindex="-1"]:focus {
    outline: none;
}

/* The standalone status pages -- /Error, /not-found, /forbidden -- share one look, so these
   rules cannot live in any one component's .razor.css. They mirror the landing page on
   purpose: all four render outside the portal chrome under ErrorLayout or LandingLayout, and
   a visitor who never got a session should see the same public front door throughout rather
   than something dressed as the signed-in app. */
.status-page {
    min-height: 100vh;
    margin: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 2rem;
    background:
        radial-gradient(ellipse 80% 60% at 20% 20%, rgba(56, 189, 248, 0.22), transparent 55%),
        radial-gradient(ellipse 70% 50% at 85% 75%, rgba(13, 148, 136, 0.16), transparent 50%),
        linear-gradient(155deg, #071526 0%, #0f2d55 45%, #1d4ed8 100%);
    color: #ffffff;
}

.status-title {
    margin: 0 0 1rem;
    font-size: clamp(2rem, 5vw, 3rem);
    font-weight: 650;
    letter-spacing: -0.03em;
    line-height: 1.1;
    color: #ffffff;
}

.status-message {
    margin: 0 0 1.5rem;
    max-width: 34rem;
    font-size: 1.05rem;
    line-height: 1.6;
    color: rgba(255, 255, 255, 0.85);
}

.status-reference {
    margin: 0 0 2rem;
    font-size: 0.9rem;
    color: rgba(255, 255, 255, 0.65);
}

.status-reference code {
    word-break: break-all;
}

.status-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 11rem;
    min-height: 3rem;
    padding: 0.85rem 2rem;
    border-radius: 0.5rem;
    background: #ffffff;
    color: #071526;
    font-size: 1.05rem;
    font-weight: 700;
    line-height: 1;
    text-decoration: none;
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.35);
}

.status-action:hover,
.status-action:focus-visible {
    background: #ffffff;
    color: #071526;
    outline: 3px solid rgba(255, 255, 255, 0.65);
    outline-offset: 3px;
}

/* Drag-and-drop file areas. See Components/Shared/FileDropZone.razor.
   Coloured from MudBlazor's palette variables rather than fixed values so the zone
   follows whichever of the three themes the login has chosen. */
.file-drop-zone {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    width: 100%;
    padding: 1.5rem 1rem;
    /* Long-hand, and every var() carries a fallback. A var() that resolves to nothing
       inside the `border` shorthand invalidates the whole declaration, which leaves the
       zone with no border at all rather than an uncoloured one. */
    border-width: 2px;
    border-style: dashed;
    border-color: var(--mud-palette-lines-default, currentColor);
    border-radius: var(--mud-default-borderradius, 4px);
    background-color: var(--mud-palette-surface, transparent);
    text-align: center;
    transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1),
                border-color 250ms cubic-bezier(0.4, 0, 0.2, 1);
}

.file-drop-zone:hover,
.file-drop-zone:focus-within {
    border-color: var(--mud-palette-primary, currentColor);
}

.file-drop-zone--active {
    border-color: var(--mud-palette-primary, currentColor);
    background-color: var(--mud-palette-action-default-hover, transparent);
}

.file-drop-zone--disabled {
    color: var(--mud-palette-action-disabled, gray);
}

.file-drop-zone--disabled,
.file-drop-zone--disabled:hover {
    border-color: var(--mud-palette-lines-default, currentColor);
}

/* The real file input, stretched invisibly over the whole zone so that a drop anywhere
   in it lands on the input and the browser does the rest. */
.file-drop-zone__input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
}

.file-drop-zone--disabled .file-drop-zone__input {
    cursor: default;
}

/* Lifted back above the overlay: the button stays the click and keyboard target for
   people who are not dragging anything. */
.file-drop-zone__action {
    position: relative;
    z-index: 1;
}

/* ---------------------------------------------------------------------------
   Fly-outs

   A MudBlazor dialog reshaped into a panel against the right edge of the window.
   DialogOptions can position the container but cannot carry a class, so every rule
   here hangs off `.flyout`, which FlyoutPanel puts on the dialog itself. This file
   is loaded after MudBlazor.min.css, so `.mud-dialog.flyout` wins on specificity
   without any !important.

   MudBlazor's own title/content/actions rules already make the body the only part
   that scrolls, so nothing here repeats them.
   --------------------------------------------------------------------------- */

/* The centre-right position leaves 32px between the dialog and the window edge,
   which on a full-height panel reads as a misalignment rather than a margin.
   Scoped with :has so ordinary right-positioned dialogs keep their gap. */
.mud-dialog-container.mud-dialog-centerright:has(.flyout) {
    padding-right: 0;
}

.mud-dialog.flyout {
    width: 520px;
    max-width: 100vw;
    height: 100%;
    /* Beats the base rule's calc(100dvh - appbar): a fly-out covers the app bar
       rather than tucking under it. */
    max-height: 100%;
    /* Square: the panel is flush against the window edge, not floating over it. */
    border-radius: 0;
}

/* A detail view lays two columns out; a form does not. */
.mud-dialog.flyout--wide {
    width: 880px;
}

.flyout__actions {
    border-top: 1px solid var(--mud-palette-lines-default);
}

/* A dialog is drawn on `surface`, and so is every MudPaper on it, so a panel full of
   sections came out as one flat sheet with nothing to say where one card ended and the
   next began. The body drops to the page background instead -- the same relationship a
   page already has with the papers on it -- and the header and footer stay on surface so
   they read as chrome the body scrolls under.

   `background` and `surface` are only a few points apart in the light themes, which is
   enough for a page but not for a panel of stacked cards, so the papers also take the
   hairline the theme already uses for dividers. Alerts are left alone: they carry their
   own severity colour and a border would fight it. */
.mud-dialog.flyout .mud-dialog-content {
    background-color: var(--mud-palette-background);
}

.mud-dialog.flyout .mud-dialog-title,
.mud-dialog.flyout .flyout__actions {
    background-color: var(--mud-palette-surface);
}

.mud-dialog.flyout .mud-dialog-content .mud-paper:not(.mud-alert) {
    border: 1px solid var(--mud-palette-lines-default);
}

/* And a card is lifted off the body, which is what carries the separation in Midnight.
   A light theme tells a card from its background two ways -- the step down to the page
   colour, and the card's own drop shadow. Midnight has neither to spare: its background
   and surface are four points apart (#0B1220 against #111827), and a black shadow on a
   near-black body contributes nothing at all, which left the hairline doing the whole job.

   This is Material's dark elevation overlay: a card is mixed a little way towards white,
   so the further from the body it is meant to sit, the lighter it is drawn. On Aurora and
   Slate the surface is already #FFFFFF, so the mix resolves to the same white and nothing
   changes -- one rule, and only the theme that needs it moves.

   The plain declaration first is the fallback for a browser without color-mix(), which
   keeps the hairline and loses only the lift. */
.mud-dialog.flyout .mud-dialog-content .mud-paper:not(.mud-alert) {
    background-color: var(--mud-palette-surface);
    background-color: color-mix(in srgb, var(--mud-palette-surface) 92%, #ffffff 8%);
}

@media (prefers-reduced-motion: no-preference) {
    /* Replaces MudBlazor's centre-scale open, which on a panel pinned to one edge
       looks like the window is growing rather than something sliding in. */
    .mud-dialog.flyout {
        animation: flyout-slide-in 180ms ease-out;
    }

    @keyframes flyout-slide-in {
        from {
            transform: translateX(100%);
        }

        to {
            transform: translateX(0);
        }
    }
}

/* Below MudBlazor's sm breakpoint a 520px panel would be the whole screen minus a
   sliver of unusable list, so it becomes a full sheet instead. */
@media (max-width: 599.98px) {
    .mud-dialog.flyout,
    .mud-dialog.flyout--wide {
        width: 100vw;
    }
}

/* ---------------------------------------------------------------------------
   Busy overlay

   A translucent veil over a block of UI while it waits on the server -- a save, an
   upload, a delete -- so the action reads as "working" rather than a screen that
   quietly froze behind a disabled button. The plain declaration is the fallback for a
   browser without color-mix(); it loses the tint but keeps the veil opaque enough to
   read as blocking.
   --------------------------------------------------------------------------- */
.busy-overlay {
    position: relative;
}

.busy-overlay__veil {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--mud-palette-surface);
    background-color: color-mix(in srgb, var(--mud-palette-surface) 80%, transparent);
    z-index: 10;
}

/* ---------------------------------------------------------------------------
   Full-height task lists

   A table shrink-wraps its rows, so a list with four tasks used to sit in the top
   third of an empty page and read as something still loading. These rules give the
   list the rest of the window instead: the frame reaches the bottom whatever the row
   count, the header stays put, the pager stays on the bottom edge, and the rows
   scroll between them.
   --------------------------------------------------------------------------- */
.table-fill {
    /* What MudMainContent leaves us: the viewport, less the padding-top it takes for a
       dense app bar and less the pb-4 the layout puts underneath. The first term is
       copied from MudBlazor's own `.mud-appbar-dense ~ .mud-main-content` rule, var and
       all, so the two stay in step if the app bar height is ever themed. */
    --table-fill-height: calc(100dvh - (var(--mud-appbar-height, 64px) - var(--mud-appbar-height, 64px) / 4) - 16px);

    display: flex;
    flex-direction: column;
    height: var(--table-fill-height);
}

/* A floor rather than a ceiling, for a page that carries more than a heading above its
   list -- the dashboard has a greeting, the stat tiles and an action row, which on a short
   window is already more than fits. The list still grows into whatever space is spare, but
   when there is none the page scrolls as usual instead of squeezing the list to nothing. */
.table-fill--soft {
    height: auto;
    min-height: var(--table-fill-height);
}

/* Everything above the list keeps its own height... */
.table-fill > * {
    flex: 0 0 auto;
}

/* ...and the list is the only thing told to grow into what is left. A progress bar or an
   empty-state alert inside it keeps its own height and simply leaves the rest blank.
   On the dashboard the list sits one level down, inside the split, so the split is what
   grows there and passes the height on. */
.table-fill > .table-fill__body,
.table-fill > .dashboard-split {
    flex: 1 1 auto;
    /* Without this a flex item refuses to shrink below its content, and a long table
       would push the pager off the bottom of the window instead of scrolling. */
    min-height: 0;
}

.table-fill__body {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

.table-fill__body > .mud-table {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

.table-fill__body > .mud-table > .mud-table-container {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

/* ---------------------------------------------------------------------------
   Dashboard split

   The home page carries two lists that answer different questions -- what needs
   doing, and what has lately been said about it -- so they sit next to each other
   rather than one below the fold. The task table keeps the full-height treatment
   above; the comments column scrolls inside the same frame so the two bottom edges
   line up however long either list is.
   --------------------------------------------------------------------------- */
.dashboard-split {
    display: flex;
    gap: 16px;
    align-items: stretch;
}

.dashboard-split > section {
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
}

.dashboard-split__main {
    flex: 1 1 60%;
}

/* A basis rather than a share, so the comments stay a readable column instead of
   growing with the window while the task table is the thing that wants the room. */
.dashboard-split__side {
    flex: 1 1 320px;
    max-width: 420px;
}

.dashboard-split__scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

/* Below MudBlazor's md breakpoint two columns leave neither of them readable, so they
   stack -- and the comments give up their own scrollbar, because a short nested
   scroller inside a page that already scrolls is a trap on a touch screen. */
@media (max-width: 959.98px) {
    .dashboard-split {
        flex-direction: column;
    }

    .dashboard-split__side {
        max-width: none;
    }

    .dashboard-split__scroll {
        overflow-y: visible;
    }
}

/* One comment: who said it and how it opened, on two lines that do not wrap. A card
   apiece was taller than the table beside it, which made the comments -- not the work --
   the thing deciding how far down the page went. */
.dashboard-comment {
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--mud-palette-lines-default, currentColor);
}

.dashboard-comment:last-child {
    border-bottom: none;
}

.dashboard-comment__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
}

/* min-width:0 is what lets a flex item's text actually ellipsise: without it the item
   refuses to shrink below its content and the timestamp is pushed out of the column. */
.dashboard-comment__task,
.dashboard-comment__body {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dashboard-comment__when {
    flex: 0 0 auto;
}


/* A task number is read as a code, not as prose: tabular figures so the digits line up
   down a column, and never wrapped -- T26-001 broken across two lines stops being one
   thing. */
.task-number {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   App bar folding

   Six targets -- title, language, theme, profile, sign out, and the company
   itself -- fit a desktop bar but not a phone-width one. Below the sm breakpoint
   everything but the drawer toggle and the company (the one thing worth a glance
   mid-task) collapses into a single "more" menu instead of wrapping or shrinking
   in place. See MainLayout.razor for which controls carry which class.
   --------------------------------------------------------------------------- */
.appbar-show-narrow {
    display: none;
}

@media (max-width: 599.98px) {
    .appbar-hide-narrow {
        display: none !important;
    }

    .appbar-show-narrow {
        display: inline-flex;
    }
}

/* The sign-in/out anchor repeated inside the narrow menu, styled to sit among
   MudMenuItems without becoming one -- see the comment in MainLayout.razor on
   why it stays a plain <a>. */
.appbar-auth-link--menu {
    display: block;
    padding: 6px 16px;
    color: inherit;
    text-decoration: none;
}

.appbar-auth-link--menu:hover,
.appbar-auth-link--menu:focus-visible {
    background-color: var(--mud-palette-action-default-hover);
}
