/* ===== LEDGERWISE APP STYLES ===== */

/* Override MudBlazor default uppercase on tabs */
.mud-tab {
    text-transform: none;
}

/* Let the project-invoices status hover tooltip escape every MudBlazor
   ancestor that defaults to overflow:hidden: MudCard (rounded-corner clip),
   MudTabs root, .mud-tabs-panels (slide-transition clip), and individual
   .mud-tab-panel. Scoped via :has() so this only fires when an element from
   ProjectInvoices is rendered inside that container; other pages keep their
   normal overflow behaviour. */
.mud-card:has(.pi-status-stack),
.mud-tabs:has(.pi-status-stack),
.mud-tabs-panels:has(.pi-status-stack),
.mud-tab-panel:has(.pi-status-stack) {
    overflow: visible !important;
}

a:not([class]) {
    color: var(--lw-primary-blue);
}

.content {
    padding-top: 1.1rem;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid var(--lw-status-success-fg);
}

.invalid {
    outline: 1px solid var(--lw-status-error-fg);
}

.validation-message {
    color: var(--lw-status-error-fg);
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA9NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, var(--lw-destructive);
    padding: 1rem 1rem 1rem 3.7rem;
    color: var(--lw-white);
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

/* ===== Budget table scroll containers ===== */
.budget-scroll-table {
    border: 1px solid var(--lw-border);
    border-radius: var(--lw-radius-sm);
}

.budget-scroll-table > .mud-table-container {
    max-height: 45vh;
    overflow-y: auto;
}

.fullscreen-budget-table > .mud-table-container {
    max-height: calc(100vh - 140px);
    overflow-y: auto;
}

/* ===== Compact budget rows ===== */
.budget-scroll-table td,
.fullscreen-budget-table td {
    padding-top: 2px !important;
    padding-bottom: 2px !important;
}

.budget-scroll-table .mud-input-control,
.fullscreen-budget-table .mud-input-control {
    margin-top: 0 !important;
}

.budget-scroll-table .mud-input,
.fullscreen-budget-table .mud-input {
    min-height: unset !important;
}

.budget-scroll-table .mud-input-slot,
.fullscreen-budget-table .mud-input-slot {
    padding-top: 4px !important;
    padding-bottom: 4px !important;
    font-size: 0.8125rem;
}

.budget-scroll-table .mud-input-outlined-border,
.fullscreen-budget-table .mud-input-outlined-border {
    padding-top: 0 !important;
}

.budget-scroll-table .mud-input-label,
.fullscreen-budget-table .mud-input-label {
    display: none;
}

.budget-scroll-table .mud-input-control-input-container,
.fullscreen-budget-table .mud-input-control-input-container {
    margin-top: 0 !important;
}
/* Sidebar drawer -- global, non-scoped (Mud subcomponents never receive
   scoped-CSS attributes). Mirrors ledgerwise-ui side-nav.tsx: navy bg,
   1px white/10 right border, 56px bordered logo row (h-14 px-6). */
.mud-drawer.lw-drawer,
.mud-drawer.lw-drawer .mud-drawer-content,
.lw-drawer-header {
    background-color: var(--lw-primary-dark) !important;
}

.mud-drawer.lw-drawer {
    border-right: 1px solid var(--lw-sidebar-border);
}

.mud-drawer.lw-drawer .lw-drawer-header {
    height: 56px;
    min-height: 56px;
    align-items: center;
    padding: 0 24px;
    border-bottom: 1px solid var(--lw-sidebar-border);
}

/* Mobile (<768px): the desktop drawer is replaced by the top sheet in
   MainLayout; hide it and tighten the toolbar (reference mobile px-2). */
@media (max-width: 767.98px) {
    .mud-drawer.lw-drawer {
        display: none !important;
    }

    /* MudLayout still reserves the mini-drawer width (margin-left: 64px on
       both the appbar and main content) even with the drawer hidden -- the
       mobile shell owns the full width. */
    .mud-appbar.lw-appbar,
    .mud-main-content {
        margin-left: 0 !important;
    }

    .mud-appbar.lw-appbar {
        width: 100% !important; /* Mud computes calc(100% - drawer width) */
    }

    /* Content sits flush under the 56px app bar. MudMainContent derives its
   padding-top from the stale 80px theme AppbarHeight; the Dense appbar's
   sibling selector (.mud-appbar-dense ~ .mud-main-content) computes 60px and
   out-specifies a bare .mud-main-content rule, so match that selector. */
.mud-appbar-dense ~ .mud-main-content,
.mud-main-content {
    padding-top: 56px;
}

.mud-appbar.lw-appbar .mud-toolbar {
        padding-left: 8px;
        padding-right: 8px;
        gap: 4px;
    }
}

/* Collapsed (mini) drawer centers the emblem. */
.mud-drawer--mini.lw-drawer .lw-drawer-header {
    justify-content: center;
    padding: 0 8px;
}

/* AppBar (header) -- global, non-scoped. Mirrors ledgerwise-ui header.tsx
   "h-14 border-b border-border bg-background px-6". Lives here (not in
   MainLayout.razor.css) because MudAppBar is a subcomponent and never
   receives this file's scope attribute. */
.mud-appbar.lw-appbar {
    background-color: #ffffff !important;
    border-bottom: 1px solid rgba(145, 158, 171, 0.2) !important;
    box-shadow: none !important;
    color: #18181b;
    height: 56px !important;
    min-height: 56px !important;
    font-family: var(--lw-font-family, sans-serif);
}

/* Scrolled state -- mirrors ledgerwise-ui TopBar: translucent white + blur
   once the page has scrolled. Toggled by js/blazor-helpers.js. Declared after
   the base rule so the equal-importance background wins. */
.mud-appbar.lw-appbar.lw-appbar-scrolled {
    background-color: rgba(255, 255, 255, 0.85) !important;
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
}

/* Content sits flush under the 56px app bar. MudMainContent derives its
   padding-top from the stale 80px theme AppbarHeight; the Dense appbar's
   sibling selector (.mud-appbar-dense ~ .mud-main-content) computes 60px and
   out-specifies a bare .mud-main-content rule, so match that selector. */
.mud-appbar-dense ~ .mud-main-content,
.mud-main-content {
    padding-top: 56px;
}

.mud-appbar.lw-appbar .mud-toolbar {
    height: 56px !important;
    min-height: 56px !important;
    padding-left: 24px;
    padding-right: 24px;
    gap: 12px;
}

/* ===== LOGIN TRANSITION OVERLAY ===== */
@keyframes lw-lt-bg-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}
@keyframes lw-lt-logo-in {
    from { opacity: 0; transform: translateY(-30px); }
    to   { opacity: 1; transform: translateY(0); }
}
@keyframes lw-lt-float {
    0%,  100% { transform: translateY(0)    rotate(0deg); }
    25%        { transform: translateY(-4px) rotate(-0.5deg); }
    75%        { transform: translateY(3px)  rotate(0.5deg); }
}
@keyframes lw-lt-logo-out {
    to { opacity: 0; transform: scale(4); }
}
@keyframes lw-lt-bg-out {
    to { opacity: 0; }
}

#lw-lt-overlay {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}

.lw-lt-bg {
    position: absolute;
    inset: 0;
    background: #020B2B;
}

.lw-lt-logo {
    position: relative;
    z-index: 1;
}

.lw-lt-box {
    width: 100px;
    height: 100px;
    border-radius: 16px;
    background: #020B2B;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

.lw-lt-box img {
    width: 48px;
    height: 48px;
    object-fit: contain;
}

@media (min-width: 768px) {
    .lw-lt-box {
        width: 140px;
        height: 140px;
    }

    .lw-lt-box img {
        width: 68px;
        height: 68px;
    }
}

/* Phase: enter (login page -- backdrop fades in, logo hidden) */
#lw-lt-overlay[data-phase="enter"] .lw-lt-bg {
    animation: lw-lt-bg-in 0.5s ease-out forwards;
}
#lw-lt-overlay[data-phase="enter"] .lw-lt-logo {
    opacity: 0;
}

/* Phase: hold (new page -- logo drops in then floats) */
#lw-lt-overlay[data-phase="hold"] .lw-lt-bg {
    opacity: 1;
}
#lw-lt-overlay[data-phase="hold"] .lw-lt-logo {
    animation: lw-lt-logo-in 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}
#lw-lt-overlay[data-phase="hold"] .lw-lt-box {
    animation: lw-lt-float 3s ease-in-out infinite 0.5s;
}

/* Phase: exit */
#lw-lt-overlay[data-phase="exit"] .lw-lt-bg {
    animation: lw-lt-bg-out 0.8s ease-in-out forwards;
}
#lw-lt-overlay[data-phase="exit"] .lw-lt-logo {
    animation: lw-lt-logo-out 1.0s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}

/* While the hold-phase overlay is on screen, paint everything underneath dark
   so the new page's layout containers (MudMainContent, etc.) don't show a
   half-white / half-blue split before the overlay fades out. */
html:has(#lw-lt-overlay[data-phase="hold"]),
html:has(#lw-lt-overlay[data-phase="enter"]) {
    background: #020B2B !important;
}
html:has(#lw-lt-overlay[data-phase="hold"]) body,
html:has(#lw-lt-overlay[data-phase="enter"]) body,
html:has(#lw-lt-overlay[data-phase="hold"]) body .mud-layout,
html:has(#lw-lt-overlay[data-phase="hold"]) body .mud-main-content,
html:has(#lw-lt-overlay[data-phase="hold"]) body .mud-container {
    background-color: #020B2B !important;
}

/* Home dashboard: pending-invoices hover popover (MudPopover wrapper) --
   teleported by MudPopoverProvider, so styled globally. Inner content classes
   are styled in Home.razor.css and retain their scoped-CSS attribute when
   teleported. We render with Paper="false" so the MudPopover wrapper has no
   default chrome; the .hd-pending-pop div below paints the dark surface. The
   shadow lives here because it conceptually belongs to the popover, not its
   inner content. */
.hd-pending-popover.mud-popover {
    background-color: transparent;
    box-shadow: 0 12px 32px rgba(2, 11, 43, 0.32);
    margin-bottom: 6px;
    pointer-events: auto;
}


/* ===== Lw modal shell (source: ledgerwise-app src/components/ui/dialog.tsx).
   Region chrome lives in the LwModalHeader/LwModalBody/LwModalFooter atoms
   (Components/Shared/Atoms/); only the dialog frame and the neutralization
   of MudDialog's own region wrappers can live here, because MudDialog
   teleports to MudDialogProvider, out of scoped-CSS reach. Apply
   Class="lw-modal" on any MudDialog composing those atoms. ===== */
.lw-modal.mud-dialog {
    border-radius: var(--lw-radius-card);
    overflow: hidden;
    /* Reference dialog.tsx panel: max-h-[92dvh] flex-col, body scrolls. */
    display: flex;
    flex-direction: column;
    max-height: 92dvh;
    /* Panel = form-surface; the header atom paints itself white on top,
       body/footer are transparent so this light tint shows through. */
    background: var(--lw-form-surface);
}

/* Zero Mud's region wrappers so the atoms own padding/background/borders. */
.lw-modal .mud-dialog-title {
    padding: 0;
    flex-shrink: 0;
}


/* Reference dialog size=sm (dialog.tsx md:max-w-[480px]); Mud MaxWidth.Small
   is 600px, so the sm dialogs narrow themselves with this modifier. */
.lw-modal.lw-modal--sm {
    max-width: 480px;
}

/* Reference dialog size=md (dialog.tsx md:max-w-[640px]); pair with Mud
   MaxWidth.Medium (960px) so this cap is what actually sizes the dialog. */
.lw-modal.lw-modal--md {
    max-width: 640px;
}

/* Reference dialog size=lg (dialog.tsx md:max-w-[820px]); pair with Mud
   MaxWidth.Medium so this cap is what actually sizes the dialog. */
.lw-modal.lw-modal--lg {
    max-width: 820px;
}

/* Reference DialogBody: flex-1 overflow-y-auto. min-height:0 lets the flex
   child actually shrink below its content height so the body scrolls. */
.lw-modal .mud-dialog-content {
    padding: 0;
    background: transparent;
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

.lw-modal .mud-dialog-actions {
    display: block;
    padding: 0;
    margin: 0;
    background: transparent;
    flex-shrink: 0;
}

/* Fullscreen lw-modals (DialogOptions.FullScreen): edge-to-edge geometry.
   Mud's own .mud-dialog-fullscreen sizing loses to .lw-modal.mud-dialog on
   specificity, so re-assert it here -- no floating 92dvh clamp, square
   corners. The base .mud-dialog-content overflow-y:auto then scrolls the
   body internally (Bug-658: the lien waiver document must scroll to reach
   the sign block). */
.lw-modal.mud-dialog.mud-dialog-fullscreen {
    max-height: none;
    border-radius: 0;
}

/* Non-scroll dialogs let inline popout menus (SearchableDropdown) escape
   instead of clipping at the body/frame edge; rounded corners survive via
   the header/footer radii below (2026-07-23). Scroll dialogs keep the
   clip -- their bodies must scroll. Fullscreen dialogs are exempt too
   (Bug-658): the frame IS the viewport, so there is nowhere to escape to,
   and dropping the clip turns any body taller than the screen into an
   unscrollable overflow. Placed AFTER the base content rule so the
   equal-specificity override wins. */
.lw-modal:not(.lw-modal--scroll):not(.mud-dialog-fullscreen).mud-dialog {
    overflow: visible;
}

.lw-modal:not(.lw-modal--scroll):not(.mud-dialog-fullscreen) .mud-dialog-content {
    /* BOTH axes must be visible: per spec, overflow-y:visible computes to
       auto whenever overflow-x is non-visible (Mud sets it), which silently
       re-enabled the clip. */
    overflow: visible !important;
    /* Lift the content's paint order above the footer so an open popout
       menu overlays the action row instead of being painted over. */
    position: relative;
    z-index: 1;
}

.lw-modal:not(.lw-modal--scroll):not(.mud-dialog-fullscreen) .lw-modal-header {
    border-radius: var(--lw-radius-card) var(--lw-radius-card) 0 0;
}

.lw-modal:not(.lw-modal--scroll):not(.mud-dialog-fullscreen) .lw-modal-footer {
    border-radius: 0 0 var(--lw-radius-card) var(--lw-radius-card);
}

/* Tall, content-heavy dialogs (lw-modal--scroll): bound the frame to the
   viewport as a flex column so the LwModalFooter action row stays pinned and
   visible while the body region scrolls internally. Without this, a body
   taller than the viewport pushes the footer past the frame's overflow:hidden
   clip and the primary action (e.g. Submit) becomes unreachable. */
.lw-modal--scroll.mud-dialog {
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 96px);
}

.lw-modal--scroll .mud-dialog-content {
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

.lw-modal--scroll .lw-modal-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
}

/* Summary panel inside modal bodies (label/value rows on a white card --
   the "what am I acting on" recap used by confirm-style modals). */
.lw-modal-summary {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 12px 16px;
    background: var(--lw-white);
    border: 1px solid var(--lw-border);
    border-radius: var(--lw-radius-card);
}

.lw-modal-summary-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
}

.lw-modal-summary-label {
    font-size: 13px;
    color: var(--lw-gray-graphite);
}

.lw-modal-summary-value {
    font-size: 14px;
    font-weight: var(--lw-font-semibold);
    color: var(--lw-text-primary);
    text-align: right;
}

/* ===== Confirm-body pieces (ConfirmationModal) =====
   Tinted icon chip beside the message inside a canonical LwModalBody
   (reference confirm-delete-dialog.tsx chip). Global because MudDialog
   teleports to the provider. */
.lw-confirm-row {
    display: flex;
    align-items: flex-start;
    gap: 16px;
}

.lw-confirm-chip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    flex-shrink: 0;
    border-radius: var(--lw-radius-control);
}

.lw-confirm-chip--danger  { background: var(--lw-status-error-bg);   color: var(--lw-destructive); }
.lw-confirm-chip--warning { background: var(--lw-status-warning-bg); color: var(--lw-status-warning-fg); }
.lw-confirm-chip--info    { background: var(--lw-status-info-bg);    color: var(--lw-primary-blue); }

.lw-confirm-desc {
    margin: 0;
    font-size: 14px;
    line-height: 1.5;
    color: var(--lw-gray-graphite);
}

/* Project filter modal: compact dialog (reference dialog size="sm"). */
.lw-filter-modal.mud-dialog {
    max-width: 440px;
}

/* Button-embedded loading spinner -- the reference uses a single rotating
   arc (lucide Loader2 + animate-spin); MudProgressCircular's dash+rotate
   combo reads as a "spinning spinner gif" inside our flat buttons. Colors
   derive from currentColor so it works on navy fills and outline buttons. */
.lw-btn-spinner {
    display: inline-block;
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    box-sizing: border-box;
    border-radius: 9999px;
    border: 2px solid color-mix(in srgb, currentColor 25%, transparent);
    border-top-color: currentColor;
    animation: lw-btn-spin 0.8s linear infinite;
}

@keyframes lw-btn-spin {
    to { transform: rotate(360deg); }
}
