/* ── Design tokens — the palette the app already uses, named. New styles (and the
      shared shell/auth/loading components) draw from these; ported page CSS keeps its
      literal values so it stays diffable against the old app — except max-width,
      which every page now takes from the width tokens below (see
      docs/frontend-divergences.md). ── */

:root {
    /* Accent */
    --mtl-accent: #2563eb;
    --mtl-accent-strong: #1d4ed8;
    --mtl-accent-soft: #eff6ff;

    /* Surfaces */
    --mtl-bg: #f8fafc;
    --mtl-surface: #ffffff;

    /* Borders */
    --mtl-border: #dbe3ee;
    --mtl-border-strong: #cbd5e1;

    /* Text */
    --mtl-text: #0f172a;
    --mtl-text-secondary: #475569;
    --mtl-text-muted: #64748b;
    --mtl-text-faint: #9ca3af;

    /* Status */
    --mtl-danger: #b42318;
    --mtl-danger-bg: #fef3f2;
    --mtl-danger-border: #fecdca;

    /* Widths — the one exception to the ported pages' literal-value rule. Every
       browse/board/detail screen caps at the page width; add/edit form surfaces cap
       at the deliberately narrower form width, where full-width inputs stop reading
       well. One name each, so content stops jumping width between screens. */
    --mtl-page-max: 1280px;
    --mtl-form-max: 1180px;

    /* Shape + elevation */
    --mtl-radius: 10px;
    --mtl-radius-lg: 12px;
    --mtl-shadow-card: 0 8px 24px rgba(15, 23, 42, 0.06);
    --mtl-focus-ring: 0 0 0 3px rgba(37, 99, 235, 0.10);
}

/* ── Globals carried over verbatim from the old app's app.css so ported pages render
      1:1. Loaded after bootstrap.min.css, same as the old app — order matters where
      these override Bootstrap. ── */

html, body {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    /* Rewrite-only: the old app painted this via its .app-shell; hoisted to body here. */
    background-color: #f8fafc;
}

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;
}

.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;
}

.search-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    background: #ffffff;
    border: 1px solid #e5e7eb;
    border-radius: 10px;
    padding: 10px 12px;
    transition: border-color .2s ease, box-shadow .2s ease;
}

.search-bar:focus-within {
    border-color: #2563eb;
    box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}

.search-icon {
    font-size: 14px;
    opacity: 0.5;
    flex-shrink: 0;
}

.search-input {
    flex: 1;
    border: none;
    outline: none;
    font-size: 14px;
    color: #0f172a;
    background: transparent;
    font-family: inherit;
}

.search-input::placeholder {
    color: #9ca3af;
}

.search-clear {
    background: none;
    border: none;
    cursor: pointer;
    color: #9ca3af;
    font-size: 14px;
    padding: 0 2px;
    line-height: 1;
    flex-shrink: 0;
    transition: color .2s ease;
}

.search-clear:hover {
    color: #374151;
}

.admin-page .queue-filters {
    border: 1px solid #d9e1ea;
    border-radius: 12px;
    background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
    box-sizing: border-box;
    padding: 18px;
    margin-bottom: 18px;
    width: 100%;
    box-shadow: 0 8px 24px rgba(15, 23, 42, 0.06);
}

.admin-page .filter-heading {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 14px;
}

.admin-page .filter-heading h2 {
    margin: 0;
    color: #0f172a;
    font-size: 17px;
    font-weight: 700;
}

.admin-page .filter-count {
    border: 1px solid #bfdbfe;
    border-radius: 8px;
    background: #eff6ff;
    color: #1d4ed8;
    padding: 7px 11px;
    font-size: 12px;
    font-weight: 700;
    white-space: nowrap;
}

.admin-page .filter-shell {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 12px;
    align-items: stretch;
    width: 100%;
}

.admin-page .filter-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.admin-page .filter-field label,
.admin-page .date-panel-title {
    color: #334155;
    font-size: 12px;
    font-weight: 700;
}

.admin-page .filter-field input {
    width: 100%;
    border: 1px solid #cbd5e1;
    border-radius: 8px;
    background: #fbfcfe;
    color: #0f172a;
    font: inherit;
    font-size: 14px;
    min-height: 42px;
    padding: 10px 12px;
    transition: border-color .2s ease, box-shadow .2s ease;
}

.admin-page .filter-field input:focus {
    outline: none;
    border-color: #2563eb;
    box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.10);
}

.admin-page .date-panel {
    border: 1px solid #cbd5e1;
    border-radius: 10px;
    background: #ffffff;
    padding: 12px;
    width: 100%;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8);
}

.admin-page .date-panel-title {
    margin-bottom: 8px;
}

.admin-page .date-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(140px, 1fr));
    gap: 10px;
}

.admin-page .filter-actions {
    display: flex;
    flex-direction: column;
    justify-content: end;
    gap: 8px;
}

.admin-page .filter-actions button {
    border: 1px solid #cbd5e1;
    border-radius: 8px;
    cursor: pointer;
    font-size: 14px;
    font-weight: 700;
    min-height: 42px;
    padding: 10px 16px;
    white-space: nowrap;
    transition: background .2s ease, border-color .2s ease, box-shadow .2s ease;
}

.admin-page .apply-button {
    background: #2563eb;
    border-color: #2563eb;
    color: #ffffff;
    box-shadow: 0 6px 14px rgba(37, 99, 235, 0.18);
}

.admin-page .reset-button {
    background: #ffffff;
    color: #0f172a;
}

.admin-page .filter-actions button:hover:not(:disabled) {
    border-color: #2563eb;
}

.admin-page .apply-button:hover:not(:disabled) {
    background: #1d4ed8;
    box-shadow: 0 8px 18px rgba(37, 99, 235, 0.22);
}

.admin-page .reset-button:hover:not(:disabled) {
    background: #f8fafc;
}

.admin-page .filter-actions button:disabled {
    cursor: not-allowed;
    opacity: .45;
}

.admin-page .search-bar {
    margin: 14px 18px 0;
}

@media (max-width: 900px) {
    .admin-page .filter-heading {
        align-items: flex-start;
        flex-direction: column;
    }

    .admin-page .filter-shell {
        grid-template-columns: 1fr;
    }

    .admin-page .filter-actions {
        flex-direction: row;
    }

    .admin-page .filter-actions button {
        flex: 1;
    }
}

@media (max-width: 520px) {
    .admin-page .date-grid,
    .admin-page .filter-actions {
        grid-template-columns: 1fr;
        flex-direction: column;
    }
}

/* ── WASM boot screen (index.html) — shown while the runtime downloads. Lives here,
      not in scoped CSS, because Blazor hasn't started yet. ── */

.boot-screen {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    background:
        radial-gradient(1100px 550px at 50% -10%, #e8effc 0%, rgba(232, 239, 252, 0) 60%),
        var(--mtl-bg);
    animation: boot-fade-in .4s ease;
}

.boot-brand {
    font-size: 1.35rem;
    font-weight: 700;
    letter-spacing: 0.01em;
    color: var(--mtl-text);
}

/* The piggy bank, wordmark, progress bar, percentage — stacked by .boot-screen's column
   flex. Sized by width with an aspect ratio rather than a square box: the mark is wider
   than it is tall (742 × 640) and the tail must not letterbox. */
.boot-logo {
    width: 9rem;
    aspect-ratio: 742 / 640;
    margin-bottom: 2px;
}

/* Progress lives in a bar because the mark has ears and a tail — a ring can only wrap
   the disc, and would slice through both. Blazor keeps --blazor-load-percentage as a
   percentage value, so the fill takes it as a width directly. */
.boot-bar {
    width: 11rem;
    height: 0.3rem;
    border-radius: 999px;
    background: #dbe4f0;
    overflow: hidden;
}

    .boot-bar span {
        display: block;
        width: var(--blazor-load-percentage, 0%);
        height: 100%;
        border-radius: inherit;
        /* Teal to match the disc, not the app's action blue. */
        background: #0e93a3;
        transition: width 0.05s ease-in-out;
    }

.boot-percentage {
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--mtl-text-secondary);
    font-variant-numeric: tabular-nums;
}

    .boot-percentage::after {
        content: var(--blazor-load-percentage-text, "…");
    }

@keyframes boot-fade-in {
    from { opacity: 0; }
}

/* No reduced-motion rule for the mark here: mtl-piggy.svg carries its own
   prefers-reduced-motion block, which applies to the referenced document. */

/* ── unhandled-error banner — Blazor only ever sets display: block on it, so the layout
      can't rely on flex and the dismiss wiring lives in js/app.js. ── */

#blazor-error-ui {
    color-scheme: light only;
    display: none;
    position: fixed;
    left: 50%;
    bottom: 20px;
    transform: translateX(-50%);
    z-index: 1000;
    box-sizing: border-box;
    max-width: min(560px, calc(100vw - 32px));
    padding: 12px 40px 12px 16px;
    background: var(--mtl-surface);
    border: 1px solid var(--mtl-danger-border);
    border-left: 4px solid var(--mtl-danger);
    border-radius: var(--mtl-radius);
    box-shadow: 0 12px 32px rgba(15, 23, 42, 0.18);
    color: var(--mtl-text);
    font-size: 0.9rem;
    line-height: 1.5;
}

    #blazor-error-ui .reload {
        margin-left: 4px;
        color: var(--mtl-accent);
        font-weight: 600;
        text-decoration: none;
    }

        #blazor-error-ui .reload:hover {
            text-decoration: underline;
        }

    #blazor-error-ui .dismiss {
        position: absolute;
        top: 6px;
        right: 8px;
        padding: 4px;
        border: none;
        background: none;
        color: var(--mtl-text-faint);
        font-size: 14px;
        line-height: 1;
        cursor: pointer;
    }

        #blazor-error-ui .dismiss:hover {
            color: #374151;
        }

/* ── Viewport-fitting pages ───────────────────────────────────────────────────
   Browsing surfaces (boards, queues, lists) fill the space the shell hands them
   and scroll INSIDE one designated region, so the page itself never scrolls.
   Detail, form and approval surfaces deliberately don't use this: capping a form
   would hide inputs, and a hard-clipped page becomes unreachable under browser
   zoom (WCAG 1.4.4).

   The same zoom concern applies to the pages that DO opt in — heavy browser zoom
   (or just a short laptop) shrinks the CSS viewport until fitting means crushing
   the scroll region toward nothing. So fitting is conditional: below 640px of
   viewport height the media query at the end of this block stands the system down
   and pages flow and scroll like ordinary documents, with the shell relaxing its
   exact-viewport height in step (Layout/MainLayout.razor.css, same query). Fit
   when it fits; ordinary scrolling when it can't.

   This replaces the old per-page `max-height: calc(100vh - <guessed px>)`. Those
   numbers had to be re-counted by hand whenever the chrome above them changed —
   a new filter row, a wrapped heading, a conditional error banner — and were
   measurably wrong. Height now flows from the shell, so nothing needs counting.

   Contract for a page opting in:
     1. root element gets .page-fit
     2. every wrapper between the root and the scrolling region is a flex column
        with `min-height: 0`
     3. exactly ONE region gets `flex: 1; min-height: 0; overflow-y: auto`
   Miss the `min-height: 0` on a link in that chain and the region refuses to
   shrink below its content — the page silently starts scrolling again. */
.page-fit {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    /* These pages centre themselves with `max-width` + `margin: 0 auto`. As a flex item
       those auto margins would absorb all the free space and shrink the page to its
       content width, so state the fill explicitly — max-width still caps it. */
    width: 100%;
}

/* The region inside a .page-fit page that takes the slack and scrolls. */
.page-fit-scroll {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
}

/* The short-viewport fallback the block comment promises: there is no leftover
   height worth handing out, so stop fitting and let content be its natural size —
   the document (not the region) scrolls, courtesy of the shell's matching query. */
@media (max-height: 640px) {
    .page-fit {
        flex: none;
        overflow: visible;
    }

    .page-fit-scroll {
        flex: none;
        min-height: auto;
        overflow-y: visible;
    }
}
