/* ────────────────────────────────────────────────────────────────
   Buildessa / Lisa global theme
   Component-scoped styles live next to each .razor file as *.razor.css.
   This file holds tokens and resets that apply to the whole document.

   Theme system (Phase 1D):
     • Variables below are the canonical surface area — every CSS file
       in the project uses var(--<name>) to read them.
     • Two built-in themes (buildessa-dark / buildessa-light) provide
       all values, seeded by BootstrapSeeder into the Themes DB table.
     • <html data-theme="..."> attribute selects which set is active.
     • The inline boot script in App.razor sets data-theme before paint
       so there's no flash of wrong-theme content (FOUC).
     • Custom admin-defined themes get rendered as a <style id="buildessa-custom-theme">
       element by Routes.razor when the user has picked one.
   ──────────────────────────────────────────────────────────────── */

/* Default (dark) — applied when no data-theme is set, and explicitly
   inherited when data-theme="dark". Mirrors the seeded Buildessa Dark theme. */
:root,
html[data-theme="dark"] {
    color-scheme: dark;

    /* Surfaces */
    --bg:            #0a0a0a;
    --bg-elevated:   #161616;
    --bg-overlay:    #1a1a1a;
    --bg-input:      #0d0d0d;

    /* Foreground */
    --fg:            #ececec;
    --fg-dim:        #d0d0d0;
    --muted:         #8a8a8a;

    /* Borders */
    --border:        #2a2a2a;
    --border-strong: #3a3a3a;

    /* Accent */
    --accent:        #c4b5fd;
    --accent-soft:   #6d5fbe;
    --accent-fg:     #1a1a1a;

    /* Status */
    --success:       #8cdcaa;
    --warning:       #ffd58a;
    --danger:        #e87a7a;
    --info:          #7ec8e3;

    /* Chat-specific */
    --composer-bg:   #161616;

    /* Effects */
    --hover-overlay: rgba(255, 255, 255, 0.05);
    --focus-ring:    rgba(196, 181, 253, 0.30);
    --shadow-card:   0 10px 30px rgba(0, 0, 0, 0.4);
}

/* Light theme — explicit + inherited when prefers-color-scheme=light + no
   manual override. Mirrors the seeded Buildessa Light theme. */
html[data-theme="light"] {
    color-scheme: light;

    --bg:            #fdfdfd;
    --bg-elevated:   #ffffff;
    --bg-overlay:    #f4f4f5;
    --bg-input:      #ffffff;

    --fg:            #1a1a1a;
    --fg-dim:        #3a3a3a;
    --muted:         #6a6a6a;

    --border:        #e4e4e7;
    --border-strong: #c4c4c8;

    --accent:        #6d5fbe;
    --accent-soft:   #c4b5fd;
    --accent-fg:     #ffffff;

    --success:       #1f7a3a;
    --warning:       #a06000;
    --danger:        #b32424;
    --info:          #1e6391;

    --composer-bg:   #f4f4f5;

    --hover-overlay: rgba(0, 0, 0, 0.04);
    --focus-ring:    rgba(109, 95, 190, 0.30);
    --shadow-card:   0 4px 16px rgba(0, 0, 0, 0.08);
}

/* Auto mode — when the user hasn't manually picked, follow OS preference.
   The inline boot script resolves "auto" to either "dark" or "light" by
   calling matchMedia, so these media queries are belt-and-suspenders for
   any case where data-theme=auto leaks through (no JS, etc.). */
@media (prefers-color-scheme: light) {
    html[data-theme="auto"] {
        color-scheme: light;
        --bg:            #fdfdfd;
        --bg-elevated:   #ffffff;
        --bg-overlay:    #f4f4f5;
        --bg-input:      #ffffff;
        --fg:            #1a1a1a;
        --fg-dim:        #3a3a3a;
        --muted:         #6a6a6a;
        --border:        #e4e4e7;
        --border-strong: #c4c4c8;
        --accent:        #6d5fbe;
        --accent-soft:   #c4b5fd;
        --accent-fg:     #ffffff;
        --success:       #1f7a3a;
        --warning:       #a06000;
        --danger:        #b32424;
        --info:          #1e6391;
        --composer-bg:   #f4f4f5;
        --hover-overlay: rgba(0, 0, 0, 0.04);
        --focus-ring:    rgba(109, 95, 190, 0.30);
        --shadow-card:   0 4px 16px rgba(0, 0, 0, 0.08);
    }
}

html, body {
    margin: 0;
    padding: 0;
    min-height: 100dvh;
    background: var(--bg);
    color: var(--fg);
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
                 Arial, "Noto Sans", sans-serif;
    -webkit-font-smoothing: antialiased;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid var(--success);
}

.invalid {
    outline: 1px solid var(--danger);
}

.validation-message {
    color: var(--danger);
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, var(--danger);
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: var(--border-strong);
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--muted);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* ────────────────────────────────────────────────────────────────
   Chat page layout + styling (global because both Chat.razor and
   BuildingChat.razor share these class names. Scoped CSS would only
   apply to one of them — keeping them here guarantees both pages get
   the same shell behaviour with no duplication.)
   ──────────────────────────────────────────────────────────────── */

/* Grid: [header spans both cols] / [thread | rail 280px] / [composer]. */
.chat-page {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 280px;
    grid-template-rows: auto minmax(0, 1fr) auto;
    flex: 1;
    min-height: 0;
    min-width: 0;
}
.chat-page > .chat-header     { grid-column: 1 / -1; grid-row: 1; }
.chat-page > .chat-thread     { grid-column: 1;       grid-row: 2; }
.chat-page > .chat-empty      { grid-column: 1 / -1;  grid-row: 2; }
.chat-page > .context-rail    { grid-column: 2;       grid-row: 2; min-width: 0; }
.chat-page > .composer-pinned { grid-column: 1;       grid-row: 3; }

/* Rail is a desktop affordance. On narrow viewports collapse to a single
   column and hide the rail — the chat takes priority, and the user can
   still set context via the inline suggestion chip. */
@media (max-width: 1100px) {
    .chat-page                     { grid-template-columns: minmax(0, 1fr); }
    .chat-page > .context-rail     { display: none; }
    .chat-page > .composer-pinned  { grid-column: 1 / -1; }
}

/* Header — Claude-style breadcrumb ───────────────────────────────── */

/* Phase 34h.2 — chat title-bar actions (Copy / Save). Lives in app.css
   rather than the component's scoped CSS so the styles apply even
   before Blazor regenerates scoped-CSS attributes after a fresh
   component file is added (which can lag hot-reload). */
.chat-actions {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
/* Session usage readout (tokens in/out + cost) left of the Copy/Save buttons. */
.chat-usage {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-right: 4px;
    padding: 3px 9px;
    border: 1px solid var(--border, #e2e2e7);
    border-radius: 999px;
    font-size: 11.5px;
    color: #6a6a73;
    white-space: nowrap;
}
.chat-usage-io { font-variant-numeric: tabular-nums; }
.chat-usage-cost {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    color: #4a4a55;
}
.chat-action-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 10px;
    background: var(--bg-overlay);
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--muted);
    cursor: pointer;
    font-size: 12px;
    line-height: 1;
    transition: color 0.15s ease-out, background 0.15s ease-out, border-color 0.15s ease-out;
}
.chat-action-btn:hover:not(:disabled) {
    color: var(--fg);
    background: var(--hover-overlay);
    border-color: var(--accent, #6366f1);
}
.chat-action-btn:disabled { cursor: progress; opacity: 0.7; }
.chat-action-btn.action-ok {
    color: var(--success, #2a9d8f);
    border-color: color-mix(in srgb, var(--success, #2a9d8f) 40%, var(--border));
}
.chat-action-btn.action-fail {
    color: var(--danger, #c0392b);
    border-color: color-mix(in srgb, var(--danger, #c0392b) 40%, var(--border));
}
.chat-action-btn svg { flex-shrink: 0; }
.chat-action-label { font-size: 12px; line-height: 1; }

/* Phase 34y — split Save button (main label + chevron) with a small
   format picker (Markdown / Word / PDF) that drops below. The two
   halves are visually fused by adjusting the inner border radii so
   they read as one control. */
.chat-save-split {
    position: relative;
    display: inline-flex;
    align-items: stretch;
}
.chat-save-split .chat-save-main {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
    border-right: 0;
}
.chat-save-split .chat-save-chev {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
    padding-left: 7px;
    padding-right: 7px;
    font-size: 11px;
    color: var(--muted);
}
.chat-save-split .chat-save-chev:hover:not(:disabled) {
    color: var(--fg);
}
.chat-save-split.open .chat-save-chev {
    color: var(--accent, #6366f1);
    border-color: var(--accent, #6366f1);
}
.chat-save-menu {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    z-index: 20;
    min-width: 220px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
    padding: 4px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.chat-save-menu-item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 7px 10px;
    background: transparent;
    border: 0;
    border-radius: 4px;
    color: var(--fg);
    font-size: 13px;
    cursor: pointer;
    text-align: left;
    font: inherit;
}
.chat-save-menu-item:hover {
    background: var(--hover-overlay);
}
.chat-save-menu-item.active {
    background: color-mix(in srgb, var(--accent, #6366f1) 12%, transparent);
    color: var(--accent, #6366f1);
    font-weight: 600;
}
.chat-save-menu-glyph {
    font-size: 14px;
    line-height: 1;
}

.chat-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 24px;
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 10;
    background: var(--bg);
}
.crumb {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    min-width: 0;
}
.crumb-root,
.crumb-branch {
    color: var(--accent);
    text-decoration: none;
    font-weight: 600;
}
.crumb-root:hover,
.crumb-branch:hover {
    text-decoration: underline;
}
/* Voice toggle fused to Lisa's name (2026-08-16): no button chrome — the
   waveform sits tight against "Lisa" in the same accent color, so the icon
   reads as HER voice, not a generic control. Off = muted + slash overlay. */
.crumb-voice {
    background: transparent;
    border: none;
    padding: 1px 2px;
    margin-left: 2px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    vertical-align: middle;
    color: var(--accent);
}
.crumb-voice:hover { opacity: 0.75; }
.crumb-voice.voice-off { color: var(--muted); opacity: 0.65; }
.crumb-sep {
    color: var(--muted);
    font-weight: 400;
}
.crumb-leaf {
    color: var(--fg-dim);
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 60ch;
}

/* Empty state — centred hero + composer ──────────────────────────── */

.chat-empty {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 24px;
    gap: 28px;
}
.hero-block { text-align: center; }
.hero {
    font-size: clamp(40px, 6vw, 64px);
    font-weight: 600;
    margin: 0;
    letter-spacing: -1px;
    background: linear-gradient(135deg, var(--fg) 0%, var(--accent) 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}
.hero-sub {
    margin: 6px 0 0;
    color: var(--muted);
    font-size: 15px;
}
.hero-prompt {
    margin: 24px 0 0;
    font-size: 18px;
    color: var(--fg-dim);
}
.hero-hint {
    color: var(--muted);
    font-size: 13px;
    margin: 0;
    text-align: center;
}
.composer-center {
    width: min(720px, 100%);
}

/* Thread state — full-width Claude-style ─────────────────────────── */

.chat-thread {
    flex: 1;
    overflow-y: auto;
    /* Bottom padding leaves room for the sticky composer overlay. */
    padding: 28px 40px 160px;
}
/* Stretches to the pane width; horizontal padding above keeps text from
   hugging the sidebar/edge. No max-width cap — uses all available space
   per the Claude-style full-bleed thread. */
.messages {
    width: 100%;
    margin: 0;
    display: flex;
    flex-direction: column;
    /* Tight inter-message spacing — the per-message footer collapses
       when idle, so this gap is the only thing separating turns. */
    gap: 8px;
}

/* Read-only preview banner — shown when the user opens another chat while a
   turn is still running on a different one (the session can't be swapped out
   from under the running turn). Shared by Chat.razor + BuildingChat.razor. */
.preview-banner {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin: 0 0 16px;
    padding: 10px 14px;
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: 8px;
    background: var(--bg-overlay);
    color: var(--fg-dim);
    font-size: 13px;
    line-height: 1.45;
}
.preview-banner-icon { flex: 0 0 auto; }
.preview-banner-text { flex: 1 1 240px; min-width: 0; }
.preview-banner-text a { color: var(--accent); text-decoration: none; }
.preview-banner-text a:hover { text-decoration: underline; }
.preview-open-btn {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    white-space: nowrap;
    background: var(--accent);
    color: #fff;
    border: 0;
    border-radius: 6px;
    padding: 6px 12px;
    font: inherit;
    font-size: 13px;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
}
.preview-open-btn:hover { filter: brightness(1.08); text-decoration: none; }

/* Jump-to-latest — pinned just ABOVE the composer (it lives inside the
   composer footer now, not the scrolling thread which clipped it
   mid-content). JS toggles `.visible` based on scroll position. */
.jump-anchor {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    /* Float just above the composer's top edge; adds no layout height. */
    transform: translateY(calc(-100% - 6px));
    height: 0;
    display: flex;
    justify-content: center;
    pointer-events: none;
    z-index: 6;
}
.jump-bottom {
    pointer-events: auto;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 14px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--bg-overlay, #fff);
    color: var(--fg);
    font-size: 12.5px;
    font-weight: 500;
    cursor: pointer;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.12);
    /* Hidden by default; JS adds .visible when the user is scrolled up. */
    opacity: 0;
    visibility: hidden;
    transform: translateY(8px);
    transition: opacity 0.18s ease-out, transform 0.18s ease-out, visibility 0.18s;
}
.jump-bottom:hover {
    border-color: var(--accent, #6366f1);
    color: var(--accent, #6366f1);
}
.jump-bottom.visible {
    opacity: 1;
    visibility: visible;
    transform: none;
}

.composer-pinned {
    position: sticky;
    bottom: 0;
    padding: 12px 40px 20px;
    background: linear-gradient(to top, var(--bg) 65%, transparent 100%);
}
/* Composer matches the thread width (full pane, padded by the parent). */
.composer-pinned > :first-child {
    width: 100%;
    margin: 0;
}

/* Narrow viewports — shrink the horizontal padding so chat doesn't feel
   crammed against the edge on mobile / split screens. */
@media (max-width: 700px) {
    .chat-thread     { padding: 20px 16px 140px; }
    .composer-pinned { padding: 12px 16px 20px; }
}

/* ── Routed-page ErrorBoundary fallback ─────────────────────────────
   Card-style error surface for exceptions caught by <ErrorBoundary>
   in Routes.razor. Replaces the entire routed-page slot when a
   throw escapes a page's lifecycle. In development the stack trace
   is shown inside a collapsible <details>; in production we render
   only the message. */
.route-error-card {
    max-width: 720px;
    margin: 48px auto;
    padding: 24px 28px;
    background: var(--bg-elevated, #1b1b1f);
    border: 1px solid var(--border, #2a2a2f);
    border-left: 4px solid var(--danger, #b84a4a);
    border-radius: 10px;
    color: var(--fg, #ececec);
}
.route-error-card h1 {
    margin: 0 0 12px;
    font-size: 18px;
    font-weight: 600;
}
.route-error-message {
    margin: 0 0 16px;
    color: var(--fg-dim, #d0d0d0);
    line-height: 1.5;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 13px;
    background: var(--bg-overlay, rgba(255,255,255,0.04));
    padding: 10px 12px;
    border-radius: 6px;
    white-space: pre-wrap;
    word-break: break-word;
}
.route-error-details {
    margin: 12px 0;
}
.route-error-details summary {
    cursor: pointer;
    color: var(--muted, #8a8a8a);
    font-size: 12.5px;
    padding: 4px 0;
}
.route-error-details pre {
    margin: 8px 0 0;
    padding: 10px 12px;
    background: var(--bg-input, #15151a);
    border: 1px solid var(--border);
    border-radius: 6px;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 11.5px;
    line-height: 1.5;
    color: var(--muted, #b0b0b0);
    overflow-x: auto;
    max-height: 400px;
}
.route-error-actions {
    margin: 16px 0 0;
    color: var(--muted, #8a8a8a);
    font-size: 13px;
}
.route-error-actions a {
    color: var(--accent, #c4b5fd);
    text-decoration: none;
}
.route-error-actions a:hover { text-decoration: underline; }

/* ── blazor-error-ui detail slot ─────────────────────────────────────
   The actual exception text is captured from console.error by the JS
   in App.razor and injected here when the global banner becomes
   visible. Capped height + scroll so a 50-line stack doesn't grow
   the banner past the viewport. */
#blazor-error-ui .blazor-error-detail {
    display: block;
    margin: 6px 0;
    padding: 8px 10px;
    background: rgba(0, 0, 0, 0.06);
    border-radius: 4px;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 12px;
    line-height: 1.45;
    color: #333;
    white-space: pre-wrap;
    word-break: break-word;
    max-height: 220px;
    overflow-y: auto;
}
/* Hide the empty <pre> when there's no detail yet (e.g. very early
   in startup before the JS hook ran) — avoids a stray empty box. */
#blazor-error-ui .blazor-error-detail:empty {
    display: none;
}

/* Phase 34e.3 — cost estimator table on /admin/providers. Plain
   look-and-feel, monospace prices for easy column scanning. */
.cost-table {
    width: 100%;
    border-collapse: collapse;
    margin: 8px 0 4px;
    font-size: 13px;
}
.cost-table thead th {
    text-align: left;
    padding: 8px 12px;
    background: var(--bg-overlay);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    color: var(--muted);
    font-weight: 600;
    border-bottom: 1px solid var(--border);
}
.cost-table tbody td {
    padding: 8px 12px;
    border-bottom: 1px solid var(--border);
    vertical-align: middle;
}
.cost-table tbody tr:last-child td { border-bottom: none; }
.cost-table .num {
    text-align: right;
    font-variant-numeric: tabular-nums;
    font-family: ui-monospace, "Cascadia Code", "Source Code Pro", monospace;
}
.cost-table tr.cost-total {
    background: var(--bg-overlay);
}

/* ── Building Overview / settings page ──────────────────────────────── */

.overview-page {
    height: 100%;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
}

/* Configure Lisa — per-user assistant settings page */
.lc-page { gap: 16px; padding: 24px clamp(16px, 5vw, 56px); max-width: 760px; margin: 0 auto; width: 100%; box-sizing: border-box; }
.lc-head { display: flex; gap: 16px; align-items: flex-start; }
.lc-head h1 { margin: 0 0 4px; font-size: 22px; }
.lc-avatar {
    flex: none; width: 44px; height: 44px; border-radius: 50%;
    display: grid; place-items: center; font-weight: 700; font-size: 18px;
    background: var(--accent); color: var(--accent-fg);
}
.lc-card {
    background: var(--bg-overlay); border: 1px solid var(--border);
    border-radius: 12px; padding: 16px 18px; display: flex; flex-direction: column; gap: 10px;
}
.lc-card h2 { margin: 0; font-size: 13px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
.lc-field { display: flex; flex-direction: column; gap: 6px; font-size: 13px; }
.lc-field > span { color: var(--fg); font-weight: 500; }
.lc-preview .lc-intro { font-size: 16px; margin: 0; }
.lc-pre {
    white-space: pre-wrap; font-size: 12px; color: var(--muted);
    background: var(--bg-input); border: 1px solid var(--border); border-radius: 8px;
    padding: 10px; margin: 8px 0 0; max-height: 280px; overflow: auto;
}
.lc-actions { display: flex; align-items: center; gap: 12px; padding-bottom: 24px; }

/* §5.19 — cross-chat memory list on Configure Lisa */
.lc-memories { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.lc-memory {
    display: flex; align-items: baseline; gap: 10px;
    padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px;
    background: var(--bg-input);
}
.lc-memory-kind {
    flex: 0 0 auto; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
    color: var(--muted); border: 1px solid var(--border); border-radius: 999px; padding: 1px 8px;
}
.lc-memory-text { flex: 1 1 auto; font-size: 13px; }
.lc-memory-retire { flex: 0 0 auto; font-size: 12px; }

.overview-body {
    width: 100%;
    max-width: 780px;
    margin: 0 auto;
    padding: 22px 20px 64px;
    display: flex;
    flex-direction: column;
    gap: 18px;
}

.overview-page .small { font-size: 12px; }

.ov-note {
    background: var(--bg-overlay);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 8px 12px;
    font-size: 13px;
    color: var(--muted);
}

/* Tabs (Details / Profile / Settings) */
.ov-tabs {
    display: flex;
    gap: 2px;
    border-bottom: 1px solid var(--border);
    margin-bottom: 4px;
}
.ov-tab {
    appearance: none;
    border: 0;
    background: transparent;
    cursor: pointer;
    font: inherit;
    font-size: 14px;
    font-weight: 500;
    color: var(--muted);
    padding: 9px 16px;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.ov-tab:hover { color: var(--fg); }
.ov-tab.active { color: var(--fg); border-bottom-color: var(--accent); }
.ov-tab-badge {
    font-size: 11px; font-weight: 700; min-width: 16px; height: 16px; padding: 0 5px;
    border-radius: 999px; background: var(--accent); color: var(--accent-fg);
    display: inline-flex; align-items: center; justify-content: center;
}
.ov-tab-dot {
    width: 8px; height: 8px; border-radius: 50%; background: var(--accent);
    animation: ov-pulse 1.2s ease-in-out infinite;
}
@keyframes ov-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }

.ov-section {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 18px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.ov-h2 { margin: 0; font-size: 15px; font-weight: 600; color: var(--fg); }

.ov-field { display: flex; flex-direction: column; gap: 6px; }

.ov-label-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }

.ov-label {
    font-size: 12px;
    font-weight: 600;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .04em;
}

.ov-input {
    width: 100%;
    box-sizing: border-box;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: 8px;
    color: var(--fg);
    padding: 8px 10px;
    font: inherit;
    font-size: 14px;
}
.ov-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--focus-ring); }
.ov-input:disabled { opacity: .65; cursor: not-allowed; }
.ov-textarea { resize: vertical; }
.ov-select { cursor: pointer; }

.ov-search-row { display: flex; gap: 8px; }
.ov-search-row .ov-input { flex: 1; }

.ov-results {
    list-style: none;
    margin: 6px 0 0;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: 8px;
    overflow: hidden;
}
.ov-result {
    width: 100%;
    text-align: left;
    background: transparent;
    border: 0;
    border-bottom: 1px solid var(--border);
    padding: 8px 10px;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    gap: 2px;
    font: inherit;
    color: var(--fg);
}
.ov-results li:last-child .ov-result { border-bottom: 0; }
.ov-result:hover { background: var(--hover-overlay); }
.ov-result-name { font-size: 13px; }
.ov-result-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ov-kind {
    display: inline-block;
    font-size: 11px;
    font-weight: 600;
    padding: 1px 7px;
    border-radius: 999px;
    border: 1px solid transparent;
    white-space: nowrap;
}
.ov-kind-business {
    color: var(--info);
    background: color-mix(in srgb, var(--info) 14%, transparent);
    border-color: color-mix(in srgb, var(--info) 35%, transparent);
}
.ov-kind-residential {
    color: var(--success);
    background: color-mix(in srgb, var(--success) 14%, transparent);
    border-color: color-mix(in srgb, var(--success) 35%, transparent);
}

.ov-map {
    height: 340px;
    width: 100%;
    border: 1px solid var(--border);
    border-radius: 10px;
    overflow: hidden;
    position: relative;
    z-index: 0; /* contain Leaflet's internal z-index stack */
    background: var(--bg-overlay);
}

.ov-muted { color: var(--muted); }

.ov-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ov-status { font-size: 13px; }
.ov-status.ok { color: var(--success); }
.ov-status.fail { color: var(--danger); }

.ov-meta {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 10px;
    margin: 6px 0 0;
    padding-top: 12px;
    border-top: 1px solid var(--border);
}
.ov-meta dt { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
.ov-meta dd { margin: 2px 0 0; font-size: 13px; color: var(--fg); }

.ov-radio { display: flex; align-items: center; gap: 8px; font-size: 14px; color: var(--fg); cursor: pointer; }
.ov-radio input { accent-color: var(--accent); }

.ov-collab-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.ov-collab { display: flex; align-items: center; gap: 10px; padding: 6px 0; border-bottom: 1px solid var(--border); }
.ov-collab:last-child { border-bottom: 0; }
.ov-collab-who { flex: 1; font-size: 13px; word-break: break-all; }
.ov-collab-role { font-size: 12px; color: var(--muted); }
.ov-collab-add { display: flex; gap: 8px; align-items: center; margin-top: 8px; flex-wrap: wrap; }
.ov-collab-add .ov-input { flex: 1; min-width: 160px; }
.ov-collab-add .ov-select { flex: 0 0 120px; }

.ov-btn {
    appearance: none;
    border: 1px solid var(--border);
    background: var(--bg-elevated);
    color: var(--fg);
    padding: 8px 14px;
    border-radius: 8px;
    font: inherit;
    font-size: 13px;
    cursor: pointer;
}
.ov-btn:hover:not(:disabled) { background: var(--bg-overlay); }
.ov-btn:disabled { opacity: .5; cursor: not-allowed; }
.ov-btn-primary {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-fg);
    font-weight: 600;
}
.ov-btn-primary:hover:not(:disabled) { filter: brightness(1.06); background: var(--accent); }
.ov-btn-danger { border-color: var(--danger); color: var(--danger); background: transparent; }
.ov-btn-danger:hover:not(:disabled) { background: color-mix(in srgb, var(--danger) 12%, transparent); }
.ov-btn-small { padding: 4px 10px; font-size: 12px; }

.ov-danger { border-color: color-mix(in srgb, var(--danger) 40%, var(--border)); }
.ov-danger .ov-h2 { color: var(--danger); }

/* Read-only address box + change-address modal */
.ov-input[readonly], .ov-readonly { background: var(--bg-overlay); cursor: default; }

.ov-modal-backdrop {
    position: fixed;
    inset: 0;
    z-index: 2000;
    background: rgba(0, 0, 0, 0.45);
}
.ov-modal {
    position: fixed;
    z-index: 2001;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: min(560px, calc(100vw - 32px));
    max-height: min(82vh, 660px);
    overflow-y: auto;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: var(--shadow-card);
    padding: 18px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}
.ov-modal-head { display: flex; align-items: center; justify-content: space-between; }
.ov-modal-title { margin: 0; font-size: 16px; font-weight: 600; color: var(--fg); }
.ov-modal-x {
    appearance: none;
    border: 0;
    background: transparent;
    color: var(--muted);
    font-size: 15px;
    line-height: 1;
    cursor: pointer;
    padding: 6px 8px;
    border-radius: 6px;
}
.ov-modal-x:hover { background: var(--hover-overlay); color: var(--fg); }
.ov-modal-results { max-height: 340px; overflow-y: auto; }
.ov-name-confirm { display: flex; flex-direction: column; gap: 8px; }
.ov-name-confirm p { margin: 0; }
.ov-confirm-addr { font-size: 13px; color: var(--fg); }

/* Building profile + research review */
.ov-profile-cat { margin-top: 4px; }
.ov-profile-cat-h {
    font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
    color: var(--muted); margin: 10px 0 2px;
}
.ov-profile-row {
    display: flex; gap: 12px; align-items: baseline;
    padding: 5px 0; border-bottom: 1px solid var(--border);
}
.ov-profile-row:last-child { border-bottom: 0; }
.ov-profile-key { flex: 0 0 38%; max-width: 38%; font-size: 13px; color: var(--muted); }
.ov-profile-val { flex: 1; display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: 13px; color: var(--fg); }

.ov-grade {
    font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .03em;
    padding: 1px 6px; border-radius: 999px; border: 1px solid transparent; white-space: nowrap;
}
.ov-grade-measured, .ov-grade-confirmed { color: var(--success); background: color-mix(in srgb, var(--success) 14%, transparent); }
.ov-grade-cited { color: var(--accent); background: color-mix(in srgb, var(--accent) 16%, transparent); }
.ov-grade-documented { color: var(--info); background: color-mix(in srgb, var(--info) 14%, transparent); }
.ov-grade-reported { color: var(--warning); background: color-mix(in srgb, var(--warning) 14%, transparent); }
.ov-grade-inferred { color: var(--muted); background: var(--bg-overlay); }

.ov-cite { font-size: 11px; color: var(--muted); font-family: ui-monospace, "Cascadia Code", monospace; }
.ov-source { font-size: 11px; color: var(--accent); text-decoration: none; }
.ov-source:hover { text-decoration: underline; }

.ov-x-mini {
    appearance: none; border: 0; background: transparent; color: var(--muted);
    cursor: pointer; font-size: 12px; line-height: 1; padding: 0 4px; border-radius: 4px; margin-left: auto;
}
.ov-x-mini:hover { color: var(--danger); background: var(--hover-overlay); }
.ov-remove-confirm { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--danger); white-space: nowrap; margin-left: auto; }
.ov-undo {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    padding: 8px 12px; border-radius: 8px; font-size: 13px; margin: 4px 0;
    background: var(--bg-overlay); border: 1px solid var(--border); color: var(--muted);
}

.ov-modal-wide { width: min(680px, calc(100vw - 32px)); }
.ov-review-list { list-style: none; margin: 4px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.ov-review-row { border: 1px solid var(--border); border-radius: 8px; padding: 10px; display: flex; flex-direction: column; gap: 6px; }
.ov-review-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ov-review-body { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ov-review-body .ov-input { flex: 1; min-width: 180px; }
.ov-review-actions { display: flex; gap: 8px; }

.ov-conf { font-size: 10px; font-weight: 700; text-transform: uppercase; padding: 1px 6px; border-radius: 999px; }
.ov-conf-high { color: var(--success); background: color-mix(in srgb, var(--success) 14%, transparent); }
.ov-conf-medium { color: var(--warning); background: color-mix(in srgb, var(--warning) 14%, transparent); }
.ov-conf-low { color: var(--muted); background: var(--bg-overlay); }

.ov-gaps { margin: 2px 0; }
.ov-gaps summary { cursor: pointer; }
.ov-gap-list { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.ov-gap-chip {
    font-size: 11px;
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--bg-overlay);
    color: var(--muted);
    border: 1px dashed var(--border);
}
.ov-gap-chip-btn { appearance: none; cursor: pointer; font: inherit; font-size: 11px; }
.ov-gap-chip-btn:hover {
    border-style: solid;
    border-color: var(--accent);
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 8%, transparent);
}
.ov-removed-val { color: var(--muted); text-decoration: line-through; }

/* Discovery status banner + progress + inline review table */
.ov-discovery {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 12px; border-radius: 8px; font-size: 13px; color: var(--fg);
    background: color-mix(in srgb, var(--accent) 10%, var(--bg-overlay));
    border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border));
}
.ov-discovery.fail {
    background: color-mix(in srgb, var(--danger) 10%, var(--bg-overlay));
    border-color: color-mix(in srgb, var(--danger) 35%, var(--border));
}
.ov-discovery.idle {
    background: var(--bg-overlay);
    border-color: var(--border);
    color: var(--muted);
    align-items: flex-start;
}
.ov-check { color: var(--success); font-weight: 700; flex: 0 0 auto; }
.ov-spinner {
    width: 14px; height: 14px; flex: 0 0 14px;
    border: 2px solid color-mix(in srgb, var(--accent) 30%, transparent);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: ov-spin 0.7s linear infinite;
}
@keyframes ov-spin { to { transform: rotate(360deg); } }

.ov-progress { display: flex; align-items: center; gap: 10px; margin: 2px 0; }
.ov-progress-track { flex: 1; height: 6px; border-radius: 999px; background: var(--bg-overlay); overflow: hidden; }
.ov-progress-fill { display: block; height: 100%; background: var(--accent); border-radius: 999px; transition: width .3s ease; }

.ov-review-inline {
    border: 1px solid color-mix(in srgb, var(--accent) 25%, var(--border));
    border-radius: 10px; padding: 12px; margin: 4px 0;
    background: color-mix(in srgb, var(--accent) 4%, transparent);
}
.ov-table-wrap { overflow-x: auto; }
.ov-review-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.ov-review-table th {
    text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
    color: var(--muted); font-weight: 600; padding: 4px 8px; border-bottom: 1px solid var(--border);
}
.ov-review-table td { padding: 8px; border-bottom: 1px solid var(--border); vertical-align: top; }
.ov-review-table tr:last-child td { border-bottom: 0; }
.ov-rt-attr { font-weight: 500; color: var(--fg); }
.ov-rt-input { width: 100%; min-width: 140px; }
.ov-rt-actions { white-space: nowrap; display: flex; gap: 6px; }
.ov-rt-act-h { width: 1%; }
.ov-source { display: inline-block; max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }

/* ── Utilities ─────────────────────────────────────────────────────────── */
/* Single-line ellipsis. Needs a min-width:0 (or explicit width) on the flex
   item it sits in to actually clamp. Used app-wide (file names, titles). */
.truncate {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}
