/* ── Site-wide text size bump ──
   FluentUI's own type ramp (--type-ramp-*-font-size/-line-height, consumed both by plain
   elements like <h1>/<button> and inside every Fluent component's shadow DOM) drives most of the
   app's text — a root font-size change alone doesn't reach it, since these are fixed px custom
   properties, not em/rem. Shifted the whole ramp up by one of FluentUI's own steps (e.g. base
   14px->16px, the old plus-1) rather than picking new numbers, so proportions and line-heights
   stay exactly as tuned. plus-6 has no next step to borrow, so it's extrapolated at the same
   +8px/+12px cadence as the step below it.
   !important is load-bearing here, not decoration: these defaults aren't set by any rule in
   reboot.css/fluentui-bundle.css (confirmed — neither file defines them) but by a Constructable
   StyleSheet the Fluent JS bundle adds to document.adoptedStyleSheets at runtime. Adopted
   stylesheets cascade after every <link>/<style> in the document regardless of load order, so a
   plain :root rule here — even loaded last — still loses to it on source order once specificity
   ties (:root vs :root). !important is the only lever that reaches over it from a static file. */
:root {
    --type-ramp-minus-2-font-size: 12px !important;
    --type-ramp-minus-2-line-height: 16px !important;
    --type-ramp-minus-1-font-size: 14px !important;
    --type-ramp-minus-1-line-height: 20px !important;
    --type-ramp-base-font-size: 16px !important;
    --type-ramp-base-line-height: 22px !important;
    --type-ramp-plus-1-font-size: 20px !important;
    --type-ramp-plus-1-line-height: 26px !important;
    --type-ramp-plus-2-font-size: 24px !important;
    --type-ramp-plus-2-line-height: 32px !important;
    --type-ramp-plus-3-font-size: 28px !important;
    --type-ramp-plus-3-line-height: 36px !important;
    --type-ramp-plus-4-font-size: 32px !important;
    --type-ramp-plus-4-line-height: 40px !important;
    --type-ramp-plus-5-font-size: 40px !important;
    --type-ramp-plus-5-line-height: 52px !important;
    --type-ramp-plus-6-font-size: 48px !important;
    --type-ramp-plus-6-line-height: 64px !important;

    /* The muted grey used throughout for secondary text (meta lines, timestamps, placeholders —
       every var(--neutral-foreground-hint) in this file) wasn't readable enough against the
       app's near-black backgrounds. Same adopted-stylesheet mechanism as the type ramp above, so
       same !important requirement. Flattened to plain white rather than a lighter grey: this app
       doesn't otherwise lean on a hint/rest text-colour distinction to convey meaning, so there's
       nothing lost by dropping it. */
    --neutral-foreground-hint: #ffffff !important;
}

/* Our own CSS below is written in rem against this root size, not FluentUI's ramp above — bumped
   the same modest notch (16px -> 17px) so it scales in step with it. */
html {
    font-size: 17px;
}

/* A second bump, phones only: on top of the site-wide notch above, mobile gets one more step of
   both the root size (for our rem-based classes) and FluentUI's ramp (for everything else) —
   small screens are read at arm's length more than a desktop monitor is. */
@media (max-width: 768px) {
    html {
        font-size: 18px;
    }

    :root {
        --type-ramp-minus-2-font-size: 14px !important;
        --type-ramp-minus-2-line-height: 20px !important;
        --type-ramp-minus-1-font-size: 16px !important;
        --type-ramp-minus-1-line-height: 22px !important;
        --type-ramp-base-font-size: 20px !important;
        --type-ramp-base-line-height: 26px !important;
        --type-ramp-plus-1-font-size: 24px !important;
        --type-ramp-plus-1-line-height: 32px !important;
        --type-ramp-plus-2-font-size: 28px !important;
        --type-ramp-plus-2-line-height: 36px !important;
        --type-ramp-plus-3-font-size: 32px !important;
        --type-ramp-plus-3-line-height: 40px !important;
        --type-ramp-plus-4-font-size: 40px !important;
        --type-ramp-plus-4-line-height: 52px !important;
        --type-ramp-plus-5-font-size: 48px !important;
        --type-ramp-plus-5-line-height: 64px !important;
        --type-ramp-plus-6-font-size: 56px !important;
        --type-ramp-plus-6-line-height: 76px !important;
    }
}

html, body {
    margin: 0;
    font-family: var(--body-font);
}

.content {
    padding: 1rem;
}

/* ── Post-navigation heading focus ──
   Routes.razor's <FocusOnNavigate Selector="h1" /> moves focus to each page's <h1> after a
   client-side navigation, so screen readers announce the new page - SPA nav never reloads
   the document, so without this, focus silently stays put and assistive tech gets no cue
   anything changed. That's a tabindex="-1" + focus() call, not a real interactive element,
   so the browser's default focus ring reads as a stray box around plain text. Scoped to h1
   rather than a blanket *:focus rule, which would also strip the ring from buttons/links/
   inputs that genuinely need it (WCAG 2.4.7). */
h1:focus {
    outline: none;
}

/* ── Dialog title focus ──
   Same shape of problem as the h1 rule above, one level deeper: FluentUI's dialog header
   wraps its <h4> title in its own tabindex="0" div and focuses THAT (not a real interactive
   element) when a dialog opens, for the same reason - so assistive tech announces the title.
   Normally invisible: opening a dialog synchronously from a click handler focuses the title
   close enough to the mouse gesture that Chromium's :focus-visible heuristic suppresses the
   ring. A dialog opened after an `await` (e.g. Cases.razor's OfferInitialFinancialAsync,
   chained onto the case save completing) is far enough removed that the heuristic flips and
   draws it - first noticed on that dialog's two-line title, but the underlying gap applies to
   any dialog opened outside a direct click. Scoped to the header's title div specifically
   (not a blanket rule) - the close button is a separate <fluent-button>, never touched by this. */
.fluent-dialog-header div:focus {
    outline: none;
}

/* ── FluentUI dialog ──
   The bundled dialog stylesheet never bounds ::part(control)'s height for the
   default (non side-panel) variant, so a long form just grows past the
   viewport with nothing to scroll it back into view — its lower portion, and
   the rounded corners of the panel itself, end up unclipped and overlapping
   whatever's behind the dialog. Bound it and let the body scroll internally
   instead. Also darken the overlay: 30% black barely dims an already-dark
   theme, so the page behind the dialog stays fully legible and the whole
   thing reads as "see-through" even though nothing is literally transparent. */
fluent-dialog::part(overlay) {
    background: rgba(0, 0, 0, 0.55);
}

fluent-dialog:not([class~="right"]):not([class~="left"])::part(control) {
    display: grid;
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr auto;
    grid-template-areas:
        'dialog-header'
        'dialog-body'
        'dialog-footer';
    max-height: calc(100dvh - 4rem);
    overflow: hidden;
}

fluent-dialog:not([class~="right"]):not([class~="left"])  .fluent-dialog-body {
    overflow-y: auto;
}

/* ── FluentSelect / FluentAccordion default width ──
   Both web components ship with a fixed, non-percentage default width from their own
   internal styling (not this bundle - it's baked into the component itself), rather than
   stretching to fill whatever container they're placed in. On a wide desktop dialog that
   default happens to read as "close enough" to full width; on a narrow mobile dialog
   (CaseEditDialog's Status select, its accordion sections) the gap between the component
   and its actual container is wide open and obvious. Not scoped to dialogs - the same gap
   exists for the header's firm-switcher FluentSelect (MainLayout.razor), it's just less
   visible there because .account-dropdown is already narrow; this is the general fix for
   both, and correct at any container width since the parent stack/dialog-body already
   determines the real available space. Extended to the other leaf FluentUI input
   components for the same reason - fluent-text-field/text-area/number-field all ship the
   same fixed intrinsic width rather than filling their FluentStack parent. */
fluent-select,
fluent-accordion,
fluent-accordion-item,
fluent-text-field,
fluent-text-area,
fluent-number-field {
    width: 100%;
}

/* ── Nested FluentStack width inside an accordion item ──
   FluentStack's own default cross-axis alignment doesn't stretch its children to fill
   available width (each child sizes to its own content instead) - a top-level accordion
   section's stack still reads full-width because its several leaf fields' combined natural
   sizing happens to reach it, but a FluentStack NESTED one level deeper (CaseEditDialog's
   Client/Interested Party fields, each wrapping a combobox + "+ Create ..." button in their
   own vertical FluentStack) has nothing forcing it wide, so it - and the plain unstyled
   <div> around it - both shrink to the combobox's own width instead of the section's full
   width. `.stack-vertical` is the class FluentStack renders regardless of nesting depth, so
   this catches every level, not just the one currently narrow. CaseEditDialog's quick-intake
   view (`.intake-quick-fields`) renders the same Client-picker fragment outside any accordion
   item, so it needs the identical fix - narrow enough to miss on a wide desktop dialog, but
   obvious on mobile. `.intake-quick-fields` is itself the outer stack-vertical (the Class
   parameter adds it alongside FluentStack's own rendered class on the SAME element), so the
   "Client" div's parent plays both roles at once - a plain descendant-combinator match against
   `.stack-vertical` never fires for it, unlike the accordion case where the outer stack and the
   div's immediate parent are still two different elements. `.intake-quick-fields > div` covers
   that one directly. */
fluent-accordion-item .stack-vertical,
fluent-accordion-item .stack-vertical > div,
.intake-quick-fields .stack-vertical,
.intake-quick-fields .stack-vertical > div,
.intake-quick-fields > div {
    width: 100%;
}

/* ── Native date/datetime inputs inside an accordion item ──
   CaseEditDialog's Hearings/Appointments/Payment sections use plain <input type="date"> /
   <input type="datetime-local"> (not a FluentUI component - see the DateTimeOffset/Boise-
   wall-clock comment on the proxies in the code-behind), each in its own unstyled wrapper
   div. The rule above already stretches that wrapper div to full width; this stretches the
   input inside it too, so the field itself fills the row rather than sitting narrow under a
   now-full-width label. box-sizing: border-box keeps the native calendar-picker icon inside
   the same width instead of pushing it past the section's edge. */
fluent-accordion-item input[type="date"],
fluent-accordion-item input[type="datetime-local"] {
    width: 100%;
    box-sizing: border-box;
}

/* ── Unauthenticated landing (/app, signed out) ── */
.landing {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.75rem;
    text-align: center;
    padding: 4rem 1rem;
}

.landing h1 {
    margin: 0;
}

.landing p {
    color: var(--neutral-foreground-hint);
    margin: 0 0 0.5rem;
}

/* ── Header ── */
.header-brand {
    font-weight: 700;
    font-size: 1.1rem;
    white-space: nowrap;
    color: inherit;
    text-decoration: none;
}

/* ── Account menu ── */
.account-menu-wrap {
    position: relative;
}

.account-dropdown {
    position: absolute;
    right: 0;
    top: 100%;
    z-index: 1000;
    min-width: 200px;
    max-width: calc(100vw - 1rem);
    background: var(--neutral-layer-2);
    border: 1px solid var(--neutral-stroke-subtle-rest);
    border-radius: 0.375rem;
    padding: 0.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}

/* ── Sidebar – hidden on mobile, shown on desktop ── */
.sidebar-nav {
    display: block;
}

/* ── Bottom nav – hidden on desktop, shown on mobile ── */
.bottom-nav {
    display: none;
}

/* ── Record grid (clients, cases) – shown on desktop ── */
.record-grid {
    display: block;
}

/* ── Record cards (clients, cases) – hidden on desktop, shown on mobile ── */
.record-cards {
    display: none;
}

/* ── Action column (e.g. the per-row Edit button on CaseFinancials) – narrow and centered so the
   button isn't left cramped against the row's edges. app.css is a plain global stylesheet (not a
   scoped *.razor.css), so this reaches the <td class="col-action"> FluentDataGrid renders our
   TemplateColumn content into directly, without needing ::deep. ── */
.col-action {
    display: flex;
    align-items: center;
    justify-content: center;
}

/* ── Click affordance ── */
.record-grid ::deep tr[role="row"] { cursor: pointer; }
.record-card { cursor: pointer; }

/* ── Filter bar row (search + add button) ── */
.record-filter-row {
    display: flex;
    gap: 0.5rem;
    align-items: center;
}

.record-filter-row input {
    flex: 1;
}

.add-record-btn {
    white-space: nowrap;
    flex-shrink: 0;
}

/* ── Filter bar — visible on both desktop and mobile ── */
.record-filter-bar {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
    margin-bottom: 0.5rem;
}

.record-filter-bar input {
    width: 100%;
    padding: 0.5rem 0.75rem;
    border-radius: 0.5rem;
    border: 1px solid var(--neutral-stroke-subtle-rest);
    background: var(--neutral-layer-2);
    color: var(--neutral-foreground-rest);
    font-size: 0.9rem;
    box-sizing: border-box;
}

.record-filter-buttons {
    display: flex;
}

.record-filter-buttons button {
    flex: 1;
    position: relative;
    padding: 0.3rem 0.5rem;
    border: 1px solid var(--neutral-stroke-rest);
    background: transparent;
    color: var(--neutral-foreground-rest);
    font-size: 0.75rem;
    font-weight: 500;
    cursor: pointer;
}

/* Joined split-button look (same technique as .intake-mode-toggle below): borders
   overlap via negative margin, rounding lives only on the group's outer corners
   instead of on every button, and the active segment's border comes to front. */
.record-filter-buttons button + button {
    margin-left: -1px;
}

.record-filter-buttons button:first-child {
    border-radius: 999px 0 0 999px;
}

.record-filter-buttons button:last-child {
    border-radius: 0 999px 999px 0;
}

.record-filter-buttons button.active-filter {
    background: var(--accent-fill-rest);
    color: var(--foreground-on-accent-rest);
    border-color: var(--accent-fill-rest);
    z-index: 1;
}

/* ── Intake mode toggle (CaseEditDialog "New Case", ClientEditDialog "New Client") — a
   genuinely joined two-segment button group (Bootstrap's split-button look): borders
   overlap via negative margin and the active segment's border is brought to front with
   z-index, rather than each button being its own pill like .record-filter-buttons above. ── */
.intake-mode-toggle {
    display: flex;
}

.intake-mode-toggle button {
    flex: 0 0 auto;
    position: relative;
    padding: 0.4rem 0.75rem;
    border: 1px solid var(--neutral-stroke-rest);
    background: transparent;
    color: var(--neutral-foreground-rest);
    font-size: 0.85rem;
    font-weight: 500;
    white-space: nowrap;
    cursor: pointer;
}

.intake-mode-toggle button + button {
    margin-left: -1px;
}

.intake-mode-toggle button:first-child {
    border-radius: 0.5rem 0 0 0.5rem;
}

.intake-mode-toggle button:last-child {
    border-radius: 0 0.5rem 0.5rem 0;
}

.intake-mode-toggle button.active {
    background: var(--accent-fill-rest);
    color: var(--foreground-on-accent-rest);
    border-color: var(--accent-fill-rest);
    z-index: 1;
}

/* ── County filter (Cases) — styled to match the search box above it in .record-filter-bar ── */
.county-filter-select {
    width: 100%;
    padding: 0.5rem 0.75rem;
    border-radius: 0.5rem;
    border: 1px solid var(--neutral-stroke-subtle-rest);
    background: var(--neutral-layer-2);
    color: var(--neutral-foreground-rest);
    font-size: 0.9rem;
    box-sizing: border-box;
}

/* ── To-do highlighting (Cases) — mirrors the desktop's per-cell tint for a case carrying a
   pending attorney or client/assistant to-do (CaseSelection.Pane.cs's
   ManagedCasesGridView_CellFormatting: AttyColor / AsstColor). Colors are fixed hex rather
   than theme tokens, same approach as .status-badge above, so they read consistently in both
   themes. A left-edge stripe rather than a full background tint keeps the row's own status
   badge and text legible underneath. ── */
.record-grid tr.todo-atty td:first-child {
    box-shadow: inset 6px 0 0 #2e7d32;
}

.record-grid tr.todo-client td:first-child {
    box-shadow: inset 6px 0 0 #1565c0;
}

.record-grid tr.todo-atty.todo-client td:first-child {
    box-shadow: inset 6px 0 0 #2e7d32, inset 12px 0 0 #1565c0;
}

/* The home page's to-do rows are .mini-cards, not .record-cards, so they're listed here rather
   than given their own colours — the green/blue pair is defined exactly once. They never carry
   both classes (a TodoItem has one owner), hence no combined rule for them below. */
.record-card.todo-atty,
.home-todo-row.todo-atty {
    box-shadow: inset 6px 0 0 #2e7d32;
}

.record-card.todo-client,
.home-todo-row.todo-client {
    box-shadow: inset 6px 0 0 #1565c0;
}

.record-card.todo-atty.todo-client {
    box-shadow: inset 6px 0 0 #2e7d32, inset 12px 0 0 #1565c0;
}

/* ── Status badges — available on both desktop and mobile ── */
.status-badge {
    display: inline-block;
    font-size: 0.65rem;
    font-weight: 600;
    padding: 0.1rem 0.4rem;
    border-radius: 999px;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.status-badge.active {
    background: #107c1033;
    color: #107c10;
}

.status-badge.inactive {
    background: var(--neutral-stroke-subtle-rest);
    color: var(--neutral-foreground-hint);
}

.status-badge.archived {
    background: #d83b0133;
    color: #d83b01;
}

/* Run history states (Automations page, case2courtdocs/projects/workflow-automation's Phase 3) */
.status-badge.running {
    background: #0078d433;
    color: #0078d4;
}

.status-badge.succeeded {
    background: #107c1033;
    color: #107c10;
}

.status-badge.failed {
    background: #c42b1c33;
    color: #c42b1c;
}

/* ── Mini card — a compact, always-visible summary row (e.g. the embedded case list on
   ClientDetail). Deliberately separate from .record-card: that class's visual styling
   (background/border/padding) only exists inside the mobile media query below, since its
   only prior consumer was the mobile card list, which is display:none on desktop anyway. */
.mini-card {
    display: block;
    background: var(--neutral-layer-2);
    border-radius: 0.5rem;
    padding: 0.625rem 0.75rem;
    border: 1px solid var(--neutral-stroke-subtle-rest);
    cursor: pointer;
    text-decoration: none;
    color: inherit;
}

.mini-card .mini-card-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.mini-card .mini-card-title {
    font-weight: 600;
    font-size: 1.15rem;
}

.mini-card .mini-card-detail {
    font-size: 1rem;
    color: var(--neutral-foreground-hint);
}

.mini-card .mini-card-meta {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.25rem;
    font-size: 0.9rem;
    color: var(--neutral-foreground-hint);
}

/* ── Circuit reconnect UI — global ──
   Styled entirely off our own classes (added by reconnect.js), never off the
   components-reconnect-* classes Blazor sets: the framework adds those the moment the
   socket drops, and the whole point here is to stay invisible through a reconnect that
   finishes in a few hundred milliseconds. Default is hidden; reconnect.js decides when
   the banner has waited long enough to be worth showing. */
#components-reconnect-modal {
    display: none;
}

#components-reconnect-modal.reconnect-visible {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    position: fixed;
    top: 0.75rem;
    left: 50%;
    transform: translateX(-50%);
    /* Above fluent-dialog, which sits at 1000 — a dead circuit outranks anything it covers. */
    z-index: 10001;
    padding: 0.5rem 1rem;
    border-radius: 999px;
    background: var(--neutral-layer-2);
    color: var(--neutral-foreground-rest);
    border: 1px solid var(--neutral-stroke-subtle-rest);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
    font-size: 0.85rem;
}

/* Escalation: the framework has given up retrying, so stop pretending the page is live
   and cover it rather than let clicks and keystrokes vanish into a dead circuit. */
#components-reconnect-modal.reconnect-blocking {
    top: 0;
    left: 0;
    transform: none;
    width: 100%;
    height: 100%;
    border: none;
    border-radius: 0;
    box-shadow: none;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    font-size: 1rem;
}

/* Diagnostics, revealed by reconnect.js only once a reconnect is dragging. Users are on
   mobile browsers with no console, so this line is the only way a stalled reconnect can
   be reported back. */
.reconnect-detail {
    display: none;
    opacity: 0.7;
    font-size: 0.75rem;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

#components-reconnect-modal.reconnect-detailed .reconnect-detail {
    display: inline;
}

.reconnect-dot {
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    flex-shrink: 0;
    background: var(--accent-fill-rest);
    animation: reconnect-pulse 1.2s ease-in-out infinite;
}

#components-reconnect-modal.reconnect-blocking .reconnect-dot {
    background: #fff;
}

@keyframes reconnect-pulse {
    50% { opacity: 0.25; }
}

@media (prefers-reduced-motion: reduce) {
    .reconnect-dot {
        animation: none;
    }
}

/* ── Blazor error UI — global ── */
#blazor-error-ui {
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.5rem;
}

/* ── Financial summary groups (CaseFinancials) — the two account roll-up panels.
   Side by side where there's room, stacked below the breakpoint via the flex-wrap. ── */
.summary-groups {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    margin-top: 0.5rem;
}

.summary-group {
    flex: 1 1 18rem;
    background: var(--neutral-layer-2);
    border: 1px solid var(--neutral-stroke-subtle-rest);
    border-radius: 0.5rem;
    padding: 0.75rem 1rem;
}

.summary-group h2 {
    font-size: 0.95rem;
    margin: 0 0 0.5rem;
    color: var(--neutral-foreground-hint);
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.summary-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 1rem;
    padding: 0.3rem 0;
}

.summary-row + .summary-row {
    border-top: 1px solid var(--neutral-stroke-subtle-rest);
}

.summary-row .summary-label {
    font-size: 0.9rem;
}

/* Tabular figures so the amounts align down the column rather than jittering by glyph width */
.summary-row .summary-amount {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.summary-row.emphasis {
    font-weight: 600;
}

.col-amount {
    font-variant-numeric: tabular-nums;
}

/* ── Home dashboard ──
   Layout is deliberately borrowed rather than invented: the agenda and to-do panels are
   .summary-groups/.summary-group (above), whose `flex: 1 1 18rem` inside a wrapping flex row
   gives side-by-side-then-stacked with no media query of its own, and every row inside them is
   a .mini-card, which is styled outside the mobile block and so works at both widths. The rules
   here only cover what those two don't: the search box and the dashboard's own spacing. */
.home-greeting {
    margin-bottom: 0.75rem;
}

.home-search {
    position: relative;
    margin-bottom: 1rem;
}

.home-search-input {
    width: 100%;
    box-sizing: border-box;
    padding: 0.6rem 0.75rem;
    font-size: 1.1rem;
    font-family: inherit;
    color: var(--neutral-foreground-rest);
    background: var(--neutral-layer-2);
    border: 1px solid var(--neutral-stroke-rest);
    border-radius: 0.5rem;
}

.home-search-input:focus {
    outline: none;
    border-color: var(--accent-fill-rest);
}

/* Keyboard-shortcut badge, sitting inside the box's right edge. pointer-events:none so it never
   swallows a click meant for the input underneath it. */
.home-search-hint {
    position: absolute;
    top: 50%;
    right: 0.6rem;
    transform: translateY(-50%);
    pointer-events: none;
    padding: 0.1rem 0.4rem;
    border: 1px solid var(--neutral-stroke-subtle-rest);
    border-radius: 0.25rem;
    font-size: 0.7rem;
    color: var(--neutral-foreground-hint);
}

/* Absolutely positioned so the panels below don't jump down as results appear. z-index clears
   the .summary-group cards immediately underneath, which are in the same stacking context. */
.home-search-results {
    position: absolute;
    top: calc(100% + 0.25rem);
    left: 0;
    right: 0;
    z-index: 50;
    max-height: 24rem;
    overflow-y: auto;
    background: var(--neutral-layer-1);
    border: 1px solid var(--neutral-stroke-rest);
    border-radius: 0.5rem;
    box-shadow: 0 8px 24px #0006;
}

/* An <a>, not a div — middle-click and Ctrl-click open a result in a new tab, matching how every
   other result row in the app behaves. The link styling has to be reset back to body text. */
.home-search-result {
    display: flex;
    align-items: baseline;
    gap: 0.625rem;
    padding: 0.5rem 0.75rem;
    cursor: pointer;
    text-decoration: none;
    color: inherit;
}

/* Keyboard highlight and hover share .highlighted (see below); this keeps a real focus ring
   available if a row is ever reached by Tab. */
.home-search-result:focus-visible {
    outline: 2px solid var(--accent-fill-rest);
    outline-offset: -2px;
}

.home-search-result + .home-search-result {
    border-top: 1px solid var(--neutral-stroke-subtle-rest);
}

/* Hover and keyboard selection share one class — Home.razor sets _highlighted on mouseenter too,
   so the pointer and the arrow keys can never disagree about which row Enter would open. */
.home-search-result.highlighted {
    background: var(--neutral-fill-secondary-hover);
}

.home-search-kind {
    flex-shrink: 0;
    min-width: 3.5rem;
    padding: 0.1rem 0.4rem;
    border-radius: 0.75rem;
    font-size: 0.7rem;
    text-align: center;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    background: var(--neutral-fill-secondary-rest);
    color: var(--neutral-foreground-hint);
}

.home-search-kind.case {
    background: #0078d433;
    color: #4aa3e8;
}

.home-search-kind.client {
    background: #10893e33;
    color: #4cbb7a;
}

.home-search-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.home-search-title {
    font-weight: 600;
}

.home-search-subtitle,
.home-search-empty {
    font-size: 0.8rem;
    color: var(--neutral-foreground-hint);
}

.home-search-empty {
    padding: 0.75rem;
}

/* flex-start rather than the flex default of stretch: with one panel full and the other empty
   ("Nothing on the calendar this week"), stretch gives the empty one the full height of its
   neighbour, so the dashboard opens on a large blank rectangle. Scoped to .home-page so
   CaseFinancials' own equal-height summary panels keep the default. */
.home-page .summary-groups {
    align-items: flex-start;
}

/* Panels scroll internally rather than growing without bound — a firm with fifty to-dos should
   not push "Recently touched" off the bottom of a desktop screen. */
.home-panel {
    max-height: 26rem;
    overflow-y: auto;
}

/* The agenda section itself carries no card styling — each event is its own .mini-card below
   it, not a list nested inside a bigger card. */
.home-agenda-section h2 {
    font-size: 1.1rem;
    margin: 0 0 0.5rem;
    color: var(--neutral-foreground-hint);
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

/* Wider left padding than a plain .mini-card, to clear the 6px to-do stripe with room to spare —
   the same allowance .record-card makes for the same stripe. */
.home-todo-row {
    padding-left: 1.125rem;
}

.home-panel .mini-card + .mini-card,
.home-agenda-section .mini-card + .mini-card,
.home-recent-list .mini-card + .mini-card {
    margin-top: 0.375rem;
}

.home-day-heading {
    margin: 0.75rem 0 0.375rem;
    font-size: 0.9rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--neutral-foreground-hint);
}

.home-day-heading:first-of-type {
    margin-top: 0;
}

.home-agenda-time {
    flex-shrink: 0;
    font-variant-numeric: tabular-nums;
    font-size: 0.95rem;
    color: var(--neutral-foreground-hint);
}

.agenda-kind {
    margin-left: auto;
    flex-shrink: 0;
    padding: 0.05rem 0.4rem;
    border-radius: 0.75rem;
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.agenda-kind.hearing {
    background: #8764b833;
    color: #a98fd6;
}

.agenda-kind.appointment {
    background: #ca500033;
    color: #e08a4a;
}

/* Case-level context (charge/judge/opposing counsel/handling attorney) on an agenda card — one
   line per item rather than packed onto a shared row. */
.home-agenda-case-meta {
    margin-top: 0.25rem;
    font-size: 0.9rem;
    color: var(--neutral-foreground-hint);
}

/* An agenda card's body is three unlabeled subsections — who's involved, what the event/case is,
   and any to-dos — each a .home-agenda-group, separated by a rule line rather than a background
   tint: a tinted band (tried first) lightens the card's own background enough to drop the
   hint-grey field labels, and the existing to-do green/blue, below readable contrast. A divider
   keeps every child on the card's own background instead. Uses --neutral-stroke-rest, not
   --neutral-stroke-subtle-rest (used for hairline borders elsewhere in this file, e.g.
   .mini-card) — that token resolves to an empty string in this app's theme, silently dropping
   any border that relies on it. */
.home-agenda-group {
    margin-top: 0.375rem;
}

.home-agenda-group + .home-agenda-group {
    border-top: 1px solid var(--neutral-stroke-rest);
    margin-top: 0.5rem;
    padding-top: 0.375rem;
}

/* Reuses the case list's own to-do colours (.todo-atty/.todo-client, defined above for
   .record-card) as text colour rather than the card-edge stripe those use elsewhere — an agenda
   card already has a kind badge occupying that visual slot. Clamped for the same reason
   .home-todo-row's detail line was: to-do text is free-form and can run long. */
.home-agenda-todo {
    margin-top: 0.25rem;
    font-size: 0.95rem;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.home-agenda-todo.todo-atty {
    color: #2e7d32;
}

.home-agenda-todo.todo-client {
    color: #1565c0;
}

/* The to-do rows reuse the case list's own tint classes (.todo-atty / .todo-client, defined
   above for .record-card), so a to-do is the same colour here as on the case list. */

.home-panel-heading {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.home-panel-heading h2 {
    margin: 0 0 0.5rem;
}

.todo-sort {
    display: flex;
    gap: 0.25rem;
    margin-bottom: 0.5rem;
}

.todo-sort-btn {
    border: 1px solid var(--neutral-stroke-subtle-rest);
    border-radius: 999px;
    background: transparent;
    color: var(--neutral-foreground-hint);
    font-size: 0.7rem;
    padding: 0.15rem 0.6rem;
    cursor: pointer;
}

.todo-sort-btn.active {
    border-color: var(--accent-fill-rest);
    color: var(--accent-fill-rest);
}

.home-todo-subheading {
    margin: 0.75rem 0 0.375rem;
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--neutral-foreground-hint);
}

.home-todo-subheading:first-of-type {
    margin-top: 0;
}

.home-todo-row .mini-card-detail {
    /* To-do text is free-form and can be long; cap it at three lines rather than letting one
       verbose entry own the panel. */
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.home-recent {
    margin-top: 1rem;
}

.home-recent h2 {
    font-size: 0.95rem;
    margin: 0 0 0.5rem;
    color: var(--neutral-foreground-hint);
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

/* auto-fill rather than auto-fit: with a handful of cards, auto-fit would stretch each one across
   the full width; auto-fill keeps them at a readable column width and leaves the row short. */
.home-recent-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: 0.5rem;
}

.home-empty {
    margin: 0.25rem 0;
    font-size: 1rem;
    color: var(--neutral-foreground-hint);
}

.home-more {
    display: inline-block;
    margin-top: 0.5rem;
    font-size: 0.8rem;
    color: var(--accent-fill-rest);
    text-decoration: none;
}

/* ── Responsive breakpoint ── */
@media (max-width: 768px) {
    /* Stack header items; allow wrapping */
    fluent-header::part(control) {
        flex-wrap: wrap;
        gap: 0.25rem;
        padding: 0.5rem;
    }

    /* Hide sidebar */
    .sidebar-nav {
        display: none !important;
    }

    /* Push body content to full width; add bottom padding so
       the fixed bottom nav doesn't cover content */
    .body-content {
        width: 100% !important;
        padding-bottom: 4rem;
    }

    /* Reduce content padding on mobile, and set up flex layout
       so the virtualized card list fills remaining height. */
    .content {
        padding: 0.5rem;
        display: flex;
        flex-direction: column;
        height: calc(100dvh - 3.5rem);
        overflow: hidden;
    }

    .content h1 {
        margin: 0.25rem 0;
    }

    .content .record-filter-bar {
        flex-shrink: 0;
    }

    .content .record-cards {
        overflow-y: auto;
        height: calc(100dvh - 9.5rem);
        width: 100%;
    }

    /* CaseFinancials opts out of the layout above: that scroll-pane sizing was designed for
       Clients/Cases, a virtualized list under a fixed filter bar, where only the list itself
       should scroll under a pinned header. Financials instead has two summary panels above a
       short, non-virtualized transaction list - giving the cards their own fixed-height pane left
       only a sliver of the screen to scroll transactions in, squeezed below the summaries. Un-cap
       both the outer pane and the card list here so the whole page scrolls as one normal flow.
       :has() lets this reach .content, which CaseFinancials doesn't render itself (MainLayout
       does) - safe to rely on given the surrounding rules already require dvh support. */
    .content:has(.financials-page) {
        height: auto;
        overflow: visible;
    }

    .financials-page .record-cards {
        height: auto;
        overflow-y: visible;
    }

    /* The home dashboard opts out for the same reason CaseFinancials does, and it is the same
       shape of page: a search box and three short panels, not one virtualized list under a
       pinned filter bar. Left capped, the panels would be squeezed into a sliver and
       "Recently touched" would be unreachable below them. */
    .content:has(.home-page) {
        height: auto;
        overflow: visible;
    }

    /* CaseDetail is the same shape as the home dashboard: a handful of stacked sections,
       not a virtualized list under a pinned filter bar. Case History can grow to any
       length (free-flowing text, not a capped textarea), so without this the default
       .content height cap silently clips it under the fixed bottom nav with no way to
       scroll to the rest. */
    .content:has(.case-detail-page) {
        height: auto;
        overflow: visible;
    }

    /* Automations is the same shape as CaseDetail: several stacked sections (jobs, schedules,
       history), not one virtualized list under a pinned filter bar. Without this, the default
       .content height cap silently clips the run history section under the fixed bottom nav. */
    .content:has(.automations-page) {
        height: auto;
        overflow: visible;
    }

    .automations-page .record-cards {
        height: auto;
        overflow-y: visible;
    }

    /* On a phone the panels are stacked in one scrolling column, so their desktop internal
       scroll caps (.home-panel above) would make two nested scroll areas — the classic
       "wrong thing scrolls" trap. Let the page scroll as one flow instead. */
    .home-panel {
        max-height: none;
        overflow-y: visible;
    }

    .home-search-results {
        max-height: 60vh;
    }

    /* No Ctrl key on a phone, and the narrow box needs the width more than the hint. */
    .home-search-hint {
        display: none;
    }

    /* Show bottom navigation bar */
    .bottom-nav {
        display: flex;
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        z-index: 999;
        background: var(--neutral-layer-1);
        border-top: 1px solid var(--neutral-stroke-divider-rest);
        justify-content: space-around;
        padding: 0.25rem 0;
        /* safe-area for phones with home indicator */
        padding-bottom: max(0.25rem, env(safe-area-inset-bottom));
    }

    /* app.css is a plain global stylesheet, not a scoped *.razor.css - "::deep" only means
       anything to Blazor's CSS-isolation build step, so it's invalid syntax here and browsers
       drop the whole rule. That was masking a second, unrelated problem: fluent-anchor is a
       native custom element with a real (not Blazor-scoped) shadow root, and plain descendant
       selectors can never cross that boundary regardless of ::deep - only ::part() can, and
       only for parts the component author explicitly exposes (here: control/start/content/end).
       Without Slot="start" on the icon below, it lands in the same "content" part as the text
       (both unnamed-slotted), rendered as one inline run rather than two independently
       positioned parts - which is what actually produced the misalignment, not the missing
       flex-direction. */
    .bottom-nav fluent-anchor {
        /* The shadow root's own :host rule sets a fixed height via
           calc((base-height-multiplier + density) * design-unit * 1px) - 32px here - sized
           for the shipped one-line icon+label row, not our two-line stack. Overriding height
           on this host element is a plain box-model property, not shadow-internal layout, so
           (unlike flex-direction/display) it needs no ::part() to take effect; part="control"
           then fills it via its own shipped flex-grow + default cross-axis stretch, with no
           height override needed on that part at all - see the note below on why one was
           tried and abandoned. */
        height: 3.25rem;
        font-size: 0.7rem;
        padding: 0 1rem;
        text-decoration: none;
        color: var(--neutral-foreground-rest);
    }

    /* The active item's label - color is an inherited CSS property, so unlike the
       structural fixes below it cascades into the shadow tree from a plain host-level
       rule with no ::part() needed. The icon's own color is driven separately, by the
       Color parameter passed to FluentIcon in the markup: its fill comes from an inline
       style, which no stylesheet rule (::part() included) can override. */
    .bottom-nav fluent-anchor.active {
        color: var(--accent-fill-rest);
        font-weight: 600;
    }

    /* part="control" is the shadow-internal <a> that actually arranges start/content/end -
       it ships as a horizontal row (flex-direction: row) by default, which is why overriding
       flex-direction on the host element above never had any visual effect. This is the real
       layout container; ::part() is the only selector form that can reach it from outside.
       Deliberately NOT setting height here: its own shipped rule already has flex-grow:1
       inside the host's row-direction flex context, so with no explicit height it falls back
       to the default cross-axis "stretch", correctly filling the host's height above. An
       earlier attempt at `height: auto` here to fix the same problem produced a worse result
       (~20px, shorter than the shipped default) - auto on a flex item that's itself a nested
       flex container triggers cross-axis sizing quirks around its own children's hypothetical
       size, not simple content-based growth. Fixing the real height at the host level sidesteps
       that entirely: part="control" never needs to size itself, just fill what it's given. */
    .bottom-nav fluent-anchor::part(control) {
        flex-direction: column;
        gap: 0.15rem;
    }

    /* part="start"'s own default margin-right:11px exists to space the icon from the label in
       the row layout above - in the column layout it just pushes the icon 5.5px left of center
       (half the margin, since align-items:center centers the icon's box including that margin).
       The control's own gap already spaces icon from label vertically, so this is pure excess. */
    .bottom-nav fluent-anchor::part(start) {
        margin: 0;
    }

    /* Hide grid, show cards */
    .record-grid {
        display: none !important;
    }

    .record-cards {
        display: flex;
        flex-direction: column;
        gap: 0.375rem;
    }

    .record-card {
        background: var(--neutral-layer-2);
        border-radius: 0.5rem;
        /* Left padding is wider than the others: it has to clear the 12px inset to-do stripe
           (.todo-atty.todo-client below) with room to spare, or the text sits flush against it. */
        padding: 0.625rem 0.75rem 0.625rem 1.125rem;
        border: 1px solid var(--neutral-stroke-subtle-rest);
    }

    .record-card .record-card-header {
        display: flex;
        align-items: center;
        gap: 0.5rem;
    }

    .record-card .record-name {
        font-weight: 600;
        font-size: 1rem;
    }

    .record-card .record-detail {
        font-size: 0.85rem;
        color: var(--neutral-foreground-hint);
    }

    .record-card .record-detail-row {
        display: flex;
        gap: 0.5rem;
        flex-wrap: wrap;
        margin-top: 0.25rem;
    }

    .record-card .record-card-meta {
        display: flex;
        align-items: center;
        gap: 0.5rem;
        margin-top: 0.25rem;
        font-size: 0.75rem;
        color: var(--neutral-foreground-hint);
    }

        .record-card {
            flex-direction: row;
            display: flex;
        }

        .record-card-body {
            flex: 1;
            min-width: 0;
        }

        .record-card-rail {
            display: flex;
            flex-direction: column;
            flex-shrink: 0;
            margin-left: 0.375rem;
        }

        .record-action-btn {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            width: 2rem;
            flex: 1 1 0;
            min-height: 1.75rem;
            border: 1px solid var(--neutral-stroke-subtle-rest);
            border-bottom: none;
            color: var(--accent-fill-rest);
            background: transparent;
            text-decoration: none;
        }

        .record-action-btn:first-child {
            border-radius: 0.375rem 0.375rem 0 0;
        }

        .record-action-btn:last-child {
            border-bottom: 1px solid var(--neutral-stroke-subtle-rest);
            border-radius: 0 0 0.375rem 0.375rem;
        }

        .record-action-btn:only-child {
            border-radius: 0.375rem;
            border-bottom: 1px solid var(--neutral-stroke-subtle-rest);
            height: 100%;
            min-height: 3rem;
        }
}

/* ── Record detail page (client, case) ── */
.back-link {
    display: inline-block;
    margin-bottom: 0.5rem;
    color: var(--accent-fill-rest);
    text-decoration: none;
}

.record-landing-actions {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    max-width: 420px;
}

.record-landing-btn {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.75rem 1rem;
    border: 1px solid var(--neutral-stroke-subtle-rest);
    border-radius: 0.5rem;
    background: var(--neutral-layer-2);
    color: var(--neutral-foreground-rest);
    text-decoration: none;
    font-size: 1rem;
    font: inherit;
    cursor: pointer;
    width: 100%;
    box-sizing: border-box;
    text-align: left;
}

.record-landing-btn svg {
    flex-shrink: 0;
    color: var(--accent-fill-rest);
}

.record-landing-btn.disabled {
    opacity: 0.4;
    pointer-events: none;
}
