﻿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;
}

/* TinyMCE renders its floating toolbar popups (the "Paragraph" blocks menu, color pickers, etc.)
   into a .tox-tinymce-aux container appended to <body>, with a bundled z-index of 1201 — lower
   than MudBlazor's dialog stack. When the editor is opened inside a MudDialog, those popups paint
   underneath the dialog and are invisible. Force them above every MudBlazor layer. */
.tox-tinymce-aux {
    z-index: 20000 !important;
}

.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;
}

/* Served from wwwroot, NOT from Google Fonts.

   The font used to be fetched from fonts.gstatic.com at runtime, which meant that on any machine
   that could not reach it — no internet, a corporate proxy, a firewall — every Lao string silently
   fell back to whatever the operating system happened to have. Nothing failed and nothing was
   logged; the text simply rendered in the wrong shapes. A Lao-market back office cannot have its
   own language depend on an outside service being reachable.

   One file, ~21 KB: this is the variable font, and Google's own stylesheet points weights 400, 500
   and 700 at exactly this URL. Only the Lao subset is shipped — Latin has no matching unicode-range
   here, so it falls through to Roboto/Helvetica/Arial below, which is what English already uses.

   Licensed under the SIL Open Font License; see fonts/OFL.txt. */
@font-face {
    font-family: 'Noto Sans Lao';
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url('fonts/noto-sans-lao-v33-lao.woff2') format('woff2');
    unicode-range: U+0E81-0EDF, U+200C-200D, U+25CC;
}

/* Applied when the active culture is Lao — MudBlazor generates a separate
   --mud-typography-*-family variable per typography variant rather than having them all derive from
   one, so each needs to be overridden here for full coverage. */
html[lang="lo"] {
    font-family: 'Noto Sans Lao', Roboto, Helvetica, Arial, sans-serif;
    --mud-typography-default-family: 'Noto Sans Lao', Roboto, Helvetica, Arial, sans-serif;
    --mud-typography-h1-family: 'Noto Sans Lao', Roboto, Helvetica, Arial, sans-serif;
    --mud-typography-h2-family: 'Noto Sans Lao', Roboto, Helvetica, Arial, sans-serif;
    --mud-typography-h3-family: 'Noto Sans Lao', Roboto, Helvetica, Arial, sans-serif;
    --mud-typography-h4-family: 'Noto Sans Lao', Roboto, Helvetica, Arial, sans-serif;
    --mud-typography-h5-family: 'Noto Sans Lao', Roboto, Helvetica, Arial, sans-serif;
    --mud-typography-h6-family: 'Noto Sans Lao', Roboto, Helvetica, Arial, sans-serif;
    --mud-typography-subtitle1-family: 'Noto Sans Lao', Roboto, Helvetica, Arial, sans-serif;
    --mud-typography-subtitle2-family: 'Noto Sans Lao', Roboto, Helvetica, Arial, sans-serif;
    --mud-typography-body1-family: 'Noto Sans Lao', Roboto, Helvetica, Arial, sans-serif;
    --mud-typography-body2-family: 'Noto Sans Lao', Roboto, Helvetica, Arial, sans-serif;
    --mud-typography-button-family: 'Noto Sans Lao', Roboto, Helvetica, Arial, sans-serif;
    --mud-typography-caption-family: 'Noto Sans Lao', Roboto, Helvetica, Arial, sans-serif;
    --mud-typography-overline-family: 'Noto Sans Lao', Roboto, Helvetica, Arial, sans-serif;
}

.tl-brand {
    display: flex;
    align-items: center;
    margin-left: 8px;
    color: inherit;
    text-decoration: none;
}

.tl-brand-logo {
    flex-shrink: 0;
}

/* The company's uploaded Logo 1, when one is configured.

   Height-constrained with width:auto so any aspect ratio fits the bar — the four logo slots hold a
   square emblem and three wider lockups, and a fixed width would squash whichever one is set.

   ⚠ Placed straight on the app bar with no background, by request. Worth knowing what that costs:
   the bar is the primary (navy) colour and the current Logo 1 is navy and red on a transparent
   ground, so the navy half of the shield reads faintly against it. A white pill behind the image
   (background/border-radius/padding) is the one-line fix if it ever looks wrong; the better one is a
   reversed, light-on-dark logo — but that cannot go in slot 1, because the PDFs use the same image
   on white paper, where a light logo would disappear instead. */
.tl-brand-logo-image {
    height: 32px;
    width: auto;
    max-width: 160px;
    object-fit: contain;
}

/* On small screens (phones and narrow tablet/desktop windows — MudBlazor's xs+sm range) the app
   bar has too little room for the icon buttons, language toggle, and profile menu alongside the
   full company name — keep only the logo mark. */
@media (max-width: 959.98px) {
    .tl-brand-text {
        display: none;
    }
}

/* The profile menu shows the signed-in user's full name when there's room, and falls back to
   just "Profile" on small screens where the full name wouldn't fit in the app bar. */
.tl-profile-label {
    display: none;
}

@media (max-width: 959.98px) {
    .tl-profile-fullname {
        display: none;
    }

    .tl-profile-label {
        display: inline;
    }
}

/* Clickable Dashboard stat/quick-link tiles — a plain hover lift is enough to signal they're
   navigable, matching MudBlazor's own elevation-on-hover convention used elsewhere. */
/* The hover lift, shared by every card on the dashboard so they all respond the same way.
   ⚠ Deliberately separate from the pointer cursor below: the lift is presentation, the cursor is a
   PROMISE that clicking does something. The summary cards are read-only, so they get the first and
   not the second — a pointer on a card that ignores the click is how a UI teaches people that
   clicking is pointless. */
.tl-hover-lift,
.tl-dashboard-tile {
    transition: box-shadow 0.15s ease-in-out, transform 0.15s ease-in-out;
}

.tl-hover-lift:hover,
.tl-dashboard-tile:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    transform: translateY(-2px);
}

/* Only the tiles that actually navigate. */
.tl-dashboard-tile {
    cursor: pointer;
}

/* Applied to a table/pagination wrapper while a page change (or any reload) is in flight — dims
   and disables pointer interaction so a slow network reads as "working on it" instead of a
   frozen/broken UI, without needing a full-screen overlay for what's usually a sub-second wait. */
.tl-loading-dim {
    opacity: 0.5;
    pointer-events: none;
    transition: opacity 0.15s ease-in-out;
}

/* Outlined text fields/selects/etc. share --mud-default-borderradius (10px) with cards, buttons,
   and dialogs sitewide — that's too heavy on a compact input border, so it's overridden here
   specifically for input outlines rather than lowering the shared theme token everywhere. */
.mud-input-outlined-border {
    border-radius: 4px;
}

/* Shared PageLoading component (Components/Shared/PageLoading.razor) — centres the spinner in the
   content area rather than letting it sit in the top-left corner. min-height is set inline so each
   usage can size the reserved space. */
.tl-page-loading {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
}

/* Signature capture pads (Application wizard). The canvas width/height ATTRIBUTES set the drawing
   buffer; these rules pin the on-screen size so a pad renders identically wherever it sits. Without
   the explicit size + align-self, a pad placed inside a MudStack (a flex column, which stretches
   its children) blows up to the full container width — and because a canvas with height:auto keeps
   its aspect ratio, it grows just as dramatically in height. */
.tl-signature-pad {
    display: block;
    width: 500px;
    /* height:auto, NOT a fixed height. A canvas is a replaced element, so auto height follows the
       intrinsic ratio of its width/height ATTRIBUTES -- which is what keeps the drawing buffer and
       the on-screen box in proportion once max-width kicks in. Pinning the height instead lets the
       box squash horizontally while the buffer stays 500 wide, and signaturePad.js then scales x and
       y by different factors: the stroke still tracks the cursor, but the SAVED png comes out
       horizontally stretched compared to what was signed. Also serves both pad sizes in use
       (500x200 on the profile, 500x150 on the application) from one rule. */
    height: auto;
    max-width: 100%;
    align-self: flex-start;
    flex: 0 0 auto;
    border: 1px solid var(--mud-palette-lines-default);
    background: white;
    touch-action: none;
}

/* Shared PageHeader component (Components/Shared/PageHeader.razor) — title stacked above any
   action buttons, never on the same row, regardless of MudBlazor's own default display rules. */
.tl-page-header {
    display: block;
    width: 100%;
}

.tl-page-header-title {
    display: block;
    width: 100%;
}

.tl-page-header-actions {
    display: block;
    width: 100%;
}
/* Multi-line fields the user may drag taller — the underwriting assessment's narrative boxes, where an
   entry can run to a few hundred words and a fixed window means reading it through a slot.

   Manual only. MudBlazor 9.7.0 has no AutoGrow parameter -- setting one compiles, because these
   components capture unmatched attributes, and then silently does nothing but add an attribute to the
   DOM. So the fields carry a generous starting Lines instead, and the handle covers the rest.

   VERTICAL only, deliberately. `resize: both` lets the handle drag the box wider than the grid column
   it sits in, which overlaps the field beside it and cannot be undone without a reload. */
/* The selector has to out-specify MudBlazor's own `.mud-input > textarea.mud-input-root { resize: none }`
   (0,2,2). `.tl-resizable textarea` alone is (0,1,1) and loses silently -- the handle simply never
   appears. Matching its shape and adding our class makes it (0,3,2), which wins without !important. */
.tl-resizable .mud-input > textarea.mud-input-root {
    resize: vertical;
}

/* Full-screen submission blocker (see BlockingProgressOverlay.razor).

   z-index 2000 clears MudBlazor's whole stack — drawer 1100, popover 1200, appbar 1300, dialog 1400,
   snackbar 1500, tooltip 1600 — so nothing on the page can sit on top of it and nothing behind it can
   be clicked. It stays well under the 20000 the TinyMCE aux container needs above, so the two hacks
   do not fight.

   position: fixed with all four insets rather than width/height 100%: on a page that is already
   scrolled, a percentage box is measured against the document and leaves the bottom of the viewport
   uncovered, which is exactly where the Submit button the agent just pressed lives. */
.tl-blocking-overlay {
    position: fixed;
    inset: 0;
    z-index: 2000;
    display: flex;
    align-items: center;
    justify-content: center;
    /* Deliberately a literal rgba rather than a MudBlazor palette variable: this must read as "the
       page is not available" in both light and dark themes, and a theme-following surface colour does
       not. */
    background: rgba(0, 0, 0, 0.55);
}

.tl-blocking-overlay-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 32px 40px;
    border-radius: 8px;
    max-width: 90vw;
    background: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
    box-shadow: var(--mud-elevation-8);
}

/* A status chip must never break mid-word: the label is one token and a wrapped chip pushes the
   whole table row open. Lao labels are the ones that hit this first. */
.tl-nowrap-chip {
    white-space: nowrap;
}

/* A date picker's Style is applied to the popover PAPER as well as the input, so a
   Style="max-width:220px" written for the input also caps the calendar — and that paper has
   overflow:hidden, so the calendar is not just narrow, it is cut off. Saturday and Sunday
   disappear and most of the month with them.

   Measured on the live pages: the calendar needs 310px and every picker in the app declared
   between 190px and 250px, so all fourteen were clipped.

   Scoped to .mud-popover so it only releases the floating calendar — the input keeps the width
   the page asked for, because that element is not inside the popover provider. Fixed here rather
   than at fourteen call sites so the next picker written the same way is not broken again. */
.mud-popover .mud-picker-paper {
    /* !important is required, not lazy: the cap is an INLINE style written onto the paper by
       MudBlazor from the picker's Style parameter, and no stylesheet selector outranks an
       inline style. The alternative is removing Style from all fourteen call sites and
       reaching the input through a class instead, which is more code to say the same thing and
       leaves the next picker free to reintroduce the bug. */
    max-width: none !important;
}

.mud-picker .mud-picker-container {
    min-width: max-content;
}

/* A shadow along the sidebar's RIGHT EDGE, so the menu reads as a panel sitting above the page
   rather than a column drawn on it.

   MudDrawer's Elevation cannot do this. Elevation maps to Material's shadow set, and every offset in
   it is VERTICAL — at elevation 8 the drawer gets
   `0 5px 5px -3px, 0 8px 10px 1px, 0 3px 14px 2px`. A downward shadow on a full-height panel is
   invisible at the side, which is the one edge anybody looks at, so raising Elevation changed the
   computed style and nothing a person could see.

   Scoped to the LEFT-positioned drawer: a right-hand drawer would need the offset mirrored, and
   nothing in this app has one, so it stays out of the selector rather than being guessed at. */
.mud-drawer.mud-drawer-pos-left {
    /* Two layers on purpose. The wide, soft one gives the panel its lift; the tight, darker one keeps
       a crisp boundary, which a single large blur loses — the edge goes vague and the sidebar starts
       to look out of focus. */
    box-shadow:
        10px 0 24px rgba(0, 0, 0, .14),
        2px 0 6px rgba(0, 0, 0, .09);

    /* ⚠ The part that carries DARK MODE, and it is a border rather than a second shadow for a
       reason. A shadow works by darkening what is behind it; over a near-black page there is almost
       nothing left to darken, so the same rule that lifts the panel on white does nothing at all on
       dark. A divider is theme-derived, so it stays visible either way.

       This also cannot be solved by branching on a class: MudBlazor 8 does not mark dark mode on
       <html> or <body> — measured, both are empty — it swaps CSS custom properties on :root. An
       earlier attempt here used `.mud-theme-dark` and was simply dead CSS. And prefers-color-scheme
       is the wrong signal too: the theme is a manual toggle kept in local storage, so the OS setting
       says nothing about which one this user is looking at. */
    border-right: 1px solid var(--mud-palette-divider);
}

/* The unread count sits on the bell rather than floating off it.

   MudBadge positions itself against the wrapped element's bounding box, and here that is a
   MudIconButton: 48x48 with 12px of padding around a 24px icon. So the badge anchors to the BUTTON's
   corner, which is 12px out and 12px up from the icon's own corner — measured at 22px right and 22px
   up from the icon's centre, which is what made it look detached.

   Translating by half the badge (10px of its 20px) puts its centre on the icon's top-right corner,
   which is the conventional Material position.

   ⚠ Done here, not by reducing the button's padding. That 48px box is the touch target — trimming it
   to reposition a badge would make the bell measurably harder to hit on a phone, which is a real cost
   for a cosmetic gain. */
.tl-bell-badge .mud-badge {
    transform: translate(-10px, 10px);
}

/* Data markers on a line chart.

   MudBlazor 9.7.0 already emits one <circle r="5.5"> per data point inside .mud-charts-line-series,
   but with opacity="0" — they exist only as hover targets for the tooltip. The supported switch,
   ShowDataMarkers, lives on LineChartOptions, which does NOT derive from ChartOptions and therefore
   cannot be passed to MudChart at all (it is a compile error, not a silent no-op). So the markers
   are revealed here instead.

   A CSS declaration always beats an SVG presentation attribute, so `opacity: 1` overrides the
   attribute without !important. `r` as a CSS property needs Chrome/Edge/Firefox or Safari 16+; where
   it is unsupported the markers simply stay at their native 5.5 radius, which still looks correct. */
.tl-chart-markers .mud-charts-line-series circle {
    r: 3;
    opacity: 1;
    fill: var(--mud-palette-surface, #fff);
    stroke: #3B7DDB;
    stroke-width: 2;
}

/* Stat tiles: label pinned at the top, figure centred in whatever space is left.

   The tiles are a fixed height so every card on the dashboard matches, which left the number
   stranded against the top edge of a mostly empty card. Centring is done with flex rather than a
   fixed margin so it stays correct if the tile height changes again. */
.tl-stat-tile {
    position: relative;
}

/* Taken out of flow and centred against the CARD, not against the space left under the label.
   In flow it sat 16px low, because the label consumed the top of the box before the figure was
   centred in the remainder. The label stays pinned top-left in normal flow above it. */
.tl-stat-tile .tl-stat-value {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0;
}

/* The detail dialog opened from the Policies / Quotations / Applications lists (RouteDetail).

   Nearly the whole viewport, but NOT FullScreen: a little backdrop showing on every side is what
   says "the list is still underneath, this closes" — full screen reads as a page you navigated to,
   and then the browser's Back button is the only obvious way out. The gutter is in vw/vh so it
   stays proportional rather than vanishing on a laptop and gaping on a wall display. */
.tl-route-detail-dialog {
    width: 94vw;
    max-width: 94vw;
    height: 92vh;
    max-height: 92vh;
}

/* The detail screens are written for a page, so they size to their content; without this the
   dialog's own body scrolls independently of the scroll container inside it and the header can be
   pushed out of reach. */
.tl-route-detail-dialog .mud-dialog-content {
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
