/* ==========================================================================
   Design tokens — the whole palette, in one place.

   Rules of the road:
     - No literal colour, radius, or shadow below this block. If you need a
       value that isn't here, add a token rather than a one-off; a hex string
       in a rule body is how a palette quietly forks.
     - Greys are ONE ramp (--ink-900 .. --ink-200). Pick the nearest step
       instead of inventing #767676. --text and --text-muted are aliases kept
       because most of the app already says them.
     - Tints come in pairs: a soft --tint-* background and the --on-tint-*
       text colour that stays legible on it. Never mix across pairs.
   ========================================================================== */
:root {
    /* --- Brand ---------------------------------------------------------- */
    --primary: #e85d26;
    --primary-light: #fef0eb;
    --primary-dark: #d0432a;   /* pressed / destructive hover              */
    --on-accent: #fff;         /* text and glyphs sitting on a filled fill */

    /* --- Neutral ramp, darkest to faintest ------------------------------ */
    --ink-900: #1a1a1a;   /* headings, the meal name  */
    --ink-800: #333333;   /* body copy                */
    --ink-700: #444444;   /* secondary button label   */
    --ink-600: #666666;   /* supporting copy          */
    --ink-500: #777777;   /* captions                 */
    --ink-400: #888888;   /* muted / metadata         */
    --ink-300: #999999;   /* labels, eyebrows         */
    --ink-200: #bbbbbb;   /* faint hints              */

    /* Legacy aliases. Both resolve into the ramp above, so there is still
       exactly one definition of each grey. */
    --text: var(--ink-800);
    --text-muted: var(--ink-400);

    /* A second, warmer grey pair for the warm canvas. The neutral ramp above
       goes cold against --canvas, so the recipe screens' timings, footnotes and
       "missing 2" counts sit here instead. Two steps only — resist a third. */
    --ink-warm-300: #a8a29a;   /* timings, footnotes, scope notes  */
    --ink-warm-500: #8d8780;   /* "missing 2", "none on hand"      */

    /* --- Surfaces ------------------------------------------------------- */
    --bg: #fafafa;         /* page                                  */
    --card-bg: #ffffff;    /* cards, modals, popovers               */
    --canvas: #f7f5f3;     /* warm inset canvas (the Today block)   */
    --border: #e0e0e0;      /* default hairline                     */
    --border-light: #f0f0f0; /* lighter rule between list rows      */
    --border-warm: #e0ddd9;  /* hairline on the warm canvas         */
    --border-warm-light: #f4f1ee; /* rule between rows on the warm canvas */
    --border-dashed: #c9c3bb;     /* the "not linked to your pantry" outline */
    --surface-warm: #faf8f6;      /* inset row fill on a warm card    */

    /* --- Status --------------------------------------------------------- */
    --success: #4caf50;
    --warning: #ff9800;
    --danger: #f44336;

    /* --- Tint pairs (chips, badges, tags) -------------------------------
       One declaration per line so a linter — or the var-reference check in
       scripts/check-css-tokens.py — can see every name. */
    --tint-warm: #fff3e0;
    --on-tint-warm: #e65100;
    --tint-green: #e8f5e9;
    --on-tint-green: #2e7d32;
    --tint-red: #ffebee;
    --on-tint-red: #c62828;
    --tint-purple: #f3e5f5;
    --on-tint-purple: #7b1fa2;
    --tint-blue: #e3f2fd;
    --on-tint-blue: #1565c0;

    /* Softer one-off tints that aren't part of a text pair — a background wash
       with the surrounding copy left at its normal colour. */
    --tint-amber: #fff4e5;        /* over-parsed receipt warning         */
    --tint-amber-border: #ffd9a8;
    --tint-amber-soft: #fff8ec;   /* pantry-check row needing a re-check */
    --tint-green-soft: #eef8ef;   /* pantry-check row confirmed          */
    --tint-neutral: #f2f2f2;      /* pantry-check row skipped            */

    /* --- Confidence ------------------------------------------------------
       The vocabulary itself — glyphs, phrasing, which level a stale pantry
       earns — lives in Shared/Confidence.cs; only the colours live here, so a
       palette change stays a one-file change. */
    --confidence-verified: #1f9254;
    --confidence-verified-text: #1f7a45;
    --confidence-verified-soft: #4a8a52;
    --confidence-verified-bg: #f0f7f1;
    --confidence-verified-border: #cfe6d3;
    --confidence-estimated: #d9852b;
    --confidence-estimated-text: #b3691f;

    /* --- Radius scale ----------------------------------------------------
       Steps up with the size of the thing: chips and small buttons at the
       bottom, the hero card at the top. */
    --radius-xs: 4px;
    --radius-sm: 6px;
    --radius-md: 8px;
    --radius-lg: 10px;
    --radius-xl: 12px;
    --radius-2xl: 14px;
    --radius-3xl: 16px;
    --radius-4xl: 18px;
    --radius-5xl: 20px;   /* bottom-sheet top corners */
    --radius-pill: 999px;
    --radius-circle: 50%;

    /* --- Elevation -------------------------------------------------------
       Four heights, by how far the thing floats off the page. Cards barely
       lift; a toast sits above everything. */
    --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.06);
    --shadow-popover: 0 4px 12px rgba(0, 0, 0, 0.12);
    --shadow-menu: 0 8px 24px rgba(0, 0, 0, 0.12);
    --shadow-toast: 0 4px 18px rgba(0, 0, 0, 0.25);
    --focus-ring: 0 0 0 2px var(--primary-light);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

/* ==========================================================================
   Primitives — the handful of shapes the app repeats.

   Each is written once and the existing class names are grouped onto it, so
   markup didn't have to change to get the dedupe. New markup should reach for
   the primitive name (.card, .text-meta) rather than minting another alias.
   ========================================================================== */

/* A bordered content card: the Recipes and Family lists, and anything else
   that's a boxed record rather than a raised surface. Note this is the flat,
   bordered card — the Today screen's hero uses --shadow-card and no border,
   because it's the answer, not a record. */
.card,
.member-card {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 16px;
    margin-bottom: 12px;
}

/* Small muted metadata: a count, a timestamp, an "on hand" note. */
.text-meta,
.quick-snack-qty,
.review-context-detail,
.review-count {
    font-size: 0.8rem;
    color: var(--text-muted);
}

body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    background: var(--bg);
    color: var(--text);
    line-height: 1.5;
}

.app-container {
    max-width: 800px;
    margin: 0 auto;
    padding: 0 16px;
}

.top-bar {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 16px 0;
    border-bottom: 2px solid var(--primary);
    margin-bottom: 24px;
}

.top-bar-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}

.top-bar h1 {
    font-size: 1.4rem;
    color: var(--primary);
}

.top-bar nav {
    display: flex;
    gap: 16px;
}

.top-bar nav a {
    text-decoration: none;
    color: var(--text-muted);
    font-size: 0.9rem;
    font-weight: 500;
}

.top-bar nav a:hover {
    color: var(--primary);
}

/* Count badge on a nav link (e.g. imports waiting for review). */
.nav-badge {
    display: inline-block;
    min-width: 18px;
    margin-left: 4px;
    padding: 0 5px;
    /* Fully rounded: the browser clamps to half the 18px height, which is what
       the old hand-written 9px was spelling out the long way. */
    border-radius: var(--radius-pill);
    background: var(--primary);
    color: var(--on-accent);
    font-size: 0.7rem;
    font-weight: 700;
    line-height: 18px;
    text-align: center;
    vertical-align: middle;
}

/* Hamburger toggle for the nav — only shown on narrow screens (see media query). */
.nav-toggle {
    display: none;
    position: relative;
    align-items: center;
    background: none;
    border: none;
    padding: 4px 8px;
    margin: -4px -8px;
    color: var(--primary);
    cursor: pointer;
    line-height: 1;
}

.nav-toggle-icon {
    font-size: 1.5rem;
}

/* When the badge rides on the collapsed toggle, pin it to the corner of the icon. */
.nav-toggle .nav-badge {
    position: absolute;
    top: -2px;
    right: -4px;
    margin-left: 0;
}

.content {
    padding-bottom: 40px;
}

/* Phones: the eight-link nav row is wider than the screen, so collapse it behind
   the hamburger toggle and drop it open as a full-width column. This is the app's
   first breakpoint — before it the row overflowed and scrolled the whole page. */
@media (max-width: 640px) {
    .nav-toggle {
        display: inline-flex;
    }

    .top-bar nav {
        display: none;
        flex-direction: column;
        gap: 4px;
        width: 100%;
    }

    .top-bar nav.open {
        display: flex;
    }

    .top-bar nav a {
        padding: 10px 4px;
        font-size: 1rem;
        border-bottom: 1px solid var(--border);
    }
}

/* Day View */
.day-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 20px;
}

.day-header h2 {
    font-size: 1.2rem;
}

.day-nav {
    display: flex;
    gap: 8px;
}

/*
    Only style the bare nav buttons (Prev / Today / Next). `.day-nav button`
    outranks single-class rules like `.btn-primary`, so without the :not(.btn)
    guard this repainted action buttons' backgrounds white while `.btn-primary`
    kept the text white — invisible white-on-white buttons (#78).
*/
.day-nav button:not(.btn) {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 6px 14px;
    cursor: pointer;
    font-size: 0.85rem;
}

.day-nav button:not(.btn):hover {
    background: var(--primary-light);
}

/* Meal Cards */
.meal-slot {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 16px;
    margin-bottom: 16px;
}

.meal-slot-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 10px;
}

.meal-type-badge {
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    padding: 3px 10px;
    border-radius: var(--radius-xl);
}

.meal-type-breakfast { background: var(--tint-warm); color: var(--on-tint-warm); }
.meal-type-lunch { background: var(--tint-green); color: var(--on-tint-green); }
.meal-type-dinner { background: var(--tint-blue); color: var(--on-tint-blue); }
.meal-type-snack { background: var(--tint-purple); color: var(--on-tint-purple); }

.meal-name {
    font-size: 1.05rem;
    font-weight: 600;
}

.meal-details {
    font-size: 0.85rem;
    color: var(--text-muted);
    margin-top: 4px;
}

.empty-meal {
    color: var(--text-muted);
    font-style: italic;
    font-size: 0.9rem;
    text-align: center;
    padding: 12px;
}

/* Log Meal Button */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* ~44px min touch target so buttons are comfortable one-handed (#62). */
    min-height: 44px;
    padding: 8px 16px;
    border-radius: var(--radius-sm);
    border: none;
    cursor: pointer;
    font-size: 0.85rem;
    font-weight: 500;
}

.btn:disabled {
    opacity: 0.5;
    cursor: default;
}

.btn-primary {
    background: var(--primary);
    color: white;
}

.btn-primary:hover {
    opacity: 0.9;
}

.btn-outline {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--text);
}

.btn-outline:hover {
    background: var(--primary-light);
}

/* Destructive actions: solid for the confirm dialog, outline for in-context. */
.btn-danger {
    background: var(--danger);
    color: white;
}

.btn-danger:hover {
    opacity: 0.9;
}

.btn-danger-outline {
    background: transparent;
    border: 1px solid var(--danger);
    color: var(--danger);
}

.btn-danger-outline:hover {
    background: var(--tint-red);
}

/* Small buttons stay compact but keep a usable height. */
.btn-sm {
    min-height: 36px;
    padding: 4px 10px;
    font-size: 0.8rem;
}

/* Pantry */
.storage-section {
    margin-bottom: 24px;
}

.storage-section h3 {
    font-size: 1rem;
    margin-bottom: 8px;
    padding-bottom: 4px;
    border-bottom: 1px solid var(--border);
}

/* Collapsible + sticky storage-area header (#76). Pins to the top while its
   section scrolls so the area label stays visible and stays a tap target; the
   count gives an at-a-glance sense of how much is in each area. */
.storage-section-header {
    position: sticky;
    top: 0;
    z-index: 5;
    display: flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    /* Opaque background so list rows scroll cleanly underneath the pinned header. */
    background: var(--bg);
    /* Pull the bottom border in via padding so the sticky band fully covers rows. */
    padding-top: 8px;
    margin-bottom: 8px;
    user-select: none;
}

.storage-section-header:hover .storage-section-title,
.storage-section-header:focus-visible .storage-section-title {
    color: var(--primary);
}

.storage-section-header:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

.storage-section-chevron {
    color: var(--text-muted);
    font-size: 0.9rem;
    line-height: 1;
    transition: transform 0.15s ease;
}

/* Chevron points down when open, right when collapsed. */
.storage-section-header.is-collapsed .storage-section-chevron {
    transform: rotate(-90deg);
}

.storage-section-title {
    flex: 1;
    min-width: 0;
}

/* Pill count badge, like the unread counts in Gmail labels. */
.storage-section-count {
    flex-shrink: 0;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--text-muted);
    background: var(--primary-light);
    border-radius: var(--radius-pill);
    padding: 2px 9px;
    min-width: 24px;
    text-align: center;
}

.pantry-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 8px 0;
    border-bottom: 1px solid var(--border-light);
    font-size: 0.9rem;
}

/* Tappable pantry row (#62/#53): the whole row opens the item detail view, so
   it needs a comfortable height and a clear "tap me" affordance. */
.pantry-row {
    min-height: 52px;
    padding: 10px 6px;
    gap: 12px;
    cursor: pointer;
    border-radius: var(--radius-md);
    transition: background 0.12s ease;
}

.pantry-row:hover,
.pantry-row:focus-visible {
    background: var(--primary-light);
    outline: none;
}

.pantry-row-main {
    flex: 1;
    min-width: 0;
}

/* Small category glyph (e.g. snack 🥨) that sits just before an item's name. */
.category-tag {
    margin-right: 4px;
}

.pantry-row-side {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
}

.pantry-row-chevron {
    color: var(--text-muted);
    font-size: 1.3rem;
    line-height: 1;
}

.pantry-quantity {
    font-weight: 600;
    color: var(--primary);
    min-width: 80px;
    text-align: right;
}

/* Quick-snack modal (#54): a tappable grid of pantry snacks. */
.quick-snack-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: 8px;
    margin-top: 12px;
}

.quick-snack-chip {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    text-align: left;
}

.quick-snack-name {
    font-weight: 600;
}

.quick-snack-debit {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.9rem;
    color: var(--text-muted);
}

.expiring-soon {
    color: var(--warning);
    font-weight: 600;
}

.expired {
    color: var(--danger);
    font-weight: 600;
}

/* Stock ledger deltas (ItemDetail) — credits green, debits red. */
.delta-add {
    color: var(--success);
}

.delta-remove {
    color: var(--danger);
}

/* Friendly empty state (#60) */
.empty-state {
    text-align: center;
    padding: 40px 20px;
    background: var(--card-bg);
    border: 1px dashed var(--border);
    border-radius: var(--radius-xl);
    margin-bottom: 16px;
}

.empty-state-compact {
    padding: 20px 16px;
    background: transparent;
    border: none;
}

.empty-state-icon {
    font-size: 2.4rem;
    line-height: 1;
    margin-bottom: 10px;
}

.empty-state-compact .empty-state-icon {
    font-size: 1.6rem;
    margin-bottom: 6px;
}

.empty-state-title {
    font-size: 1.05rem;
    font-weight: 600;
    margin-bottom: 4px;
}

.empty-state-subtitle {
    font-size: 0.9rem;
    color: var(--text-muted);
    max-width: 420px;
    margin: 0 auto 14px;
}

.empty-state-actions {
    display: flex;
    gap: 10px;
    justify-content: center;
    flex-wrap: wrap;
}

/* Confirm dialog (#61) */
.confirm-modal {
    max-width: 360px;
}

.confirm-message {
    font-size: 0.9rem;
    color: var(--text-muted);
    margin-bottom: 8px;
}

/* Item detail view (#53) */
.detail-stats {
    display: flex;
    gap: 8px;
    margin-bottom: 16px;
}

.detail-stat {
    flex: 1;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 8px 10px;
}

.detail-stat-label {
    display: block;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    margin-bottom: 2px;
}

.detail-stat-value {
    font-size: 0.95rem;
    font-weight: 600;
}

.detail-actions {
    display: flex;
    gap: 8px;
    margin-bottom: 8px;
}

.detail-actions .btn {
    flex: 1;
}

.detail-actions-secondary {
    margin-bottom: 16px;
}

/* Empty-item hint + Used/Restock amount picker (#55) */
.detail-empty-note {
    margin: -4px 0 10px;
}

/* Purchase-rhythm one-liner on the item detail (#118) */
.detail-rhythm {
    margin: -4px 0 14px;
    font-size: 0.85rem;
}

/* "Also measured in" — the permanent home for "1 loaf = 20 oz". A standing row,
   not a prompt: it states what is known (or honestly, what isn't) and offers to
   change it. Never nags, because the fact is only urgent inside a recipe. */
.detail-conversions {
    margin-bottom: 14px;
    padding: 12px 13px;
    border: 1px solid var(--border-light);
    border-radius: var(--radius-lg);
    background: var(--surface-warm);
}

.detail-conversions-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}

.detail-conversions-title {
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--ink-900);
}

.detail-conversions-line {
    margin-top: 3px;
    font-size: 0.8rem;
    color: var(--ink-warm-300);
}

.detail-conversions-line.is-set { color: var(--confidence-verified-text); }
.detail-conversions-by { color: var(--ink-warm-300); }

.detail-conversions-form {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 10px;
}

.detail-conversions-form input { width: 84px; }
.detail-conversions-unit { width: 72px; }

.detail-conversions-existing {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-top: 8px;
    font-size: 0.8rem;
    color: var(--confidence-verified-text);
}

/* "Probably running low" section on the Pantry page (#118): a rhythm-driven
   companion to the balance-driven low-stock badges.

   The tint this comment promised never shipped. The rule here was
   `color: var(--accent, inherit)` and --accent was never defined, so it always
   resolved to `inherit` — and since .storage-section-header sets no colour of
   its own, that was identical to having no rule at all. Removed rather than
   left looking like working code. If the nudge should actually read warmer,
   that's `color: var(--primary)` and a deliberate design call, not a
   find-and-replace. */

.amount-picker {
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 10px;
    margin-bottom: 16px;
    background: var(--bg);
}

.amount-picker-label {
    display: block;
    font-size: 0.85rem;
    font-weight: 600;
    margin-bottom: 8px;
}

.amount-picker-quick {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 8px;
}

.amount-picker-custom {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.amount-picker-custom input {
    width: 90px;
}

.amount-picker-unit {
    color: var(--text-muted);
    font-size: 0.85rem;
}

.detail-ledger-heading {
    font-size: 0.95rem;
    margin-bottom: 8px;
    padding-bottom: 4px;
    border-bottom: 1px solid var(--border);
}

.detail-ledger-summary {
    margin-bottom: 10px;
}

/* The ledger as a running debit/credit statement. */
.ledger {
    max-height: 320px;
    overflow-y: auto;
}

.ledger-row {
    display: grid;
    grid-template-columns: 1fr auto auto;
    gap: 10px;
    align-items: start;
    padding: 8px 0;
    border-bottom: 1px solid var(--border-light);
    font-size: 0.9rem;
}

.ledger-head {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    font-weight: 600;
}

.ledger-balance {
    font-weight: 600;
    text-align: right;
    white-space: nowrap;
}

.ledger-date {
    color: var(--text-muted);
    text-align: right;
    white-space: nowrap;
}

/* "Needs attention" dashboard panel (Day view) */
.attention-panel {
    background: var(--primary-light);
    border: 1px solid var(--primary);
    border-radius: var(--radius-lg);
    padding: 12px 16px;
    margin-bottom: 20px;
}

.attention-panel h3 {
    font-size: 0.95rem;
    color: var(--primary);
    margin-bottom: 8px;
}

.attention-group {
    margin-bottom: 8px;
}

.attention-group:last-child {
    margin-bottom: 0;
}

.attention-label {
    display: block;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    margin-bottom: 2px;
}

.attention-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 5px 0;
    text-decoration: none;
    color: var(--text);
    font-size: 0.9rem;
    border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}

.attention-item:last-child {
    border-bottom: none;
}

/* Toast / snackbar (app-wide, with optional Undo) */
.toast {
    position: fixed;
    left: 50%;
    bottom: 24px;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: 14px;
    max-width: 90vw;
    background: var(--text);
    color: var(--on-accent);
    padding: 11px 16px;
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-toast);
    font-size: 0.9rem;
    z-index: 1000;
}

.toast-msg {
    flex: 1;
}

.toast-undo {
    border: none;
    color: var(--primary);
    background: var(--card-bg);
    padding: 4px 10px;
    border-radius: var(--radius-sm);
    font-weight: 700;
    font-size: 0.85rem;
    cursor: pointer;
}

.toast-close {
    background: transparent;
    border: none;
    color: var(--on-accent);
    opacity: 0.6;
    cursor: pointer;
    font-size: 0.85rem;
    line-height: 1;
}

.toast-close:hover {
    opacity: 1;
}

/* Family */
.member-name {
    font-weight: 600;
    font-size: 1rem;
}

.member-info {
    font-size: 0.85rem;
    color: var(--text-muted);
}

.tag {
    display: inline-block;
    padding: 2px 8px;
    border-radius: var(--radius-lg);
    font-size: 0.75rem;
    margin: 2px 2px;
}

.tag-like { background: var(--tint-green); color: var(--on-tint-green); }
.tag-dislike { background: var(--tint-red); color: var(--on-tint-red); }
.tag-restriction { background: var(--tint-warm); color: var(--on-tint-warm); }

/* Week View */
.week-grid {
    display: grid;
    gap: 12px;
}

.week-day {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 12px;
}

.week-day-name {
    font-weight: 600;
    font-size: 0.9rem;
    margin-bottom: 6px;
    color: var(--primary);
}

.week-day-today {
    border-color: var(--primary);
    border-width: 2px;
}

.week-meal-item {
    font-size: 0.85rem;
    padding: 2px 0;
}

/* A planned meal can be dragged onto another day to move it. */
.draggable-plan {
    cursor: move;
}

/* Every day cell hints it can receive a dragged plan while a drag is in flight. */
.week-day.drop-target {
    outline: 2px dashed var(--primary);
    outline-offset: 2px;
}

/* Week/Month segmented toggle in the day-nav. */
.view-toggle {
    display: inline-flex;
}

.view-toggle button.active {
    background: var(--primary);
    color: var(--on-accent);
    border-color: var(--primary);
}


/* Log Meal Modal */
.modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.4);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 100;
}

.modal {
    background: white;
    border-radius: var(--radius-xl);
    padding: 24px;
    width: 90%;
    max-width: 400px;
    /* Never grow taller than the viewport — scroll the modal as a safety net
       so content (and the action buttons) can't be clipped off-screen (#75). */
    max-height: 90vh;
    overflow-y: auto;
}

.modal h3 {
    margin-bottom: 16px;
}

.form-group {
    margin-bottom: 12px;
}

.form-group label {
    display: block;
    font-size: 0.85rem;
    font-weight: 500;
    margin-bottom: 4px;
}

.form-group select,
.form-group input,
.form-group textarea {
    width: 100%;
    padding: 8px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-size: 0.9rem;
}

/* Instructions is a multi-line field: inherit the UI font (textareas default to
   monospace) and let it grow vertically but not blow out the modal width. */
.form-group textarea {
    font-family: inherit;
    resize: vertical;
    min-height: 68px;
}

/* Required-field marker next to a label. */
.label-req {
    color: var(--danger);
    font-weight: 700;
}

/* Checkboxes/radios are affordances, not text fields: the full-width + padding
   above stretches an inline checkbox so it shoves adjacent labels/buttons into
   each other (e.g. the "to taste" toggle overlapping the remove ✕ on recipe
   ingredient rows). Keep them at their natural size. */
.form-group input[type="checkbox"],
.form-group input[type="radio"] {
    width: auto;
    padding: 0;
    accent-color: var(--primary);
}

.modal-actions {
    display: flex;
    gap: 8px;
    justify-content: flex-end;
    margin-top: 16px;
}

/* Review-before-save modal (#28/#29): a long list of proposed items used to
   push the header and action buttons off-screen (#75). Lay the modal out as a
   flex column so the header and intro stay pinned at the top, the item list
   scrolls in the middle, and the action buttons stay anchored at the bottom. */
.review-modal {
    max-width: 640px;
    display: flex;
    flex-direction: column;
    /* Inner list owns the scrolling, so the modal itself doesn't scroll too. */
    overflow: hidden;
}

.review-modal > h3,
.review-modal > .review-intro,
.review-modal > .review-context,
.review-modal > .review-warning,
.review-modal > .review-toolbar {
    flex-shrink: 0;
}

/* Order context shown above the list so the user can sanity-check the source (#78). */
.review-context {
    display: flex;
    flex-direction: column;
    gap: 2px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 8px 10px;
    margin-bottom: 8px;
}

.review-context-title {
    font-weight: 600;
    font-size: 0.9rem;
}

/* Heads-up for an unusually large (likely over-parsed) receipt (#78). */
.review-warning {
    background: var(--tint-amber);
    border: 1px solid var(--tint-amber-border);
    border-radius: var(--radius-md);
    padding: 8px 10px;
    margin-bottom: 8px;
}

/* Select-all / count row between the intro and the scrolling list. */
.review-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 4px;
}

/* Current balance + projected result on a matched review row (#82). */
.review-onhand {
    display: block;
    font-size: 0.8rem;
    color: var(--primary);
    font-weight: 500;
}

.review-list {
    flex: 1 1 auto;
    overflow-y: auto;
    /* Bleed to the modal edges so the scrollbar hugs the side; padding keeps
       focus outlines from being clipped. */
    margin: 4px -8px;
    padding: 4px 8px;
}

.review-modal .modal-actions {
    flex-shrink: 0;
    padding-top: 12px;
    border-top: 1px solid var(--border);
}

/* Blazor unhandled-error bar: hidden until Blazor's JS reveals it on a real
   error. Without this rule the div renders as a normal block and shows
   permanently. */
#blazor-error-ui {
    color-scheme: light only;
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

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

/* --- Global search (#52) --------------------------------------------------- */
.global-search {
    position: relative;
    display: flex;
    align-items: center;
    width: 100%;
    /* Above its own click-away catcher (.gs-backdrop, 90) so the input stays usable
       while the results panel is open — but BELOW the modal/sheet layer (100), or the
       search box floats over every dialog in the app and clips its heading. */
    z-index: 91;
}

.global-search-icon {
    position: absolute;
    left: 12px;
    font-size: 0.9rem;
    pointer-events: none;
    opacity: 0.6;
}

.global-search-input {
    width: 100%;
    padding: 9px 12px 9px 34px;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--card-bg);
    font-size: 0.95rem;
    color: var(--text);
}

.global-search-input:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: var(--focus-ring);
}

/* Click-away catcher behind the results panel. */
.gs-backdrop {
    position: fixed;
    inset: 0;
    z-index: 90;
    background: transparent;
}

.global-search-panel {
    position: absolute;
    /* Same layer as the input it belongs to — see .global-search. */
    z-index: 91;
    left: 0;
    right: 0;
    top: calc(100% + 6px);
    max-height: 60vh;
    overflow-y: auto;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-menu);
    padding: 6px;
}

.gs-status {
    padding: 14px 12px;
    color: var(--text-muted);
    font-size: 0.9rem;
}

.gs-group-label {
    padding: 8px 10px 4px;
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.gs-row {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    text-align: left;
    background: transparent;
    border: none;
    border-radius: var(--radius-md);
    padding: 8px 10px;
    cursor: pointer;
    color: var(--text);
}

.gs-row:hover,
.gs-row:focus {
    background: var(--primary-light);
    outline: none;
}

.gs-row-icon {
    font-size: 1.05rem;
    flex-shrink: 0;
}

.gs-row-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.gs-row-title {
    font-size: 0.92rem;
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.gs-row-sub {
    font-size: 0.78rem;
    color: var(--text-muted);
}

/* Brief pulse on an item deep-linked to from search. */
.search-highlight {
    animation: search-pulse 2s ease-out 1;
}

@keyframes search-pulse {
    0% { box-shadow: 0 0 0 3px var(--primary); }
    100% { box-shadow: 0 0 0 3px rgba(232, 93, 38, 0); }
}

/* Blazor WASM boot spinner shown in #app while the app loads. Without these
   rules the SVG circles fall back to a solid black fill — the "black blob"
   that used to flash before the app rendered. Styled as a thin progress ring
   in the app's accent color, driven by --blazor-load-percentage. */
.loading-progress {
    position: relative;
    display: block;
    width: 8rem;
    height: 8rem;
    margin: 20vh auto 1rem auto;
}

.loading-progress circle {
    fill: none;
    stroke: var(--primary-light);
    stroke-width: 0.5rem;
    transform-origin: 50% 50%;
    transform: rotate(-90deg);
}

.loading-progress circle:last-child {
    stroke: var(--primary);
    stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
    transition: stroke-dasharray 0.05s ease-in-out;
}

.loading-progress-text {
    position: absolute;
    inset: calc(20vh + 3.3rem) 0 auto 0.2rem;
    text-align: center;
    font-weight: bold;
    color: var(--text);
}

.loading-progress-text:after {
    content: var(--blazor-load-percentage-text, "Loading");
}

/* --- Pantry typeahead (#105, shared with #100) --------------------------- */
.typeahead {
    position: relative;
}

.typeahead-input {
    width: 100%;
}

.typeahead-menu {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 20;
    margin: 2px 0 0;
    padding: 0;
    list-style: none;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-popover);
    max-height: 240px;
    overflow-y: auto;
}

.typeahead-empty {
    padding: 8px 10px;
    font-size: 0.85rem;
    color: var(--text-muted);
}

.typeahead-option {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 10px;
    width: 100%;
    padding: 8px 10px;
    border: none;
    background: none;
    text-align: left;
    cursor: pointer;
    font-size: 0.9rem;
    color: var(--text);
}

.typeahead-option:hover {
    background: var(--primary-light);
}

.typeahead-option-stock {
    font-size: 0.8rem;
    color: var(--text-muted);
    white-space: nowrap;
}

/* The on-hand amount is the reason this picker is good, so it keeps its hedge:
   green when a pantry check counted it, amber when the balance is derived. The
   colours are the shared --confidence-* ones, not picker-local greens. */
.typeahead-option-stock.verified { color: var(--confidence-verified); }
.typeahead-option-stock.estimated { color: var(--confidence-estimated-text); }

/* Non-food items stay reachable, one row down, in muted text. They're real
   pantry items with real stock — they just aren't ingredients by default. */
.typeahead-disclosure {
    display: block;
    width: 100%;
    padding: 7px 10px;
    border: none;
    background: var(--canvas);
    text-align: left;
    cursor: pointer;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--ink-400);
}

.typeahead-option.is-nonfood { color: var(--ink-500); }

/* Triage in passing: one tap retires an item from ingredient search. No settings
   screen, no bulk cleanup chore — it sits on the row where you noticed it. */
.typeahead-notfood {
    display: block;
    margin: -4px 10px 6px auto;
    padding: 2px 8px;
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    background: none;
    cursor: pointer;
    font-size: 0.7rem;
    color: var(--ink-400);
}

.typeahead-notfood:hover {
    border-color: var(--primary);
    color: var(--primary);
}

/* A grouped block per ingredient, so the controls never fight for one cramped
   line. The recipe editor has its own full-screen treatment (.rxe-row); this is
   the custom-meal row on the Day screen. */
.ingredient-row {
    display: flex;
    flex-direction: column;
    gap: 8px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 10px;
    margin-bottom: 8px;
}

/* Spending: by-item / store analytics + budgets (#87) --------------------- */

/* Thin separator between the Day/Week/Month buttons and the By order / By item
   toggle in the Spending header. */
.day-nav-divider {
    align-self: stretch;
    width: 1px;
    margin: 0 4px;
    background: var(--border);
}

/* Store-filter chip row. Reuses .btn styling; just lays the chips out and lets
   them wrap on a narrow phone. */
.store-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 12px;
}

/* Drill-down inside an expanded "By item" group: a small heading over the
   price-by-store rows and the underlying purchases, each row nudged in so the
   nesting reads. */
.item-drill-heading {
    margin-top: 8px;
    font-weight: 600;
}

.item-drill-row {
    padding-left: 12px;
}

/* Budget set/clear rows. */
.budget-edit {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-top: 8px;
}

.budget-edit-row {
    display: flex;
    gap: 6px;
    align-items: center;
    flex-wrap: wrap;
}

.budget-edit-label {
    width: 56px;
}

/* Budget vs. actual progress (BudgetProgressCard). */
.budget-progress {
    margin-top: 10px;
}

.budget-progress-label {
    display: flex;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 4px;
}

.budget-bar {
    height: 8px;
    border-radius: var(--radius-xs);
    background: var(--border);
    overflow: hidden;
}

.budget-bar-fill {
    height: 100%;
    border-radius: var(--radius-xs);
    transition: width 0.2s ease;
}

/* Warms green → amber → red as spend approaches and passes the line. */
.budget-bar-ok { background: var(--success); }
.budget-bar-warn { background: var(--warning); }
.budget-bar-over { background: var(--danger); }

/* Pantry check (#106): compact reconciliation rows. Each row keeps a pending
   verdict in page state; nothing saves until the batch "Save reconciliation". */
.check-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 10px;
}

/* Review-state chips (All / Unreviewed / Changed / Confirmed / Skipped). */
.check-filter {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 14px;
}

.check-filter-chip {
    border: 1px solid var(--border);
    background: var(--card-bg);
    border-radius: var(--radius-pill);
    padding: 4px 12px;
    font-size: 0.8rem;
    cursor: pointer;
    color: var(--text);
}

.check-filter-chip.is-active {
    background: var(--primary);
    border-color: var(--primary);
    color: white;
}

.check-filter-count {
    font-weight: 600;
    opacity: 0.75;
    margin-left: 2px;
}

/* One item row: name/hints, current → verified count, quick actions. Wraps on
   narrow phones so the count controls drop under the name instead of clipping. */
.check-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px 12px;
    padding: 8px 6px;
    border-bottom: 1px solid var(--border-light);
    border-left: 3px solid transparent;
    border-radius: var(--radius-xs);
    font-size: 0.9rem;
}

/* Pending-state tint on the row edge: green = confirmed, primary = changed,
   muted = skipped, warning = the server said re-check this one. */
.check-row.is-confirmed { border-left-color: var(--success); }
.check-row.is-changed { border-left-color: var(--primary); }
.check-row.is-skipped { border-left-color: var(--border); opacity: 0.65; }
.check-row.has-conflict { border-left-color: var(--warning); background: var(--tint-amber-soft); }

.check-row-main {
    flex: 1 1 170px;
    min-width: 0;
}

.check-row-name {
    font-weight: 600;
    display: block;
}

/* ≈ badge: this item's recent history is estimate-heavy, so its balance is shaky. */
.check-estimate {
    color: var(--warning);
    font-weight: 700;
    margin-left: 4px;
    cursor: help;
}

.check-row-hint {
    display: block;
}

.check-conflict {
    display: block;
    color: var(--warning);
    font-size: 0.8rem;
    font-weight: 600;
}

.check-row-count {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
}

.check-onhand {
    color: var(--text-muted);
    min-width: 64px;
    text-align: right;
}

.check-arrow {
    color: var(--text-muted);
}

.check-input {
    width: 72px;
    min-height: 36px;
    padding: 4px 6px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    text-align: right;
    font-size: 0.9rem;
}

/* +/- steppers: square-ish, easy thumb targets. */
.check-step {
    min-width: 36px;
    padding: 4px 0;
    font-weight: 700;
}

.check-row-actions {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    margin-left: auto;
}

/* Pending-verdict chip shown once a row is reviewed. */
.check-status {
    font-size: 0.8rem;
    font-weight: 600;
    border-radius: var(--radius-pill);
    padding: 3px 10px;
}

.check-status.confirmed { color: var(--success); background: var(--tint-green-soft); }
.check-status.changed { color: var(--primary); background: var(--primary-light); }
.check-status.skipped { color: var(--text-muted); background: var(--tint-neutral); }

/* The single batch action, pinned so it's reachable mid-list on a long kitchen. */
.check-savebar {
    position: sticky;
    bottom: 0;
    z-index: 6;
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px;
    padding: 10px 6px;
    background: var(--bg);
    border-top: 1px solid var(--border);
}

/* Month calendar (read-only, print-friendly) — a 7-column grid, one cell per day. */
.month-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 6px;
}

.month-dow {
    font-weight: 600;
    font-size: 0.75rem;
    color: var(--ink-600);
    text-align: center;
    padding: 2px 0;
}

.month-cell {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 6px;
    min-height: 84px;
    font-size: 0.78rem;
}

.month-cell-empty {
    background: transparent;
    border: none;
}

.month-cell-today {
    border-color: var(--primary);
    border-width: 2px;
}

.month-cell-date {
    font-weight: 600;
    font-size: 0.8rem;
    margin-bottom: 4px;
    color: var(--primary);
}

.month-cell-item {
    display: flex;
    gap: 4px;
    align-items: baseline;
    padding: 1px 0;
    line-height: 1.2;
}

/* Print: strip the app chrome so Shopping and the Month calendar come out as clean
   paper. Anything tagged .no-print (nav, search, per-item action buttons) drops out. */
@media print {
    .top-bar,
    .day-nav,
    .nav-toggle,
    .toast-host,
    .no-print {
        display: none !important;
    }

    body,
    .app-container,
    .content {
        background: var(--card-bg) !important;
        margin: 0;
        padding: 0;
    }

    a { color: #000; text-decoration: none; }

    .meal-slot,
    .week-day,
    .month-cell {
        break-inside: avoid;
    }
}

/* ==========================================================================
   Today screen — "what's for dinner tonight"

   The answer-first block at the top of the Day page. Its job is to answer
   "what's for dinner, do we have it, can we afford it" in the first three
   seconds, in a register of relief rather than achievement — so: one answer,
   one primary action, evidence instead of chores, and the budget as a single
   quiet line (the progress bar lives on Spending, where a decision is being
   made).

   Geometry here is from the design handoff and is deliberate — the type
   scale steps in half-pixels (12.5 / 13.5) and the radii step per element
   size (10 small buttons -> 18 hero). The one substitution: the handoff's
   orange #E85A2A renders as the app's existing --primary (#e85d26), because
   an app-wide token beats a near-duplicate nobody can tell apart.
   ========================================================================== */

/* Designed at 380px and capped at 480px on desktop: the fix for
   "desktop mirrors mobile" is a narrower column, not more columns. The
   negative margin cancels .app-container's padding so the canvas reaches
   the screen edge on a phone and content still sits 16px in. */
.tonight {
    display: flex;
    flex-direction: column;
    gap: 14px;
    max-width: 480px;
    margin: 0 auto 24px;
    padding: 16px;
    background: var(--canvas);
}

@media (max-width: 640px) {
    .tonight {
        margin-left: -16px;
        margin-right: -16px;
    }
}

.tonight-date {
    font-size: 20px;
    font-weight: 800;
    color: var(--ink-900);
    line-height: 1.25;
}

.tonight-status {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--ink-300);
}

/* "Today" only earns the accent colour when there is actually a plan to point at. */
.tonight-status.is-planned {
    font-weight: 700;
    color: var(--primary);
}

.tonight-hero {
    background: var(--card-bg);
    border-radius: var(--radius-4xl);
    padding: 18px;
    box-shadow: var(--shadow-card);
}

.tonight-eyebrow {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--ink-300);
}

.tonight-meal {
    font-size: 25px;
    font-weight: 800;
    color: var(--ink-900);
    margin-top: 4px;
    line-height: 1.15;
    text-wrap: pretty;
}

/* Why this meal — always recall ("you haven't had this in 18 days"), never
   discovery. Families rotate through the same 15-20 meals and that is fine. */
.tonight-reason {
    font-size: 13.5px;
    color: var(--ink-600);
    margin-top: 6px;
    text-wrap: pretty;
}

.tonight-confidence {
    display: flex;
    align-items: center;
    gap: 7px;
    margin-top: 11px;
}

.tonight-dot {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    border-radius: var(--radius-circle);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--on-accent);
    font-size: 11px;
    font-weight: 800;
    line-height: 1;
}

.tonight-confidence-label {
    font-size: 14px;
    font-weight: 600;
}

.tonight-dot.verified { background: var(--confidence-verified); }
.tonight-dot.estimated,
.tonight-dot.unknown {
    background: var(--confidence-estimated);
    font-size: 12px;
}

.tonight-confidence-label.verified { color: var(--confidence-verified); }
.tonight-confidence-label.estimated,
.tonight-confidence-label.unknown { color: var(--confidence-estimated-text); }

/* A firm one-liner: "No extra cost." Never "$0.00" — a zero reads as missing data. */
.tonight-cost {
    font-size: 13.5px;
    color: var(--ink-400);
    margin-top: 5px;
}

/* The hedge: names *what* is unsure and *why*, rather than shrugging at the
   whole meal. Multi-line, so it gets the looser leading. */
.tonight-detail {
    font-size: 12.5px;
    color: var(--ink-300);
    margin-top: 6px;
    line-height: 1.45;
    text-wrap: pretty;
}

/* Stated exactly once, for a kitchen the app has never seen. */
.tonight-hedge {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--confidence-estimated-text);
    margin-top: 9px;
    line-height: 1.45;
    text-wrap: pretty;
}

.tonight-actions {
    display: flex;
    gap: 8px;
    margin-top: 16px;
}

.tonight-btn {
    flex: 1;
    min-height: 44px;
    border-radius: var(--radius-xl);
    font-size: 15px;
    font-weight: 700;
    font-family: inherit;
    cursor: pointer;
    text-align: center;
}

.tonight-btn:disabled {
    opacity: 0.55;
    cursor: default;
}

/* The primary action is wider on purpose: at 5:40pm the common case is one tap. */
.tonight-btn-primary {
    flex: 1.3;
    background: var(--primary);
    border: none;
    color: var(--on-accent);
    padding: 13px 0;
}

.tonight-btn-secondary {
    background: var(--card-bg);
    border: 1.5px solid var(--border-warm);
    color: var(--ink-700);
    padding: 11.5px 0;
}

/* Escape hatch: when the planned meal is a guess, hand over a *certain*
   alternative rather than a price range. A range is unactionable at 5:40pm. */
.tonight-escape {
    background: var(--confidence-verified-bg);
    border: 1.5px solid var(--confidence-verified-border);
    border-radius: var(--radius-3xl);
    padding: 14px 16px;
}

.tonight-escape-body {
    font-size: 14px;
    color: var(--ink-900);
    line-height: 1.45;
    text-wrap: pretty;
}

.tonight-escape-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-top: 10px;
}

.tonight-escape-note {
    font-size: 12px;
    font-weight: 600;
    color: var(--confidence-verified-soft);
}

.tonight-switch {
    flex-shrink: 0;
    background: transparent;
    font-size: 13px;
    font-weight: 700;
    font-family: inherit;
    color: var(--confidence-verified-text);
    border: 1.5px solid var(--confidence-verified);
    padding: 7px 12px;
    border-radius: var(--radius-lg);
    cursor: pointer;
}

.tonight-alternates {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.tonight-alternate {
    background: var(--card-bg);
    border-radius: var(--radius-2xl);
    padding: 13px 15px;
    box-shadow: var(--shadow-card);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}

.tonight-alternate-name {
    font-size: 15px;
    font-weight: 700;
    color: var(--ink-900);
}

.tonight-alternate-reason {
    font-size: 12px;
    color: var(--ink-500);
    margin-top: 3px;
    text-wrap: pretty;
}

.tonight-alternate-stock {
    font-size: 12px;
    font-weight: 600;
    margin-top: 3px;
}

.tonight-alternate-stock.verified { color: var(--confidence-verified-soft); }
.tonight-alternate-stock.estimated { color: var(--confidence-estimated-text); }

.tonight-pick {
    flex-shrink: 0;
    background: transparent;
    font-size: 13px;
    font-weight: 700;
    font-family: inherit;
    color: var(--primary);
    border: 1.5px solid var(--primary);
    padding: 7px 11px;
    border-radius: var(--radius-lg);
    cursor: pointer;
}

/* Already-eaten meals are evidence, not checklist chores — one line, no rows,
   no checkboxes, no per-row actions. */
.tonight-evidence {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 0 4px;
}

.tonight-evidence-label {
    font-size: 12.5px;
    color: var(--ink-300);
}

.tonight-evidence-meals {
    font-size: 13px;
    color: var(--ink-600);
    text-wrap: pretty;
}

/* One quiet line. It escalates only when there is a decision to make. */
.tonight-budget {
    font-size: 12.5px;
    color: var(--ink-300);
    padding: 0 4px;
}

.tonight-budget.is-tight {
    font-weight: 600;
    color: var(--confidence-estimated-text);
}

/* Setup is invited, never required before value. */
.tonight-footer {
    font-size: 12.5px;
    color: var(--ink-300);
    line-height: 1.5;
    padding: 0 4px;
    text-wrap: pretty;
}

.tonight-footer a {
    color: inherit;
    text-decoration: underline;
}

.tonight-empty {
    font-size: 13.5px;
    color: var(--ink-400);
    padding: 0 4px;
}

/* ==========================================================================
   Recipes — browse list, detail view, binding (#137)

   The list that replaced the expanded-card list. Every card used to render
   every ingredient chip plus the whole method, so Beef Burgers took half a
   screen and you saw two recipes; these cards are exactly two lines and eight
   fit. Everything that left the card (chips, method, meal badges, the Cooked
   button) lives in the detail view or, in the Cooked button's case, nowhere in
   this section at all — cooking is confirmed from the plan and from Today.

   The palette is the warm one: --canvas underneath, --ink-warm-* for timings
   and footnotes, and the shared --confidence-* colours for anything claiming
   availability. There is no red on this screen: being short two things is
   information, not a failure.
   ========================================================================== */

/* --- Header + controls --------------------------------------------------- */
.rx-header {
    position: sticky;
    top: 0;
    z-index: 10;
    display: flex;
    flex-direction: column;
    gap: 7px;
    padding: 9px 0 6px;
    background: var(--canvas);
}

.rx-title-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}

.rx-title {
    font-size: 18px;
    font-weight: 800;
    color: var(--ink-900);
}

.rx-add {
    border: none;
    background: none;
    padding: 0;
    cursor: pointer;
    font-size: 13px;
    font-weight: 700;
    color: var(--primary);
}

.rx-search {
    width: 100%;
    padding: 7px 11px;
    border: 1px solid var(--border-warm);
    border-radius: var(--radius-lg);
    background: var(--card-bg);
    font-size: 14.5px;
    color: var(--text);
}

.rx-search:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: var(--focus-ring);
}

.rx-filter-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}

/* The app's real advantage gets the one prominent control on the screen. */
.rx-ready {
    flex: 0 0 auto;
    padding: 6px 11px;
    border: 1.5px solid var(--border-warm);
    border-radius: var(--radius-pill);
    background: var(--card-bg);
    cursor: pointer;
    font-size: 12px;
    font-weight: 700;
    color: var(--ink-600);
    white-space: nowrap;
}

.rx-ready.is-on {
    background: var(--primary);
    border-color: var(--primary);
    color: var(--on-accent);
}

/* Meal types are not a second row of pills: one text button that states the
   active lens and opens the sheet. */
.rx-lens {
    flex: 1 1 auto;
    min-width: 0;
    border: none;
    background: none;
    padding: 0;
    cursor: pointer;
    text-align: right;
    font-size: 12px;
    font-weight: 700;
    color: var(--ink-600);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.rx-lens-caret { color: var(--ink-warm-300); }

/* The availability rule, said once per screen — never as a caveat on 29 cards. */
.rx-scope-note {
    font-size: 11px;
    line-height: 1.4;
    color: var(--ink-warm-300);
    text-wrap: pretty;
}

/* --- Browse cards -------------------------------------------------------- */
.rx-list {
    display: flex;
    flex-direction: column;
    gap: 7px;
    margin-top: 4px;
}

.rx-card-row {
    display: block;
    width: 100%;
    padding: 11px 13px;
    border: none;
    border-radius: var(--radius-xl);
    background: var(--card-bg);
    box-shadow: var(--shadow-card);
    text-align: left;
    cursor: pointer;
}

.rx-row-top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
}

.rx-row-name {
    font-size: 15.5px;
    font-weight: 700;
    color: var(--ink-900);
}

.rx-row-minutes {
    flex: 0 0 auto;
    font-size: 11.5px;
    color: var(--ink-warm-300);
}

/* Recall · availability — the same sentence the Today screen speaks, and the
   same sentence the detail view opens with. */
.rx-row-line {
    margin-top: 4px;
    font-size: 13px;
    line-height: 1.4;
    color: var(--ink-600);
}

.rx-avail { font-weight: 600; }
.rx-avail.have { color: var(--confidence-verified); }
.rx-avail.probably { color: var(--confidence-estimated-text); }
/* No alarm colour: short two things is information, not a failure. */
.rx-avail.missing { color: var(--ink-warm-500); font-weight: 500; }
/* "Can't tell yet" borrows the estimated amber: it is genuine uncertainty, which
   is the one job amber is reserved for on these screens. */
.rx-avail.cant-tell { color: var(--confidence-estimated-text); }

.rx-count {
    margin: 12px 0 4px;
    font-size: 12px;
    color: var(--ink-300);
}

/* --- Detail -------------------------------------------------------------- */
.rx-back {
    position: sticky;
    top: 0;
    z-index: 10;
    padding: 11px 0;
    background: var(--canvas);
}

.rx-back-link {
    border: none;
    background: none;
    padding: 0;
    cursor: pointer;
    font-size: 13px;
    font-weight: 700;
    color: var(--primary);
}

.rx-detail-title {
    font-size: 22px;
    font-weight: 800;
    line-height: 1.25;
    color: var(--ink-900);
}

.rx-detail-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 10px;
    margin: 4px 0 12px;
    font-size: 12px;
    color: var(--ink-300);
}

/* The answer card. Its first line is the card's line verbatim, so opening a
   recipe reads as a continuation and nothing has to be re-read. */
.rx-answer,
.rx-card {
    background: var(--card-bg);
    border-radius: var(--radius-3xl);
    padding: 15px 16px;
    margin-bottom: 12px;
    box-shadow: var(--shadow-card);
}

.rx-answer-line {
    font-size: 14.5px;
    color: var(--ink-600);
}

.rx-answer-basis {
    margin-top: 5px;
    font-size: 12px;
    line-height: 1.45;
    color: var(--ink-300);
    text-wrap: pretty;
}

.rx-answer-actions {
    display: flex;
    gap: 10px;
    margin-top: 14px;
}

.rx-btn {
    padding: 12px;
    border-radius: var(--radius-xl);
    border: 1.5px solid transparent;
    cursor: pointer;
    font-size: 14.5px;
    font-weight: 700;
}

.rx-btn-primary {
    flex: 1.5;
    background: var(--primary);
    border-color: var(--primary);
    color: var(--on-accent);
}

.rx-btn-secondary {
    flex: 1;
    background: var(--card-bg);
    border-color: var(--border-warm);
    color: var(--ink-700);
}

.rx-btn-outline {
    width: 100%;
    background: var(--card-bg);
    border-color: var(--primary);
    color: var(--primary);
}

.rx-btn-sm {
    padding: 7px 12px;
    font-size: 12.5px;
    border-radius: var(--radius-md);
}

.rx-btn:disabled { opacity: 0.55; cursor: default; }

/* Missing things get named and roughly priced, with the next step one tap away
   — the same escape hatch Today uses instead of a bare count. */
.rx-missing {
    background: var(--card-bg);
    border: 1.5px solid var(--border-warm);
    border-radius: var(--radius-2xl);
    padding: 14px 15px;
    margin-bottom: 12px;
}

.rx-missing-names {
    font-size: 14.5px;
    color: var(--ink-900);
    text-wrap: pretty;
}

.rx-missing-cost {
    margin: 3px 0 12px;
    font-size: 12.5px;
    color: var(--ink-300);
}

.rx-eyebrow {
    margin-bottom: 6px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--ink-300);
}

.rx-ing {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding: 9px 0;
    border-bottom: 1px solid var(--border-warm-light);
}

.rx-ing-name {
    font-size: 13.5px;
    color: var(--ink-900);
}

.rx-ing.is-untracked .rx-ing-name { color: var(--ink-warm-500); }

.rx-ing-stock {
    flex: 0 0 auto;
    font-size: 12px;
    text-align: right;
}

.rx-ing-stock.verified { color: var(--confidence-verified); }
.rx-ing-stock.estimated { color: var(--confidence-estimated-text); }
.rx-ing-stock.out { color: var(--ink-warm-500); }
.rx-ing-stock.muted { color: var(--ink-warm-300); }

/* Dashed outline = not linked to your pantry. A quiet difference and a one-tap
   fix — never a warning icon, never an "incomplete recipe" badge. */
.rx-untracked-tag {
    margin-left: 6px;
    padding: 1px 7px;
    border: 1px dashed var(--border-dashed);
    border-radius: var(--radius-pill);
    background: none;
    cursor: pointer;
    font-size: 10px;
    color: var(--ink-warm-500);
}

.rx-card-foot {
    margin-top: 10px;
    font-size: 11px;
    line-height: 1.45;
    color: var(--ink-warm-300);
    text-wrap: pretty;
}

/* --- The deferred conversion, asked where it finally gets in the way -------
   A question, not a warning: one line of plain grey, one field, one button. The
   number box is the only accented thing, because it is the only thing being
   asked for. */
.rx-convert {
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--border-warm-light);
}

.rx-convert-q {
    font-size: 13.5px;
    font-weight: 700;
    color: var(--ink-900);
}

.rx-convert-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 7px;
    margin-top: 10px;
}

.rx-convert-prefix,
.rx-convert-unit {
    font-size: 13.5px;
    font-weight: 600;
    color: var(--ink-700);
}

.rx-convert-input {
    width: 72px;
    padding: 8px;
    border: 1.5px solid var(--primary);
    border-radius: var(--radius-lg);
    font: inherit;
    font-weight: 700;
    text-align: center;
    color: var(--ink-900);
    background: var(--card-bg);
}

.rx-convert-note {
    margin-top: 9px;
    font-size: 11.5px;
    line-height: 1.45;
    color: var(--ink-warm-300);
    text-wrap: pretty;
}

/* Reference material, not decision material — so it sits last. */
.rx-method {
    font-size: 13.5px;
    line-height: 1.6;
    color: var(--ink-600);
    white-space: pre-wrap;
}

.rx-detail-foot {
    padding: 0 4px 8px;
    font-size: 12px;
    color: var(--ink-300);
}

/* --- Bottom sheets (filter & sort, tap-to-link) -------------------------- */
.sheet-backdrop {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    background: rgba(20, 18, 16, 0.32);
}

.sheet {
    width: 100%;
    max-width: 480px;
    max-height: 88vh;
    overflow-y: auto;
    padding: 16px 16px 20px;
    border-radius: var(--radius-5xl) var(--radius-5xl) 0 0;
    background: var(--card-bg);
}

.sheet-handle {
    width: 36px;
    height: 4px;
    margin: 0 auto 12px;
    border-radius: var(--radius-pill);
    background: var(--border-warm);
}

.sheet-title {
    margin-bottom: 8px;
    font-size: 15.5px;
    font-weight: 700;
    color: var(--ink-900);
}

.sheet-group-label {
    margin: 14px 0 2px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--ink-300);
}

.sheet-row {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 10px 0;
    border: none;
    border-bottom: 1px solid var(--border-warm-light);
    background: none;
    cursor: pointer;
    text-align: left;
    font-size: 14.5px;
    font-weight: 500;
    color: var(--ink-600);
}

.sheet-row.is-selected {
    font-weight: 700;
    color: var(--ink-900);
}

.sheet-row-name { flex: 1; }

.sheet-row-count { color: var(--ink-warm-300); }

.sheet-row-stock { font-size: 12px; }
.sheet-row-stock.verified { color: var(--confidence-verified); }
.sheet-row-stock.estimated { color: var(--confidence-estimated-text); }
.sheet-row-stock.unknown { color: var(--ink-warm-300); }

.sheet-row-check {
    width: 12px;
    color: var(--primary);
}

.sheet-note {
    margin: 12px 0;
    font-size: 11.5px;
    line-height: 1.45;
    color: var(--ink-warm-300);
    text-wrap: pretty;
}

.sheet-note-top { margin-top: 0; }

.sheet-done {
    width: 100%;
    padding: 12px;
    border: none;
    border-radius: var(--radius-xl);
    background: var(--primary);
    cursor: pointer;
    font-size: 14.5px;
    font-weight: 700;
    color: var(--on-accent);
}

/* Dismissing without deciding. Quiet, because it is not one of the answers. */
.sheet-dismiss {
    display: block;
    width: 100%;
    padding: 10px;
    border: none;
    background: none;
    cursor: pointer;
    font-size: 13px;
    font-weight: 600;
    color: var(--ink-300);
}

/* "Keep untracked" is a real answer sitting beside the matches, not a cancel. */
.sheet-secondary {
    width: 100%;
    margin: 14px 0 8px;
    padding: 11px;
    border: 1.5px solid var(--border-warm);
    border-radius: var(--radius-xl);
    background: var(--card-bg);
    cursor: pointer;
    font-size: 13.5px;
    font-weight: 700;
    color: var(--ink-700);
}

/* --- Ingredient binding in the editor ------------------------------------ */
.rx-link-btn {
    flex: 0 0 auto;
    padding: 4px 12px;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--card-bg);
    cursor: pointer;
    font-size: 12px;
    font-weight: 600;
    color: var(--ink-700);
}

/* ==========================================================================
   The recipe editor, full screen (#139).

   A dialog is for a decision; this is a document — so: sticky header, scrolling
   body, sticky save bar, and per-row expansions that are allowed to be as tall
   as they need to be.

   Three registers live in here and they are styled apart on purpose, because
   amber was doing three jobs at once and none of them well:
     · .rxe-offer       — an OFFER. The only accent-outlined thing on a row.
     · .rxe-consequence — a CONSEQUENCE. Plain grey prose, neutral chips, and a
                          single amber accent on the field itself (.is-needed).
     · .rxe-question    — a QUESTION. One grey line and an underlined action.
   ========================================================================== */
.rxe {
    display: flex;
    flex-direction: column;
    min-height: 60vh;
}

.rxe-bar {
    position: sticky;
    top: 0;
    z-index: 3;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 0 10px;
    border-bottom: 1px solid var(--border-warm-light);
    background: var(--card-bg);
}

.rxe-bar-left,
.rxe-bar-save {
    min-width: 58px;
    padding: 4px 0;
    border: none;
    background: none;
    cursor: pointer;
    font: inherit;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--ink-warm-500);
}

.rxe-bar-save {
    text-align: right;
    font-weight: 800;
    color: var(--ink-200);
}

.rxe-bar-save.is-on { color: var(--primary); }

.rxe-bar-title {
    flex: 1;
    text-align: center;
    font-size: 14.5px;
    font-weight: 800;
    color: var(--ink-900);
}

.rxe-body {
    display: flex;
    flex-direction: column;
    gap: 13px;
    padding: 14px 0 20px;
    background: var(--canvas);
}

.rxe-card {
    padding: 13px 14px;
    border-radius: var(--radius-2xl);
    background: var(--card-bg);
    box-shadow: var(--shadow-card);
}

.rxe-label {
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--ink-warm-300);
}

.rxe-label-spaced { margin-top: 12px; }

.rxe-name {
    width: 100%;
    margin-top: 5px;
    padding: 8px 0 7px;
    border: none;
    border-bottom: 1.5px solid var(--border-warm-light);
    border-radius: 0;
    font: inherit;
    font-size: 17px;
    font-weight: 700;
    color: var(--ink-900);
}

.rxe-name:focus { outline: none; border-bottom-color: var(--primary); }

.rxe-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 7px;
}

.rxe-chip {
    padding: 7px 12px;
    border: 1.5px solid var(--border-warm);
    border-radius: var(--radius-5xl);
    background: var(--card-bg);
    cursor: pointer;
    font: inherit;
    font-size: 12.5px;
    font-weight: 700;
    color: var(--ink-warm-500);
}

.rxe-chip.is-on {
    border-color: var(--primary);
    background: var(--primary-light);
    color: var(--primary-dark);
}

.rxe-hint,
.rxe-hint-inline {
    font-size: 11.5px;
    line-height: 1.45;
    color: var(--ink-warm-300);
}

.rxe-hint { margin-top: 7px; }

.rxe-section {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.rxe-section-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    padding: 0 3px;
}

.rxe-section-title {
    font-size: 15px;
    font-weight: 800;
    color: var(--ink-900);
}

.rxe-section-count {
    font-size: 12px;
    font-weight: 600;
    color: var(--ink-warm-300);
}

.rxe-empty-pantry {
    padding: 12px 13px;
    border: 1px solid var(--border-warm);
    border-radius: var(--radius-xl);
    background: var(--card-bg);
    font-size: 13px;
    line-height: 1.5;
    color: var(--ink-700);
    text-wrap: pretty;
}

.rxe-row {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 10px 11px;
    border: 1px solid var(--border-warm);
    border-radius: var(--radius-xl);
    background: var(--card-bg);
}

.rxe-row-top {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* Let the pantry typeahead own the width; min-width:0 lets it shrink instead of
   pushing the remove button off the row. */
.rxe-row-name {
    flex: 1 1 auto;
    min-width: 0;
}

.rxe-row-name .typeahead-input {
    border-radius: var(--radius-lg);
    font-weight: 600;
}

/* Unlinked is a quiet difference, never an error: a dashed outline and nothing
   else. An offered near-miss warms the outline to match the offer below it. */
.rxe-row-name.is-unlinked .typeahead-input { border-style: dashed; }
.rxe-row-name.is-offered .typeahead-input { border: 1.5px solid var(--confidence-estimated); }

.rxe-row-remove {
    flex: 0 0 auto;
    width: 32px;
    height: 36px;
    border: none;
    background: none;
    cursor: pointer;
    font-size: 15px;
    color: var(--ink-200);
}

.rxe-amount {
    display: flex;
    align-items: center;
    gap: 6px;
}

.rxe-qty {
    width: 72px;
    flex: 0 0 auto;
    padding: 7px 9px;
    border: 1px solid var(--border-warm);
    border-radius: var(--radius-md);
    font: inherit;
    font-size: 13.5px;
    font-weight: 600;
    text-align: center;
    color: var(--ink-900);
}

.rxe-unit {
    flex: 1 1 auto;
    min-width: 0;
    padding: 7px 9px;
    border: 1px solid var(--border-warm);
    border-radius: var(--radius-md);
    font: inherit;
    font-size: 13.5px;
    color: var(--ink-700);
}

/* The single amber touch that a missing unit gets. The consequence below is
   stated in grey; the field is what needs filling, so the field is what is
   marked. */
.rxe-unit.is-needed {
    border: 1.5px solid var(--confidence-estimated);
    background: var(--tint-amber-soft);
    font-weight: 700;
    color: var(--confidence-estimated-text);
}

.rxe-taste-pill {
    padding: 6px 12px;
    border-radius: var(--radius-5xl);
    background: var(--primary-light);
    font-size: 12.5px;
    font-weight: 700;
    color: var(--primary);
}

/* A text action that isn't a button-shaped thing — used for the reversible
   amount/to-taste switch and for deferring the conversion. */
.rxe-linkish {
    padding: 7px 0;
    border: none;
    background: none;
    cursor: pointer;
    font: inherit;
    font-size: 12px;
    font-weight: 700;
    color: var(--ink-warm-500);
    text-decoration: underline;
}

.rxe-taste-toggle {
    flex: 0 0 auto;
    padding: 7px 4px 7px 9px;
    text-decoration: none;
}

.rxe-link {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12.5px;
}

.rxe-link-unbound { justify-content: space-between; }

.rxe-linked {
    font-weight: 700;
    color: var(--confidence-verified-text);
}

.rxe-link-stock.verified { color: var(--confidence-verified); }
.rxe-link-stock.estimated { color: var(--confidence-estimated-text); }
.rxe-link-stock.unknown { color: var(--ink-400); }

.rxe-unlinked { color: var(--ink-warm-300); }

/* CONSEQUENCE — a statement of fact in plain grey, with one-tap chips. Neutral
   on purpose: nothing here is asking to be tapped, it is explaining what
   follows from leaving it alone. */
.rxe-consequence-line {
    font-size: 12.5px;
    line-height: 1.45;
    color: var(--ink-warm-500);
    text-wrap: pretty;
}

.rxe-unit-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 8px;
}

.rxe-unit-chip {
    padding: 6px 11px;
    border: 1.5px solid var(--border-warm);
    border-radius: var(--radius-md);
    background: var(--card-bg);
    cursor: pointer;
    font: inherit;
    font-size: 12.5px;
    font-weight: 700;
    color: var(--ink-700);
}

/* QUESTION — one grey line and an underlined action. Deferrable forever, and
   deferring costs nothing: the ingredient saves as unknown either way. */
.rxe-question {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 10px;
}

.rxe-question-line {
    font-size: 12.5px;
    line-height: 1.45;
    color: var(--ink-warm-500);
    text-wrap: pretty;
}

.rxe-question-answered {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--confidence-verified-text);
}

.rxe-convert-inline {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 7px;
    font-size: 13px;
    font-weight: 600;
    color: var(--ink-700);
}

/* OFFER — the only accent-outlined element on a row, because it is the only one
   asking to be tapped. */
.rxe-offer {
    padding: 11px 12px;
    border: 1.5px solid var(--primary);
    border-radius: var(--radius-lg);
    background: var(--card-bg);
}

.rxe-offer-body {
    font-size: 13px;
    line-height: 1.45;
    color: var(--ink-900);
    text-wrap: pretty;
}

.rxe-offer-stock {
    margin-top: 3px;
    font-size: 11.5px;
}

.rxe-offer-stock.verified { color: var(--confidence-verified); }
.rxe-offer-stock.estimated { color: var(--confidence-estimated-text); }
.rxe-offer-stock.unknown { color: var(--ink-300); }

.rxe-offer-actions {
    display: flex;
    gap: 7px;
    margin-top: 10px;
}

.rxe-add-row {
    width: 100%;
    padding: 12px;
    border: 1.5px dashed var(--border-dashed);
    border-radius: var(--radius-xl);
    background: var(--surface-warm);
    cursor: pointer;
    font: inherit;
    font-size: 13.5px;
    font-weight: 700;
    color: var(--ink-warm-500);
}

.rxe-method {
    width: 100%;
    margin-top: 7px;
    padding: 8px 9px;
    border: 1px solid var(--border-warm);
    border-radius: var(--radius-md);
    font: inherit;
    font-size: 13.5px;
    line-height: 1.55;
    color: var(--ink-700);
    resize: vertical;
}

.rxe-details-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    width: 100%;
    padding: 0;
    border: none;
    background: none;
    cursor: pointer;
    text-align: left;
    font: inherit;
}

.rxe-details-summary {
    display: block;
    margin-top: 3px;
    font-size: 12px;
    color: var(--ink-warm-300);
}

.rxe-details-caret {
    font-size: 13px;
    font-weight: 700;
    color: var(--ink-200);
}

.rxe-details-grid {
    display: flex;
    gap: 8px;
    margin-top: 12px;
}

.rxe-details-grid > div { flex: 1; }
.rxe-details-grid input { width: 100%; }

/* The save bar. No tally of what's unfinished — counting a draft's flaws on the
   way out grades it, and an imperfect recipe is the point. */
.rxe-savebar {
    position: sticky;
    bottom: 0;
    z-index: 3;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 0 14px;
    border-top: 1px solid var(--border-warm-light);
    background: var(--card-bg);
}

.rxe-savebar-note {
    flex: 1;
    font-size: 11.5px;
    line-height: 1.4;
    color: var(--ink-warm-300);
}

.rxe-savebar-btn {
    flex: 0 0 auto;
    padding: 12px 22px;
    border: none;
    border-radius: var(--radius-xl);
    background: var(--border-warm-light);
    cursor: pointer;
    font: inherit;
    font-size: 14.5px;
    font-weight: 800;
    color: var(--ink-200);
}

.rxe-savebar-btn.is-on {
    background: var(--primary);
    color: var(--on-accent);
}

/* ==========================================================================
   Recipe capture (#139) — the door, and the review that copies receipt import
   beat for beat.

   The one structural rule: amber marks the rows we're unsure about, and those
   rows are OPT-IN. Everything else on this screen is information.
   ========================================================================== */
.rxc-door { gap: 14px; }

.rxc-title {
    font-size: 21px;
    font-weight: 800;
    line-height: 1.25;
    color: var(--ink-900);
}

.rxc-sub {
    margin-top: 6px;
    font-size: 13.5px;
    line-height: 1.5;
    color: var(--ink-600);
    text-wrap: pretty;
}

.rxc-door-card {
    display: flex;
    align-items: center;
    gap: 13px;
    width: 100%;
    padding: 14px 15px;
    border: 1px solid var(--border-warm);
    border-radius: var(--radius-2xl);
    background: var(--card-bg);
    cursor: pointer;
    text-align: left;
    font: inherit;
}

/* One door is the recommended one. It gets the accent outline; the others don't
   compete for it. */
.rxc-door-card.is-primary {
    border: 1.5px solid var(--primary);
    box-shadow: var(--shadow-card);
}

.rxc-door-card.is-plain { padding: 13px 15px; justify-content: space-between; }

.rxc-door-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    border-radius: var(--radius-xl);
    background: var(--surface-warm);
    font-size: 17px;
    color: var(--ink-600);
}

.rxc-door-icon.is-primary {
    background: var(--primary-light);
    color: var(--ink-900);
    font-size: 19px;
}

.rxc-door-text { flex: 1; }

.rxc-door-title {
    display: block;
    font-size: 15px;
    font-weight: 800;
    color: var(--ink-900);
}

.rxc-door-sub {
    display: block;
    margin-top: 2px;
    font-size: 12.5px;
    color: var(--ink-warm-500);
}

.rxc-door-caret {
    flex: 0 0 auto;
    font-size: 16px;
    font-weight: 700;
    color: var(--ink-200);
}

.rxc-door-caret.is-primary { color: var(--primary); }

.rxc-or {
    display: flex;
    align-items: center;
    gap: 10px;
    color: var(--ink-200);
    font-size: 11.5px;
    font-weight: 700;
}

.rxc-or::before,
.rxc-or::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--border-warm);
}

.rxc-foot,
.rxc-legend {
    font-size: 12px;
    line-height: 1.5;
    color: var(--ink-warm-300);
    text-wrap: pretty;
}

.rxc-legend { margin: -4px 3px 0; }

.rxc-error {
    padding: 11px 12px;
    border: 1px solid var(--tint-amber-border);
    border-radius: var(--radius-lg);
    background: var(--tint-amber);
    font-size: 13px;
    line-height: 1.5;
    color: var(--ink-700);
}

.rxc-file { margin-top: 10px; }

.rxc-source {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 10px;
}

.rxc-source-chip {
    padding: 5px 10px;
    border-radius: var(--radius-5xl);
    background: var(--surface-warm);
    font-size: 11.5px;
    font-weight: 700;
    color: var(--ink-600);
}

.rxc-summary {
    font-size: 13.5px;
    line-height: 1.5;
    color: var(--ink-700);
    text-wrap: pretty;
}

.rxc-trio {
    display: flex;
    gap: 8px;
    margin-top: 12px;
}

.rxc-trio > div { flex: 1; }
.rxc-trio input { width: 100%; text-align: center; }

/* Amber, and only here: a range the page gave that we refuse to pick from. */
.rxe-label.is-unsure { color: var(--confidence-estimated-text); }

.rxc-trio input.is-unsure {
    border: 1.5px solid var(--confidence-estimated);
    background: var(--tint-amber-soft);
    color: var(--confidence-estimated-text);
    font-weight: 700;
}

.rxc-unsure-note {
    margin-top: 8px;
    font-size: 11.5px;
    line-height: 1.45;
    color: var(--confidence-estimated-text);
}

.rxc-row {
    padding: 10px 11px;
    border: 1.5px solid var(--border-warm);
    border-radius: var(--radius-xl);
    background: var(--card-bg);
}

.rxc-row.is-unsure {
    border-color: var(--tint-amber-border);
    background: var(--tint-amber-soft);
}

.rxc-row-main {
    display: flex;
    align-items: center;
    gap: 10px;
}

.rxc-tick {
    flex: 0 0 auto;
    width: 22px;
    height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 2px solid var(--border-dashed);
    border-radius: var(--radius-sm);
    background: var(--card-bg);
    cursor: pointer;
    font-size: 13px;
    font-weight: 800;
    color: var(--on-accent);
}

.rxc-tick.is-on {
    border-color: var(--primary);
    background: var(--primary);
}

.rxc-tick.is-unsure { border-color: var(--confidence-estimated); }

.rxc-qty,
.rxc-unit,
.rxc-name {
    padding: 6px 7px;
    border: 1px solid var(--border-warm);
    border-radius: var(--radius-sm);
    background: var(--card-bg);
    font: inherit;
    font-size: 13.5px;
    color: var(--ink-900);
}

.rxc-qty { flex: 0 0 auto; width: 62px; text-align: center; font-weight: 700; }
.rxc-unit { flex: 0 0 auto; width: 66px; text-align: center; }
.rxc-name { flex: 1 1 auto; min-width: 0; font-weight: 600; }

.rxc-qty.is-unsure,
.rxc-unit.is-unsure {
    border-color: var(--tint-amber-border);
    color: var(--confidence-estimated-text);
}

/* Dashed = saved as typed, untracked. The same quiet difference the editor uses,
   and never a reason to withhold the tick. */
.rxc-name.is-untracked { border-style: dashed; }

.rxc-row-note {
    margin-top: 9px;
    padding-left: 32px;
}

.rxc-note-unsure,
.rxc-note-muted {
    font-size: 12.5px;
    line-height: 1.45;
    text-wrap: pretty;
}

.rxc-note-unsure { color: var(--confidence-estimated-text); }
.rxc-note-muted { color: var(--ink-warm-500); }

.rxc-row-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 7px;
    margin-top: 9px;
}

.rxc-method-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
}

/* The save bar stacks here: the button carries the count, and the line under it
   is the promise that made every unsure row above safe to leave alone. */
.rxc-savebar { flex-direction: column; align-items: stretch; gap: 7px; }

.rxc-save-btn {
    width: 100%;
    padding: 14px;
    border: none;
    border-radius: var(--radius-xl);
    background: var(--primary);
    cursor: pointer;
    font: inherit;
    font-size: 15px;
    font-weight: 800;
    color: var(--on-accent);
}

.rxc-save-btn:disabled {
    background: var(--border-warm-light);
    color: var(--ink-200);
    cursor: default;
}

.rxc-save-note {
    font-size: 11.5px;
    text-align: center;
    color: var(--ink-warm-300);
}
