/* Shelf Aware — clean, minimal design system (polish unlocked after Phase 4, DESIGN.md §8). */
:root {
    --ink: #1f2328;
    --ink-soft: #59636e;
    --line: #e2e5e9;
    --surface: #ffffff;
    --surface-alt: #f0f2f5;
    --bg: #f6f7f9;
    --accent: #2563eb;
    --accent-ink: #1d4ed8;

    --overdue-bg: #fde7e9;  --overdue-ink: #b42318;  --overdue-line: #e8625a;
    --duesoon-bg: #fef4d6;  --duesoon-ink: #8a6100;  --duesoon-line: #e3b341;
    --stocked-bg: #dcf3e6;  --stocked-ink: #116b3c;  --stocked-line: #58b487;
    --unknown-bg: #e8eaed;  --unknown-ink: #4a525b;  --unknown-line: #b6bdc6;

    --radius: 8px;
    --shadow: 0 1px 2px rgba(16, 24, 40, 0.06), 0 1px 3px rgba(16, 24, 40, 0.05);

    /* Report chart series — fixed slot order (the ordering IS the colorblind-safety mechanism:
       it maximizes adjacent-pair CVD distance; validated against --surface in both modes).
       Series are assigned 1..8 in rank order and never cycled; pooled series wear --chart-pooled. */
    --chart-1: #2a78d6;
    --chart-2: #1baf7a;
    --chart-3: #eda100;
    --chart-4: #008300;
    --chart-5: #4a3aa7;
    --chart-6: #e34948;
    --chart-7: #e87ba4;
    --chart-8: #eb6834;
    --chart-pooled: #8a939e;
}

@media (prefers-color-scheme: dark) {
    :root {
        --ink: #e6e8eb;
        --ink-soft: #9aa4af;
        --line: #2b3138;
        --surface: #1b1f24;
        --surface-alt: #20252b;
        --bg: #131619;
        --accent: #4b86ff;
        --accent-ink: #7aa6ff;

        --overdue-bg: #3a1d1f;  --overdue-ink: #f4a6a0;  --overdue-line: #b4524a;
        --duesoon-bg: #38300f;  --duesoon-ink: #eccf85;  --duesoon-line: #9a7d2c;
        --stocked-bg: #143427;  --stocked-ink: #84dfac;  --stocked-line: #3f8e64;
        --unknown-bg: #2a2f36;  --unknown-ink: #aab2bb;  --unknown-line: #586675;

        --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 1px 3px rgba(0, 0, 0, 0.3);

        /* Same eight hues re-stepped for the dark surface (validated separately) — not a hue flip. */
        --chart-1: #3987e5;
        --chart-2: #199e70;
        --chart-3: #c98500;
        --chart-4: #008300;
        --chart-5: #9085e9;
        --chart-6: #e66767;
        --chart-7: #d55181;
        --chart-8: #d95926;
        --chart-pooled: #6d7783;
    }
}

body {
    font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
    margin: 0;
    color: var(--ink);
    background: var(--bg);
    line-height: 1.45;
}

/* ---- App chrome ---- */
.site-header {
    display: flex;
    align-items: baseline;
    gap: 1.5rem;
    padding: 0.85rem 1.5rem;
    background: var(--surface);
    border-bottom: 1px solid var(--line);
}

.site-header .brand {
    font-weight: 700;
    letter-spacing: -0.01em;
}

.site-header nav a {
    margin-right: 1rem;
    color: var(--ink-soft);
    text-decoration: none;
    font-weight: 500;
    /* The row wraps, and without this a two-word label wraps INSIDE itself — "Count Stock" split across
       two lines reads as two separate links, both highlighted when active. Latent for "Grocery List" too;
       it just never happened to land on the break. Wrap between links, never through one. */
    white-space: nowrap;
}

.site-header nav a:hover {
    color: var(--accent-ink);
}

.site-header nav a.active {
    color: var(--accent-ink);
    font-weight: 700;
}

/* Mobile-only nav disclosure (☰). Hidden on desktop, where the full link row renders as always. */
.nav-toggle {
    display: none;
    background: none;
    border: 1px solid var(--line);
    border-radius: 8px;
    color: var(--ink);
    font-size: 1.1rem;
    line-height: 1;
    padding: 0.35rem 0.6rem;
    cursor: pointer;
}

/* Small screens: eight links swamp a phone, so the nav collapses behind the toggle and opens as a
   full-width column under the bar. Desktop (above the breakpoint) is untouched. */
@media (max-width: 768px) {
    .site-header {
        flex-wrap: wrap;
        align-items: center;
        gap: 0.75rem;
        padding: 0.7rem 1rem;
    }

    .nav-toggle {
        display: block;
        order: 5; /* visual right edge; DOM (tab) order stays brand → toggle → nav → account */
    }

    .site-header nav {
        display: none;
        order: 6;
        width: 100%;
        flex-direction: column;
        border-top: 1px solid var(--line);
        padding-top: 0.25rem;
    }

    .site-header.nav-open nav {
        display: flex;
    }

    .site-header nav a {
        margin-right: 0;
        padding: 0.55rem 0.25rem;
        border-bottom: 1px solid var(--line);
    }

    .site-header nav a:last-child {
        border-bottom: none;
    }

    .site-header .account-email {
        max-width: 9rem;
    }
}

.site-main {
    padding: 1.5rem;
    max-width: 56rem;
    margin: 0 auto;
}

h1 {
    font-size: 1.6rem;
    letter-spacing: -0.02em;
    margin: 0;
}

h2 {
    font-size: 1.05rem;
}

h1:focus {
    outline: none;
}

/* Content links use the chart blue (--accent) so they match the price-history line charts.
   The top nav (.site-header nav a) has its own muted/active treatment and overrides this. */
a {
    color: var(--accent);
}

/* ---- Tables (Products, Upload, "everything else") ---- */
table {
    border-collapse: collapse;
    margin: 1rem 0;
    width: 100%;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

/* Wide grids scroll horizontally inside their own box instead of overflowing the page.
   The wrapper is the SINGLE scroll container: keep the inner table a normal table so the
   narrow-screen `table { display:block; overflow-x:auto }` rule (below) can't stack a SECOND
   horizontal scrollbar inside .table-scroll — that nesting is the "two scrollbars" bug. */
.table-scroll {
    overflow-x: auto;
    margin: 1rem 0;
}

.table-scroll table {
    margin: 0;
    display: table;
    overflow-x: visible;
    white-space: normal;
}

/* The wide editable review grid also caps its height to the viewport and pins its header, so
   its horizontal scrollbar stays reachable instead of buried at the bottom of a tall receipt
   (you no longer scroll the whole page down just to find it). Scoped to the review grid — the
   report tables fit horizontally, so they don't need a scroll box. */
.table-scroll--capped {
    max-height: min(75vh, 42rem);
    overflow: auto;
}

.table-scroll--capped thead th {
    position: sticky;
    top: 0;
    z-index: 1;
}

th, td {
    border-bottom: 1px solid var(--line);
    padding: 0.5rem 0.75rem;
    text-align: left;
}

th {
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--ink-soft);
    background: var(--surface-alt);
}

tbody tr:last-child td {
    border-bottom: none;
}

/* ---- Buttons & inputs ---- */
button {
    font: inherit;
    padding: 0.4rem 0.8rem;
    border: 1px solid var(--accent);
    background: var(--accent);
    color: #fff;
    border-radius: 6px;
    cursor: pointer;
}

button:hover:not(:disabled) {
    background: var(--accent-ink);
    border-color: var(--accent-ink);
}

button:disabled {
    opacity: 0.5;
    cursor: default;
}

button.secondary {
    background: var(--surface);
    color: var(--ink);
    border-color: var(--line);
}

button.secondary:hover:not(:disabled) {
    background: var(--surface-alt);
    border-color: var(--ink-soft);
}

/* Destructive action — reuses the "overdue" red so delete reads as a real warning. */
button.danger {
    background: var(--overdue-ink);
    border-color: var(--overdue-ink);
    color: #fff;
}

button.danger:hover:not(:disabled) {
    background: var(--overdue-line);
    border-color: var(--overdue-line);
}

.danger-zone { border-color: var(--overdue-line); }

.danger-confirm {
    margin-top: 0.75rem;
    padding: 0.85rem 1rem;
    background: var(--overdue-bg);
    border: 1px solid var(--overdue-line);
    border-radius: 8px;
}

.danger-confirm label { display: block; margin: 0.6rem 0 0; font-weight: 600; }
.danger-confirm input { display: block; margin-top: 0.3rem; }

/* Keyboard-shortcut hint (e.g. the grocery-list Download shortcut). */
.kbd-hint { align-self: center; }
kbd {
    font: inherit;
    font-size: 0.85em;
    padding: 0.05rem 0.4rem;
    border: 1px solid var(--line);
    border-bottom-width: 2px;
    border-radius: 4px;
    background: var(--surface-alt);
    color: var(--ink);
}

/* ---- Split button (primary action + caret menu) ---- */
.split-btn {
    position: relative;
    display: inline-flex;
}

.split-btn .split-main {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
    border-right: none;
}

.split-btn .split-caret {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
    padding-left: 0.4rem;
    padding-right: 0.5rem;
    display: inline-flex;
    align-items: center;
}

.split-menu {
    position: absolute;
    top: calc(100% + 0.25rem);
    right: 0;
    z-index: 20;
    display: flex;
    flex-direction: column;
    min-width: 100%;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 6px;
    box-shadow: var(--shadow);
    overflow: hidden;
}

.split-menu button {
    border: none;
    border-radius: 0;
    white-space: nowrap;
    text-align: left;
    width: 100%;
}

.split-backdrop {
    position: fixed;
    inset: 0;
    z-index: 10;
}

input, select {
    font: inherit;
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--line);
    border-radius: 6px;
    background: var(--surface);
    color: var(--ink);
}

input:focus, select:focus {
    outline: 2px solid var(--accent);
    outline-offset: -1px;
}

/* ---- Panels ---- */
.panel {
    margin: 1rem 0;
    padding: 1rem 1.15rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: var(--shadow);
}

.panel h2 {
    margin-top: 0;
}

.panel form {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    flex-wrap: wrap;
}

.panel.temp {
    border-style: dashed;
    box-shadow: none;
    background: transparent;
}

/* ---- Dashboard: header + summary ---- */
.page-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.summary {
    display: flex;
    gap: 0.4rem;
    align-items: center;
}

.summary .all-good {
    color: var(--stocked-ink);
    font-weight: 600;
}

.empty-note {
    margin-top: 0.25rem;
}

/* ---- Dashboard: running-low cards ---- */
.cards {
    list-style: none;
    padding: 0;
    margin: 1rem 0;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.card {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.85rem 1rem 0.85rem 1.15rem;
    background: var(--surface);
    border: 1px solid var(--line);
    border-left: 4px solid var(--unknown-line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

.card.accent-overdue { border-left-color: var(--overdue-line); }
.card.accent-duesoon { border-left-color: var(--duesoon-line); }

/* "You keep running out of these" — the burn-rate-≪-rebuy habit insight promoted from Product Detail. */
.runs-out-early .habit-list {
    list-style: none;
    padding: 0;
    margin: 0.5rem 0 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.runs-out-early .habit-list li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem;
}
.habit-name {
    font-weight: 600;
    color: var(--accent);
    text-decoration: none;
}
.habit-name:hover { text-decoration: underline; }
.habit-note { color: var(--ink-soft); font-size: 0.9rem; }

/* First-run "getting started" banner (keyless / empty-catalog onboarding). */
.onboarding { position: relative; border-left: 4px solid var(--accent); }
.onboarding h2 { margin-top: 0; }
.onboarding-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.75rem; }
.onboarding-x {
    position: absolute;
    top: 0.4rem;
    right: 0.5rem;
    background: none;
    border: none;
    font-size: 1.35rem;
    line-height: 1;
    color: var(--ink-soft);
    cursor: pointer;
    padding: 0.15rem 0.4rem;
}
.onboarding-x:hover { color: var(--accent-ink); }

/* ---- Guided walkthrough (Components/Layout/GuidedTour.razor) ----
   Docked bottom-LEFT because the voice assistant owns bottom-right; the two are the only fixed-position
   furniture in the app and must not stack. Non-modal on purpose: no backdrop, nothing blocked — the page
   under it stays usable, so a visitor can follow a step by actually doing it. */
.tour-panel {
    position: fixed;
    left: 1rem;
    bottom: 1rem;
    /* Above ordinary page content, deliberately BELOW the modal layer (.picker-backdrop, 60): a
       walkthrough is transient furniture, and floating it over a modal leaves the modal not modal. */
    z-index: 55;
    width: min(360px, calc(100vw - 2rem));
    margin: 0;
    border-left: 4px solid var(--accent);
    max-height: calc(100vh - 2rem);
    overflow-y: auto;
}

.tour-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}

.tour-progress {
    margin: 0;
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

.tour-title {
    margin: 0.35rem 0 0.4rem;
    font-size: 1.05rem;
}

/* The heading takes focus on every step change (tabindex=-1). Keep the focus ring for keyboard users
   but spare pointer users a ring on something they didn't click into. */
.tour-title:focus:not(:focus-visible) { outline: none; }

.tour-body {
    margin: 0;
    color: var(--ink-soft);
    font-size: 0.92rem;
    line-height: 1.5;
}

.tour-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.9rem;
}

.tour-skip { margin-left: auto; }

/* The ring around whatever the current step is about. Applied by wwwroot/js/tour.js, which may find
   nothing to ring — the step still reads, so this styling is never load-bearing for comprehension. */
.tour-spot {
    outline: 3px solid var(--accent);
    outline-offset: 4px;
    border-radius: 4px;
}

@media (prefers-reduced-motion: no-preference) {
    .tour-spot { animation: tour-spot-in 0.9s ease-out; }
}

/* ⚠️ Animates the OFFSET, never the colour. An animation is not guaranteed to run — a background tab,
   a throttled compositor, a device under load — and it holds its first keyframe when it stalls. A first
   keyframe of "outline-color: transparent" therefore leaves the ring INVISIBLE in exactly the case the
   visitor most needs it, which is how the first version of this shipped. Every frame here is a ring. */
@keyframes tour-spot-in {
    from { outline-offset: 12px; }
    to   { outline-offset: 4px; }
}

/* Narrow screens: the panel becomes a bottom sheet spanning the width, which would sit right on top of
   the voice assistant's FAB. The walkthrough is transient and the assistant isn't going anywhere, so the
   FAB stands down until the tour is closed (the tour's own copy never claims it's on screen). */
@media (max-width: 560px) {
    .tour-panel {
        left: 0;
        right: 0;
        bottom: 0;
        width: auto;
        border-radius: var(--radius) var(--radius) 0 0;
        border-left: none;
        border-top: 4px solid var(--accent);
        max-height: 60vh;
    }

    body.tour-active .voice-agent { display: none; }
}

.card-top {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.card-name {
    font-weight: 600;
    font-size: 1.05rem;
    color: var(--accent);
    text-decoration: none;
}

.card-name:hover {
    color: var(--accent-ink);
    text-decoration: underline;
}

.rhythm {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.25rem 1rem;
    margin: 0;
    max-width: 28rem;
}

.rhythm dt {
    color: var(--ink-soft);
}

.rhythm dd {
    margin: 0;
    font-weight: 600;
}

/* The two-stream "gap" insight — the actionable line on Product Detail (out-before-restock, or ahead). */
.rhythm-gap {
    margin: 0.75rem 0 0;
    padding: 0.5rem 0.75rem;
    background: var(--surface-alt);
    border-left: 3px solid var(--accent);
    border-radius: 6px;
    font-size: 0.9rem;
}

/* ---- Product detail: brands-bought breakdown ---- */
.brands {
    list-style: none;
    padding: 0;
    margin: 0.5rem 0 0;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.brands li {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 1rem;
}

.brand-name {
    font-weight: 600;
}

/* ---- Trends (spend insight) ---- */
.portfolio {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
    margin: 1rem 0 1.5rem;
}

.stat {
    flex: 1 1 9rem;
    padding: 0.75rem 1rem;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

.stat-label {
    display: block;
    font-size: 0.78rem;
    color: var(--ink-soft);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.stat-value {
    font-size: 1.4rem;
    font-weight: 700;
}

.stat.forecast {
    border-color: var(--accent);
}

.stat.forecast .stat-value {
    color: var(--accent);
}

.stat.pass { border-color: var(--stocked-line); }
.stat.pass .stat-value { color: var(--stocked-ink); }
.stat.fail { border-color: var(--overdue-line); }
.stat.fail .stat-value { color: var(--overdue-ink); }

pre {
    background: var(--surface-alt);
    padding: 0.5rem 0.75rem;
    border-radius: 6px;
    overflow-x: auto;
    border: 1px solid var(--line);
}

.change-up {
    color: var(--overdue-ink);
    font-weight: 600;
}

.change-down {
    color: var(--stocked-ink);
    font-weight: 600;
}

.linechart {
    display: block;
    vertical-align: middle;
}

.card-urgency {
    font-weight: 600;
    margin-top: 0.15rem;
}

.card.accent-overdue .card-urgency { color: var(--overdue-ink); }
.card.accent-duesoon .card-urgency { color: var(--duesoon-ink); }

.card-note {
    font-size: 0.85rem;
    color: var(--ink-soft);
    margin-top: 0.1rem;
}

.card-basis {
    margin-top: 0.1rem;
}

.card-actions {
    display: flex;
    gap: 0.4rem;
    flex-shrink: 0;
}

.quick-update input {
    flex: 1 1 18rem;
}

/* ---- Voice / push-to-talk ---- */
.voice {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.voice-divider {
    display: flex;
    align-items: center;
    text-align: center;
    color: var(--ink-soft);
    font-size: 0.82rem;
    margin: 0.85rem 0 0.15rem;
}

.voice-divider::before,
.voice-divider::after {
    content: "";
    flex: 1;
    border-top: 1px solid var(--line);
}

.voice-divider span {
    padding: 0 0.6rem;
}

button.mic {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    align-self: flex-start;
    padding: 0.5rem 1rem;
    border-radius: 999px;
    background: var(--surface);
    color: var(--accent-ink);
    border: 1px solid var(--accent);
    /* Let press-and-hold work without the browser treating it as a scroll/long-press gesture. */
    touch-action: none;
    user-select: none;
}

button.mic:hover:not(:disabled) {
    background: var(--surface-alt);
    border-color: var(--accent-ink);
}

button.mic .mic-glyph {
    font-size: 1.05rem;
    line-height: 1;
}

button.mic-recording,
button.mic-recording:hover:not(:disabled) {
    background: var(--overdue-bg);
    color: var(--overdue-ink);
    border-color: var(--overdue-line);
    animation: mic-pulse 1.2s ease-in-out infinite;
}

button.mic-thinking,
button.mic-speaking {
    color: var(--ink-soft);
    border-color: var(--line);
}

@keyframes mic-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(180, 35, 24, 0.35); }
    50% { box-shadow: 0 0 0 6px rgba(180, 35, 24, 0); }
}

@media (prefers-reduced-motion: reduce) {
    button.mic-recording { animation: none; }
}

.voice-status {
    min-height: 1.2em;
}

.voice-heard {
    margin: 0.1rem 0;
    color: var(--ink-soft);
    font-style: italic;
}

/* ---- Voice conversation (v2.1 multi-turn) ---- */
.voice-convo {
    margin-top: 0.6rem;
}

.convo-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 0.4rem;
}

.convo-log {
    display: grid;
    gap: 0.35rem;
    margin-bottom: 0.6rem;
    max-height: 16rem;
    overflow-y: auto;
}

.convo-user,
.convo-bot {
    margin: 0;
    padding: 0.3rem 0.5rem;
    border-radius: 8px;
}

.convo-user {
    background: var(--surface-alt);
}

.convo-bot {
    background: var(--stocked-bg);
    color: var(--stocked-ink);
}

.convo-user .who,
.convo-bot .who {
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    opacity: 0.7;
    margin-right: 0.4rem;
}

.convo-phase {
    margin: 0.35rem 0 0;
    font-size: 0.85rem;
    font-weight: 600;
}

.convo-phase.listening {
    color: var(--stocked-ink);
}

.convo-phase.speaking {
    color: var(--accent-ink);
}

@media (prefers-reduced-motion: no-preference) {
    .convo-phase.listening::before {
        content: "";
        display: inline-block;
        width: 0.55rem;
        height: 0.55rem;
        margin-right: 0.4rem;
        border-radius: 50%;
        background: var(--stocked-line);
        vertical-align: middle;
        animation: convo-listen-pulse 1.2s ease-in-out infinite;
    }
}

@keyframes convo-listen-pulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50% { opacity: 0.35; transform: scale(0.75); }
}

/* ---- Persistent voice assistant (floating, hosted in MainLayout) ---- */
.voice-agent {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 60;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.4rem;
    max-width: min(360px, calc(100vw - 2rem));
}

.voice-agent-fab {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.6rem 1rem;
    border-radius: 999px;
    background: var(--accent);
    color: #fff;
    border: 1px solid var(--accent-ink);
    box-shadow: var(--shadow);
    font-weight: 600;
    cursor: pointer;
}

.voice-agent-fab:hover:not(:disabled) {
    background: var(--accent-ink);
}

.voice-agent-fab:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.voice-agent-fab > span[aria-hidden] {
    font-size: 1.1rem;
    line-height: 1;
}

.voice-agent-note {
    margin: 0;
    font-size: 0.82rem;
    background: var(--surface);
    padding: 0.3rem 0.5rem;
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

.voice-agent-panel {
    width: min(360px, calc(100vw - 2rem));
    max-height: min(70vh, 30rem);
    overflow-y: auto;
    margin: 0;
    box-shadow: 0 6px 24px rgba(16, 24, 40, 0.18);
}

@media (prefers-color-scheme: dark) {
    .voice-agent-panel {
        box-shadow: 0 6px 24px rgba(0, 0, 0, 0.5);
    }
}

/* Reader / cook-along header actions (Back to assistant + close) */
.ra-head-actions {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

/* Recipes "?uses" filter banner */
.filter-banner {
    margin: 0 0 0.6rem;
    padding: 0.4rem 0.6rem;
    border-radius: var(--radius);
    background: var(--surface-alt);
    border-left: 3px solid var(--accent);
    font-size: 0.9rem;
}

.quick-update-hint {
    margin: 0.4rem 0 0;
}

/* ---- Narrow screens ---- */
@media (max-width: 560px) {
    .site-main {
        padding: 1rem;
    }

    .card {
        flex-direction: column;
        align-items: stretch;
    }

    .card-actions {
        justify-content: flex-start;
    }

    /* Keep wide management tables from overflowing the viewport. */
    table {
        display: block;
        overflow-x: auto;
        white-space: nowrap;
    }
}

/* ---- Status chips ---- */
.chip {
    display: inline-block;
    padding: 0.1rem 0.55rem;
    border-radius: 999px;
    font-size: 0.78rem;
    font-weight: 600;
    white-space: nowrap;
}

.chip-overdue { background: var(--overdue-bg); color: var(--overdue-ink); }
.chip-duesoon { background: var(--duesoon-bg); color: var(--duesoon-ink); }
.chip-stocked { background: var(--stocked-bg); color: var(--stocked-ink); }
.chip-unknown { background: var(--unknown-bg); color: var(--unknown-ink); }

/* ---- Everything-else disclosure ---- */
.everything-else summary {
    cursor: pointer;
    font-weight: 600;
    color: var(--ink-soft);
    margin: 1.25rem 0 0;
}

/* ---- Receipts page: one card per receipt, the disclosure reveals its line items ---- */
.receipt-card summary {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
    cursor: pointer;
    list-style-position: inside; /* keep the disclosure marker inline with the flex row */
}

.receipt-card .receipt-date {
    font-weight: 600;
}

.receipt-card .receipt-total {
    margin-left: auto;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.receipt-card table {
    margin-top: 0.75rem;
}

.receipt-eval-actions,
.receipt-remove-actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
    margin-top: 0.75rem;
}

/* ---- Review opt-in + accuracy self-check run row ---- */
.verify-for-eval label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-weight: 600;
}

.verify-for-eval .muted {
    display: block;
    margin-top: 0.25rem;
}

.selfeval-run {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
    margin-top: 0.75rem;
}

/* ---- Text helpers ---- */
.muted {
    color: var(--ink-soft);
    font-size: 0.9rem;
}

.chat-reply {
    color: var(--stocked-ink);
    font-weight: 600;
}

.error {
    color: var(--overdue-ink);
}

/* Header above the kept "Get ideas" batch: what it was for, when, and the clear action. */
.suggestions-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.suggestions-head p {
    margin: 0.5rem 0 0;
}

/* "Why is this red?" — the covering product is just predicted run-out (recipe ingredient rows). */
.maybe-have {
    color: var(--ink-soft);
    font-size: 0.85rem;
    font-style: italic;
    margin: 0 0.35rem;
}

/* Duplicate-product prompt under the Add form: a caution, not an error. */
.dup-check {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    flex-wrap: wrap;
    margin: 0.6rem 0 0;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--line);
    border-left: 3px solid var(--duesoon-ink);
    border-radius: var(--radius);
    background: var(--duesoon-bg);
}

/* ---- Upload review table cues ---- */
.low-confidence {
    background: var(--duesoon-bg);
}

.spinner {
    font-style: italic;
}

td.raw {
    font-family: ui-monospace, monospace;
    font-size: 0.85rem;
    color: var(--ink-soft);
    vertical-align: top;
}

td.raw .chip {
    margin-top: 0.3rem;
    font-family: system-ui, sans-serif;
}

.review-actions {
    display: flex;
    gap: 0.5rem;
    margin-top: 0.5rem;
}

/* ---- Accessibility ---- */
:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Screen-reader-only text (visually hidden, still announced). */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
}

/* ---- Products filter bar ---- */
.filters {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    flex-wrap: wrap;
}

.filters .count {
    margin-left: auto;
    color: var(--ink-soft);
    font-size: 0.9rem;
}

.empty-row td {
    color: var(--ink-soft);
    text-align: center;
    padding: 1rem;
}

/* Size + "usual brand" hints under the product (item) name in the Products grid. */
.item-meta {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
    margin-top: 0.2rem;
}

.usual-brand {
    font-size: 0.78rem;
    color: var(--ink-soft);
}

.usual-variety {
    font-size: 0.78rem;
    color: var(--accent);
    font-style: italic;
}

/* Tap-for-detail brand/variety breakdown (BrandVarietyHint). Expands inline — the grids sit in
   overflow-scroll containers that would clip a floating popover. */
.hint-pop {
    display: inline-block;
}

.hint-pop summary {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    cursor: pointer;
    list-style: none;
}

.hint-pop summary::-webkit-details-marker {
    display: none;
}

.hint-pop summary::after {
    content: "▾";
    font-size: 0.65rem;
    color: var(--muted);
}

.hint-pop[open] summary::after {
    content: "▴";
}

.hint-pop-body {
    margin-top: 0.25rem;
    padding: 0.3rem 0.55rem;
    border: 1px solid var(--line);
    border-radius: 6px;
    background: var(--surface-alt);
    font-size: 0.75rem;
    color: var(--ink-soft);
}

.hint-pop-body .hint-label {
    font-weight: 600;
    margin-right: 0.3rem;
}

/* Neutral pill for package size (part of the product's identity). */
.size-chip {
    display: inline-block;
    padding: 0.05rem 0.45rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--ink-soft);
    background: var(--surface-alt);
    white-space: nowrap;
    vertical-align: middle;
}

/* ── Tags: the descriptive second category layer (cloud, chips, review editor) ── */
.tag-list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    align-items: center;
}

.tag-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.08rem 0.5rem;
    border: 1px solid var(--accent);
    border-radius: 999px;
    font-size: 0.74rem;
    font-weight: 600;
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    text-decoration: none;
    line-height: 1.5;
}

a.tag-chip:hover,
button.tag-chip:hover {
    background: color-mix(in srgb, var(--accent) 20%, transparent);
}

button.tag-chip {
    cursor: pointer;
}

.tag-chip .tag-x {
    border: none;
    background: none;
    color: inherit;
    cursor: pointer;
    font-size: 0.85rem;
    line-height: 1;
    padding: 0;
}

.tag-chip-mini {
    font-size: 0.68rem;
    padding: 0.02rem 0.4rem;
    font-weight: 500;
}

.detail-tags {
    margin: -0.2rem 0 0.6rem;
}

/* The browsable tag cloud on the Products page. */
.tag-cloud {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    align-items: center;
    margin: 0.4rem 0 0.9rem;
}

.tag-cloud-chip {
    font-size: 0.8rem;
}

.tag-cloud-chip.active {
    color: var(--surface);
    background: var(--accent);
}

.tag-count {
    font-size: 0.66rem;
    opacity: 0.8;
}

.tag-clear {
    border: none;
    background: none;
    color: var(--ink-soft);
    cursor: pointer;
    font-size: 0.78rem;
    text-decoration: underline;
}

/* Per-line tag editor on the receipt review screen. */
.tags-cell {
    min-width: 11rem;
}

.tags-cell .tag-add {
    display: flex;
    gap: 0.25rem;
    margin-top: 0.3rem;
}

.tags-cell .tag-add input {
    width: 6rem;
}

.tag-warning {
    margin-top: 0.3rem;
    font-size: 0.74rem;
    color: var(--ink-soft);
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    align-items: center;
}

/* Products grid row actions: a tiny "Out" button (always-available mark-out) + delete. */
/* Per-row action cell (Products grid, Grocery List). Must stay a real table-cell — display:flex on a
   <td> pulls it out of table layout, so the cell stops stretching to the row height and its bottom
   border floats mid-row instead of joining the grid line. Space buttons with margins instead. */
.row-actions {
    white-space: nowrap;
}

.row-actions > * {
    vertical-align: middle;
}

.row-actions > * + * {
    margin-left: 0.3rem;
}

.mark-out {
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--overdue-ink);
    background: transparent;
    border: 1px solid var(--overdue-line);
    border-radius: 6px;
    padding: 0.05rem 0.45rem;
    cursor: pointer;
    line-height: 1.5;
}

.mark-out:hover {
    background: var(--overdue-bg);
}

/* Grocery-list row action — "don't want it for a while": untracks until the next purchase. A quiet
   neutral chip: borderless with a soft fill (an outline in --line was too faint on dark and read as a
   broken border), not a green "I'm stocked" statement. */
.untrack-btn {
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--ink-soft);
    background: var(--surface-alt);
    border: none;
    border-radius: 6px;
    padding: 0.15rem 0.5rem;
    cursor: pointer;
    line-height: 1.5;
    white-space: nowrap;
}

.untrack-btn:hover:not(:disabled) {
    background: var(--line);
    color: var(--ink);
}

/* "Already have it" — a Restocked signal (status-only: clears an OutNow and re-anchors the due date;
   it never feeds the cadence, §6 two-stream model). Green because it IS an "I'm stocked" statement,
   unlike Untrack's neutral chip beside it. */
.restock-btn {
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--stocked-ink);
    background: var(--stocked-bg);
    border: none;
    border-radius: 6px;
    padding: 0.15rem 0.5rem;
    cursor: pointer;
    line-height: 1.5;
    white-space: nowrap;
}

.restock-btn:hover:not(:disabled) {
    box-shadow: inset 0 0 0 1px var(--stocked-line);
}

/* Item name → product detail (grocery list + products grid). */
.item-name {
    font-weight: 600;
    color: var(--accent);
    text-decoration: none;
}

/* Product-detail rename: the h1 swaps to this input, so size it to feel like the title it replaces. */
.rename-input {
    font-size: 1.3rem;
    font-weight: 700;
    padding: 0.2rem 0.5rem;
    border: 1px solid var(--line);
    border-radius: 6px;
    background: var(--surface);
    color: var(--ink);
    min-width: 16rem;
}

/* Merge-into panel: the two controls sit side by side and wrap on narrow screens. */
.merge-controls {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    margin-top: 0.6rem;
}

.merge-controls label {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-size: 0.85rem;
    color: var(--muted);
}

.item-name:hover {
    text-decoration: underline;
}

/* ── Recipes ── */
.recipe-disclaimer {
    background: var(--duesoon-bg);
    color: var(--duesoon-ink);
    border: 1px solid var(--duesoon-line);
    border-radius: var(--radius);
    padding: 0.6rem 0.9rem;
    font-size: 0.85rem;
}

.recipe-suggestions {
    display: grid;
    gap: 0.8rem;
    margin-top: 0.9rem;
}

.recipe-card {
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 0.8rem 1rem;
    background: var(--surface-alt);
}

.recipe-card-top {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.6rem;
}

.recipe-card-top h3 {
    margin: 0;
}

.ingredient-list {
    list-style: none;
    padding: 0;
    margin: 0.5rem 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem 1rem;
}

.ingredient-list li.grab {
    color: var(--overdue-ink);
    font-weight: 600;
}

.ingredient-list li.have {
    color: var(--stocked-ink);
}

/* Ingredient row with its swap control + bubble cloud. */
.ingredient-list li {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.3rem;
}

/* When a swap cloud is open, the ingredient takes the full row so the bubbles sit under it. */
.ingredient-list li:has(.alt-cloud) {
    flex-basis: 100%;
}

.alt-swap {
    border: none;
    background: transparent;
    color: var(--accent);
    cursor: pointer;
    font-size: 0.95rem;
    line-height: 1;
    padding: 0 0.2rem;
}

.alt-swap:hover {
    color: var(--accent-ink);
}

.alt-cloud {
    flex-basis: 100%;
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    margin: 0.25rem 0 0.15rem 1.2rem;
}

.alt-bubble {
    border-radius: 999px;
    padding: 0.15rem 0.6rem;
    font-size: 0.82rem;
    font-weight: 600;
    cursor: pointer;
    border: 1px solid var(--line);
    background: var(--surface);
}

.alt-bubble.have {
    color: var(--stocked-ink);
    border-color: var(--stocked-line);
    background: var(--stocked-bg);
}

.alt-bubble.grab {
    color: var(--overdue-ink);
    border-color: var(--overdue-line);
    background: var(--overdue-bg);
}

.alt-bubble:disabled {
    opacity: 0.6;
    cursor: default;
}

.recipe-grab {
    font-size: 0.88rem;
    margin: 0.2rem 0 0;
}

.recipe-grab.all-good {
    color: var(--stocked-ink);
}

.recipe-cal {
    margin: 0.15rem 0;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--ink-soft);
}

/* ---- Settings + pending imports ---- */
.setting-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-weight: 600;
    margin: 0.6rem 0 0.15rem;
    cursor: pointer;
}

.import-mode {
    border: 1px solid var(--line);
    border-radius: 8px;
    padding: 0.6rem 0.9rem 0.8rem;
    margin: 0.6rem 0 0.4rem;
    display: grid;
    gap: 0.55rem;
}

.import-mode legend {
    font-weight: 600;
    padding: 0 0.35rem;
}

.import-mode label {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.5rem;
    align-items: baseline;
    cursor: pointer;
    font-size: 0.92rem;
}

.pending-receipts {
    list-style: none;
    padding: 0;
    margin: 0.4rem 0 0;
    display: grid;
    gap: 0.5rem;
}

.pending-receipts li {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    border-bottom: 1px solid var(--line);
    padding-bottom: 0.5rem;
}

.pending-actions {
    display: inline-flex;
    gap: 0.4rem;
}

/* ---- Multi-receipt upload: the "one receipt per image" batch ---- */
.combine-check {
    display: block;
    margin: 0.4rem 0 0.6rem;
    color: var(--ink-soft);
}

.combine-check input {
    margin-right: 0.35rem;
}

.batch-progress {
    list-style: none;
    padding: 0;
    margin: 0.75rem 0 0;
    display: grid;
    gap: 0.4rem;
}

.batch-progress li {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    flex-wrap: wrap;
    border-bottom: 1px solid var(--line);
    padding-bottom: 0.4rem;
}

.batch-progress .batch-name {
    font-weight: 600;
    overflow-wrap: anywhere;
}

.recipe-steps {
    margin-top: 0.45rem;
    font-size: 0.88rem;
}

.recipe-steps summary {
    cursor: pointer;
    color: var(--accent-ink);
    font-weight: 600;
}

.recipe-steps ol {
    margin: 0.4rem 0 0;
    padding-left: 1.3rem;
    display: grid;
    gap: 0.25rem;
}

.recipe-links {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem 0.9rem;
}

/* ---- Recipe read-aloud (Phase 3) ---- */
.read-aloud {
    border: 1px solid var(--accent);
    background: var(--surface-alt);
}

.ra-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
}

.ra-head h3 {
    margin: 0;
}

.ra-steps {
    margin: 0.6rem 0;
    padding-left: 1.4rem;
    display: grid;
    gap: 0.3rem;
}

.ra-steps li {
    padding: 0.15rem 0.4rem;
    border-radius: 6px;
}

.ra-steps li.current {
    background: var(--duesoon-bg);
    color: var(--duesoon-ink);
    font-weight: 600;
}

.ra-controls {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-top: 0.4rem;
}

/* ---- Built-in hands-free reader: what it's doing with the microphone ---- */
.ra-listen {
    margin-top: 0.7rem;
    padding-top: 0.6rem;
    border-top: 1px solid var(--line);
}

.ra-listen p {
    margin: 0.25rem 0;
}

.listen-state {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    font-weight: 600;
}

.listen-dot {
    width: 0.6rem;
    height: 0.6rem;
    border-radius: 50%;
    background: var(--ink-soft);
    flex: none;
}

/* Listening is the only state the cook needs to recognise from across a kitchen, so it's the only one
   that moves. The rest are colour alone — a room full of blinking dots reads as an alarm. */
.listen-listening {
    background: var(--accent);
    animation: listen-pulse 1.4s ease-in-out infinite;
}

.listen-thinking,
.listen-answering {
    background: var(--accent);
}

.listen-held {
    background: var(--duesoon-line);
}

@keyframes listen-pulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50% { opacity: 0.45; transform: scale(1.35); }
}

@media (prefers-reduced-motion: reduce) {
    .listen-listening { animation: none; }
}

/* ---- Listening calibration (Settings) ---- */
.calibrate-stage {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0.6rem 0;
}

.listen-values {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.25rem 0.8rem;
    margin: 0.6rem 0;
}

.listen-values dt {
    color: var(--ink-soft);
}

.listen-values dd {
    margin: 0;
}

/* What the default WOULD have been — a calibrated value you can't tell apart from a guess hasn't
   reported anything. */
.listen-delta {
    color: var(--ink-soft);
    font-size: 0.85em;
    margin-left: 0.4rem;
    white-space: nowrap;
}

@media (max-width: 30rem) {
    .listen-values {
        grid-template-columns: 1fr;
        gap: 0 0.8rem;
    }

    .listen-values dd {
        margin-bottom: 0.4rem;
    }
}

/* ---- Hands-free cook-along (Phase 4b) ---- */
.cookalong {
    border: 1px solid var(--accent);
    background: var(--surface-alt);
}

.cookalong-status {
    font-weight: 600;
    margin: 0.3rem 0;
}

.cookalong-status.listening {
    color: var(--stocked-ink);
}

.cookalong-status.speaking {
    color: var(--accent-ink);
}

.saved-recipes {
    list-style: none;
    padding: 0;
    margin: 0;
    display: grid;
    gap: 0.6rem;
}

.saved-recipes li {
    border-bottom: 1px solid var(--line);
    padding-bottom: 0.5rem;
}

/* An adapted variant, grouped under its original. */
.saved-recipes li.recipe-variant {
    margin-left: 1.25rem;
    padding-left: 0.75rem;
    border-left: 3px solid var(--accent);
    border-bottom: none;
}

.variant-chip {
    background: var(--surface-alt);
    color: var(--accent-ink);
}

/* A parent shown only as context for a matching variant under the ?uses filter — muted, no actions. */
.saved-recipes li.recipe-reference {
    opacity: 0.65;
    padding-bottom: 0.5rem;
    border-bottom: 1px dashed var(--line);
}

.saved-top {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.muted.small {
    font-size: 0.8rem;
}

.saved-top {
    gap: 0.5rem;
    flex-wrap: wrap;
    justify-content: flex-start;
}

.saved-actions {
    display: inline-flex;
    gap: 0.3rem;
    align-items: center;
    margin-left: auto;
}

.picked-banner {
    background: var(--stocked-bg);
    color: var(--stocked-ink);
    border: 1px solid var(--stocked-line);
    border-radius: var(--radius);
    padding: 0.5rem 0.8rem;
}

/* Keep the "Next buy" date + relative on one line (the popover keeps normal wrapping). */
.nextbuy {
    white-space: nowrap;
}

.nextbuy .popover {
    white-space: normal;
}

/* Integrated in-header filter row. */
.filter-row th {
    padding: 0.3rem 0.5rem;
    background: var(--surface-alt);
    font-weight: 400;
    border-bottom: 2px solid var(--line);
}

.filter-row input,
.filter-row select {
    width: 100%;
    box-sizing: border-box;
}

/* Small icon-only button (e.g. delete). */
.icon-btn {
    padding: 0.25rem 0.45rem;
    background: var(--surface);
    color: var(--ink-soft);
    border: 1px solid var(--line);
    line-height: 1;
    font-size: 1rem;
}

.icon-btn:hover:not(:disabled) {
    background: var(--surface-alt);
    border-color: var(--overdue-line);
    color: var(--overdue-ink);
}

/* Buttons that read as inline row-level actions ("Used one") — link-styled, not button-styled.
   The class predates this rule; undefined, these fell back to the full blue button styling. */
.linkish {
    background: none;
    border: none;
    padding: 0;
    margin-left: 0.4rem;
    color: var(--accent-ink);
    cursor: pointer;
    font: inherit;
    font-size: 0.85rem;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.linkish:hover:not(:disabled) {
    color: var(--accent);
}

.linkish:disabled {
    opacity: 0.5;
    cursor: default;
}

/* ---- "Which did you use?" picker (Ate it, DESIGN.md 13.3) ---- */
.picker-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 60;
}

.picker-modal {
    max-width: 26rem;
    width: calc(100% - 2rem);
    max-height: 70vh;
    overflow-y: auto;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
}

/* ---- Grocery List report ---- */
tfoot td {
    border-top: 2px solid var(--line);
    background: var(--surface-alt);
}

.subtotal-label {
    text-align: right;
    color: var(--ink-soft);
}

.subtotal {
    text-align: right;
    font-weight: 700;
}

.grand-total {
    margin: 1rem 0;
    font-size: 1.05rem;
}

/* ---- Accessible detail popover (Products grid) ---- */
.info-btn {
    padding: 0 0.3rem;
    margin-left: 0.25rem;
    background: transparent;
    border: none;
    color: var(--accent);
    cursor: pointer;
    font-size: 0.95rem;
}

.popover {
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 0.75rem 1rem;
    background: var(--surface);
    color: var(--ink);
    box-shadow: var(--shadow);
    max-width: 20rem;
}

.popover dl {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.15rem 0.75rem;
    margin: 0.5rem 0 0;
}

.popover dt {
    color: var(--ink-soft);
}

.popover dd {
    margin: 0;
}

/* ---- Grocery List actions, review date, print ---- */
.list-actions {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    margin: 0.5rem 0 1rem;
}

.review-date {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    flex-wrap: wrap;
}

/* ---- Reports ---- */
.report-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.report-presets {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin: 0.25rem 0 1.25rem;
}

.report-preset-pill {
    padding: 0.4rem 0.9rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--surface);
    color: var(--ink);
    text-decoration: none;
    font-size: 0.9rem;
}

.report-preset-pill:hover { border-color: var(--accent); }

.report-preset-pill.active {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}

.report-section {
    background: var(--surface);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 1rem 1.25rem 1.25rem;
    margin-bottom: 1.25rem;
}

.report-section h2 { margin-top: 0.25rem; }
.report-section h3 { margin-bottom: 0.25rem; }

.report-chart {
    display: block;
    width: 100%;
    height: auto;
    max-width: 860px;
}

.report-chart-tick {
    font-size: 11px;
    fill: var(--ink-soft);
    font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
}

.chart-legend {
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 1rem;
    padding: 0;
    margin: 0 0 0.5rem;
    font-size: 0.85rem;
    color: var(--ink-soft);
}

.chart-legend li {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}

.chart-legend a {
    color: inherit;
    text-decoration-style: dotted;
    text-underline-offset: 2px;
}

.chart-legend a:hover { color: var(--accent); }

/* The Products cleanup chips (Untagged / Other aisle) sit apart from real tags. */
.untagged-chip { border-style: dashed; }

.active-aisle-filter {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    color: var(--ink-soft);
    font-size: 0.9rem;
}

/* A linked chart segment behaves like the link it is. */
.report-chart a { cursor: pointer; }
.report-chart a:hover rect,
.report-chart a:focus-visible rect { opacity: 0.8; }
.report-chart a:focus-visible rect { stroke: var(--accent); stroke-width: 2; }

.chart-legend-swatch {
    width: 12px;
    height: 12px;
    border-radius: 3px;
    flex: none;
    /* Browsers strip HTML background colors when printing (the SVG chart keeps its fills, so a
       printed page had a colored chart beside a legend of blank squares). exact opts the swatch
       out; the hairline border is the grayscale-printer fallback so a square still shows. */
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
    border: 1px solid var(--line);
}

.report-data table { font-size: 0.85rem; }

/* Numeric table cells: right-aligned, digits that line up down the column. */
.num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.rank-bar-col { width: 40%; }

.rank-bar {
    display: inline-block;
    height: 10px;
    border-radius: 3px;
    background: var(--chart-1);
    min-width: 2px;
    vertical-align: middle;
    /* Same printed-background stripping as the legend swatches — a share bar that vanishes on
       paper turns the top-items table into an empty column. */
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
}

.report-empty { padding: 1.25rem; }

.builder-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 0.75rem 1rem;
    margin-bottom: 0.75rem;
}

.builder-grid label {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-size: 0.85rem;
    color: var(--ink-soft);
}

.builder-grid select,
.builder-grid input {
    font: inherit;
    color: var(--ink);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 6px;
    padding: 0.35rem 0.5rem;
}

.builder-grid .builder-check {
    flex-direction: row;
    align-items: center;
    gap: 0.5rem;
    align-self: end;
}

.builder-problems {
    color: var(--overdue-ink);
    background: var(--overdue-bg);
    border-radius: 6px;
    padding: 0.5rem 0.75rem 0.5rem 1.75rem;
    font-size: 0.85rem;
    margin: 0 0 0.75rem;
}

.builder-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.builder-save-name {
    font: inherit;
    color: var(--ink);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 6px;
    padding: 0.35rem 0.5rem;
}

.saved-reports {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin-bottom: 1rem;
}

.saved-report {
    display: inline-flex;
    align-items: center;
    gap: 0.15rem;
}

.saved-report-delete {
    background: none;
    border: none;
    color: var(--ink-soft);
    cursor: pointer;
    padding: 0.15rem 0.3rem;
    border-radius: 4px;
}

.saved-report-delete:hover {
    color: var(--overdue-ink);
    background: var(--overdue-bg);
}

/* The printed page introduces itself; on screen the app chrome already does. */
.report-print-header { display: none; }

@media print {
    .site-header,
    .list-actions,
    details.everything-else,
    .info-btn,
    .untrack-btn,
    .restock-btn,
    .report-presets,
    .report-print-btn,
    .report-builder,
    .voice-agent,
    .tour-panel {
        /* .voice-agent: the assistant is a floating control on every page — screen furniture,
           never document content. Global, not report-only: no printout wants it.
           .tour-panel: same class of thing, and the walkthrough deliberately VISITS the two pages
           built to be printed — Reports and the grocery list — so it would otherwise land in the
           printout of a page it just invited you to print. */
        display: none !important;
    }

    body {
        background: #fff;
        color: #000;
    }

    table {
        box-shadow: none;
    }

    .report-print-header {
        display: flex;
        justify-content: space-between;
        gap: 1rem;
        border-bottom: 1px solid #000;
        padding-bottom: 0.35rem;
        margin-bottom: 1rem;
        font-size: 0.9rem;
    }

    .report-section {
        box-shadow: none;
        padding: 0 0 0.5rem;
        margin-bottom: 1rem;
        break-inside: avoid;
    }

    .report-chart { max-width: 100%; }
}

/* ---- Blazor form validation + error boundary defaults ---- */
.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

/* ---- BYOK provider & keys (Settings) ---- */
.ai-settings {
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
    max-width: 34rem;
}

.ai-settings .field {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.ai-settings .field > span {
    font-weight: 600;
    font-size: 0.9rem;
}

.ai-settings input,
.ai-settings select {
    width: 100%;
    box-sizing: border-box;
}

.ai-settings .model-fields {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.7rem;
}

.ai-settings h3 {
    margin: 0.4rem 0 0;
}

.ai-settings .checkbox-field {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.5rem;
    align-items: start;
    cursor: pointer;
    font-size: 0.92rem;
}

.ai-settings .checkbox-field input {
    width: auto;
    margin-top: 0.2rem;
}

@media (max-width: 560px) {
    .ai-settings .model-fields {
        grid-template-columns: 1fr;
    }
}

/* ---- Accounts: header sign-out + the static Account pages ---- */
.site-header .account-box {
    margin-left: auto;
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
}

.site-header .account-email {
    color: var(--ink-soft);
    text-decoration: none;
    font-size: 0.9rem;
    max-width: 14rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.site-header .account-email:hover {
    color: var(--accent-ink);
}

.account-signout {
    background: none;
    border: none;
    padding: 0;
    color: var(--ink-soft);
    font: inherit;
    font-size: 0.9rem;
    cursor: pointer;
    text-decoration: underline;
}

.account-signout:hover {
    color: var(--accent-ink);
}

.auth-shell {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 3rem 1rem;
    gap: 1.25rem;
}

.auth-brand .brand {
    font-weight: 700;
    font-size: 1.3rem;
    letter-spacing: -0.01em;
}

.auth-card {
    width: 100%;
    max-width: 26rem;
    padding: 1.5rem 1.75rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: var(--shadow);
}

.auth-card h1 {
    margin-top: 0;
    font-size: 1.3rem;
}

.auth-card form {
    display: flex;
    flex-direction: column;
    gap: 0.8rem;
}

.auth-field {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.auth-field label {
    font-weight: 600;
    font-size: 0.9rem;
}

.auth-field input {
    width: 100%;
    box-sizing: border-box;
}

.auth-remember {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.92rem;
}

.auth-household {
    border: 1px dashed var(--line);
    border-radius: var(--radius);
    padding: 0.75rem 1rem 1rem;
    display: flex;
    flex-direction: column;
    gap: 0.8rem;
}

.auth-household legend {
    font-weight: 600;
    font-size: 0.9rem;
    padding: 0 0.35rem;
}

.auth-submit {
    align-self: flex-start;
    padding: 0.5rem 1.1rem;
    border: 1px solid var(--accent);
    border-radius: 6px;
    background: var(--accent);
    color: #fff;
    font-weight: 600;
    cursor: pointer;
}

.auth-submit:hover {
    filter: brightness(1.08);
}

.auth-alt {
    margin-bottom: 0;
}

.auth-external {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--line);
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.auth-external-btn {
    width: 100%;
    padding: 0.5rem 1.1rem;
    border: 1px solid var(--line);
    border-radius: 6px;
    background: var(--surface);
    color: var(--ink);
    font-weight: 600;
    cursor: pointer;
}

.auth-external-btn:hover {
    border-color: var(--accent);
}

.auth-status {
    padding: 0.6rem 0.9rem;
    border-radius: 6px;
    margin: 0.5rem 0 0.9rem;
    font-size: 0.95rem;
}

.auth-status-ok {
    background: color-mix(in srgb, var(--stocked-ink) 12%, transparent);
    color: var(--stocked-ink);
}

.auth-status-error {
    background: color-mix(in srgb, #e50000 10%, transparent);
    color: #c62828;
}

/* ---- Household panel (Settings) ---- */
.invite-row {
    align-items: center;
}

.invite-code {
    font-size: 1.05rem;
    letter-spacing: 0.12em;
    padding: 0.35rem 0.7rem;
    border: 1px dashed var(--line);
    border-radius: 6px;
    background: var(--surface-alt, transparent);
    user-select: all;
}

.member-list {
    margin: 0.25rem 0 0.75rem;
    padding-left: 1.25rem;
}

.member-list li {
    margin: 0.15rem 0;
}
