h1:focus {
    outline: none;
}

/* MudBlazor 9.1's MudDatePicker popover paper ships with overflow-x:hidden, which clips
   the rightmost weekday columns whenever the paper sizes itself narrower than the 310px
   calendar grid it wraps. Surfaced in PBI 69's schedule sub-dialog (PickerVariant.Dialog
   inside a modal), but the same clip can affect inline popovers in narrow form contexts.
   Minimal-impact fix: just remove the overflow clip so the paper grows to the natural
   width of its content. No width-force — MudBlazor's own 310px min-width is already
   right; the bug was that the clip prevented that width from showing. Global rule
   because the popover paper renders at the popover-provider level (separate DOM tree
   from any specific picker instance), so scoped-CSS approaches can't reach it. */
.mud-picker-popover-paper {
    overflow-x: visible !important;
}

.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;
}

/* EasySong custom styles */

.login-card {
    border-radius: 12px !important;
}

.social-login-btn {
    text-transform: none !important;
    font-weight: 500;
    border-color: #ddd !important;
    color: #333 !important;
}

.social-login-btn:hover {
    background-color: #f5f5f5 !important;
}

/* ===== Direct Licensing theme tokens =====
   PBI 175 — every colour a component hardcoded now lives here as a token with a light value and a
   dark override further down the file, so a component has ONE code path and the theme decides the
   value. The `-on` tokens are the text colour to use ON the matching fill: a fill that has to lift
   on a dark surface stops being able to carry white text, so the pair has to move together. */
:root {
    color-scheme: light;

    --dl-side-publishing: #7c3aed;
    --dl-side-master: #2563eb;
    --dl-side-publishing-on: #ffffff;
    --dl-side-master-on: #ffffff;

    /* Status chips — the simplified Zendesk palette from PBI 85
       (Open=Red, Waiting on Client=Blue, Waiting on Rightsholder=Black, Closed=Grey). */
    --dl-status-open-bg: #E12B27;
    --dl-status-open-fg: #FFFFFF;
    --dl-status-client-bg: #3091EC;
    --dl-status-client-fg: #FFFFFF;
    --dl-status-rightsholder-bg: #1F2D3D;
    --dl-status-rightsholder-fg: #FFFFFF;
    --dl-status-closed-bg: #87929D;
    --dl-status-closed-fg: #FFFFFF;
    --dl-status-deleted-bg: #C2C8CC;
    --dl-status-deleted-fg: #1F2D3D;

    /* Workflow pipeline (Requests + Projects) — connector rules, pending node chrome, muted labels. */
    --dl-pipeline-connector: #e0e0e0;
    --dl-pipeline-pending-border: #bdbdbd;
    --dl-pipeline-node-bg: #ffffff;
    --dl-pipeline-muted: #9e9e9e;

    /* Amber "unsaved / draft" panel shared by the ticket workspace and the reply composer. */
    --es-draft-bg: #fff8e1;
    --es-draft-border: #f0d58a;

    /* Hyperlinks inside ticket comment bodies. Deliberately NOT --mud-palette-primary: the brand
       blue is an accent tuned for fills and icons, and as link text on white it manages only
       2.5:1. This is the Material link blue the workspace already used - 4.6:1, WCAG AA. */
    --es-link: #1976d2;
}

/* ===== Direct Licensing Styles ===== */

/* Request list item hover state */
.dl-list-item:hover {
    background: var(--mud-palette-action-default-hover) !important;
}

/* Filter dropdown popovers — wider than the input so option text isn't cut off */
.dl-filter-popover {
    min-width: 240px;
}
.dl-filter-popover .mud-list-item-text {
    white-space: normal;
    word-break: break-word;
}

/* Split pane scrollable panels */
.dl-ticket {
    min-height: 0;
}

/* Timeline styling */
.dl-ticket .mud-timeline {
    padding-left: 0;
}

/* Properties and Customer/Milestones sidebars sticky positioning */
@media (min-width: 960px) {
    .dl-ticket .mud-grid > .mud-item:first-child,
    .dl-ticket .mud-grid > .mud-item:last-child {
        position: sticky;
        top: 0;
        align-self: flex-start;
    }
}

/* Compact property-field inputs to match caption-sized labels */
.pf-compact .mud-input-control {
    margin-top: 0 !important;
}
.pf-compact .mud-input,
.pf-compact .mud-select-input {
    font-size: 0.75rem !important;
    padding-top: 4px !important;
    padding-bottom: 4px !important;
}
.pf-compact .mud-input-slot {
    font-size: 0.75rem !important;
}
.pf-compact .mud-input-slot:has(textarea) {
    padding-top: 0 !important;
    padding-bottom: 0 !important;
}
.pf-compact .mud-input-control .mud-input-label {
    display: none;
}

/* Workflow pipeline responsive */
.dl-pipeline {
    overflow-x: auto;
    scrollbar-width: thin;
}

/* PBI 57 — accounting-style currency display. Tabular figures give every digit equal
   width so decimal points line up across stacked values; right-alignment makes amounts
   of different magnitudes ($165.00 vs $4,345.00) align on the decimal. Applied to
   read-only money values and to dashboard fee grid cells. */
.usd-amount {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum";
    text-align: right;
}

/* Right-align the numeric text inside financial entry fields so a stacked column of
   inputs reads like a ledger — decimals line up under each other, matching the
   read-only .usd-amount values. Targets the inner <input> of a MudNumericField. */
.usd-field input {
    text-align: right;
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum";
}

/* The trailing "USD" adornment: add a gap so it doesn't butt against the number, and
   match its size to the digits. In the compact PropertyField context (.pf-compact) the
   input text is shrunk to 0.75rem but the adornment is not — bring it down to match so
   "500.00 USD" reads as one consistent unit. */
.usd-field .mud-input-adornment-end {
    margin-left: 0.75em;
    padding-left: 0.25em;
}
.pf-compact .usd-field .mud-input-adornment-end,
.pf-compact .usd-field .mud-input-adornment-end * {
    font-size: 0.75rem;
}

/* PBI 88: visual cue applied to a Missing Fields chip's target anchor when the chip
   is clicked. Distinct from MudBlazor's per-field error styling so the agent's eye is
   drawn to the specific chip target even on a page already showing validation errors.

   Lifecycle is split between CSS and JS:
     - CSS @keyframes field-anchor-pulse runs once (0.6s) on class-add for a quick
       attention "pop" from no-shadow to the steady glow.
     - After the pulse, the steady outline + box-shadow remain (no animation loop)
       until missingFieldsInterop.scrollAndFocus removes the class at HIGHLIGHT_DURATION_MS
       (currently 2.5s). The fade-out is the transition triggered by class removal. */
.field-anchor-highlight {
    outline: 2px solid var(--mud-palette-error, #d32f2f);
    outline-offset: 4px;
    border-radius: 4px;
    box-shadow: 0 0 16px 2px rgba(211, 47, 47, 0.45);
    animation: field-anchor-pulse 0.6s ease-out 1;
    transition: outline-color 0.3s ease-out, box-shadow 0.3s ease-out;
}

@keyframes field-anchor-pulse {
    0%   { box-shadow: 0 0 0 0 rgba(211, 47, 47, 0.6); }
    100% { box-shadow: 0 0 16px 2px rgba(211, 47, 47, 0.45); }
}

/* PBI 133: make the note add/inline-edit text boxes user-resizable so a long note can be
   dragged open and read in full while editing, instead of being trapped in a fixed scrolling
   window. Shared by the Project Notes boxes (ProjectDetail) and the Requests Add-note box
   (RequestTicketView) via the .resizable-note-field class. MudTextField renders its <textarea>
   in MudBlazor's own markup, so a scoped/::deep rule from the page can't cleanly reach it —
   same reason the picker rule at the top of this file is global. We match the textarea by
   element (not an internal MudBlazor class) so the rule survives MudBlazor markup changes;
   .resizable-note-field is only ever set on note fields, so the descendant textarea is
   unambiguous. resize:vertical adds the drag grip and keeps width under layout control;
   !important overrides MudBlazor's base textarea resize:none. The component's Lines= sets the
   starting height (AC: compact default when empty); min-height is a fixed ~3-line drag-shrink
   floor (independent of Lines) so the box can't be collapsed to a sliver. */
.resizable-note-field textarea {
    resize: vertical !important;
    min-height: 64px !important;
}

/* ===== Bug 245: workspace tab overflow dropdown =====
   The "all open tabs" popover is rendered through MudPopoverProvider (teleported to the page root,
   a separate DOM subtree), so these rules must be global — a component-scoped rule can't reach the
   popover paper or its content. Same reason as the picker rule at the top of this file. The overflow
   *button* and click-away *overlay* render inline in TabStrip and are styled there
   (TabStrip.razor.css). */
.tab-strip-overflow-popover {
    min-width: 280px;
    max-width: 360px;
    border-radius: 8px;
    overflow: hidden;
}

.tab-strip-overflow-header {
    padding: 10px 14px 6px;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
}

.tab-strip-overflow-search {
    padding: 2px 12px 6px;
}

.tab-strip-overflow-list {
    max-height: 420px;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 4px 0 6px;
    scrollbar-width: thin;
    outline: none;
}

.tab-strip-overflow-empty {
    padding: 12px 16px;
    font-size: 0.8rem;
    color: var(--mud-palette-text-disabled);
}

.tab-strip-overflow-item {
    display: flex;
    align-items: stretch;
    border-left: 2px solid transparent;
    transition: background-color 100ms ease;
}

.tab-strip-overflow-item:hover,
.tab-strip-overflow-item:focus-within {
    background: var(--mud-palette-action-default-hover);
}

.tab-strip-overflow-item-active {
    background: var(--mud-palette-action-default-hover);
    border-left-color: var(--mud-palette-primary);
}

/* Primary action fills the row and is a real <button>, so keyboard activation and focus come for
   free — no hand-rolled key handling, no clickable div. Reset the native button chrome. */
.tab-strip-overflow-item-activate {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 4px 8px 12px;
    border: none;
    background: transparent;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.tab-strip-overflow-item-icon {
    flex: 0 0 auto;
    color: var(--mud-palette-text-secondary);
}

.tab-strip-overflow-item-active .tab-strip-overflow-item-icon {
    color: var(--mud-palette-primary);
}

/* Flex column; as flex items the <span> children blockify, so text-overflow:ellipsis works. */
.tab-strip-overflow-item-text {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    line-height: 1.25;
}

.tab-strip-overflow-item-title {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--mud-palette-text-primary);
}

.tab-strip-overflow-item-active .tab-strip-overflow-item-title {
    font-weight: 600;
}

.tab-strip-overflow-item-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

.tab-strip-overflow-item-subtitle {
    font-size: 0.72rem;
    color: var(--mud-palette-text-disabled);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Dirty dot inside the overflow list — re-declared globally because the scoped
   .tab-strip-dirty-dot can't reach the teleported popover. Mirrors the strip's dot. */
.tab-strip-overflow-item .tab-strip-dirty-dot {
    flex: 0 0 auto;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--mud-palette-warning);
}

.tab-strip-overflow-item-close {
    flex: 0 0 auto;
    align-self: center;
    margin-right: 8px;
    border: none;
    background: transparent;
    cursor: pointer;
    padding: 3px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--mud-palette-text-secondary);
    opacity: 0.5;
    transition: opacity 100ms ease, background-color 100ms ease;
}

.tab-strip-overflow-item:hover .tab-strip-overflow-item-close,
.tab-strip-overflow-item-close:hover,
.tab-strip-overflow-item-close:focus-visible {
    opacity: 1;
}

.tab-strip-overflow-item-close:hover {
    background: var(--mud-palette-action-default-hover);
    color: var(--mud-palette-text-primary);
}

/* ===== UI/UX review (chore/2026-07-16): accessibility baseline ===== */

/* One consistent keyboard-focus ring. :focus-visible shows it only for keyboard navigation (never
   on mouse click), and the brand primary keeps it legible on both light and dark surfaces. MudBlazor
   components manage their own focus states; this covers the plain interactive elements — anchors,
   hand-rolled <button>s, the Home hub cards — that otherwise fell back to a browser default outline
   often invisible against tinted backgrounds. */
a:focus-visible,
button:focus-visible,
[tabindex]:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 2px;
    border-radius: 4px;
}

/* PBI 172 — explainer "(i)" affordance (InfoTooltip). The global [tabindex]:focus-visible rule
   above already gives it the keyboard focus ring; this only nudges the icon into optical alignment
   with the caption text beside it and warms it on hover/focus so it reads as interactive. */
.info-tooltip {
    vertical-align: middle;
    margin-left: 2px;
    border-radius: 50%;
    transition: color 0.12s ease;
}

.info-tooltip:hover .mud-icon-root,
.info-tooltip:focus-visible .mud-icon-root {
    color: var(--mud-palette-primary) !important;
}

/* PBI 172 — Next Follow-Up color key swatches. The key lives inside a tooltip (its content is
   portaled to the popover provider at the document root), so this is not scoped to a page wrapper. */
.fu-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    flex-shrink: 0;
    display: inline-block;
}

/* Honour the OS "reduce motion" preference: users prone to motion sickness (or who simply find
   movement distracting) get near-instant state changes instead of the hover lifts, tab transitions,
   and missing-field pulse. State still changes — only the animation is removed. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* PBI 338 — global "Send feedback" FAB, pinned above page content. z-index sits below MudBlazor's
   dialog/snackbar layers (1400+) so the open dialog and toasts still overlay it.
   PBI 150 — pinned bottom-LEFT (was bottom-right) per request. Snackbars default to top-right, so the
   corner is otherwise clear. */
.feedback-fab {
    position: fixed;
    left: 24px;
    bottom: 24px;
    z-index: 1300;
}

/* PBI 100 — global-search dropdown rows. Global (not scoped) CSS because the popover content is
   teleported out of the component's DOM subtree by MudPopoverProvider, where a .razor.css scope
   attribute selector is brittle. */
.gs-section {
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    opacity: 0.6;
    padding: 8px 16px 2px;
}

.gs-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 16px;
    cursor: pointer;
}

.gs-row:hover {
    background-color: var(--mud-palette-action-default-hover);
}

.gs-row-icon {
    flex: 0 0 auto;
    opacity: 0.7;
}

.gs-row-text {
    flex: 1 1 auto;
    min-width: 0;
}

.gs-row-title,
.gs-row-sub {
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.gs-row-time {
    flex: 0 0 auto;
    white-space: nowrap;
    margin-left: 8px;
}

.gs-all-results {
    font-weight: 500;
}

.gs-disabled {
    opacity: 0.5;
    pointer-events: none;
}

/* PBI 100 / PBI 170 — free-text search match highlight. Amber background with a forced dark-navy
   foreground so contrast holds in BOTH light and dark themes (≈10:1, well past WCAG AA) and the
   colour never collides with the status/outcome badges (red/blue/black/grey). Both fg and bg are set
   explicitly so the browser's default <mark> styling (which would inherit the theme text colour and
   wash out in dark mode) is overridden. */
mark.es-hl {
    background-color: #ffd54f;
    color: #1f2d3d;
    padding: 0 1px;
    border-radius: 2px;
}

/* "matched in {field}" hint line under a row's identity cell — small, muted, wraps its own highlight. */
.es-hint {
    display: block;
    font-size: 0.72rem;
    line-height: 1.2;
    opacity: 0.75;
    margin-top: 2px;
    white-space: normal;
}

/* PBI 565 — Do-Not-Call leads revealed by the "Incl. DNC" checkbox on the Sales Leads panel.
   The row is tinted so "don't dial this one" survives whatever column layout the agent has chosen:
   the chip that names the suppression lives in the Last Call column, and that column is hideable,
   so the chip alone is not a guarantee the warning is on screen.

   Painted on the cells rather than the row because MudBlazor puts its hover background on the <tr>,
   which would otherwise paint over the tint; the second rule restores hover feedback in the tinted
   colour instead of losing it. rgba() over --mud-palette-error-rgb rather than a fixed hex so it
   tracks the active theme — a hardcoded pink goes unreadable the moment dark mode is on. Alpha is
   kept low because this is a whole-row wash behind body text, and contrast has to hold for the
   text, not just be noticeable. Colour is never the only signal (see the always-visible icon in the
   Customer cell) — roughly 1 in 12 men would not reliably read a red tint. */
.mud-table-row.sales-lead-dnc > td {
    background-color: rgba(var(--mud-palette-error-rgb), 0.09);
}

.mud-table-row.sales-lead-dnc:hover > td {
    background-color: rgba(var(--mud-palette-error-rgb), 0.18);
}

/* =====================================================================================
   PBI 175 — DARK THEME
   =====================================================================================

   Scoped to html[data-theme="dark"], which is stamped in three places that must agree:
     1. server-side in App.razor, from the theme cookie, before any markup is written;
     2. by the pre-paint script in <head>, which corrects "System" mode using the OS setting;
     3. by js/theme.js when the user changes the setting, or the OS changes under System mode.

   An attribute rather than a `prefers-color-scheme` media query, because the user's explicit
   choice has to be able to WIN over the OS — a media query cannot express "the OS says dark but
   this user asked for light". System mode is handled by resolving the OS preference to this same
   attribute, so there is exactly one selector to reason about.

   Most of the app needed nothing here: it already draws on --mud-palette-* tokens, which
   MudThemeProvider re-emits per theme. What follows is only the colour that was hardcoded, plus
   the browser-chrome declarations CSS custom properties cannot reach. */

html[data-theme="dark"] {
    color-scheme: dark;

    /* Side accents. The light purple/blue fail AA as text on the dark navy surface (2.8:1 and
       3.1:1), so both lift. Once lifted they can no longer carry white text, so the -on tokens
       flip to the dark canvas colour — 5.9:1 and 6.3:1 respectively. */
    --dl-side-publishing: #A78BFA;
    --dl-side-master: #60A5FA;
    --dl-side-publishing-on: #14242D;
    --dl-side-master-on: #14242D;

    /* Status chips. Contrast ratios below are text-on-fill, then fill-vs-surface (#14242D);
       WCAG AA wants 4.5:1 for this text size and 1.4.11 wants 3:1 for the chip boundary.
         open           #E12B27 on white text — 4.6:1 / 3.5:1. Unchanged: it already clears both,
                        and red is the one status whose urgency depends on staying saturated.
         client         lifts from #3091EC, whose white text was only 3.3:1 — 7.1:1 / 6.4:1.
         rightsholder   was #1F2D3D: 1.1:1 against the surface, i.e. an invisible chip. This is
                        the defect the audit called out. "Black" cannot survive on a dark canvas,
                        so it becomes the lightest slate that still reads as the sombre one — 5.3:1 / 4.8:1.
         closed         lifts from #87929D (white text was 3.2:1) — 5.3:1 / 3.1:1.
         deleted        goes quiet instead of loud: on a dark canvas the old near-white #C2C8CC
                        would have been the most prominent chip on screen, which is backwards for
                        a deleted record. Muted fill, 4.6:1 text. */
    --dl-status-open-bg: #E12B27;
    --dl-status-open-fg: #FFFFFF;
    --dl-status-client-bg: #5FA9F2;
    --dl-status-client-fg: #0E1B21;
    --dl-status-rightsholder-bg: #7C8FA6;
    --dl-status-rightsholder-fg: #0E1B21;
    --dl-status-closed-bg: #5F6E7A;
    --dl-status-closed-fg: #FFFFFF;
    --dl-status-deleted-bg: #2A3B47;
    --dl-status-deleted-fg: #98A6B0;

    /* Pipeline chrome: --border-soft / --border / --surface / --hint from the dark guide palette. */
    --dl-pipeline-connector: #1E3440;
    --dl-pipeline-pending-border: #3A5567;
    --dl-pipeline-node-bg: #14242D;
    --dl-pipeline-muted: #8FA4AD;

    /* Draft/unsaved amber. The light #fff8e1 is a near-white wash that would blow out the surface;
       this is the same signal at dark-surface weight. */
    --es-draft-bg: #3A3320;
    --es-draft-border: #6B5D33;

    /* Link blue lifts: the light #1976d2 manages only 2.4:1 on the dark surface. 7.6:1 here. */
    --es-link: #7EB6F6;
}

/* Blazor's own validation classes are plain CSS, not MudBlazor, so they never saw the palette.
   Point them at the theme's semantic tokens in dark mode: the literal #e50000 is 2.6:1 on the dark
   canvas and fails AA outright. Light mode keeps its existing literals untouched (AC 6). */
html[data-theme="dark"] .invalid {
    outline-color: var(--mud-palette-error);
}

html[data-theme="dark"] .validation-message {
    color: var(--mud-palette-error);
}

html[data-theme="dark"] .valid.modified:not([type=checkbox]) {
    outline-color: var(--mud-palette-success);
}

/* The social sign-in buttons force light chrome with !important, so dark mode needs the same
   weight to override it. Without this the label is #333 on the dark login card — unreadable. */
html[data-theme="dark"] .social-login-btn {
    border-color: var(--mud-palette-lines-inputs) !important;
    color: var(--mud-palette-text-primary) !important;
}

html[data-theme="dark"] .social-login-btn:hover {
    background-color: var(--mud-palette-action-default-hover) !important;
}

/* The signed-out shell's light gradient. Kept as a gradient (rather than flattened to a surface
   colour) so the login page keeps the same visual identity in both themes. */
html[data-theme="dark"] .login-gradient-bg {
    background: linear-gradient(135deg, #0E1B21 0%, #1E3440 100%) !important;
}

/* ===== Browser chrome (PBI 175 AC 4/5) =====
   `color-scheme: dark` above is what stops the UA painting light scrollbars, light form widgets,
   and a light backdrop for native date/colour inputs and <select> popups over a dark page — the
   "native light-mode control leakage" the ACs call out. It cannot be expressed as a custom
   property, which is why it is declared directly on the root element (and mirrored inline by the
   server render so it applies before this stylesheet has even parsed).

   The rules below only refine what color-scheme gives for free: Firefox takes scrollbar-color,
   and WebKit/Blink need explicit ::-webkit-scrollbar rules to match the app's surfaces rather
   than the OS default grey. Applied to the app's own scroll containers as well as the document,
   since several panels scroll internally. */
html[data-theme="dark"] {
    scrollbar-color: #3A5567 #0E1B21;
}

html[data-theme="dark"] ::-webkit-scrollbar {
    width: 12px;
    height: 12px;
}

html[data-theme="dark"] ::-webkit-scrollbar-track {
    background: #0E1B21;
}

html[data-theme="dark"] ::-webkit-scrollbar-thumb {
    background-color: #3A5567;
    border-radius: 6px;
    /* Inset border keeps the thumb visually narrower than its hit target. */
    border: 3px solid #0E1B21;
}

html[data-theme="dark"] ::-webkit-scrollbar-thumb:hover {
    background-color: #4A6A80;
}

html[data-theme="dark"] ::-webkit-scrollbar-corner {
    background: #0E1B21;
}

/* =====================================================================================
   PBI 162 — QUILL (template editor Design view)
   =====================================================================================

   Quill ships one stylesheet, snow, and it hardcodes light colours: a #ccc border, black
   toolbar icons, white canvas. Dropped in unchanged it renders a bright white slab in the
   middle of a dark dialog. It cannot use --mud-palette-* on its own, so the bridging happens
   here — the editor is told about the app's tokens rather than the app being told about
   Quill's.

   Everything below is either that bridge or a size the theme has no opinion about. If a rule
   is not one of those two things it probably belongs in the component, not here. */

.tpl-quill-wrapper .ql-toolbar.ql-snow,
.tpl-quill-wrapper .ql-container.ql-snow {
    border-color: var(--mud-palette-lines-inputs);
}

.tpl-quill-wrapper .ql-toolbar.ql-snow {
    border-top-left-radius: 4px;
    border-top-right-radius: 4px;
    background: var(--mud-palette-background-grey);
}

/* The canvas is white with dark text in BOTH themes, and the type matches Gmail's body defaults.
   That is deliberate: this is a what-you-see-is-what-they-get surface, and the recipient is reading
   the email on white in Arial 13px regardless of what theme the author happens to be using. Mirrors
   the draft-preview iframe, which has always forced a white background for the same reason. Keep
   these values in step with EmailRenderDefaults — the two describe the same thing, one for the
   authoring canvas and one for the preview document. */
.tpl-quill-wrapper .ql-container.ql-snow {
    border-bottom-left-radius: 4px;
    border-bottom-right-radius: 4px;
    background: #ffffff;
    /* Tall enough to write a licence email in without the dialog growing a second scrollbar. */
    min-height: 320px;
    font-family: Arial, Helvetica, sans-serif;
    font-size: 13px;
    line-height: 1.5;
}

.tpl-quill-wrapper .ql-editor {
    min-height: 320px;
    color: #202124;
}

.tpl-quill-wrapper .ql-editor.ql-blank::before {
    color: #9AA0A6;
    font-style: normal;
}

/* The toolbar is SVG-based: stroke and fill are set per-path, so both have to be redirected or
   half of each icon disappears against a dark toolbar. */
html[data-theme="dark"] .tpl-quill-wrapper .ql-snow .ql-stroke {
    stroke: var(--mud-palette-text-primary);
}

html[data-theme="dark"] .tpl-quill-wrapper .ql-snow .ql-fill {
    fill: var(--mud-palette-text-primary);
}

html[data-theme="dark"] .tpl-quill-wrapper .ql-snow .ql-picker-label,
html[data-theme="dark"] .tpl-quill-wrapper .ql-snow .ql-picker-item {
    color: var(--mud-palette-text-primary);
}

/* The heading dropdown is an absolutely-positioned panel, not a native select, so it needs its
   own surface — otherwise it renders as dark text on a transparent overlay. */
html[data-theme="dark"] .tpl-quill-wrapper .ql-snow .ql-picker-options {
    background: var(--mud-palette-surface);
    border-color: var(--mud-palette-lines-inputs);
}

/* Active/hover states are hardcoded to Quill's blue, which is close enough to the app's primary
   in light mode and invisible in dark. */
.tpl-quill-wrapper .ql-snow.ql-toolbar button:hover .ql-stroke,
.tpl-quill-wrapper .ql-snow.ql-toolbar button.ql-active .ql-stroke,
.tpl-quill-wrapper .ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke {
    stroke: var(--mud-palette-primary);
}

.tpl-quill-wrapper .ql-snow.ql-toolbar button:hover .ql-fill,
.tpl-quill-wrapper .ql-snow.ql-toolbar button.ql-active .ql-fill {
    fill: var(--mud-palette-primary);
}

.tpl-quill-wrapper .ql-snow.ql-toolbar button.ql-active,
.tpl-quill-wrapper .ql-snow.ql-toolbar .ql-picker-label:hover {
    color: var(--mud-palette-primary);
}

/* Link tooltip. Same problem as the picker panel — its own surface, hardcoded light. */
html[data-theme="dark"] .tpl-quill-wrapper .ql-snow .ql-tooltip {
    background: var(--mud-palette-surface);
    border-color: var(--mud-palette-lines-inputs);
    color: var(--mud-palette-text-primary);
    box-shadow: none;
}

html[data-theme="dark"] .tpl-quill-wrapper .ql-snow .ql-tooltip input[type="text"] {
    background: var(--mud-palette-background);
    border-color: var(--mud-palette-lines-inputs);
    color: var(--mud-palette-text-primary);
}
