/* iwant2eat — base styles */

:root {
    --primary: #5a9e2f;
    --primary-2: #7ab648;
    --text: #222;
    --muted: #7a7a7a;
    --bg: #fafafa;
    --surface: #ffffff;
    --border: #e4e4e4;
    --radius: 10px;
    --shadow: 0 1px 3px rgba(0, 0, 0, 0.05), 0 4px 12px rgba(0, 0, 0, 0.04);
}

html, body {
    margin: 0;
    padding: 0;
    background: var(--bg);
    color: var(--text);
    /* Body / chrome / nav / titles / meta — keep the system font so the
       page stays visually identical to what it was before fonts got
       swapped in. The custom face (Inter) is scoped to recipe step text,
       ingredient pills, and the cooking overlay only — see those rules
       in RecipeDetail.razor. */
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    font-size: 16px;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}
h1, h2, h3 { margin-top: 0; }
h1:focus { outline: none; }

a { color: var(--primary); text-decoration: none; }
a:hover { text-decoration: underline; }

.page { min-height: 100vh; display: flex; flex-direction: column; }

.topbar {
    display: flex; align-items: center; justify-content: space-between;
    padding: 0.75rem 1.25rem;
    background: var(--surface); border-bottom: 1px solid var(--border);
}
.topbar .brand {
    display: flex; flex-direction: column; align-items: center; gap: 0.15rem;
    color: var(--text); font-weight: 700; font-size: 0.85rem;
    text-decoration: none; line-height: 1;
}
.topbar .brand:hover { text-decoration: none; color: var(--primary); }
/* Clip the logo into a circle — the source PNG is square but the app
   reads cleaner with a pill-shaped brand. object-fit:cover guards against
   a future non-square source. */
.topbar .brand img {
    width: 42px; height: 42px;
    border-radius: 50%;
    object-fit: cover;
    background: #fff;
}
/* Facebook community link — sits next to the round logo as a small
   social chip. Facebook brand blue on hover, muted grey at rest so it
   doesn't compete with the primary nav. */
.topbar .fb-link {
    display: inline-flex; align-items: center; justify-content: center;
    /* The FB "f-in-circle" glyph has whitespace inside its viewBox, so the
       SVG is rendered a touch larger than the brand logo to make the
       visible blue circle match 42px. */
    width: 41px; height: 41px;
    margin-left: 10px;
    border-radius: 50%;
    color: #1877f2;
    text-decoration: none;
    transition: color 0.12s, background 0.12s;
}
.topbar .fb-link:hover {
    color: #1877f2;
    background: #eef4ff;
    text-decoration: none;
}

/* Environment badge — only rendered in Development. Orange so nobody
   mistakes a dev browser tab for the real iwant2eat.com. */
.topbar .env-badge {
    display: inline-block;
    margin-left: 0.5rem;
    padding: 0.3rem 1rem;
    font-size: 1.25rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    border-radius: 4px;
    line-height: 1.2;
    user-select: none;
}
.topbar .env-badge-dev {
    background: #f59e0b;
    color: #1f1a08;
    box-shadow: 0 1px 0 rgba(0,0,0,0.15);
}
/* Stage badge — blue so it's visually distinct from the orange DEV
   badge but still clearly "not prod". */
.topbar .env-badge-stage {
    background: #2563eb;
    color: #f0f6ff;
    box-shadow: 0 1px 0 rgba(0,0,0,0.15);
}
.topbar .env-badge-tim {
    background: #7c3aed;
    color: #f5f0ff;
    box-shadow: 0 1px 0 rgba(0,0,0,0.15);
}
/* Push the nav to the right edge so brand + fb-link + dev-badge stay
   tightly grouped on the left. Without this, justify-content:space-between
   on .topbar spreads all four children evenly and the fb link floats to
   the middle. */
.topbar nav { display: flex; align-items: center; gap: 0.5rem; margin-left: auto; }
.topbar nav a { color: var(--text); }
/* Render the primary nav links (all top-level /recipes /ai-budget /admin
   /pricing anchors) as chips. User-badge and help "?" opt out by having
   their own classes. */
.topbar nav > a:not(.user-badge):not(.help-link) {
    background: #f1ece4; color: #5a4e3a;
    border: 1px solid #e0d6c3; border-radius: 999px;
    padding: 0.3rem 0.8rem;
    font-size: 0.9rem; font-weight: 500;
    text-decoration: none;
    white-space: nowrap;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.topbar nav > a:not(.user-badge):not(.help-link):hover {
    background: var(--primary); color: #fff; border-color: var(--primary);
    text-decoration: none;
}
.topbar nav .linklike {
    background: none; border: none; color: var(--text);
    cursor: pointer; font: inherit; padding: 0;
}
.topbar nav .linklike:hover { color: var(--primary); text-decoration: underline; }
.topbar nav .user-badge {
    display: inline-flex; align-items: center; gap: 0.4rem; color: var(--text);
    text-decoration: none;
}
.topbar nav .user-badge:hover { text-decoration: none; color: var(--primary); }
.topbar nav .user-badge:hover .nav-avatar {
    border-color: var(--primary);
}
/* No-avatar fallback: two-letter initials in a round badge that matches
   the uploaded-avatar size so the topbar height stays stable. */
.topbar nav .user-badge .user-badge-initials {
    display: inline-flex; align-items: center; justify-content: center;
    width: 26px; height: 26px; border-radius: 50%;
    background: var(--primary); color: #fff;
    font-size: 0.72rem; font-weight: 700; letter-spacing: 0.02em;
    border: 1.5px solid var(--primary);
    user-select: none;
}
.topbar nav .user-badge:hover .user-badge-initials {
    filter: brightness(1.08);
}
/* Legacy class — older renders emitted the display name as text; kept so
   cached HTML from a prior deploy still styles reasonably. */
.topbar nav .user-badge .user-badge-name { font-weight: 500; }
.sub-badge {
    font-size: 0.7rem; padding: 0.15rem 0.5rem; border-radius: 999px;
    font-weight: 600; text-decoration: none;
}
.sub-badge.expired { background: #fbebe9; color: #b94737; }
.sub-badge.trial { background: #fff8f0; color: #8a6d3b; }
.nav-avatar {
    width: 26px; height: 26px; border-radius: 50%; object-fit: cover;
    border: 1.5px solid var(--border);
}
.inline-form { display: inline; }

main {
    flex: 1; max-width: 960px; width: 100%;
    margin: 0 auto; padding: 2rem 1.25rem; box-sizing: border-box;
}

.auth-container {
    max-width: 420px; margin: 4rem auto; padding: 2rem;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius); box-shadow: var(--shadow);
}
.form-control, .form-control:focus {
    display: block; width: 100%; box-sizing: border-box;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--border); border-radius: 6px;
    font-size: 1rem; background: #fff; outline: none;
}
.form-control:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(90, 158, 47, 0.15); }
.form-floating { margin-bottom: 1rem; }
.form-floating label { display: block; margin-bottom: 0.35rem; font-size: 0.85rem; color: var(--muted); }

textarea.form-control { font-family: inherit; min-height: 200px; resize: vertical; }

.btn {
    display: inline-block; padding: 0.55rem 1rem;
    border-radius: 6px; border: 1px solid var(--border);
    background: var(--surface); color: var(--text);
    font-size: 1rem; cursor: pointer; text-decoration: none;
}
.btn:hover { text-decoration: none; }
.btn-primary { background: var(--primary); border-color: var(--primary); color: #fff; }
.btn-primary:hover { background: #4a8a25; color: #fff; }
.btn-lg { padding: 0.75rem 1.25rem; font-size: 1.05rem; }
.btn-danger { background: #b94737; border-color: #b94737; color: #fff; }
.w-100 { width: 100%; }

.text-danger, .validation-message { color: #b94737; font-size: 0.9rem; }

.alert {
    padding: 0.75rem 1rem; border-radius: 6px; margin-bottom: 1rem;
    border: 1px solid transparent;
}
.alert-success { background: #eaf7e6; color: #2e6a28; border-color: #b8e1ae; }
.alert-danger { background: #fbebe9; color: #7a2720; border-color: #f2c3bc; }

.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    grid-auto-rows: 1fr;
    gap: 1rem;
}
/* Plain-anchor navigation overlay covering the full card. Lets the
   browser handle navigation when Blazor's circuit is dead (offline).
   Sits behind everything; interactive children (.card-fav-btn,
   .card-tag-chip, etc) have their own positioning and stop
   propagation so they keep working. tabindex=-1 + aria-hidden so the
   overlay doesn't double-up the card in screen readers — the card
   itself is still keyboard-reachable via its parent div's @onclick
   when Blazor is alive. */
.card-nav-overlay {
    position: absolute;
    inset: 0;
    z-index: 0;
    text-decoration: none;
}

.card {
    /* Sticky-note treatment for recipe cards. Each card gets a flat
       pastel colour cycled by nth-child below + a slight random-feel
       rotation so the grid reads as Post-its on a corkboard rather
       than a tidy UI table. Border is removed (real Post-its have no
       border) but the warm-tinted shadow gives a lift you can read on
       the near-white app background. */
    background-color: #fdfaf0;            /* matches recipe-detail page paper */
    border: 0;
    border-radius: 2px;                    /* sticky notes have sharp-ish corners */
    padding: 1rem;
    /* Asymmetric shadow — a touch heavier on the bottom-right, like a
       sticky note's adhesive holding the top while gravity pulls the
       loose corner away from the wall. Warm tint matches the paper. */
    box-shadow:
        0 1px 1px rgba(0, 0, 0, 0.12),
        2px 4px 10px rgba(60, 50, 25, 0.18);
    text-decoration: none; color: inherit;
    transition: box-shadow 0.15s ease, transform 0.15s ease;
    /* Flex column so .card-tags can use margin-top:auto to sit just above
       the absolute-positioned footer (date + icons) instead of floating
       in the middle when the title/subtitle are short. */
    display: flex; flex-direction: column;
    cursor: pointer;
}
/* (Per-slot rotations removed — sticky notes now sit flat. Background
   colour stays the classic sticky-note yellow set on .card itself.) */

.card:hover {
    /* Lift + deepen the shadow — like pulling a Post-it a tiny bit
       off the corkboard to read it. */
    box-shadow:
        0 2px 4px rgba(0, 0, 0, 0.14),
        4px 10px 22px rgba(60, 50, 25, 0.22);
    transform: translateY(-2px);
    z-index: 2;
}
.card-processing {
    cursor: not-allowed; opacity: 0.7;
    border-style: dashed; border-color: var(--muted);
    animation: pulse-processing 2s ease-in-out infinite;
}
.card-processing:hover { border-color: var(--muted); }
@keyframes pulse-processing {
    0%, 100% { opacity: 0.7; }
    50% { opacity: 0.5; }
}
.card.drag-over {
    border-color: var(--primary); border-style: dashed; border-width: 2px;
    background: #f5fbf0; transition: all 0.15s;
}
/* Recipe thumbnail rendered as a small framed picture pinned to the
   sticky note's upper-right corner. The wrap is the dark wooden frame
   (outer border + padding for frame thickness), the inner img has a
   white mat for the classic museum-print look. A light tilt and a
   richer shadow sell the "photo hung on the corkboard" feel. */
.card-thumb-wrap {
    position: absolute; top: 0.6rem; right: 0.6rem;
    width: 75px; height: 75px;
    z-index: 2;
    box-sizing: border-box;
    /* Light-oak frame — warm honey gradient with a soft beveled feel,
       thinner than the previous dark mahogany so the photo reads more
       prominently than the surround. */
    background: linear-gradient(155deg, #e3c48f 0%, #b48a56 60%, #d2ac74 100%);
    padding: 2px 2px 3px 2px;
    border: 1px solid #8b6f3e;
    box-shadow:
        0 1px 1px rgba(255, 240, 210, 0.45) inset,
        0 -1px 1px rgba(80, 55, 25, 0.25) inset,
        0 3px 6px rgba(0, 0, 0, 0.20),
        0 1px 2px rgba(0, 0, 0, 0.15);
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.card-thumb {
    width: 100%; height: 100%;
    object-fit: cover; display: block;
    /* White mat between frame and photo + a hairline mat-edge ring. */
    background: #fff;
    border: 3px solid #fff;
    box-shadow: inset 0 0 0 1px #d8d0bd;
    border-radius: 0;
    box-sizing: border-box;
    cursor: zoom-in;
}
/* Hover the whole frame, not just the photo: straighten + lift, slight
   scale, deeper shadow — like reaching up and pulling the picture
   forward off the wall to look at it. */
.card-thumb-wrap:hover {
    transform: scale(1.08);
    box-shadow:
        0 1px 1px rgba(255, 240, 210, 0.45) inset,
        0 -1px 1px rgba(80, 55, 25, 0.25) inset,
        0 6px 12px rgba(0, 0, 0, 0.25),
        0 2px 4px rgba(0, 0, 0, 0.18);
}
/* Tiny "stock" pill on the bottom-left of the thumbnail — signals the
   image is an auto-fetched stock photo so the user knows they can
   replace it with their own. */
.card-stock-badge {
    position: absolute;
    left: -2px; bottom: -2px;
    font-size: 0.58rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    line-height: 1;
    padding: 2px 5px;
    border-radius: 4px;
    background: rgba(255,255,255,0.92);
    color: #666;
    border: 1px solid var(--border);
    text-transform: lowercase;
    pointer-events: none;
    user-select: none;
}
/* (Hover treatment moved to .card-thumb-wrap so the whole picture
   frame lifts together rather than just the photo inside it.) */

/* Admin impersonation warning banner — shown site-wide while an admin is in
   "view as user" mode. Loud orange so it is impossible to miss. */
.impersonation-banner {
    background: #fff4d6;
    border-bottom: 2px solid #d89a1f;
    color: #6a4f1a;
    padding: 0.6rem 1rem;
    font-size: 0.92rem;
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem; flex-wrap: wrap;
}
.impersonation-banner strong { color: #3d2c0a; }
.impersonation-return {
    background: #d89a1f; color: #fff; border: none;
    border-radius: 6px; padding: 0.35rem 0.75rem;
    font-size: 0.85rem; font-weight: 600; cursor: pointer;
}
.impersonation-return:hover { background: #b87e14; }

/* Email-verification nudge banner — shown site-wide to signed-in users
   whose AppUser.EmailConfirmed is false. Softer than the impersonation
   warning (this is informational, not a safeguard). */
.verify-banner {
    background: #eef6ff;
    border-bottom: 1px solid #bcd7f5;
    color: #24476b;
    padding: 0.55rem 1rem;
    font-size: 0.92rem;
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem; flex-wrap: wrap;
}
.verify-banner strong { color: #1b365a; }
.verify-banner em { font-style: italic; color: #1b365a; }
.verify-resend {
    background: #3477c7; color: #fff; border: none;
    border-radius: 6px; padding: 0.3rem 0.7rem;
    font-size: 0.85rem; font-weight: 600; cursor: pointer;
}
.verify-resend:hover { background: #285d9f; }

/* Browse-only / trial-prompt banner — sits above the topbar so the prompt
   is the first thing the user sees on every page until they start the
   trial. Green primary palette to read as a friendly CTA, not an error. */
.trial-banner {
    background: #eaf7e6;
    border-bottom: 1px solid #b8e1ae;
    color: #2e6a28;
    padding: 0.55rem 1rem;
    font-size: 0.92rem;
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem; flex-wrap: wrap;
}
.trial-banner strong { color: #1f4d1c; }
.trial-banner-cta {
    background: var(--primary); color: #fff; border: none;
    border-radius: 6px; padding: 0.35rem 0.9rem;
    font-size: 0.9rem; font-weight: 600; cursor: pointer;
}
.trial-banner-cta:hover { background: #4a8a25; }
.trial-banner-btn {
    width: 100%; border: none; font: inherit; color: inherit;
    text-align: left; cursor: pointer;
}
.trial-banner-btn:hover { background: #def0d6; }
.trial-banner-btn:hover .trial-banner-cta { background: #4a8a25; }
.add-drop-banner-btn {
    width: 100%; border: none; font: inherit; cursor: pointer;
}

/* Chrome-extension promo banner — thin strip above the top nav on desktop
   Chromium browsers when the extension isn't already installed. Sits above
   the topbar so it doesn't shift main-content layout when it closes. */
.ext-promo-banner {
    background: #f4f7ed;
    border-bottom: 1px solid #d8e4c2;
    color: #3c5423;
    padding: 0.45rem 1rem;
    font-size: 0.9rem;
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem; flex-wrap: wrap;
}
.ext-promo-banner strong { color: #2c5c1f; }
.ext-promo-banner a { color: #2c5c1f; text-decoration: underline; }
.ext-promo-banner a:hover { color: #1c3d12; }
.ext-promo-close {
    background: none; border: none; cursor: pointer;
    color: #6b7a54; font-size: 1.15rem; line-height: 1;
    padding: 0 0.35rem; border-radius: 4px;
}
.ext-promo-close:hover { color: #2c5c1f; background: #e7efd4; }
/* Extension-banner.js toggles this class to hide/show. !important so Blazor
   re-renders that put hidden=false on the element don't fight us. */
.ext-promo-banner.ext-promo-hidden { display: none !important; }
/* Hidden on narrow viewports — mobile users can't install Chrome extensions. */
@media (max-width: 899px) {
    .ext-promo-banner { display: none !important; }
}

/* iOS-Shortcut promo banner — sibling of the Chrome-extension banner,
   shown only on iPhone/iPad (the platform Web Share Target ignores).
   No desktop/mobile media-query: ios-shortcut-banner.js gates it on UA. */
.ios-promo-banner {
    background: #f4f7ed;
    border-bottom: 1px solid #d8e4c2;
    color: #3c5423;
    padding: 0.45rem 1rem;
    font-size: 0.9rem;
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem; flex-wrap: wrap;
}
.ios-promo-banner strong { color: #2c5c1f; }
.ios-promo-banner a { color: #2c5c1f; text-decoration: underline; }
.ios-promo-banner a:hover { color: #1c3d12; }
.ios-promo-close {
    background: none; border: none; cursor: pointer;
    color: #6b7a54; font-size: 1.15rem; line-height: 1;
    padding: 0 0.35rem; border-radius: 4px;
}
.ios-promo-close:hover { color: #2c5c1f; background: #e7efd4; }
.ios-promo-banner.ios-promo-hidden { display: none !important; }

/* Site footer — small legal-link strip */
.site-footer {
    position: relative;
    text-align: center;
    padding: 1.5rem 1rem 2rem;
    color: var(--muted);
    font-size: 0.82rem;
    border-top: 1px solid var(--border);
    margin-top: 3rem;
}
.site-footer a { color: var(--muted); text-decoration: none; }
.site-footer a:hover { color: var(--primary); text-decoration: underline; }
.site-footer span { margin: 0 0.3rem; opacity: 0.6; }
.site-footer .site-footer-build {
    position: absolute;
    right: 0.6rem;
    bottom: 0.3rem;
    margin: 0;
    font-size: 0.65rem;
    /* Light grey — readable against the footer's tan background but
       quiet enough that the build stamp doesn't compete with content.
       Same tone as the server IP next to it. */
    opacity: 1;
    color: #8a8a8a;
    font-variant-numeric: tabular-nums;
}
.site-footer .site-footer-build .site-footer-sha {
    margin-left: 0.4rem;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    opacity: 1;
}
.site-footer .site-footer-build .site-footer-ip {
    margin-left: 0.4rem;
    /* `.site-footer span { opacity: 0.6 }` would otherwise dim this nested
       span, leaving the IP visibly lighter than the build stamp it sits
       next to. Pin to 1 so both render at the same #8a8a8a intensity. */
    opacity: 1;
}
/* DB environment chip — sits after the IP/LOCALHOST. Prod gets a tinted
   pill so you can tell at a glance you're hitting the live database;
   dev stays neutral grey. Both override the .site-footer span 0.6 dim
   so they read at full strength like the IP. */
.site-footer .site-footer-build .site-footer-db {
    margin-left: 0.4rem;
    opacity: 1;
}
.site-footer .site-footer-build .site-footer-db.is-prod {
    color: #b1411b;
    font-weight: 600;
}
.site-footer .site-footer-build .site-footer-db.is-dev {
    color: #8a8a8a;
}
/* Copyright chunk — keeps "VolicSoftware Inc." visible even though it's
   a link by not dimming the whole span. */
.site-footer .site-footer-copy { opacity: 0.85; margin: 0 0 0 0.3rem; }
.site-footer .site-footer-copy a { opacity: 1; color: var(--muted); }
.site-footer .site-footer-copy a:hover { color: var(--primary); }

/* Language picker in the footer — styled to blend into the footer link row
   rather than read as a loud form control. The underlying <form>/<select>
   is a plain GET submit so anonymous visitors can swap languages on the
   very first SSR render, before Blazor hydrates. */
.site-footer .lang-picker-form { display: inline-flex; align-items: center; margin: 0; }
.site-footer .lang-picker {
    background: transparent;
    color: var(--muted);
    border: 1px solid transparent;
    padding: 0.15rem 0.3rem;
    font: inherit;
    font-size: 0.9rem;
    cursor: pointer;
    border-radius: 4px;
}
.site-footer .lang-picker:hover,
.site-footer .lang-picker:focus {
    color: var(--primary);
    border-color: var(--border);
    outline: none;
}
.site-footer .lang-picker-go {
    margin-left: 0.3rem;
    font-size: 0.85rem;
    padding: 0.1rem 0.5rem;
}

/* Segmented two-button sort switch (e.g. Name / Date on My Recipes) */
.sort-switch {
    display: inline-flex;
    border: 1px solid var(--border);
    border-radius: 999px;
    overflow: hidden;
    background: var(--surface, #fff);
    font-size: 0.85rem;
}
.sort-switch button {
    background: none;
    border: none;
    padding: 0.35rem 0.9rem;
    cursor: pointer;
    /* var(--text-2) instead of var(--muted) so the inactive labels
       stay legible on midnight — the previous --muted (#85807a on
       midnight) sat too close to the dark --surface and washed the
       Name / Viewed labels out. */
    color: var(--text-2, var(--muted));
    font-weight: 500;
    transition: background 0.12s, color 0.12s;
}
.sort-switch button + button { border-left: 1px solid var(--border); }
/* Theme-aware hover tint via color-mix on the primary; the original
   hardcoded #f5fbf0 was a near-white pale-green that vanished on
   the midnight surface. */
.sort-switch button:hover:not(.active) {
    color: var(--primary);
    background: color-mix(in oklab, var(--primary) 12%, transparent);
}
.sort-switch button.active {
    background: var(--primary);
    color: #fff;
    cursor: default;
}

/* "?" help icon in the top navigation */
.help-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px; height: 26px;
    border-radius: 50%;
    border: 1.5px solid var(--border);
    color: var(--muted);
    font-weight: 700;
    font-size: 0.9rem;
    text-decoration: none;
    line-height: 1;
    transition: border-color 0.12s, color 0.12s, background 0.12s;
}
.help-link:hover {
    border-color: var(--primary);
    color: var(--primary);
    background: #f5fbf0;
    text-decoration: none;
}

/* External (OAuth) sign-in buttons on Login / Register pages */
.oauth-separator {
    position: relative;
    text-align: center;
    margin: 1.25rem 0 1rem;
    color: var(--muted);
    font-size: 0.82rem;
    text-transform: lowercase;
    letter-spacing: 0.02em;
}
.oauth-separator::before {
    content: "";
    position: absolute;
    top: 50%; left: 0; right: 0;
    border-top: 1px solid var(--border);
    z-index: 0;
}
.oauth-separator span {
    position: relative;
    background: var(--surface, #fff);
    padding: 0 0.75rem;
    z-index: 1;
}
.oauth-buttons {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    margin-bottom: 1rem;
}
.oauth-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.6rem;
    padding: 0.65rem 1rem;
    border-radius: 8px;
    border: 1px solid var(--border);
    font-size: 0.95rem;
    font-weight: 500;
    text-decoration: none;
    transition: filter 0.12s, border-color 0.12s, background 0.12s;
}
.oauth-btn:hover { text-decoration: none; filter: brightness(0.96); }
.oauth-btn svg { width: 20px; height: 20px; flex: 0 0 20px; }

.oauth-btn--facebook {
    background: #1877F2;
    border-color: #1877F2;
    color: #fff;
}
.oauth-btn--facebook:hover { background: #166fe5; border-color: #166fe5; color: #fff; }

.oauth-btn--google {
    background: #fff;
    color: #3c4043;
}
.oauth-btn--google:hover { background: #f8f9fa; border-color: #c6c6c6; color: #3c4043; }

/* Temporarily disabled external provider button */
.oauth-btn.is-disabled,
.oauth-btn.is-disabled:hover {
    opacity: 0.5;
    filter: grayscale(100%);
    pointer-events: none;
    cursor: not-allowed;
}

/* Sub-headline under the Sign up / Sign in H1 — soft reassurance ("14 days
   free, no card") lives here so it doesn't fight the OAuth buttons. */
.auth-sub {
    color: var(--muted); font-size: 0.92rem;
    margin: -0.25rem 0 1.1rem;
    text-align: center;
}

/* Password field with an inline Show / Hide toggle — replaces the
   confirm-password field for both register and login. The toggle is an
   unstyled button so the password still clears correctly on form reset. */
.pw-field { position: relative; }
.pw-field .pw-input { padding-right: 3.5rem; }
.pw-toggle {
    position: absolute;
    right: 0.75rem;
    top: 50%;
    transform: translateY(-50%);
    background: none; border: none;
    color: var(--muted);
    font-size: 0.82rem;
    padding: 0.25rem 0.35rem;
    cursor: pointer;
    border-radius: 4px;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}
.pw-toggle:hover { color: var(--primary); background: #f4f4ee; }
.pw-toggle:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }

/* Show/hide toggle injected by js/password-toggle.js */
.password-field {
    position: relative;
    display: block;
}
.password-field > input {
    padding-right: 2.4rem !important;
    width: 100%;
    box-sizing: border-box;
}
.password-toggle {
    position: absolute;
    right: 0.3rem;
    top: 50%;
    transform: translateY(-50%);
    background: none;
    border: none;
    padding: 0.25rem 0.5rem;
    cursor: pointer;
    color: #888;
    font-size: 1rem;
    line-height: 1;
    user-select: none;
}
.password-toggle:hover { color: var(--primary); }

/* AI-generated document (Write a correct recipe of, etc.) */
.ai-document {
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 3px solid var(--primary);
    border-radius: var(--radius);
    padding: 1.25rem 1.5rem;
    margin-bottom: 1.5rem;
    box-shadow: var(--shadow);
    line-height: 1.55;
    font-size: 0.95rem;
}
.ai-document h1 { font-size: 1.4rem; color: var(--primary); margin: 0 0 0.4rem; }
.ai-document h2 { font-size: 1.05rem; margin: 1.1rem 0 0.35rem; color: #333; }
.ai-document h3 { font-size: 0.95rem; margin: 0.8rem 0 0.25rem; color: #444; }
.ai-document ul, .ai-document ol { padding-left: 1.4rem; margin: 0.3rem 0 0.6rem; }
.ai-document li { margin: 0.15rem 0; }
.ai-document p { margin: 0.35rem 0; }
.ai-document hr { border: none; border-top: 1px solid var(--border); margin: 1rem 0; }
.ai-document em { color: var(--muted); }
.ai-document strong { color: #222; }
.ai-document-actions {
    display: flex; gap: 0.4rem; justify-content: flex-end;
    margin-bottom: 0.5rem;
}
.ai-document-actions .btn { padding: 0.3rem 0.6rem; font-size: 0.8rem; }

/* Print / "Save as PDF" — isolate the .ai-document on the page. The
   :has() guard scopes the aggressive visibility: hidden rule to pages
   where an .ai-document element is actually present; every other page
   (recipe detail, shopping list, meal plan) uses the general
   @media print block further down. */
@media print {
    body:has(.ai-document) * { visibility: hidden !important; }
    body:has(.ai-document) .ai-document,
    body:has(.ai-document) .ai-document * { visibility: visible !important; }
    .ai-document {
        position: absolute; inset: 0;
        box-shadow: none; border: none; padding: 0;
        margin: 0; max-width: none;
    }
    .ai-document-actions { display: none !important; }
    /* Keep headings on the same page as the first item under them */
    .ai-document h1, .ai-document h2, .ai-document h3 { break-after: avoid; }
    .ai-document li, .ai-document p { break-inside: avoid; }
}

/* Lightbox overlay for expanded thumbnail */
.thumb-lightbox {
    position: fixed; inset: 0;
    background: rgba(0,0,0,0.55);
    display: flex; align-items: center; justify-content: center;
    z-index: 9999;
    animation: thumb-fade 0.15s ease;
    cursor: zoom-out;
}
.thumb-lightbox img {
    max-width: 90vw; max-height: 90vh;
    border-radius: 10px;
    box-shadow: 0 18px 48px rgba(0,0,0,0.55);
    background: #fff;
}
@keyframes thumb-fade {
    from { opacity: 0; }
    to { opacity: 1; }
}
.card:has(.card-thumb) .title,
.card:has(.card-thumb) .subtitle,
.card:has(.card-thumb) .meta { padding-right: 64px; }
/* Reserve room on the right when the card has a framed thumbnail in
   its upper-right corner, so a long title or subtitle doesn't crowd
   the picture. 75px frame + 5px gap. */
.card:has(.card-thumb-wrap) .title,
.card:has(.card-thumb-wrap) .subtitle,
.card:has(.card-thumb-wrap) .card-ai-reason {
    padding-right: 80px;
}
.card .card-ai-reason {
    color: var(--primary);
    font-size: 0.85rem;
    margin-top: 0.3rem;
}
.card .title { font-weight: 600; margin-bottom: 0.15rem; }
.card .subtitle { color: var(--muted); font-size: 0.82rem; margin-bottom: 0.25rem; font-style: italic; }
.card .meta { color: var(--muted); font-size: 0.8rem; }

.card-added {
    position: absolute; bottom: 0.5rem; left: 0.8rem;
    color: var(--muted); font-size: 0.72rem; font-style: italic;
    pointer-events: none;
    display: flex; gap: 0.5rem; align-items: baseline;
}
.card-added .card-times {
    font-style: normal;
    color: var(--muted);
}
/* Reserve space at the bottom so the added-date never collides with the meta line.
   The min-height keeps short cards (e.g. a failure row with just a title) tall
   enough that the absolute-positioned trash button at the bottom-right doesn't
   end up behind the 56x56 thumbnail pinned at the top-right. */
.card { padding-bottom: 1.9rem; min-height: 6.5rem; }

.trash-btn {
    position: absolute; bottom: 0.3rem; right: 0.6rem;
    background: none; border: none; cursor: pointer;
    color: #ccc; padding: 2px; border-radius: 4px;
    transition: color 0.15s;
    line-height: 0;
    /* Sits above the thumbnail if the card is ever too short to fully clear
       it — the trash button must stay clickable no matter what. */
    z-index: 2;
}
.trash-btn:hover { color: #b94737; }

/* -------- Tags -------- */
.tag-bar {
    display: flex; flex-wrap: wrap; gap: 0.4rem;
    align-items: center; margin-bottom: 0.6rem;
}
.tag-bar-bottom {
    margin-top: 1rem;
    padding-top: 0.8rem;
    border-top: 1px dashed var(--border);
}
.tag-chip {
    display: inline-flex; align-items: center; gap: 0.3rem;
    padding: 0.2rem 0.5rem 0.2rem 0.65rem;
    background: #f1ece4; color: #5a4e3a;
    border: 1px solid #e0d6c3; border-radius: 999px;
    font-size: 0.85rem; cursor: pointer; user-select: none;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.tag-chip:hover { background: #e9e0cf; }
.tag-chip.active {
    background: var(--primary); color: #fff; border-color: var(--primary);
}
.tag-chip.active:hover { background: #4e8a2a; }
.tag-chip[draggable="true"] { cursor: grab; }
.tag-chip[draggable="true"]:active { cursor: grabbing; }
/* Touch fallback (tag-drop-touch.js): the long-pressed source chip and
   every droppable card get a subtle highlight while a tag is "picked up"
   so the user has visual feedback that the next tap will apply the tag. */
.tag-chip.tag-drag-source {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
    box-shadow: 0 0 0 4px rgba(90, 158, 47, 0.15);
}
body.tag-drag-active .card { box-shadow: 0 0 0 1px rgba(90, 158, 47, 0.35) inset; }
.tag-chip-count {
    font-size: 0.75rem; opacity: 0.7;
}
.tag-chip.active .tag-chip-count { opacity: 0.9; }
.tag-chip-x {
    background: none; border: none; cursor: pointer;
    color: inherit; opacity: 0.55; font-size: 1rem; line-height: 1;
    padding: 0 0 0 0.1rem;
}
.tag-chip.editing {
    background: #fff; color: var(--text); cursor: text;
    padding: 0.15rem 0.4rem;
}
.tag-chip-edit {
    background: transparent; border: none; outline: none;
    color: inherit; font: inherit; width: 9rem;
    padding: 0;
}
.tag-chip-x:hover { opacity: 1; }
.tag-add-input {
    background: transparent; border: 1px dashed #c7bda6;
    border-radius: 999px; padding: 0.2rem 0.7rem;
    color: var(--muted); font-size: 0.85rem; min-width: 8rem;
}
.tag-add-input:focus { outline: none; border-color: var(--primary); color: var(--text); }
.tag-delete-confirm {
    background: #fff8f0; border: 1px solid #f0d8b0; color: #8a6d3b;
    border-radius: 8px; padding: 0.6rem 0.9rem;
    margin-bottom: 0.75rem; font-size: 0.92rem;
    display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;
}
.tag-delete-confirm .btn-yes {
    background: #b94737; color: #fff; border: none; border-radius: 4px;
    padding: 0.2rem 0.7rem; cursor: pointer; font-size: 0.85rem;
}
.tag-delete-confirm .btn-no {
    background: #e4e4e4; color: #333; border: none; border-radius: 4px;
    padding: 0.2rem 0.7rem; cursor: pointer; font-size: 0.85rem;
}
.card-tags {
    display: flex; flex-wrap: wrap; gap: 0.25rem;
    margin-top: auto;
    padding-top: 0.4rem;
}
.card-tag-chip {
    display: inline-flex; align-items: center; gap: 0.25rem;
    background: #f1ece4; color: #5a4e3a;
    border: 1px solid #e0d6c3; border-radius: 999px;
    padding: 0.08rem 0.45rem 0.08rem 0.55rem;
    font-size: 0.75rem;
}
.card.tag-drop-hover {
    border-color: var(--primary); border-style: dashed;
    background: #f5fbf0;
}

/* Heart icon tucked into the top-left corner of a recipe card. Transparent
   background so it doesn't cover the title; only the heart shape itself is
   visible. Grey outline by default, red filled once the recipe is a favorite. */
.card-fav-btn {
    position: absolute; top: 0.1rem; left: 0.1rem;
    background: none; border: none; cursor: pointer;
    color: #ccc; padding: 2px; border-radius: 999px;
    line-height: 0;
    transition: color 0.15s, transform 0.15s;
    z-index: 1;
}
.card-fav-btn:hover { color: #e0493a; transform: scale(1.15); }
.card-fav-btn.on { color: #e0493a; }

/* "shared" pill on a recipe card. Sits in the same upper-left cluster
   as the heart, immediately to its right — mirrors the heart-cluster
   on the recipe detail page where the toolbar "shared" pill sits left
   of the image-upgrade button. Visible only when the recipe currently
   has a live ShareToken; hidden otherwise. Click stops propagation
   (the card itself is the recipe-open click target) and toggles an
   inline Yes / No confirmation in the same spot. */
.card-shared-btn {
    position: absolute; top: 0.2rem; left: 1.85rem;
    background: rgba(127, 162, 71, 0.14);
    border: 1px solid rgba(127, 162, 71, 0.4);
    color: var(--primary);
    border-radius: 999px;
    padding: 3px 6px;
    line-height: 0;
    cursor: pointer;
    transition: background 0.12s, transform 0.12s;
    z-index: 1;
}
.card-shared-btn:hover {
    background: rgba(127, 162, 71, 0.28);
    transform: scale(1.06);
}
.card-shared-confirm {
    position: absolute; top: 0.15rem; left: 1.85rem;
    display: inline-flex; align-items: center; gap: 0.3rem;
    padding: 0.18rem 0.45rem;
    background: rgba(190, 100, 30, 0.96);
    color: #fff;
    border-radius: 6px;
    font-size: 0.75rem; font-weight: 500;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.18);
    z-index: 2;
    white-space: nowrap;
}
.card-shared-confirm .btn-yes,
.card-shared-confirm .btn-no {
    border: 0; border-radius: 4px;
    padding: 0.05rem 0.4rem;
    font-size: 0.72rem; font-weight: 600;
    cursor: pointer;
    line-height: 1.3;
}
.card-shared-confirm .btn-yes {
    background: #fff; color: #b76a1f;
}
.card-shared-confirm .btn-no {
    background: transparent; color: rgba(255, 255, 255, 0.85);
}
.card-shared-confirm .btn-no:hover { color: #fff; }

/* Target-people control used on both the shopping list detail page and
   the create-list modal — a circle with the current number always visible,
   and −/+ buttons that fade in when the wrapper is hovered or focused. */
.sl-people {
    display: inline-flex; align-items: center; gap: 0;
    padding: 0.25rem 0.5rem; border-radius: 999px;
    outline: none; cursor: default;
}
.sl-people-circle {
    display: inline-flex; align-items: center; justify-content: center;
    width: 2.25rem; height: 2.25rem; border-radius: 50%;
    background: var(--primary); color: #fff;
    font-weight: 600; font-size: 0.95rem;
    font-variant-numeric: tabular-nums;
}
.sl-people-btn {
    background: #f2f2f2; border: 1px solid var(--border); color: #555;
    width: 2rem; height: 2rem; border-radius: 50%;
    line-height: 1; font-size: 1.1rem;
    cursor: pointer; padding: 0;
    opacity: 1; transform: scale(1);
    pointer-events: auto;
    transition: background 0.15s, color 0.15s;
    margin: 0;
}
.sl-people-dec { margin-right: 0.4rem; }
.sl-people-inc { margin-left: 0.4rem; }
.sl-people-btn:hover:not(:disabled) { background: #e4e4e4; color: #000; }
.sl-people-btn:disabled { opacity: 0.3 !important; cursor: default; }

/* Cart icon next to the heart — adds the recipe to a shopping list. */
.card-sl-btn {
    position: absolute; top: 0.25rem; left: 2rem;
    background: none; border: none; cursor: pointer;
    color: #c9a349; padding: 2px; border-radius: 999px;
    line-height: 0;
    transition: color 0.15s, transform 0.15s;
    z-index: 1;
}
.card-sl-btn:hover { color: #8a6c1f; transform: scale(1.15); }

/* Shared picker modal — matches RecipeDetail's reprocess-modal styling. */
.sl-pick-backdrop {
    position: fixed; inset: 0; background: rgba(0,0,0,0.4);
    display: flex; align-items: center; justify-content: center;
    z-index: 1000;
}
.sl-pick-modal {
    background: #fff; border-radius: 8px; padding: 1.25rem 1.5rem;
    max-width: 460px; width: calc(100% - 2rem);
    box-shadow: 0 10px 40px rgba(0,0,0,0.25);
}
.sl-pick-modal h3 { margin: 0 0 0.6rem; font-size: 1.05rem; }
.sl-pick-list {
    list-style: none; padding: 0; margin: 0.5rem 0 1rem;
    max-height: 40vh; overflow-y: auto;
}
.sl-pick-list > li { margin: 0.3rem 0; }
.sl-pick-option {
    display: block; width: 100%; text-align: left;
    background: #fef9ed; border: 1px solid #e8d9a8; border-radius: 6px;
    padding: 0.5rem 0.75rem; cursor: pointer;
    transition: border-color 0.15s;
}
.sl-pick-option:hover:not(:disabled) { border-color: #c99620; }
.sl-pick-option:disabled { opacity: 0.55; cursor: default; }
.sl-pick-option.current { background: #f3e5b8; }
.sl-pick-name { font-weight: 600; display: block; }
.sl-pick-meta { color: var(--muted); font-size: 0.85rem; }
.sl-pick-current { color: #7a5e0c; font-style: italic; margin-left: 0.25rem; }

/* Heart button next to the recipe detail title. Inherits the h1 red. */
.fav-btn {
    background: none; border: none; cursor: pointer;
    color: #d0d0d0; padding: 2px; border-radius: 6px;
    line-height: 0;
    transition: color 0.15s, transform 0.15s;
}
.fav-btn:hover { color: #e0493a; transform: scale(1.08); }
.fav-btn.on { color: #e0493a; }

/* Favorites-only filter toggle in the toolbar. Same sizing as the sort
   switch so they sit flush next to each other. Heart turns red when on. */
.fav-toggle {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: 6px; padding: 0.35rem 0.55rem; cursor: pointer;
    color: #bbb; line-height: 0;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.fav-toggle:hover { color: #e0493a; border-color: #e0c5c1; }
.fav-toggle.on {
    color: #e0493a; border-color: #e0493a; background: #fff5f3;
}

/* Admin-only reprocess icon, sits just to the left of the trash icon.
   Bottom-aligned with the card date and the trash icon. */
.reprocess-btn {
    position: absolute; bottom: 0.3rem; right: 1.9rem;
    background: none; border: none; cursor: pointer;
    color: #ccc; padding: 2px; border-radius: 4px;
    transition: color 0.15s;
    line-height: 0;
    z-index: 2;
}
.reprocess-btn:hover { color: var(--primary); }
.reprocess-btn:disabled { cursor: default; color: var(--primary); }
.reprocess-btn .spin {
    animation: rp-spin 0.9s linear infinite;
    transform-origin: 50% 50%;
}
@keyframes rp-spin {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}

.delete-confirm {
    position: absolute; bottom: 0.5rem; right: 0.5rem;
    display: flex; align-items: center; gap: 0.4rem;
    background: #fff8f7; border: 1px solid #f2c3bc; border-radius: 6px;
    padding: 0.25rem 0.5rem; font-size: 0.8rem;
}
.delete-confirm span { color: #7a2720; }
.delete-confirm .btn-yes {
    background: #b94737; color: #fff; border: none; border-radius: 4px;
    padding: 0.15rem 0.5rem; cursor: pointer; font-size: 0.75rem;
}
.delete-confirm .btn-yes:hover { background: #9a3a2d; }
.delete-confirm .btn-no {
    background: #e4e4e4; color: #333; border: none; border-radius: 4px;
    padding: 0.15rem 0.5rem; cursor: pointer; font-size: 0.75rem;
}

.add-drop-banner {
    display: block; text-align: center; text-decoration: none;
    background: var(--primary); color: #fff;
    border-radius: var(--radius); padding: 1.25rem 1.5rem;
    margin-bottom: 1rem; transition: all 0.15s;
}
.add-drop-banner:hover { background: #4a8a25; text-decoration: none; color: #fff; }
.add-drop-banner .add-drop-hint { font-size: 0.9rem; opacity: 0.85; }
.add-drop-banner.drag-over {
    background: #4a8a25; transform: scale(1.02);
    box-shadow: 0 0 16px rgba(90, 158, 47, 0.5);
    border: 2px dashed #fff;
}
.add-drop-banner.drag-over .add-drop-hint::after { content: ' here'; }

.pagination {
    display: flex; gap: 0.4rem; justify-content: center;
    margin-top: 1.5rem; flex-wrap: wrap;
}
.pagination .btn { min-width: 2.2rem; padding: 0.4rem 0.6rem; font-size: 0.9rem; }

.empty-hint {
    padding: 2rem; border: 2px dashed var(--border); border-radius: var(--radius);
    text-align: center; color: var(--muted);
}

/* First-time-user onboarding panel on /recipes when the vault is empty.
   Three cards explaining the intake channels (email / paste / extension),
   plus a "load samples" button so the user always has content to poke. */
.onboarding {
    max-width: 920px; margin: 1rem auto 2rem;
}
.onboarding-title { margin: 0 0 0.25rem; font-size: 1.6rem; }
.onboarding-lead { color: var(--muted); margin: 0 0 1.25rem; }
.onboarding-grid {
    display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem;
    margin-bottom: 1.5rem;
}
.onboarding-card {
    background: #fff; border: 1px solid var(--border); border-radius: 10px;
    padding: 1rem 1.1rem;
    display: flex; flex-direction: column; gap: 0.5rem;
}
.onboarding-card h3 { margin: 0; font-size: 1.05rem; }
.onboarding-card p { margin: 0; color: #444; font-size: 0.92rem; }
.onboarding-card .btn { align-self: flex-start; margin-top: 0.2rem; }
.onboarding-icon { font-size: 1.6rem; line-height: 1; }
.onboarding-email {
    display: inline-block; padding: 0.15rem 0.4rem;
    background: #fbf8ef; border: 1px solid #e8dfc3; border-radius: 4px;
    font-size: 0.9rem;
}
.onboarding-small { color: var(--muted); font-size: 0.82rem; }
.onboarding-samples {
    padding: 1rem 1.1rem;
    background: #fbf9f3; border: 1px dashed #d8cfb9; border-radius: 8px;
    display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
}
.onboarding-samples .onboarding-small { margin: 0; flex: 1; min-width: 16rem; }
@media (max-width: 760px) {
    .onboarding-grid { grid-template-columns: 1fr; }
}
.email-list { list-style: none; padding: 0; }
.email-list li {
    display: flex; justify-content: space-between; align-items: center;
    padding: 0.5rem 0; border-bottom: 1px solid var(--border);
}
.email-list .chip {
    font-size: 0.75rem; padding: 0.15rem 0.5rem; border-radius: 999px;
    background: #f0f0f0; color: var(--muted);
}
.email-list .chip.primary { background: #ffe3de; color: var(--primary); }
.email-list .chip.verified { background: #e5f2de; color: #2e6a28; }

.valid.modified:not([type=checkbox]) { outline: 1px solid #26b050; }
.invalid { outline: 1px solid #e50000; }
.darker-border-checkbox.form-check-input { border-color: #929292; }

#blazor-error-ui {
    background: lightyellow; bottom: 0;
    box-shadow: 0 -1px 2px rgba(0,0,0,0.2); display: none;
    left: 0; padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed; width: 100%; z-index: 1000;
}
#blazor-error-ui .dismiss { cursor: pointer; position: absolute; right: 0.75rem; top: 0.5rem; }

.blazor-error-boundary {
    background: #b32121; color: white; padding: 1rem;
}
.blazor-error-boundary::after { content: "An error has occurred."; }

/* ---------- MyRecipes: two-level sidebar navigation ---------- */
.recipes-layout {
    display: flex; gap: 1.25rem; align-items: flex-start;
}
.recipes-sidebar {
    width: 200px; flex-shrink: 0;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 0.6rem;
    position: sticky; top: 1rem;
}
.recipes-content { flex: 1; min-width: 0; }

.sidebar-section + .sidebar-section { margin-top: 1rem; }
.sidebar-heading {
    font-size: 0.72rem; font-weight: 700; letter-spacing: 0.08em;
    text-transform: uppercase; color: var(--muted);
    padding: 0.25rem 0.5rem 0.35rem; margin: 0;
}
.sidebar-item {
    display: flex; align-items: center; justify-content: space-between;
    gap: 0.4rem; width: 100%;
    padding: 0.4rem 0.55rem; margin: 0.1rem 0;
    background: transparent; border: 0; border-radius: 6px;
    font: inherit; color: var(--text); text-align: left;
    cursor: pointer;
}
.sidebar-item:hover { background: rgba(90, 158, 47, 0.08); }
.sidebar-item.active {
    background: rgba(90, 158, 47, 0.15);
    color: var(--primary); font-weight: 600;
}
.sidebar-subitem {
    padding-left: 1rem; font-size: 0.9rem;
}
.sidebar-label {
    flex: 1; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sidebar-count {
    color: var(--muted); font-size: 0.78rem;
    background: var(--bg); padding: 0.05rem 0.4rem; border-radius: 10px;
    flex-shrink: 0;
}
.sidebar-item.active .sidebar-count { background: var(--surface); color: var(--primary); }

@media (max-width: 720px) {
    .recipes-layout { flex-direction: column; }
    .recipes-sidebar {
        width: 100%; position: static;
        /* Cap height on mobile so a long title list doesn't push the cards off-screen. */
        max-height: 50vh; overflow-y: auto;
    }
}

/* Small row that hosts the Print + PDF buttons on the recipe, shopping-
   list, and meal-plan pages. Stays out of the way on screen and the
   whole row is .no-print so it never lands in the output. */
.page-actions {
    display: flex; gap: 0.5rem; flex-wrap: wrap;
    margin: 0 0 1rem;
}
.page-actions .btn { padding: 0.3rem 0.75rem; font-size: 0.85rem; }

/* Browser print output: hide every piece of chrome (topbar nav, footer,
   in-page buttons, toolbars, install prompt, cooking overlay). What's
   left is the page body — the thing the user actually wants on paper. */
@page { margin: 12mm 14mm; }
@media print {
    .topbar, .site-footer, .no-print, .install-prompt,
    #blazor-error-ui { display: none !important; }
    main { max-width: none !important; padding: 0 !important; }
    body { background: #fff !important; }
    a { color: inherit !important; text-decoration: none !important; }
}

/* PWA install prompt — small banner pinned to the bottom, built by
   /js/install-prompt.js. Styled here so first paint isn't flashes-of-
   unstyled-banner on slow connections. */
.install-prompt {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 2000;
    display: flex; align-items: center; gap: 0.75rem;
    padding: 0.65rem 0.9rem;
    background: #fff; color: #333;
    border-top: 1px solid var(--border);
    box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.08);
    font-size: 0.9rem;
    padding-bottom: calc(0.65rem + env(safe-area-inset-bottom));
}
.install-prompt-icon {
    width: 40px; height: 40px; border-radius: 8px; flex-shrink: 0;
}
.install-prompt-text {
    flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.25;
}
.install-prompt-text strong { font-size: 0.95rem; }
.install-prompt-text span { color: #666; font-size: 0.85rem; }
.install-prompt-text em { font-style: normal; font-weight: 600; color: #333; }
.install-prompt-action {
    background: var(--primary); color: #fff; border: none;
    border-radius: 6px; padding: 0.5rem 0.9rem;
    font-weight: 600; font-size: 0.9rem; cursor: pointer;
}
.install-prompt-action:hover { filter: brightness(0.95); }
.install-prompt-close {
    background: none; border: none; color: #888; font-size: 1.4rem;
    line-height: 1; cursor: pointer; padding: 0 0.25rem;
}
.install-prompt-close:hover { color: #333; }

@media (max-width: 640px) {
    /* Mobile header is two rows:
         row 1: logo + FB (left) ............. avatar + help (right)
         row 2: tab chips (Recipes / Shopping / Meal plan)
       To pull the avatar + help out of <nav> without changing markup, we
       collapse <nav> with display:contents so its children become flex
       items of .topbar, then reorder. A flex-basis:100% pseudo-element
       on .topbar acts as the row break before the tab chips. */
    .topbar {
        flex-wrap: wrap;
        padding: 0.6rem 0.75rem;
        gap: 0.5rem;
        justify-content: flex-start;
    }
    .topbar .brand { font-size: 1rem; }
    .topbar .fb-link { margin-left: 0; }

    .topbar nav {
        display: contents;
        font-size: 0.9rem;
    }

    .topbar nav .user-badge {
        order: 1;
        margin-left: auto;
        gap: 0.3rem;
    }
    .topbar nav .help-link { order: 2; }
    /* Anchor help-link to the right when there's no user-badge (signed-out). */
    .topbar nav:not(:has(.user-badge)) .help-link { margin-left: auto; }

    .topbar::after {
        content: "";
        order: 3;
        flex-basis: 100%;
        height: 0;
    }

    .topbar nav > a:not(.user-badge):not(.help-link) {
        order: 4;
    }
    /* Center the tab chips as a group on row 2. The first <a> in <nav> is
       always a tab; the last tab is the one immediately before .user-badge
       (signed-in) or .help-link (signed-out). Auto-margins on those two
       collapse the leading and trailing space and pull the group centred. */
    .topbar nav > a:first-of-type {
        margin-left: auto;
    }
    .topbar nav > a:has(+ .user-badge),
    .topbar nav > a:not(.user-badge):has(+ .help-link) {
        margin-right: auto;
    }

    /* Scale avatar + help to match the ~42px logo / FB chip on row 1. */
    .topbar .nav-avatar,
    .topbar nav .user-badge .user-badge-initials {
        width: 42px; height: 42px;
    }
    .topbar nav .user-badge .user-badge-initials {
        font-size: 1rem;
    }
    .topbar .help-link {
        width: 42px; height: 42px;
        font-size: 1.1rem;
    }
}

/* AI search bar on /my-recipes — desktop keeps the long scope note and roomy
   inputs; on narrow screens we swap in a shorter note and let the dropdown +
   input + button share a single row. */
.ai-scope-note {
    font-size: 0.85rem;
    color: #666;
    margin-bottom: 0.4rem;
}
.ai-scope-note-short { display: none; }
.ai-search-row {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    margin-bottom: 1rem;
    flex-wrap: wrap;
}
.ai-search-row .ai-search-prompt { width: auto; min-width: 240px; }
.ai-search-row .ai-search-details { flex: 1; min-width: 150px; max-width: 350px; }
.ai-search-row .ai-search-clear { padding: 0.4rem 0.6rem; }

@media (max-width: 640px) {
    .ai-scope-note-full { display: none; }
    .ai-scope-note-short { display: inline; }
    .ai-search-row { flex-wrap: nowrap; }
    .ai-search-row .ai-search-prompt {
        min-width: 0;
        flex: 0 1 auto;
        max-width: 9rem;
    }
    .ai-search-row .ai-search-details {
        min-width: 0;
        flex: 1 1 auto;
        max-width: none;
    }
    .ai-search-row .ai-search-ask { white-space: nowrap; }
}

/* ----------------------------------------------------------------
   Offline / SW integration styles. These were previously inline in
   App.razor's <body>; moving them here lets the browser parse + apply
   them once via the head <link rel=stylesheet>, instead of stalling
   HTML parse on the body-inline <style> block. The rules only fire
   when the body has the .iwe-net-offline class set by iwe-offline.js.
   ---------------------------------------------------------------- */

#iwe-net-banner {
    background: #fbebe9;
    color: #7a2720;
    border-bottom: 1px solid #e8b5ad;
    padding: 0.45rem 1rem;
    text-align: center;
    font-size: 0.9rem;
    font-weight: 500;
    position: sticky;
    top: 0;
    z-index: 1000;
}

body.iwe-net-offline #components-reconnect-modal { display: none !important; }

body.iwe-net-offline .iwe-needs-network {
    opacity: 0.55;
    pointer-events: none;
}
body.iwe-net-offline .iwe-needs-network button,
body.iwe-net-offline .iwe-needs-network input[type=submit] {
    cursor: not-allowed;
}
.iwe-offline-only { display: none; }
body.iwe-net-offline .iwe-offline-only { display: inline; }

body.iwe-net-offline .fav-btn,
body.iwe-net-offline .translate-btn,
body.iwe-net-offline .export-menu-wrap,
body.iwe-net-offline .img-upgrade-btn,
body.iwe-net-offline .sl-toolbar-btn,
body.iwe-net-offline .copy-recipe-btn,
body.iwe-net-offline .shared-pill,
body.iwe-net-offline .add-drop-banner,
body.iwe-net-offline #recipe-edit-bar,
body.iwe-net-offline .recipe-edit-bar,
body.iwe-net-offline .myrecipes-search,
body.iwe-net-offline .myrecipes-toolbar .ai-search,
body.iwe-net-offline .myrecipes-toolbar .add-banner,
/* Per-card write controls in the recipes grid. The live MyRecipes
   renders these unconditionally; gating them on .iwe-net-offline
   keeps the offline mirror (and the live page when fake-offline is
   toggled) free of buttons that would no-op or spin forever. */
body.iwe-net-offline .card .trash-btn,
body.iwe-net-offline .card .reprocess-btn,
body.iwe-net-offline .card .card-shared-btn,
body.iwe-net-offline .card .tag-chip-x {
    display: none !important;
}
/* Heart stays visible (read-only state indicator) but unclickable. */
body.iwe-net-offline .card .card-fav-btn {
    pointer-events: none;
    cursor: default;
}
body.iwe-net-offline .iwe-online-only { display: none !important; }

/* Single-row topbar on phones — overrides the legacy two-row layout
   that used display:contents + topbar::after row-break trick. */
@media (max-width: 640px) {
    /* Kill the pseudo-element row-break from the old 2-row layout */
    .topbar::after { display: none !important; }
    /* Restore nav to a normal flex item (old layout used display:contents) */
    .topbar nav {
        display: flex !important;
        order: 0;
        margin-left: auto;
        gap: 0.2rem;
    }
    /* Reset order values the old layout set on nav links so DOM order applies */
    .topbar nav > a:not(.user-badge):not(.help-link) { order: 0; }
    /* Undo auto-margins that were used to center chips on row 2 */
    .topbar nav > a:first-of-type { margin-left: 0; }
    .topbar nav > a:has(+ .user-badge),
    .topbar nav > a:not(.user-badge):has(+ .help-link) { margin-right: 0; }
    /* Single-row topbar sizing */
    .topbar {
        padding: 0.4rem 0.5rem;
        gap: 0.25rem;
        flex-wrap: nowrap;
        justify-content: flex-start;
    }
    .topbar .brand { flex-shrink: 0; }
    .topbar .brand img { height: 26px; width: 26px; }
    .topbar .fb-link { width: 26px; height: 26px; margin-left: 0; flex-shrink: 0; }
    .topbar .fb-link svg { width: 26px; height: 26px; }
    .topbar .env-badge {
        font-size: 0.6rem;
        padding: 0.1rem 0.25rem;
        margin-left: 0;
        letter-spacing: 0;
    }
    .topbar nav > a:not(.user-badge):not(.help-link) {
        padding: 0.2rem 0.45rem;
        font-size: 0.75rem;
    }
}
