/* /Components/Layout/MainLayout.razor.rz.scp.css */
/* "Slate" layout shell - fixed left sidebar + a scrollable main content column,
   replacing "Survey Line"'s sticky top bar (project-knowledge.md "Design system").
   A left sidebar is the single most recognizable "this looks like real SaaS"
   signal (Notion/Linear/Asana/Salesforce all use one) and is a genuine structural
   change, not just new colors on the same top-bar shape, after two top-bar-based
   themes were both rejected. The sidebar's own width/look live in
   NavMenu.razor.css (its own scoped root element) - this file only owns the
   two-column shell and the main content column. */
.app-shell[b-hkbgme66wb] {
    display: flex;
    align-items: flex-start;
    min-height: 100vh;
    background: var(--bg);
}

.app-main[b-hkbgme66wb] {
    flex: 1 1 auto;
    /* min-width:0 is required on a flex item that contains wide content (tables) -
       without it the item refuses to shrink below its content's intrinsic width
       and can overflow the viewport instead of scrolling internally. */
    min-width: 0;
    width: 100%;
    box-sizing: border-box;
    padding: 2rem 2.5rem 3rem;
    /* Content width cap (2026-07-28 UX polish pass): on ultra-wide monitors the
       pages previously stretched edge-to-edge, putting a row's label and its
       value thousands of pixels apart. Centered in the space remaining next to
       the sidebar (2026-07-31 follow-up: the original left-anchored version
       left the entire right half of an ultra-wide screen empty, which read as
       broken rather than intentional — auto margins on a flex item absorb the
       free space evenly on both sides). */
    max-width: 1360px;
    margin-inline: auto;
}

/* Sidebar stacks above the content on narrow viewports rather than a hamburger
   menu - this app's pages are static SSR with no client-side interactivity to
   drive a collapsible off-canvas panel, so a plain stack is the simplest
   correct behavior. */
@media (max-width: 900px) {
    .app-shell[b-hkbgme66wb] {
        flex-direction: column;
    }

    .app-main[b-hkbgme66wb] {
        padding: 1.5rem 1.25rem 2rem;
    }
}
/* /Components/Layout/NavMenu.razor.rz.scp.css */
/* "Slate" sidebar - fixed left column, replacing "Survey Line"'s sticky ink top
   bar (project-knowledge.md "Design system"). ~240px, sticky so it stays in view
   while the (possibly long) main content column scrolls. */
.sidebar[b-bnx7wbqykk] {
    flex: none;
    width: 240px;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    padding: 1.25rem 1rem;
    /* Deep teal, not --bg-subtle gray - 2026-07-24 color pass. The colored
       sidebar block against the (now tinted) lighter content area is the
       single biggest lever against the "too much white" feeling; the strong
       color contrast against the main content also replaces the need for a
       separating border-right. */
    background: var(--sidebar-bg);
    position: sticky;
    top: 0;
    align-self: flex-start;
    min-height: 100vh;
}

@media (max-width: 900px) {
    .sidebar[b-bnx7wbqykk] {
        width: 100%;
        min-height: auto;
        position: static;
        border-bottom: 1px solid var(--sidebar-border);
    }
}

.sidebar-brand[b-bnx7wbqykk] {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding-bottom: .9rem;
    margin-bottom: .5rem;
    border-bottom: 1px solid var(--sidebar-border);
}

.sidebar-brand:hover[b-bnx7wbqykk] {
    text-decoration: none;
}

/* Small colored rounded-square with initials - no crosshair glyph, no image.
   White chip + teal letters (was solid-accent + white) - 2026-07-24: two close
   teals stacked (this mark's old --accent fill on the new --sidebar-bg) read
   as low-contrast/muddy, so a solid white chip is used instead, guaranteed to
   read clearly regardless of the exact sidebar shade.
   Dark-mode round (2026-08), Lighthouse-caught: color was var(--accent), which is fine paired
   with this fixed white background in LIGHT mode (~5.2:1) but fails badly in DARK mode
   (~1.9:1) once --accent flips to the bright mint dark token - this chip's white background is
   deliberately theme-INVARIANT (the sidebar is always dark regardless of app theme, per the
   comment above), so its text needs to be theme-invariant too, not tied to a token that flips.
   var(--sidebar-bg) (#0d4941, itself already fixed regardless of app theme) gives ~5.5:1 on
   white and reads as the mark done in the sidebar's own brand teal either way. */
.sidebar-brand-mark[b-bnx7wbqykk] {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 7px;
    background: #ffffff;
    color: var(--sidebar-bg);
    font-family: 'IBM Plex Sans', 'Segoe UI', system-ui, sans-serif;
    font-weight: 700;
    font-size: .7rem;
}

.sidebar-brand-name[b-bnx7wbqykk] {
    font-family: 'IBM Plex Sans', 'Segoe UI', system-ui, sans-serif;
    font-weight: 700;
    font-size: 1rem;
    color: var(--sidebar-ink);
}

/* Command palette hint button (Ctrl+K, Wave 4) — quiet styling, sits above the company
   switcher/nav list. Literal markup child of this component (not a child component's output),
   so it doesn't need ::deep. */
.sidebar-palette-hint[b-bnx7wbqykk] {
    display: flex;
    align-items: center;
    gap: .5rem;
    width: 100%;
    margin-bottom: .75rem;
    padding: .45rem .6rem;
    background: none;
    border: 1px solid var(--sidebar-border);
    border-radius: var(--radius-sm);
    color: var(--sidebar-ink-soft);
    font-family: 'IBM Plex Sans', 'Segoe UI', system-ui, sans-serif;
    font-size: .8rem;
    text-align: left;
    cursor: pointer;
}

.sidebar-palette-hint:hover[b-bnx7wbqykk] {
    color: var(--sidebar-ink);
    background: var(--sidebar-hover-bg);
}

.sidebar-palette-hint-label[b-bnx7wbqykk] {
    flex: 1 1 auto;
}

.sidebar-palette-kbd[b-bnx7wbqykk] {
    flex: none;
    font-family: 'IBM Plex Mono', monospace;
    font-size: .68rem;
    /* ink-soft, not ink-faint (2026-09-24): faint is the separator token, and as text on this chip it
       measured 4.14:1 in Lighthouse - under WCAG 1.4.3's 4.5:1 for small text. */
    color: var(--sidebar-ink-soft);
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid var(--sidebar-border);
    border-radius: 4px;
    padding: .1rem .35rem;
}

[b-bnx7wbqykk] .sidebar-company {
    padding: .1rem .35rem 1rem;
    margin-bottom: .5rem;
    border-bottom: 1px solid var(--sidebar-border);
}

[b-bnx7wbqykk] .sidebar-company-switcher {
    margin-bottom: .35rem;
}

/* Company-switcher dropdown (Phase 5) - single <select>, auto-submits on change, replacing
   the old always-visible link-per-other-membership list. Disabled (not hidden) when there's
   only one option, so the active company name stays visible either way - this is also what
   keeps a platform admin's virtual/foreign entry visible (Phase 4 review finding: the old
   active-company label went missing entirely for a membership with no real CompanyUser row). */
[b-bnx7wbqykk] .sidebar-company-select {
    width: 100%;
    background: var(--sidebar-bg);
    color: var(--sidebar-ink);
    border: 1px solid var(--sidebar-border);
    border-radius: 5px;
    padding: .3rem .4rem;
    font-family: 'IBM Plex Sans', 'Segoe UI', system-ui, sans-serif;
    font-weight: 600;
    font-size: .85rem;
    cursor: pointer;
}

[b-bnx7wbqykk] .sidebar-company-select:disabled {
    cursor: default;
    opacity: 1;
    -webkit-text-fill-color: var(--sidebar-ink);
    border-color: transparent;
    padding-left: 0;
}

[b-bnx7wbqykk] .sidebar-company-select option {
    color: #000000;
}

[b-bnx7wbqykk] .sidebar-company-new {
    display: block;
    margin-top: .3rem;
    font-size: .8rem;
    color: var(--sidebar-mint);
}

[b-bnx7wbqykk] .sidebar-company-new:hover {
    color: #ffffff;
    text-decoration: underline;
}

/* Trial countdown (Problem B, 2026-08-30 audit) - quiet by design: same border-bottom separator
   as .sidebar-company above it, not a loud/colored banner, since it appears on every page for the
   whole 30-day window and must not read as an error or a nag. */
[b-bnx7wbqykk] .sidebar-trial {
    display: flex;
    flex-direction: column;
    gap: .15rem;
    padding: 0 .35rem .75rem;
    margin-bottom: .5rem;
    border-bottom: 1px solid var(--sidebar-border);
    font-family: 'IBM Plex Sans', 'Segoe UI', system-ui, sans-serif;
    font-size: .78rem;
}

[b-bnx7wbqykk] .sidebar-trial-text {
    color: var(--sidebar-ink-soft);
}

[b-bnx7wbqykk] .sidebar-trial-link {
    color: var(--sidebar-mint);
    font-weight: 600;
}

[b-bnx7wbqykk] .sidebar-trial-link:hover {
    color: #ffffff;
    text-decoration: underline;
}

[b-bnx7wbqykk] .sidebar-nav {
    list-style: none;
    margin: 0;
    padding: 0;
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

[b-bnx7wbqykk] .sidebar-nav-group {
    font-family: 'IBM Plex Sans', 'Segoe UI', system-ui, sans-serif;
    font-size: .7rem;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--sidebar-ink-soft);
    opacity: .65;
    padding: .85rem .65rem .3rem;
}

[b-bnx7wbqykk] .sidebar-nav-group:first-child {
    padding-top: 0;
}

[b-bnx7wbqykk] .nav-link {
    display: flex;
    align-items: center;
    gap: .65rem;
    font-family: 'IBM Plex Sans', 'Segoe UI', system-ui, sans-serif;
    font-size: .875rem;
    font-weight: 500;
    color: var(--sidebar-ink-soft) !important;
    padding: .5rem .65rem;
    border-radius: var(--radius-sm);
    transition: background-color 120ms ease, color 120ms ease;
}

[b-bnx7wbqykk] .nav-link i {
    width: 16px;
    flex: none;
    text-align: center;
    font-size: .85em;
    color: var(--sidebar-ink-soft);
}

[b-bnx7wbqykk] .nav-link:hover {
    color: var(--sidebar-ink) !important;
    background: var(--sidebar-hover-bg);
}

[b-bnx7wbqykk] .nav-link:hover i {
    color: var(--sidebar-ink);
}

/* Active chip deliberately does NOT reuse --accent on this dark sidebar bg
   (two close teals stacked read as low-contrast) - a translucent white chip
   plus solid-white text/icon instead, so "active" stays unmistakable. */
[b-bnx7wbqykk] .nav-link.active {
    color: #ffffff !important;
    background: var(--sidebar-active-bg);
    font-weight: 600;
}

[b-bnx7wbqykk] .nav-link.active i {
    color: #ffffff;
}

.sidebar-footer[b-bnx7wbqykk] {
    margin-top: 1rem;
    padding-top: .9rem;
    border-top: 1px solid var(--sidebar-border);
}

/* Wraps <ThemeToggle /> - literal markup child of THIS component (not projected content from
   ThemeToggle's own template), so it doesn't need ::deep. */
.sidebar-theme-row[b-bnx7wbqykk] {
    margin-bottom: .6rem;
}

.sidebar-culture[b-bnx7wbqykk] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .3rem;
    padding: 0 .1rem;
    font-family: 'IBM Plex Sans', 'Segoe UI', system-ui, sans-serif;
    font-size: .75rem;
    margin-bottom: .6rem;
}

.sidebar-culture-link[b-bnx7wbqykk] {
    color: var(--sidebar-ink-soft);
}

.sidebar-culture-link:hover[b-bnx7wbqykk] {
    color: var(--sidebar-mint);
    text-decoration: underline;
}

.sidebar-culture-current[b-bnx7wbqykk] {
    color: var(--sidebar-ink);
    font-weight: 600;
}

.sidebar-culture-sep[b-bnx7wbqykk] {
    color: var(--sidebar-ink-faint);
}

[b-bnx7wbqykk] .sidebar-logout-btn {
    display: flex;
    align-items: center;
    gap: .55rem;
    width: 100%;
    text-align: left;
    background: none;
    border: 0;
    padding: .45rem .5rem;
    font-family: 'IBM Plex Sans', 'Segoe UI', system-ui, sans-serif;
    font-size: .85rem;
    font-weight: 500;
    color: var(--sidebar-ink-soft);
    border-radius: var(--radius-sm);
}

[b-bnx7wbqykk] .sidebar-logout-btn:hover {
    background: var(--sidebar-hover-bg);
    color: var(--sidebar-ink);
}

/* ==== Mobile collapsible nav (A1, 2026-08 mobile pass) ====
   Pure-CSS checkbox+label toggle - no JS, works with JS disabled (nav simply starts collapsed
   on mobile either way, same as it always renders regardless of script execution). Everything
   below is either hidden unconditionally (so desktop is unaffected without needing every rule
   inside the media query) or explicitly scoped to ≤900px. Desktop's .sidebar-brand and the
   AuthorizeView-driven nav/company/footer keep their original DOM position and CSS untouched.
   NOTE: this block is placed LAST in the file deliberately - its .sidebar-brand / .sidebar-nav
   etc. overrides share equal specificity with the base rules earlier in this file, so source
   order is what makes them win; moving this block earlier would silently break the override. */
.sidebar-mobile-toggle[b-bnx7wbqykk] {
    /* Hidden at all widths (no visible checkbox UI) - the associated <label for> still toggles
       it while display:none, standard cross-browser behavior for this pattern. */
    display: none;
}

.sidebar-topbar[b-bnx7wbqykk] {
    /* Desktop: not shown - the real .sidebar-brand above is used instead. */
    display: none;
}

/* display:contents on desktop = zero layout effect: its children (palette hint, company
   switcher, nav list, footer) remain direct flex items of .sidebar exactly as before this
   wrapper was introduced. Only becomes a real (hideable) flex container at ≤900px below. */
.sidebar-collapsible[b-bnx7wbqykk] {
    display: contents;
}

@media (max-width: 900px) {
    .sidebar-topbar[b-bnx7wbqykk] {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: .5rem;
        padding-bottom: .75rem;
        margin-bottom: .75rem;
        border-bottom: 1px solid var(--sidebar-border);
    }

    /* Replaced by the compact brand inside .sidebar-topbar above. */
    .sidebar-brand[b-bnx7wbqykk] {
        display: none;
    }

    .sidebar-topbar-brand[b-bnx7wbqykk] {
        display: flex;
        align-items: center;
        gap: .6rem;
        min-width: 0;
    }

    .sidebar-topbar-actions[b-bnx7wbqykk] {
        display: flex;
        align-items: center;
        flex: none;
    }

    .sidebar-topbar-btn[b-bnx7wbqykk] {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 44px;
        height: 44px;
        flex: none;
        background: none;
        border: 0;
        border-radius: var(--radius-sm);
        color: var(--sidebar-ink);
        font-size: 1.05rem;
        cursor: pointer;
    }

    .sidebar-topbar-btn:hover[b-bnx7wbqykk] {
        background: var(--sidebar-hover-bg);
    }

    /* Hamburger <-> close icon swap, driven by the checkbox's :checked state (no JS). */
    .sidebar-toggle-icon-open[b-bnx7wbqykk] {
        display: none;
    }

    .sidebar-mobile-toggle:checked ~ .sidebar-topbar .sidebar-toggle-icon-closed[b-bnx7wbqykk] {
        display: none;
    }

    .sidebar-mobile-toggle:checked ~ .sidebar-topbar .sidebar-toggle-icon-open[b-bnx7wbqykk] {
        display: inline;
    }

    /* Collapsed by default; expands when the checkbox (toggled by the hamburger label) is
       checked. Company switcher, full nav list, and culture/logout footer all live in here,
       so they stay reachable once expanded. */
    .sidebar-collapsible[b-bnx7wbqykk] {
        display: none;
    }

    .sidebar-mobile-toggle:checked ~ .sidebar-collapsible[b-bnx7wbqykk] {
        display: flex;
        flex-direction: column;
    }

    /* ---- Touch targets (A3): larger padding on mobile only, desktop untouched ---- */
    .sidebar-palette-hint[b-bnx7wbqykk] {
        padding: .7rem .6rem;
    }

    [b-bnx7wbqykk] .nav-link {
        padding: .75rem .65rem;
    }

    [b-bnx7wbqykk] .sidebar-logout-btn {
        padding: .7rem .5rem;
    }

    [b-bnx7wbqykk] .sidebar-company-select {
        padding: .55rem .4rem;
    }
}
/* /Components/Layout/PrintLayout.razor.rz.scp.css */
/* Bare shell for printable documents — no sidebar, no app-shell padding, plain white
   background regardless of the light/dark app chrome elsewhere (project-knowledge.md "Design
   system" tokens aren't reused here on purpose: a printed document reads as paper, not as the
   app UI). */
.print-page[b-z2d673atx4] {
    background: #ffffff;
    min-height: 100vh;
    padding: 2rem;
    /* The white above is deliberate and so is the light palette that now comes with it: the
       element carries data-bs-theme="light" (see PrintLayout.razor). Pinning one without the
       other is what made this page unreadable in dark mode. */
    color: var(--ink);
}

@media print {
    .print-page[b-z2d673atx4] {
        padding: 0;
    }

    /* Never split a row. Measured before this rule existed: a <tr> ran y 1116..1147 with the A4
       boundary at 1123, so its figures were cut in half down the middle and continued overleaf. */
    .print-page[b-z2d673atx4]  tr,
    .print-page[b-z2d673atx4]  th,
    .print-page[b-z2d673atx4]  td {
        break-inside: avoid;
    }

    /* Repeat the header row on every sheet a table spans. Without this, page two of a cost
       history is a wall of unlabelled numbers - and this is the page someone reads next to a car
       they are deciding whether to buy. */
    .print-page[b-z2d673atx4]  thead {
        display: table-header-group;
    }

    /* Keep a heading with what it introduces, and keep the gap-notice callout whole: a caveat
       split across a page break is a caveat half-read. */
    .print-page[b-z2d673atx4]  h1,
    .print-page[b-z2d673atx4]  h2,
    .print-page[b-z2d673atx4]  h3 {
        break-after: avoid;
    }

    .print-page[b-z2d673atx4]  .alert {
        break-inside: avoid;
    }
}

/* On a phone the 2rem gutters were 64px of a 390px screen - 16% of the viewport spent on white
   space, on the one page in this product most likely to be read standing next to a car. Measured
   at 390px the record page reported scrollWidth 400 against a 390 viewport: the only page in the
   app that overflowed. The tables now scroll inside their own wrappers, which is what guarantees
   this cannot come back for a long vendor name or a big number, and this breakpoint is what gives
   the content room so they rarely have to. Print is unaffected - the rule above wins by order. */
@media (max-width: 576px) {
    .print-page[b-z2d673atx4] {
        padding: 0.75rem;
    }
}
/* /Components/Layout/ReconnectModal.razor.rz.scp.css */
.components-reconnect-first-attempt-visible[b-a8nikfpg1o],
.components-reconnect-repeated-attempt-visible[b-a8nikfpg1o],
.components-reconnect-failed-visible[b-a8nikfpg1o],
.components-pause-visible[b-a8nikfpg1o],
.components-resume-failed-visible[b-a8nikfpg1o],
.components-rejoining-animation[b-a8nikfpg1o] {
    display: none;
}

#components-reconnect-modal.components-reconnect-show .components-reconnect-first-attempt-visible[b-a8nikfpg1o],
#components-reconnect-modal.components-reconnect-show .components-rejoining-animation[b-a8nikfpg1o],
#components-reconnect-modal.components-reconnect-paused .components-pause-visible[b-a8nikfpg1o],
#components-reconnect-modal.components-reconnect-resume-failed .components-resume-failed-visible[b-a8nikfpg1o],
#components-reconnect-modal.components-reconnect-retrying[b-a8nikfpg1o],
#components-reconnect-modal.components-reconnect-retrying .components-reconnect-repeated-attempt-visible[b-a8nikfpg1o],
#components-reconnect-modal.components-reconnect-retrying .components-rejoining-animation[b-a8nikfpg1o],
#components-reconnect-modal.components-reconnect-failed[b-a8nikfpg1o],
#components-reconnect-modal.components-reconnect-failed .components-reconnect-failed-visible[b-a8nikfpg1o] {
    display: block;
}

#components-reconnect-modal[b-a8nikfpg1o] {
    background-color: var(--panel, #ffffff);
    color: var(--ink, #1a1f27);
    width: 20rem;
    margin: 20vh auto;
    padding: 2rem;
    border: 1px solid var(--border, #e3e6ea);
    border-radius: var(--radius, 8px);
    box-shadow: var(--shadow-md, 0 4px 10px rgba(16, 24, 40, .06), 0 1px 3px rgba(16, 24, 40, .08));
    opacity: 0;
    transition: display 0.5s allow-discrete, overlay 0.5s allow-discrete;
    animation: components-reconnect-modal-fadeOutOpacity-b-a8nikfpg1o 0.5s both;
}

#components-reconnect-modal[open][b-a8nikfpg1o] {
    animation: components-reconnect-modal-slideUp-b-a8nikfpg1o 1.5s cubic-bezier(.05, .89, .25, 1.02) 0.3s, components-reconnect-modal-fadeInOpacity-b-a8nikfpg1o 0.5s ease-in-out 0.3s;
    animation-fill-mode: both;
}

#components-reconnect-modal[b-a8nikfpg1o]::backdrop {
    background-color: rgba(0, 0, 0, 0.6);
    animation: components-reconnect-modal-fadeInOpacity-b-a8nikfpg1o 0.5s ease-in-out;
    opacity: 1;
}

@keyframes components-reconnect-modal-slideUp-b-a8nikfpg1o {
    0% {
        transform: translateY(30px) scale(0.95);
    }

    100% {
        transform: translateY(0);
    }
}

@keyframes components-reconnect-modal-fadeInOpacity-b-a8nikfpg1o {
    0% {
        opacity: 0;
    }

    100% {
        opacity: 1;
    }
}

@keyframes components-reconnect-modal-fadeOutOpacity-b-a8nikfpg1o {
    0% {
        opacity: 1;
    }

    100% {
        opacity: 0;
    }
}

.components-reconnect-container[b-a8nikfpg1o] {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1rem;
}

#components-reconnect-modal p[b-a8nikfpg1o] {
    margin: 0;
    text-align: center;
}

#components-reconnect-modal button[b-a8nikfpg1o] {
    border: 0;
    background-color: var(--accent, #0f7a6f);
    color: #ffffff;
    font-weight: 600;
    padding: 6px 24px;
    border-radius: var(--radius-sm, 6px);
    cursor: pointer;
}

#components-reconnect-modal button:hover[b-a8nikfpg1o] {
    background-color: var(--accent-hover, #0b5f56);
}

.components-rejoining-animation[b-a8nikfpg1o] {
    position: relative;
    width: 80px;
    height: 80px;
}

.components-rejoining-animation div[b-a8nikfpg1o] {
    position: absolute;
    border: 3px solid var(--accent, #0f7a6f);
    opacity: 1;
    border-radius: 50%;
    animation: components-rejoining-animation-b-a8nikfpg1o 1.5s cubic-bezier(0, 0.2, 0.8, 1) infinite;
}

.components-rejoining-animation div:nth-child(2)[b-a8nikfpg1o] {
    animation-delay: -0.5s;
}

@media (max-width: 575.98px) {
    #components-reconnect-modal[b-a8nikfpg1o] {
        width: calc(100vw - 32px);
        margin: 12vh auto;
        padding: 1.5rem;
    }

    .components-rejoining-animation[b-a8nikfpg1o] {
        width: 64px;
        height: 64px;
    }
}

@keyframes components-rejoining-animation-b-a8nikfpg1o {
    0% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 0;
    }

    4.9% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 0;
    }

    5% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 1;
    }

    100% {
        top: 0px;
        left: 0px;
        width: 80px;
        height: 80px;
        opacity: 0;
    }
}
/* /Components/Layout/ThemeToggle.razor.rz.scp.css */
/* Dark-mode toggle (2026-08 round) - circular icon button living on the dark --sidebar-* surface
   in BOTH its mounting locations (desktop sidebar footer AND the mobile top bar - .sidebar-topbar
   is a child of the same .sidebar panel, so it shares the same dark background), matching the
   existing .sidebar-palette-hint/.sidebar-logout-btn hover treatment in NavMenu.razor.css. The
   mobile top bar additionally merges in that file's own .sidebar-topbar-btn class (44px touch
   target) via the ButtonClass parameter - the sizing below is this component's own default,
   used as-is by the desktop footer mount. */
.theme-toggle-form[b-dxo6jt7ygq] {
    display: inline-flex;
    margin: 0;
}

.theme-toggle-btn[b-dxo6jt7ygq] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* WCAG 2.2 SC 2.5.8 minimum target size even on the desktop-default (smaller) sizing. */
    width: 2rem;
    height: 2rem;
    padding: 0;
    background: none;
    border: 1px solid var(--sidebar-border);
    border-radius: 999px;
    color: var(--sidebar-ink-soft);
    font-size: .85rem;
    cursor: pointer;
    transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
}

.theme-toggle-btn:hover[b-dxo6jt7ygq] {
    color: var(--sidebar-ink);
    background: var(--sidebar-hover-bg);
    border-color: var(--sidebar-mint);
}

/* Icon shows the theme CURRENTLY in effect (moon = dark active, sun = light active), not the
   theme a click would switch to - CSS alone tracks it via [data-bs-theme] on <html>, so it stays
   correct even after theme-toggle.js flips that attribute client-side with no component re-render. */
.theme-toggle-icon-moon[b-dxo6jt7ygq] {
    display: none;
}

[data-bs-theme="dark"] .theme-toggle-icon-sun[b-dxo6jt7ygq] {
    display: none;
}

[data-bs-theme="dark"] .theme-toggle-icon-moon[b-dxo6jt7ygq] {
    display: inline;
}
/* /Components/Pages/Account/Register.razor.rz.scp.css */
/* Honeypot field for HandleRegisterAsync's anti-spam check (see RegistrationGate.cs).
   Off-screen via absolute positioning rather than display:none/visibility:hidden —
   those are the first two properties naive bot scrapers check for and skip, which
   would defeat the honeypot; moving it off-screen still hides it from sighted users
   without tripping that check. Combined with tabindex="-1"/aria-hidden on the input
   itself, it's also invisible to keyboard and screen-reader users. Ported verbatim
   from G:\Solar's sibling pattern (Register.razor.css). */
.hp-field[b-e43qbc2dhv] {
    position: absolute;
    left: -9999px;
    top: auto;
    width: 1px;
    height: 1px;
    overflow: hidden;
}
/* /Components/Pages/Home.razor.rz.scp.css */
/* ============================================================================
   The signed-in dashboard (project-knowledge.md §7.6/§7.8, designed in Design.html).

   This file previously held 507 lines of which 47 of its 51 classes had NO markup
   in Home.razor: stat-tile-*, attn-*, activity-*, feature-*, step*, landing-* -
   PropertyHub's dashboard and its old marketing page, inherited at M0 and never
   deleted. Its very first comment described a tile called "Leases expiring soon".
   A lease is PropertyHub's domain, not this one. The §7.4 leftover audit missed it
   because that audit read pages, procs and features - not scoped stylesheets.

   None of it was reusable: it was Slate-era design, which the Odometer identity
   replaced. So it is gone rather than adapted.

   The vocabulary here is the landing page's, deliberately: hairlines and ruled rows
   for evidence, one accent figure per page, the hatch for what is missing. A signed-in
   user should recognise the product they were sold. What does NOT appear is the
   odometer drum - one per page, on the figure the page exists for, and Design.html
   settled that the dashboard's figure is set plainly. The drum stays the hero device.
   ============================================================================ */

.home-head[b-bupkxlsjq8] {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1.5rem;
}

/* The date, not the product name. A signed-in user knows what they opened; what they
   do not know is how current the figures below are. */
.home-kicker[b-bupkxlsjq8] {
    font-size: .78rem;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--ink-soft);
    margin: 0 0 .2rem;
}

.home-title[b-bupkxlsjq8] {
    margin-bottom: 0;
}

.home-head-actions[b-bupkxlsjq8] {
    flex: none;
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-top: .35rem;
}

/* ---- the figures ----
   Hairline-separated columns rather than cards, matching the landing page's tier row.
   One per currency plus the coverage count, so the ordinary single-currency fleet
   renders as the designed pair. */
.home-figures[b-bupkxlsjq8] {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    background: var(--panel);
    border: 1px solid var(--border);
    margin-bottom: 1.5rem;
}

.home-figure[b-bupkxlsjq8] {
    flex: 1 1 15rem;
    min-width: 0;
    padding: 1.1rem 1.35rem 1.2rem;
}

.home-figure + .home-figure[b-bupkxlsjq8] {
    border-left: 1px solid var(--border);
}

.home-figure-n[b-bupkxlsjq8] {
    font-family: 'Saira Condensed', 'Archivo', sans-serif;
    font-weight: 700;
    font-size: clamp(1.9rem, 4.2vw, 2.6rem);
    line-height: 1;
    letter-spacing: -.01em;
    font-variant-numeric: tabular-nums;
    /* The accent marks the one figure the page exists for. Measured before use:
       4.71:1 in light, 6.54:1 in dark, both clearing AA for normal text. */
    color: var(--accent);
    margin: 0 0 .4rem;
}

.home-figure-cur[b-bupkxlsjq8],
.home-figure-of[b-bupkxlsjq8] {
    font-family: 'Archivo', 'Segoe UI', system-ui, sans-serif;
    font-size: .8rem;
    font-weight: 600;
    letter-spacing: .06em;
    color: var(--ink-soft);
    margin-left: .45rem;
}

/* Coverage is context for the figure, not a second headline - so it is set in ink,
   and only the money figure carries the accent. */
.home-figure-quiet .home-figure-n[b-bupkxlsjq8] {
    color: var(--ink);
}

.home-figure-l[b-bupkxlsjq8] {
    margin: 0;
    font-size: .8rem;
    line-height: 1.4;
    color: var(--ink-soft);
}

/* The indicative EUR line (2026-09-01). Deliberately quieter than the real figures beside it and
   never in the accent: it is an aid to thinking about a multi-currency fleet, not money anybody
   was charged, and it must not compete visually with totals that ARE. Tokens throughout, so dark
   mode swaps with the rest of the palette rather than freezing at a light value. */
.home-figure-approx .home-figure-approx-n[b-bupkxlsjq8] {
    color: var(--ink-soft);
    font-size: 1.05rem;
    letter-spacing: 0;
}

.home-figure-approx[b-bupkxlsjq8] {
    opacity: .85;
}

/* The sheet, needs-you and fleet rules moved to HomeFleetSheet.razor.css and
   HomeNeedsYouSheet.razor.css (2026-09-24) - scoped CSS only reaches the component that renders it. */

@media (max-width: 62rem) {
    .home-figure + .home-figure[b-bupkxlsjq8] {
        border-left: 0;
        border-top: 1px solid var(--border);
    }
}

/* ---- streaming placeholder (blazor.html B1) ----
   [StreamRendering] renders this component once before its queries return, so the page needs a state
   that means "not known yet" and is distinguishable from "nothing to show". Shapes only, no text and
   no invented figures: a placeholder that guesses is the page asserting something it does not have.

   No pulse animation. Everywhere else in this identity motion is spent once, on the odometer roll, and
   a shimmering skeleton on a page that resolves in milliseconds would be decoration pretending to be
   feedback. The frames alone say "content goes here". */
.home-skeleton-figures[b-bupkxlsjq8],
.home-skeleton-sheet[b-bupkxlsjq8] {
    background: var(--panel);
    border: 1px solid var(--border);
    margin-bottom: 1.5rem;
}

.home-skeleton-figures[b-bupkxlsjq8] {
    display: flex;
}

.home-skeleton-figures i[b-bupkxlsjq8] {
    flex: 1 1 15rem;
    height: 5.1rem;
}

.home-skeleton-figures i + i[b-bupkxlsjq8] {
    border-left: 1px solid var(--border);
}

.home-skeleton-sheet i[b-bupkxlsjq8] {
    display: block;
    height: 3.6rem;
}

.home-skeleton-sheet i + i[b-bupkxlsjq8] {
    border-top: 1px solid var(--border);
}
/* /Components/Pages/HomeFleetSheet.razor.rz.scp.css */
/* Scoped to HomeFleetSheet.razor. Moved here verbatim from Home.razor.css on 2026-09-24, when the Home
   split moved this markup into a child component: a *.razor.css file styles only the elements its
   OWN component renders (Blazor stamps them with a per-component scope attribute), so rules left in
   Home.razor.css silently stopped matching and the sheet rendered unstyled - no test saw it, the
   Fable review did. The shared sheet frame below is duplicated in HomeFleetSheet.razor.css and
   HomeNeedsYouSheet.razor.css for the same reason; keep the two copies identical. */

/* ---- sheets ---- */
.home-sheet[b-enderfk2di] {
    background: var(--panel);
    border: 1px solid var(--border);
    margin-bottom: 1.5rem;
}

.home-sheet-h[b-enderfk2di] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: .5rem 1rem;
    padding: .9rem 1.35rem;
    border-bottom: 1px solid var(--border);
}

.home-sheet-h h2[b-enderfk2di] {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
}

.home-sheet-note[b-enderfk2di] {
    font-size: .78rem;
    color: var(--ink-soft);
}

/* ---- the fleet ---- */
.fleet-row[b-enderfk2di] {
    display: grid;
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1.4fr) auto;
    align-items: center;
    gap: 1rem 1.5rem;
    padding: 1rem 1.35rem;
}

.fleet-row + .fleet-row[b-enderfk2di] {
    border-top: 1px solid var(--border);
}

.fleet-name a[b-enderfk2di] {
    font-weight: 600;
    color: var(--ink);
}

.fleet-name a:hover[b-enderfk2di] {
    color: var(--accent);
}

.fleet-name span[b-enderfk2di] {
    display: block;
    margin-top: .15rem;
    font-size: .8rem;
    color: var(--ink-soft);
}

.fleet-cover[b-enderfk2di] {
    min-width: 0;
}

.fleet-fig[b-enderfk2di] {
    text-align: right;
    white-space: nowrap;
}

.fleet-fig b[b-enderfk2di] {
    font-family: 'Saira Condensed', 'Archivo', sans-serif;
    font-weight: 700;
    font-size: 1.5rem;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    color: var(--ink);
}

.fleet-fig a[b-enderfk2di] {
    text-decoration: none;
}

.fleet-fig a:hover b[b-enderfk2di] {
    color: var(--accent);
}

/* The basis rides with every figure - "final" only once the vehicle is sold. A
   still-owned vehicle counts its whole purchase price as sunk, and a figure that
   does not say so is the single most misleading number this app can print. */
.fleet-fig s[b-enderfk2di] {
    display: block;
    text-decoration: none;
    margin-top: .3rem;
    font-size: .74rem;
    color: var(--ink-soft);
}

.fleet-fig-none[b-enderfk2di] {
    font-style: italic;
}

/* ---- the coverage strip: the product's second signature ---- */
.cover[b-enderfk2di] {
    display: flex;
    gap: 3px;
    align-items: flex-end;
}

.cover b[b-enderfk2di] {
    flex: 1;
    height: 16px;
    min-width: 5px;
    background: var(--ink);
}

/* Owned, nothing logged: drawn, never coloured. An empty month is a blank, not a
   warning - some of them are genuinely right (a boat in February). */
.cover b.none[b-enderfk2di] {
    /* --hatch carries a contrast contract (>= 3:1 on --panel) because WCAG 2.2 SC 1.4.11 covers
       "parts of graphics required to understand the content", and these three states ARE the
       information. Drawn with --border it measured 1.39:1 light / 1.34:1 dark. */
    background: repeating-linear-gradient(135deg,
        var(--hatch) 0 3px, var(--panel) 3px 6px);
    box-shadow: inset 0 0 0 1px var(--hatch);
}

/* Not owned: no mark at all, because nothing was expected. It keeps its width so the
   months stay aligned, and carries only a baseline so the cell does not read as a
   rendering fault. Anything stronger would be the record accusing its owner of gaps
   that could not exist. */
.cover b.unowned[b-enderfk2di] {
    background: none;
    box-shadow: none;
    height: 1px;
    /* The baseline is the only mark this state has, so it is the part "required to understand the
       content" and needs the same 3:1. It stays 1px: SC 1.4.11 sets a contrast floor, not a size
       one, and thickening it would start to look like data. */
    background-color: var(--hatch);
}

/* Month labels, only when there is one vehicle and the row has the room. */
.cover-months[b-enderfk2di] {
    display: flex;
    gap: 3px;
    margin-top: .3rem;
}

.cover-months span[b-enderfk2di] {
    flex: 1;
    min-width: 5px;
    font-size: .62rem;
    letter-spacing: .02em;
    text-align: center;
    color: var(--ink-soft);
    overflow: hidden;
}

.cover-note[b-enderfk2di] {
    margin: .45rem 0 0;
    font-size: .78rem;
    color: var(--ink-soft);
}

.cover-key[b-enderfk2di] {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem 1.25rem;
    margin: 0;
    padding: .7rem 1.35rem .8rem;
    border-top: 1px solid var(--border);
    font-size: .74rem;
    color: var(--ink-soft);
}

.cover-key s[b-enderfk2di] {
    text-decoration: none;
    display: inline-flex;
    align-items: flex-start;
    gap: .4rem;
}

.cover-key s[b-enderfk2di]::before {
    content: "";
    width: 11px;
    height: 11px;
    flex: none;
    margin-top: .2em;
    background: var(--ink);
}

.cover-key s.h[b-enderfk2di]::before {
    background: repeating-linear-gradient(135deg,
        var(--hatch) 0 3px, var(--panel) 3px 6px);
    box-shadow: inset 0 0 0 1px var(--hatch);
}

.cover-key s.o[b-enderfk2di]::before {
    background: none;
    height: 1px;
    margin-top: .8em;
    background-color: var(--hatch);
}

/* ---- one vehicle is not a table ----
   At a single vehicle the row opens out: the strip takes the full width and labels its
   months, because most owners here have one or two and a one-row table is a table for
   the developer's benefit. */
.fleet-single .fleet-row[b-enderfk2di] {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: start;
}

.fleet-single .fleet-cover[b-enderfk2di] {
    grid-column: 1 / -1;
    order: 3;
}

.fleet-single .cover b[b-enderfk2di] {
    height: 26px;
}

/* Specificity, not source order. `.fleet-single .cover b` (0,2,1) and `.cover b.unowned`
   (0,2,1) tie, and the single-vehicle rule above is declared later - so it won, and every
   not-owned month rendered as a SOLID 26px grey block instead of a baseline. Nine of them
   in a row read as data: the expanded strip was quietly accusing the owner of nine months
   of missing costs on a vehicle bought in June. This selector (0,3,1) settles it. */
.fleet-single .cover b.unowned[b-enderfk2di] {
    height: 1px;
}

@media (max-width: 62rem) {
    .fleet-row[b-enderfk2di],
    .fleet-single .fleet-row[b-enderfk2di] {
        grid-template-columns: minmax(0, 1fr);
    }

    .fleet-fig[b-enderfk2di] {
        text-align: left;
    }

    .fleet-single .fleet-cover[b-enderfk2di] {
        order: 0;
    }
}
/* /Components/Pages/HomeNeedsYouSheet.razor.rz.scp.css */
/* Scoped to HomeNeedsYouSheet.razor. Moved here verbatim from Home.razor.css on 2026-09-24, when the Home
   split moved this markup into a child component: a *.razor.css file styles only the elements its
   OWN component renders (Blazor stamps them with a per-component scope attribute), so rules left in
   Home.razor.css silently stopped matching and the sheet rendered unstyled - no test saw it, the
   Fable review did. The shared sheet frame below is duplicated in HomeFleetSheet.razor.css and
   HomeNeedsYouSheet.razor.css for the same reason; keep the two copies identical. */

/* ---- sheets ---- */
.home-sheet[b-bx1fm40mjx] {
    background: var(--panel);
    border: 1px solid var(--border);
    margin-bottom: 1.5rem;
}

.home-sheet-h[b-bx1fm40mjx] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: .5rem 1rem;
    padding: .9rem 1.35rem;
    border-bottom: 1px solid var(--border);
}

.home-sheet-h h2[b-bx1fm40mjx] {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
}

.home-sheet-note[b-bx1fm40mjx] {
    font-size: .78rem;
    color: var(--ink-soft);
}

/* ---- needs you ---- */
.needs-row[b-bx1fm40mjx] {
    display: grid;
    grid-template-columns: 8.5rem minmax(0, 1fr) auto;
    align-items: baseline;
    gap: .75rem 1rem;
    padding: .7rem 1.35rem;
    color: var(--ink);
    text-decoration: none;
}

.needs-row + .needs-row[b-bx1fm40mjx] {
    border-top: 1px solid var(--border);
}

.needs-row:hover[b-bx1fm40mjx],
.needs-row:focus-visible[b-bx1fm40mjx] {
    background: var(--bg-subtle);
    text-decoration: none;
}

.needs-when[b-bx1fm40mjx] {
    font-size: .72rem;
    font-weight: 600;
    letter-spacing: .07em;
    text-transform: uppercase;
}

.needs-when.now[b-bx1fm40mjx] {
    color: var(--danger);
}

.needs-when.soon[b-bx1fm40mjx] {
    color: var(--warn);
}

/* "Cannot be checked" is not a milder Not-Due and must not read as one: it means a
   dual-trigger reminder is running blind on its usage leg. Muted, never green. */
.needs-when.unknown[b-bx1fm40mjx] {
    color: var(--muted-status);
}

/* A due recurring template (2026-09-01). Deliberately the WARN tone rather than the danger one:
   a template coming due is a thing to do, not a deadline already missed - it has an amount and a
   date the user chose, and the app is offering to file it, not warning them. Every colour here is
   a var(--*) token so dark mode swaps with the rest of the palette. */
.needs-when.needs-due[b-bx1fm40mjx] {
    color: var(--warn);
}

.needs-what[b-bx1fm40mjx] {
    font-weight: 500;
}

.needs-who[b-bx1fm40mjx] {
    font-size: .82rem;
    color: var(--ink-soft);
    text-align: right;
}

@media (max-width: 62rem) {
    .needs-row[b-bx1fm40mjx] {
        grid-template-columns: minmax(0, 1fr);
        gap: .2rem;
    }

    .needs-who[b-bx1fm40mjx] {
        text-align: left;
    }
}
