/* ============================================================================
   MV HOME — STYLESHEET

   TOC
    1.  Design tokens
    2.  Theme overrides (fresh / ocean / warm / dark)
    3.  Global reset & base
    4.  Shared primitives (buttons, inputs, pills, tiles)
    5.  Shared shell (topbar, tabbar, modal shell, alert, toast, keyframes, skeletons)
    6.  Home tab
    7.  Alerts panel
    8.  Item modal — Add Product (add-only)
    9.  Consume Stock page
    10. Shopping List page + Bought overlay
    11. Settings
    12. Recipes rework
    13. Product Detail overlay + sheet stack
    14. Add Product modal — elevated inputs + custom dropdown
    15. Receipt scan
    16. iOS 18 polish
   ============================================================================ */


/* ============================================================================
   1. DESIGN TOKENS
   ============================================================================ */
:root {
  --font-display: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-ui: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --tabbar-clearance: calc(100px + var(--safe-bottom));
  --kb: 0px;                                   /* on-screen keyboard height, set by initKeyboardInset() */

  /* Safe-area aliases — shorthand for env(safe-area-inset-*). */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  /* Motion durations — pair with --ease-ios / --ease-spring. */
  --duration-fast: 160ms;
  --duration-base: 240ms;
  --duration-slow: 340ms;
  /* Largest corner radius (full-screen sheets, success popups). */
  --radius-xl: 28px;

  /* Numbered half-step radii — the values the design uses between the
     named scale stops. Naming them removes the raw px literals from
     the rest of the file; the computed values are identical. */
  --radius-12: 12px;
  --radius-16: 16px;
  --radius-20: 20px;
  --radius-24: 24px;
  --radius-32: 32px;
  --ease-ios: cubic-bezier(0.32, 0.72, 0, 1);  /* iOS "settle" curve */
  --ease-spring: cubic-bezier(0.34, 1.15, 0.64, 1);
  --app-scale: 1;                              /* App Scale slider value (1 = 100%) */

  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;


  /* Half-step tokens — the two values the original design used between
     the main scale stops. Naming them here removes ~40 hardcoded
     10px/14px literals from the rest of the stylesheet. */
  --space-2-5: 10px;
  --space-3-5: 14px;
  --radius-xs: 10px;
  --radius-sm: 14px;
  --radius-md: 18px;
  --radius-lg: 22px;
  --radius-pill: 999px;

  --z-base: 1;
  --z-sticky: 10;
  --z-fab: 25;
  --z-tabbar: 999;
  --z-search-results: 20;
  --z-alerts-panel: 70;
  --z-modal: 1000;
  --z-alert-modal: 2000;
  --z-toast: 1500;

  --glass-card-shadow:
    0 8px 22px rgba(0, 0, 0, 0.09),
    0 1px 3px rgba(0, 0, 0, 0.05),
    inset 0 1px 0 rgba(255, 255, 255, 0.9);
  --glass-photo-bg: rgba(255, 255, 255, 0.22);
  --glass-photo-border: rgba(255, 255, 255, 0.3);
  --glass-photo-blur: 5px;
  --glass-photo-shadow: 0 4px 10px rgba(0, 0, 0, 0.12);
  --panel-solid-bg: color-mix(in srgb, var(--surface) 70%, var(--bg) 30%);
}


/* ============================================================================
   2. THEME OVERRIDES
   ============================================================================ */
:root,
[data-theme="fresh"] {
  --bg: #FFFFFF;
  --surface: #FFFFFF;
  --surface-2: #EAF5E7;
  --surface-alt: #F1F3F1;
  --border: #DCEBD6;
  --ink: #1F2A20;
  --muted: #7C8A7A;
  --primary: #51950A;
  --primary-2: #315906;
  --primary-ink: #FFFFFF;
  --warn: #C9862E;
  --warn-bg: #FBF0DD;
  --danger: #E0483E;
  --danger-bg: #FDEAE8;
  --hero-grad: linear-gradient(135deg, #51950A 0%, #315906 100%);
  --pill-bg: #F1F3F1;
  --pill-active-bg: #E5F2D0;
  --info: #2E7FB8;
  --info-bg: #DCEEFB;
  --cat-veg-bg: #E4F3DD;
  --cat-fruit-bg: #FDEAD3;
  --cat-meat-bg: #FBE1DE;
  --cat-dairy-bg: #DCEAFB;
  --cat-bakery-bg: #F6E8D2;
  --cat-grain-bg: #EFE7D8;
  --cat-drink-bg: #DFF0F5;
  --cat-snack-bg: #F7E6C8;
  --cat-frozen-bg: #DFF1F7;
  --cat-sauce-bg: #FBE0DC;
  --cat-spice-bg: #F0E4EF;
  --cat-instant-bg: #FCEFCF;
  --cat-clean-bg: #E4EAF6;
  --cat-default-bg: #EAF5E7;
  --nutri-calorie-bg: #FDE8DD;
  --nutri-calorie-ink: #E8622C;
  --nutri-protein-bg: #FDE2E2;
  --nutri-protein-ink: #D64550;
  --nutri-fat-bg: #FFF3D6;
  --nutri-fat-ink: #D9A02A;
  --nutri-carbs-bg: #EAF3DE;
  --nutri-carbs-ink: #5C8B4A;
  --glass-bg: color-mix(in srgb, var(--surface) 70%, transparent);
  --glass-border: rgba(255, 255, 255, 0.5);
  --glass-blur: 10px;
  --hero-photo: url('Image/rain-fresh.jpg');
  --hero-pos: center 40%;
  --hero-shade: 0.45;

  --field-bg: #F4F6F8;
  --field-border: #E2E8F0;
  --card-edge: rgba(255, 255, 255, 0.7);
  --hairline: rgba(0, 0, 0, 0.04);
  --sheet-under: color-mix(in srgb, var(--primary-2) 55%, #000);
  --banner-overlay: linear-gradient(120deg, rgba(20, 90, 40, 0.82) 0%, rgba(24, 120, 58, 0.55) 55%, rgba(20, 90, 40, 0.82) 100%);
  --topbar-strong: 45%;
  --topbar-soft: 20%;
  --tile-edge: color-mix(in srgb, var(--primary) 10%, transparent);
  --tile-shadow: 0 4px 10px color-mix(in srgb, var(--primary) 14%, transparent);
  --body-rim-shadow: 0 -4px 12px rgba(0, 0, 0, 0.06);
  --body-glow-shadow: none;
  --popup-bg: var(--surface);
  --popup-edge: transparent;
  --popup-shadow: 0 16px 40px rgba(0, 0, 0, 0.18), inset 0 1px 0 rgba(255, 255, 255, 0.9);
  --tile-bg: var(--surface);
  --tile-selected-bg: var(--surface-2);
  --tile-selected-shadow: 0 4px 14px color-mix(in srgb, var(--primary) 28%, transparent);
  --amount-ink: #B3261E;
  --tabbar-inactive: #c4c4c4;
  --fab-ring: var(--surface);

  --input-bg: var(--surface);
  --input-inset-bg: var(--surface-alt);
  --input-focus-bg: var(--surface);
  --input-border: var(--border);
  --input-inset-border: transparent;
  --input-search-border: var(--hairline);

  --row-card-bg: var(--surface);
  --row-card-border: var(--hairline);
  --row-thumb-bg: var(--surface-alt);
  --row-shadow: 0 8px 22px rgba(0, 0, 0, 0.09), 0 1px 3px rgba(0, 0, 0, 0.05);
  --row-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.9);

  --nutri-card-bg: var(--row-card-bg);
  --nutri-card-edge: var(--card-edge);
  --nutri-card-shadow: var(--glass-card-shadow);

  /* Toast icon tints. Same values on every theme because the toast
     background is a fixed dark #1a1a1a regardless of theme. Promoted
     to variables so a future theme can override them if needed. */
  --toast-icon-success: #7ED321;
  --toast-icon-error:   #FF6B6B;
  --toast-icon-warn:    #FFD93D;
}

[data-theme="ocean"] {
  /* Navy palette built from the attached colour code:
     Primary  #19253D · #25375A · #344D7F · #809DCB
     Text 1   #0E1725   Text 2  #809DCB  Text 3  #C9D3E9  Text 4  #EDF0F8
     BG 1     #C1C7D0   BG 2    #F3F3F3  BG 3    #FAFBFC  BG 4    #FFFFFF
     Secondary (coral)  #FF3E80 (danger), #FF8A4F (warn)
     Tertiary  (green)  reserved for accent chips — left to --primary path */
  --bg: #FFFFFF;
  --surface: #FFFFFF;
  --surface-2: #EDF0F8;
  --surface-alt: #F3F3F3;
  --border: #C9D3E9;
  --ink: #0E1725;
  --muted: #809DCB;
  --primary: #25375A;
  --primary-2: #19253D;
  --primary-ink: #FFFFFF;
  --warn: #FF8A4F;
  --warn-bg: #FFECEC;
  --danger: #FF3E80;
  --danger-bg: #FFE0E8;
  --hero-grad: linear-gradient(135deg, #344D7F 0%, #19253D 100%);
  --pill-bg: #EDF0F8;
  --pill-active-bg: #C9D3E9;
  --info: #344D7F;
  --info-bg: #EDF0F8;
  --glass-bg: color-mix(in srgb, var(--surface) 70%, transparent);
  --glass-border: rgba(255, 255, 255, 0.5);
  --glass-blur: 10px;
  --hero-photo: url('Image/rain-ocean.jpg');
  --hero-pos: center 30%;
  --hero-shade: 0.62;
  --tile-bg: var(--surface);
  --tile-shadow: 0 4px 10px color-mix(in srgb, var(--primary) 14%, transparent);
  --tile-selected-shadow: 0 4px 14px color-mix(in srgb, var(--primary) 28%, transparent);
}

[data-theme="warm"] {
  --bg: #FFFFFF;
  --surface: #FFFFFF;
  --surface-2: #F5E8D3;
  --surface-alt: #F3E9D8;
  --border: #EBD9BC;
  --ink: #2E2113;
  --muted: #8C7A64;
  --primary: #D46A34;
  --primary-2: #A84E22;
  --primary-ink: #FFFFFF;
  --warn: #C9862E;
  --warn-bg: #F3E3C6;
  --danger: #C0392B;
  --danger-bg: #F5DAD3;
  --hero-grad: linear-gradient(135deg, #E88456 0%, #A84E22 100%);
  --pill-bg: #F3E9D8;
  --pill-active-bg: #EBDBBE;
  --info: #2E7FB8;
  --info-bg: #DCEEFB;
  --glass-bg: color-mix(in srgb, var(--surface) 70%, transparent);
  --glass-border: rgba(255, 255, 255, 0.5);
  --glass-blur: 10px;
  --hero-photo: url('Image/rain-warm.jpg');
  --hero-pos: center 50%;
  --hero-shade: 0.55;
  --tile-bg: var(--surface);
  --tile-shadow: 0 4px 10px color-mix(in srgb, var(--primary) 14%, transparent);
  --tile-selected-shadow: 0 4px 14px color-mix(in srgb, var(--primary) 28%, transparent);
}

[data-theme="cartoon"] {
  /* Cartoon — warm cream + lime green, referencing the flat-illustration
     grocery app mockups. High-contrast, playful, no dark tones. */
  --bg: #FBF3DF;                 /* warm cream */
  --surface: #FFFFFF;
  --surface-2: #F0E9C8;          /* pale yellow-tan */
  --surface-alt: #F5EDD6;
  --border: #E5D9B8;
  --ink: #2E3A1F;                /* deep olive, not black */
  --muted: #8A7E5C;
  --primary: #8CBD42;            /* lime green — the reference hero */
  --primary-2: #6A9A2E;
  --primary-ink: #FFFFFF;
  --warn: #F5A623;
  --warn-bg: #FFF3D6;
  --danger: #E5484D;
  --danger-bg: #FDEAEA;
  --hero-grad: linear-gradient(135deg, #8CBD42 0%, #6A9A2E 100%);
  --pill-bg: #F5EDD6;
  --pill-active-bg: #E5EECB;
  --info: #4A90D9;
  --info-bg: #E0EFFB;
  --cat-veg-bg: #E5F0C8;
  --cat-fruit-bg: #FFE9C8;
  --cat-meat-bg: #FFDDD8;
  --cat-dairy-bg: #DBE9F5;
  --cat-bakery-bg: #F5E5C8;
  --cat-grain-bg: #EFE7D8;
  --cat-drink-bg: #DDEEF0;
  --cat-snack-bg: #F5E5C8;
  --cat-frozen-bg: #DDEEF0;
  --cat-sauce-bg: #F5DDD8;
  --cat-spice-bg: #EDDFE0;
  --cat-instant-bg: #F5E9C8;
  --cat-clean-bg: #E0E5F0;
  --cat-default-bg: #EFE7D8;
  --nutri-calorie-bg: #FDE8DD;
  --nutri-calorie-ink: #E8622C;
  --nutri-protein-bg: #FDE2E2;
  --nutri-protein-ink: #D64550;
  --nutri-fat-bg: #FFF3D6;
  --nutri-fat-ink: #D9A02A;
  --nutri-carbs-bg: #EAF3DE;
  --nutri-carbs-ink: #5C8B4A;
  --glass-bg: color-mix(in srgb, var(--surface) 70%, transparent);
  --glass-border: rgba(255, 255, 255, 0.5);
  --glass-blur: 10px;
  /* Reuse the fresh hero photo for now — a bespoke illustration would
     complete the look, but that's a separate asset pass. */
  --hero-photo: url('Image/rain-cartoon.jpg');
  --hero-pos: center 40%;
  --hero-shade: 0.45;
  --field-bg: #F9F5E8;
  --field-border: #E5D9B8;
  --card-edge: rgba(255, 255, 255, 0.7);
  --hairline: rgba(0, 0, 0, 0.04);
  --sheet-under: color-mix(in srgb, var(--primary-2) 55%, #000);
  --banner-overlay: linear-gradient(120deg, rgba(48, 90, 20, 0.82) 0%, rgba(70, 120, 30, 0.5) 55%, rgba(48, 90, 20, 0.82) 100%);
  --topbar-strong: 42%;
  --topbar-soft: 18%;
  --tile-edge: color-mix(in srgb, var(--primary) 10%, transparent);
  --tile-shadow: 0 4px 10px color-mix(in srgb, var(--primary) 14%, transparent);
  --body-rim-shadow: 0 -4px 12px rgba(0, 0, 0, 0.06);
  --body-glow-shadow: none;
  --popup-bg: var(--surface);
  --popup-edge: transparent;
  --popup-shadow: 0 16px 40px rgba(0, 0, 0, 0.18), inset 0 1px 0 rgba(255, 255, 255, 0.9);
  --tile-bg: var(--surface);
  --tile-selected-bg: var(--surface-2);
  --tile-selected-shadow: 0 4px 14px color-mix(in srgb, var(--primary) 28%, transparent);
  --amount-ink: #D64545;
  --tabbar-inactive: #B8AC8C;
  --fab-ring: var(--surface);
  --input-bg: var(--surface);
  --input-inset-bg: var(--surface-alt);
  --input-focus-bg: var(--surface);
  --input-border: var(--border);
  --input-inset-border: transparent;
  --input-search-border: var(--hairline);
  --row-card-bg: var(--surface);
  --row-card-border: var(--hairline);
  --row-thumb-bg: var(--surface-alt);
  --row-shadow: 0 8px 22px rgba(0, 0, 0, 0.09), 0 1px 3px rgba(0, 0, 0, 0.05);
  --row-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.9);
  --nutri-card-bg: var(--row-card-bg);
  --nutri-card-edge: var(--card-edge);
  --nutri-card-shadow: var(--glass-card-shadow);
  --toast-icon-success: #7ED321;
  --toast-icon-error:   #FF6B6B;
  --toast-icon-warn:    #FFD93D;
}

/* Cartoon structural cues — the "sticker" feel. Hard-edged offset
   shadows with no blur on the primary surfaces, so cards read as flat
   shapes lifted slightly off the page. Scoped to cartoon; other themes
   keep their soft blurred shadows. Kept restrained — one lift step,
   not the exaggerated drop the reference illustrations use. */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .grid-item,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .consume-list .consume-row-content,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .settings-section,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-card-wrap {
  box-shadow: 2px 2px 0 rgba(46, 58, 31, 0.10);
  border: 1px solid rgba(46, 58, 31, 0.08);
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) .grid-item:active {
  transform: translate(1px, 1px) scale(0.98);
  box-shadow: 1px 1px 0 rgba(46, 58, 31, 0.10);
}

/* ---- Cartoon phase 2: brush-edge masking --------------------------------
   Hand-drawn wavy edges on the wide hero surfaces. Applied with
   mask-image so the wave is a real clip, not a fake border. The
   heroes were chosen because each is a single wide element with no
   absolutely-positioned children near the edges — masking them
   cannot clip functional chrome. Grid items, rows, buttons, and the
   promo banner are deliberately NOT masked: their radius is either
   load-bearing (swipe reveals) or they contain children the wave
   would clip.

   The mask is a horizontal band with rough top/bottom edges and
   straight sides (which suits a full-width hero). preserveAspectRatio
   none lets one SVG stretch across any hero width. */
:is([data-theme="cartoon"], [data-theme="cbtl"]) {
  --cartoon-brush-mask: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1000 200' preserveAspectRatio='none'%3E%3Cpath fill='black' d='M3 5 Q 30 1, 65 4 T 135 3 Q 185 0, 245 5 T 355 3 Q 415 0, 475 5 T 585 3 Q 645 0, 705 5 T 815 3 Q 875 0, 935 5 Q 970 7, 997 4 L 997 196 Q 970 200, 935 196 T 815 197 Q 755 200, 705 196 T 585 197 Q 525 200, 475 196 T 355 197 Q 295 200, 245 196 T 135 197 Q 75 200, 35 196 Q 12 194, 3 197 Z'/%3E%3C/svg%3E");
}

:is([data-theme="cartoon"], [data-theme="cbtl"]) .modal-add-hero,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-edit-hero,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-cook-hero,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-preview-hero {
  -webkit-mask-image: var(--cartoon-brush-mask);
  mask-image: var(--cartoon-brush-mask);
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

/* The mask clips shadows and outline borders, so the surfaces that
   carried a lift shadow drop it in cartoon theme and get their lift
   from the sticker offset on the cards below instead. */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .consume-header {
  position: relative;
  background: transparent;
  box-shadow: none;
  border-radius: 0;
  /* Stacking-context reset: a stacking context on the header traps the
     search dropdown below .consume-body (which paints its opaque
     background at z-index: 3). Reset every property that could
     introduce one. */
  z-index: auto;
  isolation: auto;
  opacity: 1;
  filter: none;
  -webkit-filter: none;
  transform: none;
  will-change: auto;
  contain: none;
  mix-blend-mode: normal;
  clip-path: none;
  -webkit-clip-path: none;
  perspective: none;
  -webkit-mask-image: none;
  mask-image: none;
}

/* Brush-edged gradient on a pseudo-element. The header itself must
   stay mask-free: mask-image creates a stacking context AND clips
   descendants, which is exactly what was hiding the search results
   dropdown behind .consume-body. */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .consume-header::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--header-fade, var(--hero-grad));
  -webkit-mask-image: var(--cartoon-brush-mask);
  mask-image: var(--cartoon-brush-mask);
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  pointer-events: none;
}
/* .consume-body cartoon override removed in PATCH 53 — it was
   zeroing the top corner radius, which flattened the hero/body
   transition on Settings (the only tab without a page-scoped
   .consume-body rule to re-assert the curve). The base rule's
   --radius-lg top corners now apply across all tabs. */

/* Recipe detail / cook / edit heroes sit above a body that curls up
   over them with a 22px radius. Keep that behaviour — the mask only
   waves the top and bottom edges, and the body's own radius draws
   the transition cleanly. */

/* Cartoon tabbar icons: swap the stroke-line set for a chunky
   flat-shape set. Original icons stay in the markup, just hidden. */
.tab-btn .tab-icon-cartoon { display: none; }
:is([data-theme="cartoon"], [data-theme="cbtl"]) .tab-btn > svg:not(.tab-icon-cartoon) { display: none; }
:is([data-theme="cartoon"], [data-theme="cbtl"]) .tab-btn > svg.tab-icon-cartoon {
  display: block;
  stroke-width: 2;
}

/* Cartoon promo banner decoration — a simple flat bag illustration
   pinned to the bottom-right of the banner. Purely decorative; the
   chip row keeps its own stacking context and z-index above it. */
/* Cartoon promo banner. The banner photo is loaded by
   loadPromoBannerBackground() from /api/image/banner/grocery-basket —
   NOT from --hero-photo, which only feeds modal heroes. Hide that
   image and paint a local illustration instead. The file lives at
   frontend/Image/cartoon-banner.jpg (see prompt in patch notes). */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .promo-banner-bg {
  display: none;
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) .promo-banner::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  background: url('Image/cartoon-banner.jpg') center 40% / cover no-repeat;
  opacity: calc(1 - var(--banner-shrink, 0));
  transition: opacity 0.25s ease-out;
  pointer-events: none;
}

:is([data-theme="cartoon"], [data-theme="cbtl"]) .promo-banner::after {
  content: '';
  position: absolute;
  right: 14px;
  bottom: 12px;
  width: 68px;
  height: 68px;
  z-index: 1;
  pointer-events: none;
  opacity: 0.9;
  background-repeat: no-repeat;
  background-size: contain;
  background-position: center;
  background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath d='M14 22h36l-3 32a4 4 0 0 1-4 3.6H21a4 4 0 0 1-4-3.6L14 22z' fill='%23FFF6DE' stroke='%232E3A1F' stroke-width='2.4' stroke-linejoin='round'/%3E%3Cpath d='M22 22v-4a10 10 0 0 1 20 0v4' fill='none' stroke='%232E3A1F' stroke-width='2.4' stroke-linecap='round'/%3E%3Ccircle cx='25' cy='34' r='2.2' fill='%23E5484D'/%3E%3Ccircle cx='39' cy='34' r='2.2' fill='%23E5484D'/%3E%3Cpath d='M24 42c3 2.6 13 2.6 16 0' fill='none' stroke='%232E3A1F' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* Cartoon photo filter (#cartoonify) + square brush mask were removed
   in PATCH 53 — the posterize/edge chain read as an over-saturated
   photo rather than an illustration. Thumbnails render the original
   photo with the base rounded corners. The hero brush mask from
   PATCH 50 (--cartoon-brush-mask) is unaffected. */

[data-theme="dark"] {
  --bg: #0A0E11;
  --surface: #13171A;
  --surface-2: #1B1F23;
  --surface-alt: #25292C;
  --border: #2A2F33;
  --ink: #F2F4F5;
  --muted: #8F969C;
  --primary: #7A9A2F;
  --primary-2: #5F7C22;
  --primary-ink: #FFFFFF;
  --warn: #E3B15C;
  --warn-bg: #2A2418;
  --danger: #F0505A;
  --danger-bg: #2A1719;
  --hero-grad: linear-gradient(135deg, #5F7C22 0%, #2A3A0E 100%);
  --pill-bg: #25292C;
  --pill-active-bg: #2B3818;
  --info: #6CC3C3;
  --info-bg: #0F2A2D;
  --cat-veg-bg: #1E2A18;
  --cat-fruit-bg: #2E2618;
  --cat-meat-bg: #2E1C1E;
  --cat-dairy-bg: #1C2733;
  --cat-bakery-bg: #2A2519;
  --cat-grain-bg: #262419;
  --cat-drink-bg: #16292C;
  --cat-snack-bg: #2A2616;
  --cat-frozen-bg: #172A30;
  --cat-sauce-bg: #2E1C1A;
  --cat-spice-bg: #271F2B;
  --cat-instant-bg: #2E2517;
  --cat-clean-bg: #1D2532;
  --cat-default-bg: #1E2226;
  --nutri-calorie-bg: #2E2119;
  --nutri-calorie-ink: #FF9A5A;
  --nutri-protein-bg: #2E1D20;
  --nutri-protein-ink: #FF7A85;
  --nutri-fat-bg: #2B2716;
  --nutri-fat-ink: #E8C468;
  --nutri-carbs-bg: #222E16;
  --nutri-carbs-ink: #A3C14A;
  --glass-bg: rgba(21, 24, 29, 0.72);
  --glass-border: rgba(255, 255, 255, 0.08);
  --glass-blur: 12px;
  --hero-photo: url('Image/rain-dark.jpg');
  --hero-pos: center 40%;
  --hero-shade: 0.3;
  --glass-card-shadow:
    0 8px 22px rgba(0, 0, 0, 0.5),
    0 1px 3px rgba(0, 0, 0, 0.35),
    inset 0 1px 0 rgba(255, 255, 255, 0.06);
  --glass-photo-bg: rgba(255, 255, 255, 0.06);
  --glass-photo-border: rgba(255, 255, 255, 0.10);
  --glass-photo-shadow: 0 4px 10px rgba(0, 0, 0, 0.4);
  --panel-solid-bg: #12151A;

  --input-bg: #1C2024;
  --input-inset-bg: #1C2024;
  --input-focus-bg: #23282C;
  --input-border: #2E3338;
  --input-inset-border: #2E3338;
  --input-search-border: #2E3338;
  --field-bg: var(--input-bg);
  --field-border: var(--input-border);
  --row-card-bg: #15181D;
  --row-card-border: #2A3035;
  --row-thumb-bg: #25292C;
  --row-shadow: 0 6px 16px rgba(0, 0, 0, 0.55), 0 0 18px rgba(122, 154, 47, 0.10);
  --row-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.08), inset 0 -1px 0 rgba(0, 0, 0, 0.35);
  --nutri-card-bg: var(--row-card-bg);
  --nutri-card-edge: rgba(255, 255, 255, 0.10);
  --nutri-card-shadow:
    0 8px 20px rgba(0, 0, 0, 0.5),
    0 1px 3px rgba(0, 0, 0, 0.4),
    0 0 16px rgba(122, 154, 47, 0.08),
    inset 0 1px 0 rgba(255, 255, 255, 0.12),
    inset 0 -1px 0 rgba(0, 0, 0, 0.35);
  --card-edge: rgba(255, 255, 255, 0.07);
  --hairline: rgba(255, 255, 255, 0.06);
  --sheet-under: #0F1316;
  --banner-overlay: linear-gradient(120deg, rgba(15, 25, 8, 0.86) 0%, rgba(45, 65, 20, 0.5) 55%, rgba(15, 25, 8, 0.86) 100%);
  --topbar-tint: #5F7C22;
  --header-fade: linear-gradient(180deg,
    color-mix(in srgb, var(--topbar-tint) var(--topbar-strong), transparent) 0%,
    color-mix(in srgb, var(--topbar-tint) var(--topbar-soft), transparent) 38%,
    transparent 76%);
  --body-rim-shadow:
    0 -1px 0 rgba(163, 193, 74, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.14);
  --body-glow-shadow: 0 -8px 22px rgba(95, 124, 34, 0.40);
  --popup-bg: linear-gradient(180deg, rgba(255, 255, 255, 0.06) 0%, rgba(255, 255, 255, 0) 55%), #1A1E22;
  --popup-edge: rgba(255, 255, 255, 0.10);
  --popup-shadow:
    0 24px 60px rgba(0, 0, 0, 0.65),
    0 0 26px rgba(122, 154, 47, 0.10),
    inset 0 1px 0 rgba(255, 255, 255, 0.14),
    inset 0 -1px 0 rgba(0, 0, 0, 0.35);
  --alerts-hero-shadow:
    0 1px 0 rgba(163, 193, 74, 0.45),
    0 6px 18px rgba(95, 124, 34, 0.30),
    inset 0 -1px 0 rgba(255, 255, 255, 0.10);
  --header-icon: #A3C14A;
  --topbar-strong: 38%;
  --topbar-soft: 12%;
  --tile-bg: var(--nutri-card-bg);
  --tile-edge: rgba(255, 255, 255, 0.10);
  --tile-shadow:
    0 5px 12px rgba(0, 0, 0, 0.5),
    0 0 12px rgba(122, 154, 47, 0.06),
    inset 0 1px 0 rgba(255, 255, 255, 0.12),
    inset 0 -1px 0 rgba(0, 0, 0, 0.35);
  --tile-selected-bg: linear-gradient(180deg, rgba(122, 154, 47, 0.26) 0%, rgba(122, 154, 47, 0.10) 100%), rgba(38, 43, 47, 0.9);
  --tile-selected-shadow:
    0 4px 14px rgba(122, 154, 47, 0.32),
    inset 0 1px 0 rgba(255, 255, 255, 0.18),
    inset 0 -1px 0 rgba(0, 0, 0, 0.3);
  --amount-ink: #A3C14A;
  --tabbar-inactive: #5A6167;
  --fab-ring: color-mix(in srgb, var(--surface) 75%, var(--ink) 25%);
}


/* ============================================================================
   3. GLOBAL RESET & BASE
   ============================================================================ */
* {
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
}

/* App Scale compensation: zoom on <html> scales the render but not the viewport. */
html {
  width: calc(100% / var(--app-scale));
  max-width: calc(100vw / var(--app-scale));
  min-height: calc(100vh / var(--app-scale));
  /* `clip` (not `hidden`) — `hidden` would create a scroll container and kill position: sticky. */
  overflow-x: clip;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  touch-action: pan-x pan-y;
}
body {
  overflow-x: clip;
  max-width: 100%;
  min-height: calc(100vh / var(--app-scale));
  touch-action: pan-x pan-y;
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-ui);
  padding-bottom: var(--tabbar-clearance);
  -webkit-font-smoothing: antialiased;
  transition: background 0.2s ease, color 0.2s ease;
}
html.modal-lock,
body.modal-lock {
  overflow: hidden;
  /* iOS Safari ignores overflow: hidden for its own rubber-band
     gesture. overscroll-behavior: none is the only lever that stops
     the document from bouncing when a fixed overlay is dragged. */
  overscroll-behavior: none;
  /* Belt-and-braces: block the OS-level gesture recognisers that can
     still fire even with overflow: hidden. Anything inside the modal
     that needs its own vertical scroll resets this on itself. */
  touch-action: pan-y;
}
.modal-body, .bought-list, .alerts-panel, .consume-search-results {
  overscroll-behavior-y: contain;
}

main {
  padding: 4px var(--space-4) 28px;
  max-width: 640px;
  min-height: calc(100vh / var(--app-scale));
  margin: 0 auto;
  position: relative;
}

h2 {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.2rem;
  margin: var(--space-2) 0 14px;
  letter-spacing: -0.01em;
}

.muted { color: var(--muted); }
.muted.small { font-size: 0.78rem; }

.sr-svg { position: absolute; }

.section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
  position: relative;
}
.section-header h2 { font-size: 1.1rem; font-weight: 700; margin: 0; }

/* Tab show/hide + transition */
.tab { display: none; }
.tab.active { display: block; animation: tabIn 0.22s ease-out; }
body[data-nav="push"] .tab.active { animation: tabInFromRight 0.34s var(--ease-ios); }
body[data-nav="pop"]  .tab.active { animation: tabInFromLeft 0.34s var(--ease-ios); }
body[data-nav="none"] .tab.active { animation: none; }
.tab.slide-out {
  display: block;
  position: absolute;
  top: 4px;
  left: var(--space-4);
  right: var(--space-4);
  z-index: 5;
  pointer-events: none;
  animation: tabOut 0.16s ease-out forwards;
}
body[data-nav="push"] .tab.slide-out { animation: tabOutPush 0.34s var(--ease-ios) forwards; }
body[data-nav="pop"]  .tab.slide-out { animation: tabOutPop 0.34s var(--ease-ios) forwards; }


/* ============================================================================
   4. SHARED PRIMITIVES
   ============================================================================ */

/* --- Buttons --- */
.btn {
  background: var(--primary);
  color: var(--primary-ink);
  border: none;
  border-radius: var(--radius-xs);
  padding: 11px 18px;
  font-weight: 600;
  font-size: 0.92rem;
  font-family: var(--font-ui);
  min-height: 40px;
  transition: transform 0.24s var(--ease-ios);
  text-align: center;
}
.btn:active { transform: scale(0.96); }
.btn.secondary { background: var(--surface-2); color: var(--ink); border: 1px solid var(--border); }
.btn.small { padding: 8px 13px; font-size: 0.82rem; min-height: 34px; }
.btn.danger { background: var(--danger); color: white; }

.mt-sm { margin-top: var(--space-2-5); }

.icon-btn {
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--glass-border);
  color: var(--ink);
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--glass-card-shadow);
  transition: background 0.15s ease, transform 0.24s var(--ease-ios);
}
.icon-btn.on-photo {
  background: var(--glass-photo-bg);
  backdrop-filter: blur(var(--glass-photo-blur));
  -webkit-backdrop-filter: blur(var(--glass-photo-blur));
  border: 1px solid var(--glass-photo-border);
  box-shadow: var(--glass-photo-shadow);
}
.icon-btn svg { width: 20px; height: 20px; }
.icon-btn:active { transform: scale(0.92); }
.icon-btn[hidden] { display: none; }
/* 44pt minimum touch target (Apple HIG). The icon stays visually 40px;
   a transparent pseudo-element extends the hit area by 2px on each side. */
.icon-btn { position: relative; }
.icon-btn::before { content: ''; position: absolute; inset: -2px; border-radius: inherit; }

.icon-btn.spacer { visibility: hidden; }

.add-btn-inline {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: var(--primary);
  color: var(--primary-ink);
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  box-shadow: 0 4px 10px rgba(0,0,0,0.12);
  transition: transform 0.24s var(--ease-ios);
}
.add-btn-inline:active { transform: scale(0.94); }

/* --- Inputs --- */
input[type="search"], input[type="number"], input[type="text"], input[type="date"], select, textarea {
  background: var(--input-bg);
  border: 1px solid var(--input-border);
  color: var(--ink);
  padding: 10px 13px;
  border-radius: var(--radius-xs);
  font-size: 0.9rem;
  font-family: var(--font-ui);
  box-sizing: border-box;
}
input[type="number"], input[type="text"], input[type="date"], select {
  height: 44px;
}
input[type="number"] {
  -moz-appearance: textfield;
  appearance: textfield;
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  appearance: none;
  margin: 0;
  height: auto;
}
select {
  -webkit-appearance: none;
  appearance: none;
  padding-right: 34px;
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%237C8A7A' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}
input::placeholder, textarea::placeholder { color: color-mix(in srgb, var(--muted) 75%, transparent); }
input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--primary);
  outline-offset: 1px;
}
input[type="date"] {
  width: 100%;
  color-scheme: light;
  -webkit-appearance: none;
  appearance: none;
  text-align: left;
}
input[type="date"]::-webkit-date-and-time-value { text-align: left; min-height: 1.2em; }
[data-theme="dark"] input[type="date"] { color-scheme: dark; }

.field-label {
  display: block;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--muted);
  margin: var(--space-3) 0 5px;
}
.field-label.flush { margin: 0; }

/* --- No-expiry chip --- */
.exp-field { position: relative; min-width: 0; }
.exp-field input[type="date"] { padding-right: 36px; }
.exp-field input[type="date"]::-webkit-calendar-picker-indicator {
  display: none;
  opacity: 0;
  width: 0;
  height: 0;
  margin: 0;
  padding: 0;
  pointer-events: none;
  position: absolute;
}
.exp-field.has-leading-icon input[type="date"] { padding-right: 0; }
.noexp-chip {
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  width: 28px;
  height: 28px;
  padding: 0;
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: var(--font-ui);
  font-size: 0.85rem;
  font-weight: 700;
}
.noexp-chip::before { content: ''; position: absolute; inset: -8px; }
.noexp-chip svg { width: 18px; height: 18px; flex-shrink: 0; }
.noexp-chip:active { transform: translateY(-50%) scale(0.92); }
.exp-field.has-leading-icon .noexp-chip {
  left: 2px;
  right: auto;
  width: 24px;
  height: 24px;
}
.noexp-label { display: none; }
.exp-field.no-expiry input[type="date"] { display: none; }
.exp-field.no-expiry .noexp-chip {
  position: static;
  transform: none;
  width: 100%;
  height: 44px;
  border-radius: var(--radius-12);
  border: 1px solid var(--primary);
  color: var(--primary-2);
  background: color-mix(in srgb, var(--primary) 14%, var(--input-bg));
}
.exp-field.no-expiry .noexp-chip::before { display: none; }
.exp-field.no-expiry .noexp-chip:active { transform: scale(0.97); }
.exp-field.no-expiry .noexp-label { display: inline; }

/* --- Pull-to-refresh pill (Home) --- */
.ptr-pill {
  position: fixed;
  left: 50%;
  top: calc(var(--safe-top) - 44px);
  width: 40px;
  height: 40px;
  border-radius: 50%;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface);
  color: var(--primary);
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-card-shadow);
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, 0);
  transition: transform 0.3s var(--ease-ios), opacity 0.2s ease;
}
.ptr-pill.dragging { transition: none; }
.ptr-spinner { width: 20px; height: 20px; transform: rotate(var(--ptr-rot, 0deg)); }
.ptr-pill.refreshing .ptr-spinner { animation: ptrSpin 0.8s linear infinite; }

/* --- Field grids --- */
.field-grid-2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
  align-items: end;
}
.field-grid-3 {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-3);
  align-items: end;
}
.field-grid-2 > div,
.field-grid-3 > div {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}
.field-grid-2 select,
.field-grid-2 input,
.field-grid-3 select,
.field-grid-3 input {
  width: 100%;
  box-sizing: border-box;
}

/* --- Tile selector grid (Item Modal Add tab) --- */
.tile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
  gap: var(--space-3);
  margin-bottom: var(--space-2);
}
.select-tile {
  background: var(--surface);
  border: none;
  border-radius: var(--radius-16);
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.05),
    0 4px 12px rgba(0, 0, 0, 0.06);
  padding: 12px 6px 10px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  transition:
    transform 0.18s var(--ease-ios),
    box-shadow 0.18s var(--ease-ios),
    background 0.15s ease;
  position: relative;
}
[data-theme="dark"] .select-tile {
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.35),
    0 4px 12px rgba(0, 0, 0, 0.40);
}
.select-tile:active {
  transform: scale(0.95);
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.08),
    0 8px 20px rgba(0, 0, 0, 0.10);
}
.select-tile.selected {
  background: color-mix(in srgb, var(--primary) 14%, var(--surface));
  box-shadow:
    0 1px 2px color-mix(in srgb, var(--primary) 20%, transparent),
    0 6px 16px color-mix(in srgb, var(--primary) 18%, transparent);
  color: var(--primary-2);
}
.select-tile.selected .tile-label { font-weight: 700; }
.tile-visual {
  position: relative;
  width: 60px;
  height: 60px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.tile-visual img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  display: block;
}
.tile-visual .tile-emoji {
  font-size: 1.9rem;
  line-height: 1;
  display: none;
}
.tile-visual:not(.has-img) .tile-emoji { display: block; }
.tile-visual.no-img img { display: none; }
.tile-visual.no-img .tile-emoji { display: block; }
.tile-label {
  text-align: center;
  line-height: 1.2;
  word-break: break-word;
}
@media (max-width: 360px) {
  .tile-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* --- Search pill (Home / Consume / Shopping) --- */
.search-pill-wrap { position: relative; margin-bottom: var(--space-5); }
.search-pill-wrap input[type="search"],
.search-pill-wrap input[type="text"] {
  width: 100%;
  padding: 14px 16px 14px 44px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--input-search-border);
  background: var(--input-bg);
  font-size: 0.92rem;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.04);
  height: auto;
}
.search-pill-icon {
  position: absolute;
  left: 14px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--primary);
  pointer-events: none;
}
.search-clear-btn {
  display: none;
  position: absolute;
  right: 14px;
  top: 50%;
  transform: translateY(-50%);
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: none;
  background: var(--surface-alt);
  color: var(--muted);
  align-items: center;
  justify-content: center;
  padding: 0;
  cursor: pointer;
  transition: background 0.15s ease, transform 0.24s var(--ease-ios);
}
.search-clear-btn.visible { display: flex; }
.search-clear-btn::before { content: ''; position: absolute; inset: -11px; }
.search-clear-btn:active { background: var(--border); transform: translateY(-50%) scale(0.9); }

.search-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
  position: sticky;
  top: calc(8px + var(--safe-top));
  z-index: 6;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  padding: 6px var(--space-4);
  margin-left: calc(-1 * var(--space-4));
  margin-right: calc(-1 * var(--space-4));
  margin-top: -6px;
  margin-bottom: 12px;
}
.search-row .search-pill-wrap { flex: 1; margin-bottom: 0; }

.consume-search-wrap {
  position: sticky;
  top: calc(8px + var(--safe-top));
  z-index: 6;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  padding: 6px var(--space-4);
  margin-left: calc(-1 * var(--space-4));
  margin-right: calc(-1 * var(--space-4));
  margin-bottom: var(--space-3-5);
}
.consume-search-wrap .search-pill-wrap { margin-bottom: 0; }
.consume-search-wrap .search-row {
  position: static;
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  margin-top: 0;
  margin-bottom: 0;
  margin-left: 0;
  margin-right: 0;
  padding: 0;
}
.consume-search-results {
  position: absolute;
  left: 0; right: 0; top: calc(100% - 14px);
  background: var(--surface);
  border-radius: var(--radius-md);
  box-shadow: 0 8px 24px rgba(0,0,0,0.12);
  z-index: var(--z-search-results);
  max-height: 260px;
  overflow-y: auto;
  opacity: 0;
  transform: translateY(-6px);
  pointer-events: none;
  transition: opacity 0.18s ease, transform 0.18s var(--ease-ios);
}
.consume-search-results.open {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}
.consume-search-result {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: 10px 14px;
  border-bottom: 1px solid var(--border, rgba(0,0,0,0.06));
}
.consume-search-result:last-child { border-bottom: none; }
.consume-add-btn {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: none;
  background: var(--primary);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  padding: 0;
  cursor: pointer;
  transition: transform 0.24s var(--ease-ios);
}
.consume-search-result.tappable { cursor: pointer; }
.consume-search-result.tappable:active { background: var(--surface-2); }
.consume-add-btn:active { transform: scale(0.92); }
.on-list-tag {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--muted);
  background: var(--surface-alt);
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  flex-shrink: 0;
}

/* --- Swipeable row + bulk-select (Consume + Shopping) --- */
.consume-list { display: flex; flex-direction: column; gap: var(--space-2-5); padding-bottom: var(--tabbar-clearance); }
.consume-thumb {
  width: 56px; height: 56px;
  border-radius: var(--radius-xs);
  overflow: hidden;
  flex-shrink: 0;
  background: var(--surface-alt, #f2f2f2);
}
.consume-thumb img { width: 100%; height: 100%; object-fit: cover; }
.consume-list .consume-thumb { width: 60px; height: 60px; border-radius: var(--radius-12); background: var(--row-thumb-bg); }
.consume-row-info { flex: 1; min-width: 0; }
.consume-row-name { font-weight: 700; font-size: 1.05rem; }
.consume-row-sub { color: var(--amount-ink); font-weight: 600; font-size: 0.9rem; margin-top: 2px; }

.consume-row { position: relative; overflow: hidden; touch-action: pan-y; }
.consume-list .consume-row { border-radius: var(--radius-16); box-shadow: var(--row-shadow); }
.consume-row-content {
  position: relative;
  z-index: 2;
  width: 100%;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: var(--space-3-5);
  padding: var(--space-4) 4px;
  background: var(--surface);
  transition: transform 0.15s ease;
}
.consume-row.swiped .consume-row-content { transform: translateX(-84px); }
.consume-list .consume-row-content {
  gap: 12px;
  padding: 8px 12px 8px 8px;
  background: var(--row-card-bg);
  border: 1px solid var(--row-card-border);
  border-radius: var(--radius-16);
  box-shadow: var(--row-highlight);
}
.consume-list .consume-row.swiped .consume-row-content { transform: translateX(-72px); }
.consume-row-delete-reveal {
  position: absolute;
  z-index: 1;
  top: 50%;
  right: 16px;
  transform: translateY(-50%);
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--danger);
  color: #fff;
  border: none;
  font-weight: 700;
  font-size: 0.72rem;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}
.consume-row-delete-reveal svg { width: 20px; height: 20px; pointer-events: none; }
.consume-list .consume-row-delete-reveal {
  top: 0; bottom: 0; right: 0;
  width: 96px; height: auto;
  padding: 0 0 0 24px;
  transform: none;
  border-radius: var(--radius-16);
  opacity: 0;
  transition: opacity 0.15s ease;
}
.consume-list .consume-row.swiped .consume-row-delete-reveal { opacity: 1; }
.consume-list.select-mode .consume-row-delete-reveal { display: none; }
.consume-list.select-mode .shop-qty-input { display: none; }
.consume-list .shop-qty-input { width: 72px; }

.row-select-bar {
  display: none;
  position: absolute;
  left: 0; top: 10px; bottom: 10px;
  width: 4px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--muted) 35%, transparent);
  transition: background 0.15s ease;
}
.consume-list.select-mode .row-select-bar { display: block; }
.consume-list.select-mode .row-select-bar.checked { background: var(--danger); }
.consume-list.select-mode .consume-row-content { padding-left: 16px; }
.consume-list.select-mode .consume-row-content.selected { background: color-mix(in srgb, var(--danger) 7%, var(--row-card-bg)); }

.consume-bulk-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 4px 14px;
}
.consume-bulk-bar[hidden] { display: none; }

/* --- Pill header (Consume / Shopping / Recipes / Settings) --- */
.consume-header {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: calc(14px + var(--safe-top)) var(--space-5) 34px;
  margin: -4px -16px 0;
  background: var(--header-fade, var(--hero-grad));
}
.consume-header .icon-btn {
  width: 44px;
  height: 44px;
  color: var(--header-icon, var(--primary-2));
}
.consume-header-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.consume-header-titles { flex: 1; text-align: center; min-width: 0; }
.consume-title {
  text-align: center;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.4rem;
  margin: 0;
  color: #fff;
}
.consume-body {
  position: relative;
  margin: -18px -16px 0;
  padding: var(--space-6) var(--space-4) var(--space-4);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  background: var(--bg);
  box-shadow: var(--body-rim-shadow);
  z-index: var(--z-base);
}
.consume-body::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  z-index: -1;
  pointer-events: none;
  box-shadow: var(--body-glow-shadow);
  clip-path: inset(-40px -40px calc(100% - 28px) -40px);
}

/* --- Floating submit pill (Consume / Shopping / Item Modal / Bought) --- */
.consume-submit-btn,
#item-save-btn,
.bought-submit-btn,
.recipe-cook-btn {
  display: block;
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
  padding: var(--space-3-5);
  font-weight: 700;
  font-size: 0.95rem;
  border-radius: var(--radius-pill);
  color: var(--primary-ink);
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--primary) 55%, white 15%) 0%,
    color-mix(in srgb, var(--primary) 70%, transparent) 100%
  );
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  border: 1px solid rgba(255, 255, 255, 0.4);
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.5),
    inset 0 -1px 6px rgba(0, 0, 0, 0.08),
    0 8px 24px rgba(0, 0, 0, 0.15);
}
.consume-submit-btn {
  display: none;
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: max(var(--tabbar-clearance), calc(var(--kb) + 12px));
  width: calc(100% - 32px);
  z-index: var(--z-fab);
}
.consume-submit-btn:active { transform: translateX(-50%) scale(0.96); }
body[data-tab="tab-consume"] #consume-submit-btn,
body[data-tab="tab-shopping"] #shopping-submit-btn {
  display: block;
  animation: floatIn 0.25s ease-out 0.12s backwards;
}
.consume-submit-btn:disabled, #item-save-btn:disabled, .bought-submit-btn:disabled { opacity: 0.6; }


/* ============================================================================
   5. SHARED SHELL
   ============================================================================ */

/* --- Topbar --- */
.topbar {
  padding: calc(14px + var(--safe-top)) var(--space-5) 12px;
  position: relative;
  z-index: var(--z-sticky);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--topbar-tint, var(--primary)) var(--topbar-strong), transparent) 0%,
    color-mix(in srgb, var(--topbar-tint, var(--primary)) var(--topbar-soft), transparent) 50%,
    transparent 100%
  );
  border: none;
  box-shadow: none;
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  pointer-events: none;
  /* Bleeds to screen edges now that it lives inside #tab-stock. */
  margin: -4px -16px 4px;
}
.topbar .topbar-actions, .topbar .icon-btn { pointer-events: auto; }
.topbar-spacer { width: 88px; }
.logo-wrap { display: flex; align-items: center; justify-content: center; gap: 8px; }
.brand-mark { height: 34px; width: auto; display: block; }
.brand-name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.45rem;
  letter-spacing: -0.02em;
  line-height: 1;
}
.brand-my { color: var(--ink); }
.brand-grocer { color: var(--primary); }
.topbar-actions { display: flex; gap: var(--space-2); justify-content: flex-end; }
.topbar-actions .icon-btn { position: relative; }
.topbar-badge {
  position: absolute;
  top: -2px;
  right: -2px;
  background: var(--danger);
  color: #fff;
  font-size: 0.65rem;
  font-weight: 700;
  min-width: 18px;
  height: 18px;
  border-radius: 9px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 4px;
  border: 2px solid var(--surface);
  z-index: 2;
}
.topbar-badge[hidden] { display: none; }

/* --- Bottom tab bar --- */
.tabbar {
  position: fixed;
  bottom: calc(12px + var(--safe-bottom));
  left: 50%;
  transform: translateX(-50%);
  width: calc(100% - 32px);
  max-width: 480px;
  height: 64px;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border-radius: var(--radius-xl);
  display: flex;
  justify-content: space-around;
  align-items: center;
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.08);
  border: 1px solid var(--glass-border);
  padding: 0 var(--space-3);
  z-index: var(--z-tabbar);
  box-sizing: border-box;
  margin: 0;
  transition: opacity 0.22s ease, transform 0.32s var(--ease-ios), box-shadow 0.3s ease;
  will-change: transform, opacity;
  touch-action: pan-y;
}
.tabbar.has-scroll {
  box-shadow:
    0 12px 40px rgba(0, 0, 0, 0.14),
    0 2px 8px rgba(0, 0, 0, 0.06);
}
[data-theme="dark"] .tabbar.has-scroll {
  box-shadow:
    0 12px 40px rgba(0, 0, 0, 0.7),
    0 2px 8px rgba(0, 0, 0, 0.4);
}
.tab-btn {
  background: none;
  border: none;
  color: var(--tabbar-inactive);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1;
  height: 100%;
  cursor: pointer;
  transition: color 0.2s ease, transform 0.22s var(--ease-ios);
}
.tab-btn svg { width: 22px; height: 22px; }
.tab-btn span { font-size: 0.62rem; font-weight: 600; }
.tab-btn.active { color: var(--ink); }
.fab-btn.tab-btn.active { color: var(--primary-ink); }
.tab-btn:active { transform: scale(0.9); }
.tab-btn:focus { outline: none; }
.tab-btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

.fab-wrapper { position: relative; top: -20px; flex: 0 0 56px; display: flex; justify-content: center; }
.fab-btn {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background-color: var(--primary-2);
  border: 4px solid var(--fab-ring);
  color: var(--primary-ink);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 6px 16px color-mix(in srgb, var(--primary-2) 45%, transparent);
  position: relative;
  transition: transform 0.22s var(--ease-ios), background-color 0.2s ease;
}
.fab-btn:active { transform: scale(0.93); }
.fab-badge {
  position: absolute;
  bottom: -4px;
  background-color: var(--danger);
  color: var(--primary-ink);
  font-size: 0.72rem;
  font-weight: 700;
  min-width: 18px;
  height: 18px;
  border-radius: 9px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 4px;
  border: 2px solid var(--surface);
  box-shadow: 0 2px 6px rgba(255, 59, 48, 0.4);
}
.fab-badge[hidden] { display: none; }

/* --- Generic modal shell --- */
.modal-overlay {
  /* iOS keyboard: the overlay is a self-contained layer ...
     contain: layout paint keeps its children isolated from the
     rest of the page. Combined with .modal-lock on <html> and
     <body>, this is what holds the overlay in place while iOS
     handles the on-screen keyboard. */
  contain: layout paint;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.28);
  backdrop-filter: blur(20px) saturate(1.3);
  -webkit-backdrop-filter: blur(20px) saturate(1.3);
  z-index: var(--z-modal);
  display: none;
  align-items: flex-end;
  justify-content: center;
}

/* Overlay blur is on at every width (restored in PATCH 30). The old
   <500px solid-scrim fallback was removed. */

.modal-overlay.open {
  padding-top: max(12px, var(--safe-top));
}
.modal-overlay.open.barcode-scan-overlay,
.modal-overlay.open.recipe-prompt-overlay,
.modal-overlay.open#receipt-modal {
  padding-top: 0;
}
.modal-overlay.open { display: flex; animation: overlayFadeIn 0.25s ease-out; }
.modal-overlay.closing { animation: overlayFadeOut 0.28s ease-in forwards; }
.modal-overlay.closing > .modal-sheet { animation: sheetDown 0.28s var(--ease-ios) forwards; }
.modal-sheet {
  background: var(--surface);
  border: none;
  width: 100%;
  /* svh (small viewport height) is the viewport size with the URL bar
     showing — it does NOT shrink when the keyboard opens, unlike dvh.
     That keeps the sheet a fixed height across focus changes; the
     keyboard then simply overlays the bottom of the sheet, and the
     modal-body scrolls internally to reach the focused field.
     Using dvh here caused a visible jerk every time the keyboard
     swapped type (QWERTY ↔ numeric keypad) because dvh resizes twice
     during the transition.
     The vh line above svh is a fallback for iOS < 15.4 where svh is
     unsupported; those builds also don't shrink vh for the keyboard,
     so the fallback has the same stable behaviour. */
  height: calc(90vh / var(--app-scale));
  height: calc(90svh / var(--app-scale));
  max-height: 100%;
  overflow: hidden;
  border-radius: 28px 28px 0 0;
  display: flex;
  flex-direction: column;
  animation: sheetUp 0.4s var(--ease-spring);
  position: relative;
  max-width: 640px;
  margin: 0 auto;
}
.modal-header {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  padding: 16px 18px 10px;
  border-bottom: 1px solid var(--border);
}
.modal-header h2 { margin: 0 auto 0 0; }
.modal-body {
  padding: var(--space-5) 18px calc(100px + var(--safe-bottom) + var(--kb));
  overflow-y: auto;
  position: relative;
  margin-top: -18px;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  background: var(--surface);
  box-shadow:
    0 -8px 18px rgba(0, 0, 0, 0.28),
    0 -1px 0 rgba(255, 255, 255, 0.55),
    inset 0 2px 0 rgba(255, 255, 255, 0.95);
  z-index: 2;
}
[data-theme="dark"] .modal-body,
[data-theme="dark"] .bought-list {
  box-shadow:
    0 -8px 18px rgba(0, 0, 0, 0.55),
    0 -1px 0 rgba(255, 255, 255, 0.08),
    inset 0 2px 0 rgba(255, 255, 255, 0.08);
}
.modal-footer-fixed {
  position: absolute;
  left: 0; right: 0;
  /* Fixed offset — the footer stays put when the keyboard opens and
     is instead covered by it while typing. The previous
     `calc(var(--kb) + 12px)` term lifted the footer by exactly the
     keyboard height, which read as the Add button jumping up every
     time a field was focused. */
  bottom: calc(25px + var(--safe-bottom));
  padding: 14px 18px 0;
  z-index: 5;
}

/* --- Alert modal (confirm / success / error) --- */
.alert-modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.38);
  backdrop-filter: blur(14px) saturate(1.2);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  z-index: var(--z-alert-modal);
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--space-6);
}
.alert-modal-overlay.open { display: flex; animation: overlayFadeIn 0.2s ease-out; }
.alert-modal-sheet {
  background: var(--popup-bg);
  border: 1px solid var(--popup-edge);
  box-shadow: var(--popup-shadow);
  width: 100%;
  max-width: 320px;
  border-radius: var(--radius-20);
  overflow: hidden;
  text-align: center;
  animation: popIn 0.22s var(--ease-ios);
}
.alert-modal-icon {
  width: 56px; height: 56px;
  border-radius: 50%;
  margin: 24px auto 12px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.alert-modal-icon.success { background: var(--surface-2); color: var(--primary); }
.alert-modal-icon.error { background: var(--danger-bg); color: var(--danger); }
.alert-modal-title { font-size: 1.05rem; font-weight: 700; color: var(--ink); margin: 0 20px 6px; }
.alert-modal-message {
  font-size: 0.88rem;
  color: var(--muted);
  margin: 0 20px 20px;
  line-height: 1.4;
  white-space: pre-line;
  max-height: 45vh;
  overflow-y: auto;
}
.alert-modal-actions { border-top: 1px solid var(--border); display: flex; }
.alert-modal-ok-btn, .alert-modal-cancel-btn {
  flex: 1; border: none; background: none; padding: var(--space-3-5);
  font-size: 1rem; font-weight: 600; cursor: pointer;
}
.alert-modal-ok-btn { color: var(--primary); }
.alert-modal-cancel-btn { color: var(--muted); border-right: 1px solid var(--border); }
.alert-modal-ok-btn:active, .alert-modal-cancel-btn:active { background: var(--surface-2); }

/* --- Success modal (dedicated, not the generic alert) ---
   Centered card with a soft icon circle, big title, muted message,
   and a pill primary button + optional secondary text link. */
.success-modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.42);
  backdrop-filter: blur(14px) saturate(1.2);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  z-index: 2100;
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
}
.success-modal-overlay.open { display: flex; animation: overlayFadeIn 0.2s ease-out; }
.success-modal-sheet {
  background: var(--popup-bg);
  border: 1px solid var(--popup-edge);
  box-shadow:
    0 28px 64px rgba(0, 0, 0, 0.30),
    0 10px 24px rgba(0, 0, 0, 0.18),
    0 2px 6px rgba(0, 0, 0, 0.10),
    inset 0 1px 0 rgba(255, 255, 255, 0.9);
  width: 100%;
  max-width: 340px;
  min-height: 520px;
  border-radius: var(--radius-xl);
  padding: 52px 28px 32px;
  display: flex;
  flex-direction: column;
  text-align: center;
  animation: popIn 0.24s var(--ease-ios);
}
[data-theme="dark"] .success-modal-sheet {
  box-shadow:
    0 28px 64px rgba(0, 0, 0, 0.75),
    0 10px 24px rgba(0, 0, 0, 0.55),
    0 2px 6px rgba(0, 0, 0, 0.35),
    inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
.success-modal-icon {
  --icon-bg: var(--surface-2);
  width: 108px;
  height: 108px;
  border-radius: 50%;
  margin: 0 auto 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--icon-bg);
  color: var(--primary);
  flex-shrink: 0;
}
[data-theme="dark"] .success-modal-icon {
  --icon-bg: color-mix(in srgb, var(--primary) 18%, var(--surface-2));
  background: var(--icon-bg);
}
/* The tick is punched through the filled hat by painting it with the same
   colour as the circle behind it -- reads as a clean cut-out. */
.success-modal-icon-check { stroke: var(--icon-bg); }
.success-modal-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.35rem;
  color: var(--ink);
  margin: 0 0 8px;
  letter-spacing: -0.01em;
  line-height: 1.2;
  overflow-wrap: anywhere;
}
.success-modal-message {
  font-size: 0.9rem;
  color: var(--muted);
  line-height: 1.45;
  margin: 0 0 auto;
  overflow-wrap: anywhere;
}
.success-modal-primary {
  display: block;
  width: 100%;
  padding: 15px 20px;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--primary);
  color: var(--primary-ink);
  font-family: var(--font-ui);
  font-size: 0.95rem;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 6px 16px color-mix(in srgb, var(--primary) 34%, transparent);
  transition: transform 0.24s var(--ease-ios), box-shadow 0.2s var(--ease-ios);
}
.success-modal-primary:active {
  transform: scale(0.97);
  box-shadow: 0 3px 10px color-mix(in srgb, var(--primary) 34%, transparent);
}
.success-modal-secondary {
  display: block;
  width: 100%;
  padding: 14px 20px;
  margin-top: 6px;
  border: none;
  background: none;
  font-family: var(--font-ui);
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--muted);
  cursor: pointer;
  border-radius: var(--radius-pill);
  transition: color 0.15s ease, background 0.15s ease;
}
.success-modal-secondary:active { background: var(--surface-2); color: var(--ink); }
.success-modal-secondary[hidden] { display: none; }

/* Belt-and-braces rule for the alert-modal icons. */
#alert-modal-icon > svg[hidden] { display: none; }

/* --- Toast (iOS 18 glass pill) --- */
#toast {
  position: fixed;
  left: 50%;
  top: calc(16px + var(--safe-top));
  transform: translateX(-50%) translateY(-20px) scale(0.96);
  background: #1a1a1a;
  color: #ffffff;
  padding: 10px 16px;
  border-radius: var(--radius-md);
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 600;
  box-shadow:
    0 12px 32px rgba(0, 0, 0, 0.32),
    0 2px 6px rgba(0, 0, 0, 0.20);
  opacity: 0;
  pointer-events: none;
  max-width: min(88%, 380px);
  text-align: center;
  z-index: var(--z-toast);
  transition:
    opacity 0.28s var(--ease-ios),
    transform 0.32s var(--ease-spring);
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
#toast.show { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
#toast .toast-icon {
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0; width: 16px; height: 16px;
}
#toast .toast-icon:empty { display: none; }
#toast .toast-icon svg { width: 16px; height: 16px; display: block; }
#toast[data-kind="success"] .toast-icon { color: var(--toast-icon-success); }
#toast[data-kind="error"]   .toast-icon { color: var(--toast-icon-error); }
#toast[data-kind="warn"]    .toast-icon { color: var(--toast-icon-warn); }

/* --- Keyframes --- */
@keyframes tabIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes tabOut { from { opacity: 1; } to { opacity: 0; } }
@keyframes tabInFromRight { from { opacity: 0; transform: translateX(72px); } to { opacity: 1; transform: none; } }
@keyframes tabInFromLeft  { from { opacity: 0; transform: translateX(-40px); filter: brightness(0.88); } to { opacity: 1; transform: none; filter: none; } }
@keyframes tabOutPush     { to { opacity: 0; transform: translateX(-40px); filter: brightness(0.88); } }
@keyframes tabOutPop      { to { opacity: 0; transform: translateX(72px); } }
@keyframes ptrSpin { to { transform: rotate(360deg); } }
@keyframes floatIn { from { opacity: 0; } }
@keyframes overlaySlideOut { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(16px); } }
@keyframes overlayFadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes overlayFadeOut { from { opacity: 1; } to { opacity: 0; } }
@keyframes sheetUp {
  from { transform: translateY(100%); }
  70%  { transform: translateY(-4px); }
  to   { transform: none; }
}
@keyframes sheetDown { from { transform: translateY(var(--drag-y, 0px)); } to { transform: translateY(100%); } }
@keyframes popIn { from { opacity: 0; transform: scale(0.94); } to { opacity: 1; transform: none; } }
@keyframes popOut { to { opacity: 0; transform: scale(0.96); } }
@keyframes tickPop { 0% { transform: scale(0.8); } 55% { transform: scale(1.15); } 100% { transform: scale(1); } }
@keyframes gridIn {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

.stock-grid,
.recipes-grid,
.consume-list,
.recipes-portrait {
  animation: gridIn 0.4s var(--ease-ios);
}

.alerts-panel.open { animation: alertsSlideIn 0.3s var(--ease-ios); }
@keyframes alertsSlideIn {
  from { opacity: 0; transform: translateY(-24px); }
  to   { opacity: 1; transform: none; }
}
.alerts-panel.closing { animation: alertsFadeOut 0.18s ease-in forwards; }
@keyframes alertsFadeOut {
  from { opacity: 1; }
  to   { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-delay: 0s !important;
    transition-duration: 0.01ms !important;
  }
  .tab.slide-out { display: none; }
  .modal-overlay.closing,
  .alerts-panel.closing { display: none; }
}

/* --- Skeletons --- */
@keyframes skeletonShimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
.skeleton {
  background: linear-gradient(
    90deg,
    var(--surface-alt) 25%,
    color-mix(in srgb, var(--surface-alt) 55%, var(--bg)) 50%,
    var(--surface-alt) 75%
  );
  background-size: 200% 100%;
  animation: skeletonShimmer 1.4s linear infinite;
  border-radius: 8px;
}
[data-theme="dark"] .skeleton {
  background: linear-gradient(
    90deg,
    var(--surface-alt) 25%,
    color-mix(in srgb, var(--primary) 25%, var(--surface-alt)) 50%,
    var(--surface-alt) 75%
  );
  background-size: 200% 100%;
}
.stock-grid-skeleton .grid-item,
.skeleton-card {
  pointer-events: none;
  cursor: default;
}
.skeleton-thumb-sq {
  width: 100%;
  aspect-ratio: 1;
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-2-5);
}
.skeleton-line {
  height: 12px;
  border-radius: 6px;
  margin-top: 8px;
}
.skeleton-line-lg { width: 80%; }
.skeleton-line-sm { width: 50%; }

.skeleton-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2-5);
  padding-bottom: var(--tabbar-clearance);
}
.consume-row-skeleton {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 12px 8px 8px;
  background: var(--row-card-bg);
  border: 1px solid var(--row-card-border);
  border-radius: var(--radius-16);
  box-shadow: var(--row-highlight);
  min-height: 76px;
}
.skeleton-thumb-sm {
  width: 60px;
  height: 60px;
  border-radius: var(--radius-12);
  flex-shrink: 0;
}
.skeleton-body {
  flex: 1;
  min-width: 0;
}
.skeleton-body .skeleton-line:first-child { margin-top: 0; }
.skeleton-qty {
  width: 60px;
  height: 32px;
  border-radius: var(--radius-xs);
  flex-shrink: 0;
}

.skeleton-recipe-img {
  width: 100%;
  aspect-ratio: 1;
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-2-5);
}

/* Paragraph skeleton — used for the Product Detail description slot.
   Markup is <span class="skeleton-text"><span class="skeleton">…</span></span>
   so it stays valid inside a <p>; the rule below makes the children block
   boxes so they stack with a gap. Last line is intentionally shorter so
   the shape reads as a paragraph, not a filled block. */
.skeleton-text {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.skeleton-text > .skeleton {
  display: block;
  height: 14px;
  border-radius: 6px;
}
.skeleton-text > .skeleton:last-child { width: 65%; }

/* Batch-row skeleton — mirrors .detail-batch-row (Content + Edit sheets).
   Same 3-column grid and card chrome so the swap from skeleton to real
   content doesn't shift the layout. */
.detail-batch-row-skeleton {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1.5fr) minmax(0, 1fr);
  gap: var(--space-3);
  align-items: center;
  background: var(--nutri-card-bg);
  border: 1px solid var(--nutri-card-edge);
  box-shadow: var(--nutri-card-shadow);
  border-radius: var(--radius-20);
  padding: 14px 12px;
  min-height: 52px;
}
.detail-batch-row-skeleton > .skeleton:nth-child(1) {
  width: 60%;
  height: 20px;
  border-radius: var(--radius-pill);
  justify-self: start;
}
.detail-batch-row-skeleton > .skeleton:nth-child(2) {
  width: 100%;
  height: 14px;
}
.detail-batch-row-skeleton > .skeleton:nth-child(3) {
  width: 70%;
  height: 12px;
  justify-self: end;
}

/* History-row skeleton — mirrors .history-card's 4-column grid. */
.history-card-skeleton {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 0.9fr);
  gap: 8px;
  align-items: center;
  padding: 12px 14px;
  border-radius: var(--radius-16);
  background: var(--nutri-card-bg);
  border: 1px solid var(--nutri-card-edge);
  box-shadow: var(--nutri-card-shadow);
  border-left: 4px solid var(--muted);
  min-height: 52px;
}
.history-card-skeleton > .skeleton { height: 12px; }
.history-card-skeleton > .skeleton:nth-child(1) {
  width: 75%;
  margin-left: 32px;   /* matches .history-header > span:first-child offset */
}
.history-card-skeleton > .skeleton:nth-child(4) {
  justify-self: end;
  width: 65%;
}


/* ============================================================================
   6. HOME TAB
   ============================================================================ */
.home-header {
  padding: 0 2px 12px;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-3);
}
.home-header-greeting {
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: 1.35rem;
  color: var(--ink);
  margin: 0;
  letter-spacing: -0.01em;
}
.home-header-greeting .wave { display: inline-block; margin-left: 2px; }
.home-header-sub {
  display: flex;
  align-items: center;
  gap: 5px;
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 500;
}
.home-header-sub svg { flex-shrink: 0; color: var(--primary); }

/* Alerts pill (home header) — icon button with an unread-count badge.
   .alerts-pill-count is populated by loadAlertsPanel() in app.js. */
.alerts-pill { position: relative; }
.alerts-pill-count {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: var(--radius-pill);
  background: var(--danger);
  color: #fff;
  font-family: var(--font-ui);
  font-size: 0.68rem;
  font-weight: 700;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--bg);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.20);
  pointer-events: none;
  z-index: 2;
}
.alerts-pill-count[hidden] { display: none; }

.promo-banner {
  background: var(--hero-grad);
  border-radius: var(--radius-24);
  padding: calc(12px - 8px * var(--banner-shrink, 0)) 0;
  margin-bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: calc(100px - 50px * var(--banner-shrink, 0));
  width: 100%;
  overflow: hidden;
  position: relative;
  max-height: 200px;
  box-sizing: border-box;
  contain: layout paint;
}

.sticky-filters {
  position: sticky;
  top: calc(74px + var(--safe-top));
  z-index: 5;
  margin-bottom: 0;
  --banner-shrink: 0;
}

.promo-banner-bg {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center 35%;
  z-index: 0;
  background: var(--hero-grad);
  opacity: calc(1 - var(--banner-shrink, 0));
  will-change: opacity;
  transform: translateZ(0);
  transition: opacity 0.25s ease-out;
}
.promo-banner-overlay {
  position: absolute; inset: 0;
  z-index: 2;
  background: var(--banner-overlay);
  opacity: calc(1 - 0.75 * var(--banner-shrink, 0));
  will-change: opacity;
  transition: opacity 0.25s ease-out;
}

.category-chip-row {
  display: flex;
  gap: 18px;
  overflow-x: auto;
  padding: 2px 2px 4px;
  margin-bottom: var(--space-6);
  -webkit-overflow-scrolling: touch;
}
.category-chip {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  width: 72px;
  background: none;
  border: none;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
}
.category-chip .cc-emoji {
  font-size: 1.8rem;
  width: 72px; height: 72px;
  border-radius: 50%;
  background: var(--surface-2);
  display: flex;
  align-items: center;
  justify-content: center;
}
.category-chip.active .cc-emoji { background: var(--pill-active-bg); box-shadow: 0 0 0 2px var(--primary); }
.category-chip.active { color: var(--primary-2); }

.category-chip-row.banner-categories {
  display: flex;
  flex-direction: row;
  gap: 12px;
  overflow-x: auto;
  padding:
    calc(12px - 8px * var(--banner-shrink, 0))
    16px
    calc(10px - 6px * var(--banner-shrink, 0));
  width: 100%;
  margin-bottom: 0;
  position: relative;
  z-index: 3;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  justify-content: safe center;
  mask-image: linear-gradient(to right, #000 calc(100% - 22px), transparent 100%);
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 22px), transparent 100%);
}
.category-chip-row.banner-categories::-webkit-scrollbar { display: none; }

.banner-categories .category-chip {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  width: calc(62px - 20px * var(--banner-shrink, 0));
  background: none;
  border: none;
  cursor: pointer;
  color: var(--primary-ink);
  transition: transform 0.2s ease;
}
.banner-categories .category-chip .cc-emoji {
  position: relative;
  overflow: hidden;
  width: calc(50px - 16px * var(--banner-shrink, 0));
  height: calc(50px - 16px * var(--banner-shrink, 0));
  border-radius: 50%;
  font-size: calc(1.45rem - 0.45rem * var(--banner-shrink, 0));
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--glass-photo-bg);
  border: 1.5px solid var(--glass-photo-border);
  box-shadow: var(--glass-photo-shadow);
  transition: transform 0.15s ease;
}
.banner-categories .category-chip:active .cc-emoji { transform: scale(0.93); }
.banner-categories .category-chip.active .cc-emoji {
  background: rgba(255, 255, 255, 0.95);
  border: 2px solid var(--primary);
  box-shadow: 0 4px 14px color-mix(in srgb, var(--primary) 40%, transparent);
}
.banner-categories .category-chip span:last-child {
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--primary-ink);
  text-align: center;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
  display: block;
  max-width: 66px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-height: calc(20px - 20px * var(--banner-shrink, 0));
  opacity: calc(1 - var(--banner-shrink, 0));
}
/* PNG image layer + emoji fallback for banner chips. */
.banner-categories .category-chip .cc-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  padding: 14%;
  box-sizing: border-box;
  z-index: 1;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.banner-categories .category-chip .cc-emoji.has-img .cc-img { opacity: 1; }
.banner-categories .category-chip .cc-emoji.has-img .cc-emoji-fallback { visibility: hidden; }
.banner-categories .category-chip .cc-emoji-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}

/* --- Location filter row (below the banner) --- */
.location-chip-row {
  display: flex;
  align-items: center;
  gap: 8px;
  overflow-x: auto;
  padding: calc(12px - 6px * var(--banner-shrink, 0)) 0;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.location-chip-row::-webkit-scrollbar { display: none; }

.location-chip {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  height: 34px;
  padding: 0 14px;
  border-radius: var(--radius-pill);
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-card-shadow);
  font-family: var(--font-ui);
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, transform 0.24s var(--ease-ios);
}
.location-chip:active { transform: scale(0.96); }
.location-chip.active {
  background: var(--primary);
  color: var(--primary-ink);
  border-color: var(--primary);
  font-weight: 700;
  box-shadow: 0 4px 12px color-mix(in srgb, var(--primary) 30%, transparent);
}

/* --- Stock grid --- */
.stock-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(calc(150px * var(--app-scale)), 1fr));
  gap: var(--space-4);
  animation: gridIn 0.2s ease-out;
  contain: layout style;
}
.grid-item {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  background: var(--glass-bg);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-20);
  padding: var(--space-3-5);
  box-shadow: var(--glass-card-shadow);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  text-align: left;
  position: relative;
  cursor: pointer;
  transition: transform 0.24s var(--ease-ios);
  contain: layout style;
}

/* GRID-ITEM MOBILE BLUR FALLBACK
   Each .grid-item was creating its own compositor layer via
   backdrop-filter: blur(8px). A 60-item Home grid = 60 layers on A12-class
   devices. Below 500px we replace the glass look with a solid elevated
   surface; the visual difference is negligible at that size. */
@media (max-width: 499px) {
  .grid-item {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background: var(--panel-solid-bg);
  }
}

.grid-item:active { transform: scale(0.97); }
.item-thumb-wrap {
  width: 72px; height: 72px;
  border-radius: var(--radius-md);
  background: var(--surface-2);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.item-thumb { width: 100%; height: 100%; object-fit: cover; display: block; }
.consume-thumb img,
.bought-row .consume-thumb img {
  filter: url(#feather-edge);
}
.grid-item .item-thumb {
  border-radius: var(--radius-sm);
  -webkit-user-drag: none;
  user-drag: none;
  pointer-events: none;
}
.grid-item .item-thumb-wrap { pointer-events: none; }
.grid-item .item-thumb-wrap.img-failed::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: var(--radius-sm);
  background: repeating-linear-gradient(45deg,
    var(--surface-alt), var(--surface-alt) 8px,
    color-mix(in srgb, var(--surface-alt) 82%, var(--ink)) 8px,
    color-mix(in srgb, var(--surface-alt) 82%, var(--ink)) 16px);
}
.grid-item .item-thumb-wrap.img-failed .item-thumb { visibility: hidden; }
.item-thumb-wrap.large .item-thumb { border-radius: var(--radius-md); }
.grid-item .item-thumb-wrap {
  width: 100%;
  aspect-ratio: 1;
  height: auto;
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-2-5);
  background: transparent;
  position: relative;
  overflow: hidden;
}
.grid-item .name {
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 0.92rem;
  line-height: 1.25;
  color: var(--ink);
  margin-left: 4px;
  overflow-wrap: anywhere;
}
.grid-item .grid-meta {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 6px;
  margin: 4px 4px 0;
  min-width: 0;
  margin-top: auto;
  padding-top: 8px;
}
.grid-item .grid-pack {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  flex: 1 1 auto;
}
.grid-item .grid-amount {
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--amount-ink);
  white-space: nowrap;
  flex: 0 0 auto;
  margin-left: auto;
  text-align: right;
}
.grid-item .grid-amount-num { font-weight: 700; }
.grid-item .grid-amount-unit { font-weight: 600; color: inherit; margin-left: 3px; }

.scroll-top-btn {
  position: fixed;
  right: calc(max(0px, (100vw - 480px) / 2) + var(--space-4));
  bottom: var(--tabbar-clearance);
  width: 44px;
  height: 44px;
  z-index: var(--z-fab);
  opacity: 0;
  transform: translateY(12px) scale(0.9);
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s var(--ease-ios), background 0.15s ease;
}
.scroll-top-btn svg { width: 20px; height: 20px; }
.scroll-top-btn.visible { opacity: 1; transform: none; pointer-events: auto; }
.scroll-top-btn:active { transform: scale(0.92); }

/* --- Sort button + menu --- */
.sort-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-pill);
  padding: 6px 12px;
  font-family: var(--font-ui);
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--ink);
  cursor: pointer;
  box-shadow: var(--glass-card-shadow);
  transition: color 0.15s ease, transform 0.24s var(--ease-ios);
}
/* .sort-btn is ~30px tall; expand the hit area to 44pt without
   changing the visual. */
.sort-btn { position: relative; }
.sort-btn::before { content: ''; position: absolute; inset: -7px -4px; }

.sort-btn svg { color: var(--primary); transition: transform 0.2s var(--ease-ios); }
.sort-btn:active { transform: scale(0.96); }
.sort-btn[aria-expanded="true"] svg { transform: rotate(180deg); }

.sort-menu {
  position: absolute;
  right: 0;
  top: calc(100% + 4px);
  min-width: 200px;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--glass-border);
  box-shadow: var(--popup-shadow);
  border-radius: var(--radius-md);
  padding: 6px;
  z-index: 100;
  display: flex;
  flex-direction: column;
  gap: 2px;
  animation: popIn 0.18s var(--ease-ios);
}
.sort-menu[hidden] { display: none; }
.sort-menu-item {
  background: none;
  border: none;
  text-align: left;
  padding: 12px 14px;
  border-radius: var(--radius-xs);
  font-family: var(--font-ui);
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.sort-menu-item:active { background: var(--surface-2); }
.sort-menu-item.active { color: var(--primary); background: var(--pill-active-bg); }
.sort-menu-item.active::after { content: '\2713'; font-weight: 700; }
.sort-menu-item[data-toggle="zero-balance"] {
  border-top: 1px solid var(--border);
  margin-top: 4px;
  padding-top: var(--space-3-5);
  border-radius: 0 0 10px 10px;
}
.sort-menu-item[data-toggle="zero-balance"].active::after { content: '\2713'; }

/* --- A–Z fast-scroll wheel --- */
.alphabet-wheel {
  position: fixed;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
  padding: 8px 0;
  perspective: 500px;
  perspective-origin: center center;
  z-index: 30;
  opacity: 0;
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
  transition: opacity 0.2s ease;
}
.alphabet-wheel.visible { opacity: 1; pointer-events: auto; }
.alphabet-wheel-letter {
  width: 32px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-ui);
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--muted);
  transform-origin: center center;
  will-change: transform, opacity;
  transition:
    transform 0.22s var(--ease-ios),
    opacity 0.22s ease,
    color 0.15s ease,
    font-size 0.15s ease;
}
.alphabet-wheel.touching .alphabet-wheel-letter { transition: none; }
.alphabet-wheel-letter.active {
  color: var(--primary);
  font-size: 1.15rem;
  font-weight: 800;
}
.alphabet-bubble {
  position: fixed;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--primary);
  color: var(--primary-ink);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.8rem;
  line-height: 1;
  box-shadow:
    0 10px 26px rgba(0, 0, 0, 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.35);
  z-index: 32;
  opacity: 0;
  transform: scale(0.6);
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
  transition:
    opacity 0.12s ease,
    transform 0.18s var(--ease-ios);
}
.alphabet-bubble.visible { opacity: 1; transform: scale(1); }
body.overlay-open .alphabet-wheel,
body.overlay-open .alphabet-bubble { display: none; }

/* --- Pinned padding shrink when the search row sticks --- */
.search-row.pinned,
.consume-search-wrap.pinned {
  padding-top: 2px;
  padding-bottom: 2px;
}


/* ============================================================================
   7. ALERTS PANEL
   ============================================================================ */
/* ALERTS — sticky hero + grid, sections scroll */
/* The panel is a fixed, non-scrolling flex column. Only the
   .alerts-sections block scrolls; hero and the stat-card grid stay
   pinned. Each section's .expiring-heading is sticky to the top of
   the sections scroller. */
.alerts-panel {
  --alerts-card-elev:
    0 1px 2px rgba(0, 0, 0, 0.08),
    0 4px 12px rgba(0, 0, 0, 0.10),
    0 14px 32px rgba(0, 0, 0, 0.07),
    inset 0 1px 0 rgba(255, 255, 255, 0.9);
  --alerts-row-elev:
    0 1px 2px rgba(0, 0, 0, 0.08),
    0 4px 10px rgba(0, 0, 0, 0.10),
    0 12px 28px rgba(0, 0, 0, 0.07);
  position: fixed;
  inset: 0;
  background: var(--bg);
  z-index: var(--z-alerts-panel);
  display: none;
  flex-direction: column;
  align-items: center;
  overflow: hidden;
  will-change: transform;
}
[data-theme="dark"] .alerts-panel {
  --alerts-card-elev:
    0 1px 2px rgba(0, 0, 0, 0.40),
    0 4px 12px rgba(0, 0, 0, 0.45),
    0 14px 32px rgba(0, 0, 0, 0.35),
    inset 0 1px 0 rgba(255, 255, 255, 0.06);
  --alerts-row-elev:
    0 1px 2px rgba(0, 0, 0, 0.40),
    0 4px 10px rgba(0, 0, 0, 0.45),
    0 12px 28px rgba(0, 0, 0, 0.32);
}
.alerts-panel.open { display: flex; }

.alerts-hero {
  flex: 0 0 auto;
  background: var(--header-fade, var(--hero-grad));
  color: #fff;
  padding: calc(12px + var(--safe-top)) var(--space-5) 12px;
  border-radius: 0 0 22px 22px;
  position: relative;
  z-index: 2;
  flex-shrink: 0;
  display: grid;
  grid-template-columns: 44px 1fr 44px;
  align-items: center;
  gap: var(--space-3);
  box-shadow: var(--alerts-hero-shadow,
    0 6px 14px rgba(0, 0, 0, 0.22),
    0 2px 4px rgba(0, 0, 0, 0.12),
    inset 0 -2px 5px rgba(0, 0, 0, 0.18),
    inset 0 1px 3px rgba(255, 255, 255, 0.28));
}
.alerts-hero-spacer { width: 44px; height: 44px; visibility: hidden; }
.alerts-body {
  position: relative;
  z-index: 1;
  margin-top: 0;
  background: var(--bg);
  border-radius: 0;
  padding: var(--space-6) var(--space-5) 0;
  box-shadow: none;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  touch-action: pan-y;
}
.alerts-hero h2,
#alerts-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.15rem;
  margin: 0;
  text-align: center;
  color: #fff;
  letter-spacing: -0.01em;
}
.alerts-close {
  position: static;
  width: 40px;
  height: 40px;
  padding: 0;
  margin: 0;
  justify-self: end;
}
.alerts-hero,
.alerts-body {
  width: 100%;
  max-width: 640px;
}
/* The only scroll container in the panel. Section headers stick to
   the top of this element. */
.alerts-sections {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  padding-bottom: calc(var(--space-6) + env(safe-area-inset-bottom, 0px));
  /* Pin horizontal overflow — the full-bleed section headings use
     negative margins that would otherwise create a horizontal
     scroll surface (overflow-y: auto implies overflow-x: auto). */
  overflow-x: hidden;
  width: 100%;
  box-sizing: border-box;
  /* iOS Safari quirk: with -webkit-overflow-scrolling: touch the element
     still accepts horizontal pans even when overflow-x is hidden.
     touch-action: pan-y is the only reliable block for that gesture. */
  touch-action: pan-y;
}

.alert-stat-grid {
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}
.alert-stat-card {
  position: relative;
  background: var(--nutri-card-bg);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--nutri-card-edge);
  box-shadow: var(--alerts-card-elev);
  border-radius: var(--radius-20);
  padding: 14px 16px;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-family: var(--font-ui);
  text-align: left;
  color: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.24s var(--ease-ios), border-color 0.15s ease;
}
.alert-stat-card::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.alert-stat-card > * { position: relative; z-index: 1; }
.alert-stat-card.active::before { opacity: 1; }
.alert-stat-card.active.accent-red::before    { background: color-mix(in srgb, var(--danger) 14%, transparent); }
.alert-stat-card.active.accent-orange::before { background: color-mix(in srgb, var(--warn) 14%, transparent); }
.alert-stat-card.active.accent-amber::before  { background: color-mix(in srgb, var(--warn) 10%, transparent); }
.alert-stat-card.active.accent-blue::before   { background: color-mix(in srgb, var(--info) 14%, transparent); }
.alert-stat-card:active { transform: scale(0.97); }
.alert-stat-card.is-empty {
  opacity: 0.4;
  pointer-events: none;
}
.alert-stat-card.active.accent-red {
  border-color: var(--danger);
  box-shadow: 0 0 0 1px var(--danger), var(--alerts-card-elev);
}
.alert-stat-card.active.accent-orange {
  border-color: var(--warn);
  box-shadow: 0 0 0 1px var(--warn), var(--alerts-card-elev);
}
.alert-stat-card.active.accent-amber {
  border-color: color-mix(in srgb, var(--warn) 60%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--warn) 60%, transparent), var(--alerts-card-elev);
}
.alert-stat-card.active.accent-blue {
  border-color: var(--info);
  box-shadow: 0 0 0 1px var(--info), var(--alerts-card-elev);
}
.alert-stat-icon {
  width: 46px; height: 46px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.4rem;
  flex-shrink: 0;
  margin: 0;
}
.alert-stat-card.accent-red .alert-stat-icon { background: var(--danger-bg); }
.alert-stat-card.accent-orange .alert-stat-icon { background: var(--warn-bg); }
.alert-stat-card.accent-amber .alert-stat-icon { background: color-mix(in srgb, var(--warn) 22%, transparent); }
.alert-stat-card.accent-blue .alert-stat-icon { background: var(--info-bg); }
.alert-stat-num {
  font-family: var(--font-ui);
  font-weight: 800;
  font-size: 1.15rem;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
}
.alert-stat-label { font-size: 0.76rem; color: var(--muted); margin-top: 2px; }
.alert-stat-card.accent-red .alert-stat-num { color: var(--danger); }
.alert-stat-card.accent-orange .alert-stat-num { color: var(--warn); }
.alert-stat-card.accent-amber .alert-stat-num { color: var(--warn); }
.alert-stat-card.accent-blue .alert-stat-num { color: var(--info); }

.alerts-empty { text-align: center; padding: 30px 0; }
.expiring-section,
.use-today-banner,
.alerts-empty {
  transition: opacity 0.22s ease-out, transform 0.22s var(--ease-ios);
}
@starting-style {
  .expiring-section:not([hidden]),
  .use-today-banner:not([hidden]),
  .alerts-empty:not([hidden]) {
    opacity: 0;
    transform: translateY(-4px);
  }
}
.expiring-section { margin-bottom: var(--space-5); }
.expiring-heading {
  position: sticky;
  top: 0;
  z-index: 4;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1rem;
  margin: 0 0 10px;
  padding: 10px 0 8px;
  /* Extends past the sections' gutters so scrolled content can't peek
     through at the sides of the sticky band. */
  background: var(--bg);
  margin-left: calc(-1 * var(--space-5));
  margin-right: calc(-1 * var(--space-5));
  padding-left: var(--space-5);
  padding-right: var(--space-5);
  display: flex;
  align-items: center;
  gap: 8px;
}
.expiring-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 26px;
  height: 22px;
  padding: 0 8px;
  border-radius: var(--radius-pill);
  background: var(--surface-alt);
  color: var(--muted);
  font-family: var(--font-ui);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0;
}
/* Hairline that separates active items from muted items in the
   Running Low and Zero Balance lists. */
.muted-list-divider {
  height: 1px;
  background: var(--border);
  margin: 14px 6px 12px;
  opacity: 0.7;
}

.expiring-empty {
  text-align: center;
  padding: 30px 0 12px;
  color: var(--muted);
  font-size: 0.9rem;
}
.alert-stat-grid.loading .alert-stat-num {
  color: transparent;
  background: color-mix(in srgb, var(--muted) 22%, transparent);
  border-radius: 6px;
  min-width: 1.8em;
  animation: alertSkeletonPulse 1.4s ease-in-out infinite;
}
.alert-stat-grid.loading .alert-stat-card { pointer-events: none; }
@keyframes alertSkeletonPulse {
  0%, 100% { opacity: 0.5; }
  50%      { opacity: 1; }
}
.expiring-item {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-3);
  touch-action: pan-y;
  box-shadow: var(--alerts-row-elev);
}
.expiring-item-content {
  position: relative;
  z-index: 2;
  width: 100%;
  box-sizing: border-box;
  background: var(--row-card-bg);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  border: 1px solid var(--row-card-border);
  border-left: 4px solid var(--danger);
  box-shadow: none;
  transition: transform 0.22s var(--ease-ios), opacity 0.22s ease;
}
.expiring-item.soon .expiring-item-content { border-left-color: var(--warn); }
.expiring-item.low .expiring-item-content { border-left-color: var(--info); }
.expiring-item.fresh .expiring-item-content { border-left-color: var(--primary); }
.expiring-item.swiped .expiring-item-content { transform: translateX(-72px); }
.expiring-item.tossing .expiring-item-content {
  transform: translateX(105%);
  opacity: 0;
}
.expiring-toss-reveal {
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: 96px;
  border: none;
  background: var(--danger);
  color: #fff;
  border-radius: var(--radius-sm);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 0 0 0 24px;
  cursor: pointer;
  z-index: 1;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
}
.expiring-item.swiped .expiring-toss-reveal {
  opacity: 1;
  pointer-events: auto;
}
.expiring-toss-reveal svg,
.expiring-toss-reveal span { pointer-events: none; }
.expiring-toss-reveal span {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}
.expiring-toss-reveal:active { filter: brightness(0.92); }
.expiring-toss-reveal[data-reveal-action="toggle"] {
  background: color-mix(in srgb, var(--muted) 70%, transparent);
}
.expiring-toss-reveal[data-reveal-action="toggle"].is-enable {
  background: var(--info);
}
.expiring-item.disabled .expiring-item-content {
  background: var(--surface-alt);
  border-left-color: var(--muted);
  box-shadow: none;
}
.expiring-item.disabled .expiring-item-content .expiring-item-name,
.expiring-item.disabled .expiring-item-content .expiring-item-meta {
  color: var(--muted);
}
.expiring-item.disabled .expiring-item-icon {
  filter: grayscale(0.55);
  opacity: 0.75;
}
.expiring-item.disabled .expiring-item-content .expiring-item-pill {
  background: color-mix(in srgb, var(--muted) 18%, transparent);
  color: var(--muted);
}
.expiring-item-icon {
  position: relative;
  width: 56px; height: 56px;
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.35rem;
  flex-shrink: 0;
  overflow: hidden;
  background: var(--surface);
}
.expiring-item-emoji {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%; height: 100%;
  border-radius: inherit;
}
.expiring-item-thumb {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.expiring-item-thumb:not([src]) { display: none; }
.expiring-item-icon.has-img .expiring-item-emoji { display: none; }
.expiring-item-emoji.cat-veg { background: var(--cat-veg-bg); }
.expiring-item-emoji.cat-fruit { background: var(--cat-fruit-bg); }
.expiring-item-emoji.cat-meat { background: var(--cat-meat-bg); }
.expiring-item-emoji.cat-dairy { background: var(--cat-dairy-bg); }
.expiring-item-emoji.cat-bakery { background: var(--cat-bakery-bg); }
.expiring-item-emoji.cat-grain { background: var(--cat-grain-bg); }
.expiring-item-emoji.cat-drink { background: var(--cat-drink-bg); }
.expiring-item-emoji.cat-snack { background: var(--cat-snack-bg); }
.expiring-item-emoji.cat-frozen { background: var(--cat-frozen-bg); }
.expiring-item-emoji.cat-sauce { background: var(--cat-sauce-bg); }
.expiring-item-emoji.cat-spice { background: var(--cat-spice-bg); }
.expiring-item-emoji.cat-instant { background: var(--cat-instant-bg); }
.expiring-item-emoji.cat-clean { background: var(--cat-clean-bg); }
.expiring-item-emoji.cat-default { background: var(--cat-default-bg); }
.expiring-item-body { flex: 1; min-width: 0; }
.expiring-item-name { font-weight: 600; font-size: 0.95rem; }
.expiring-item-meta { color: var(--muted); font-size: 0.78rem; margin-top: 1px; }
.expiring-item-pill {
  background: var(--danger-bg);
  color: var(--danger);
  font-size: 0.72rem;
  font-weight: 700;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  flex-shrink: 0;
}
.expiring-item.soon .expiring-item-pill { background: var(--warn-bg); color: var(--warn); }
.expiring-item.low .expiring-item-pill { background: var(--info-bg); color: var(--info); }
.expiring-item.fresh .expiring-item-pill { background: var(--pill-active-bg); color: var(--primary); }
.expiring-item.tappable { cursor: pointer; }
.expiring-item.tappable:active { transform: scale(0.98); }
.expiring-item.tappable:active .expiring-item-content {
  background: color-mix(in srgb, var(--row-card-bg) 94%, var(--ink) 6%);
}

body.overlay-open .tabbar {
  opacity: 0;
  pointer-events: none;
  transform: translateX(-50%) translateY(20px);
}
body.kb-open .tabbar { display: none; }

.alerts-scroll-top {
  position: fixed;
  right: calc(max(0px, (100vw - 480px) / 2) + var(--space-4));
  bottom: 20px;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-card-shadow);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 10;
  opacity: 0;
  transform: translateY(12px) scale(0.9);
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s var(--ease-ios);
}
.alerts-scroll-top.visible { opacity: 1; transform: none; pointer-events: auto; }
.alerts-scroll-top:active { transform: scale(0.92); }
.alerts-scroll-top svg { width: 20px; height: 20px; }

body.tabbar-hidden .tabbar {
  opacity: 0;
  pointer-events: none;
  transform: translateX(-50%) translateY(calc(100% + 24px));
}

/* Live swipe-to-switch-tabs preview. */
.tab.swiping {
  display: block;
  position: absolute;
  top: 4px;
  left: var(--space-4);
  right: var(--space-4);
  z-index: 5;
  pointer-events: none;
  will-change: transform;
  background: var(--bg);
}
.tab.swiping-in { z-index: 6; }


/* ============================================================================
   8. ITEM MODAL — ADD PRODUCT
   ============================================================================ */
.modal-add-hero {
  position: relative;
  overflow: hidden;
  color: #fff;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  flex-shrink: 0;
  background: none;
  border: none;
  box-shadow: none;
  display: flex;
  flex-direction: column;
  padding: 0;
}

/* ADD-PRODUCT HERO — gradient only
   Overrides the photographic hero on the Add Product modal
   specifically. Every other .modal-add-hero (Bought, Receipt Match)
   keeps its photo background. Uses --hero-grad, the same theme-aware
   accent gradient that fills Consume / Shopping / Recipes headers and
   the promo banner — the modal hero now matches the app chrome. */
#item-add-hero .hero-layer-base,
#bought-modal .hero-layer-base,
#receipt-match-modal .hero-layer-base {
  background: var(--hero-grad);
}
/* The photo-shaped inner glass layer (subtle white wash +
   backdrop-blur) had a purpose on a photographic hero; on a plain
   gradient it renders nothing. Drop it inside this modal only. */
#item-add-hero .modal-add-hero-overlay,
#bought-modal .modal-add-hero-overlay,
#receipt-match-modal .modal-add-hero-overlay {
  background: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border: none;
  box-shadow: none;
}
.modal-add-hero::before {
  content: '';
  position: absolute;
  top: 8px; left: 50%;
  width: 36px; height: 5px;
  margin-left: -18px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.7);
  z-index: 3;
  pointer-events: none;
}
.hero-layer { position: absolute; inset: 0; border-radius: inherit; z-index: 0; }
.hero-layer-base {
  background:
    linear-gradient(180deg,
      rgba(10, 20, 30, calc(var(--hero-shade) - 0.2)) 0%,
      rgba(10, 20, 30, var(--hero-shade)) 100%),
    var(--hero-photo) var(--hero-pos) / cover no-repeat,
    #1a2530;
}
.modal-add-hero-overlay {
  position: absolute; inset: 0;
  z-index: 1;
  border-radius: inherit;
  background: rgba(255, 255, 255, 0.06);
  backdrop-filter: blur(3px) saturate(140%);
  -webkit-backdrop-filter: blur(3px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.4);
  border-bottom: none;
  box-shadow:
    inset 0 2px 6px rgba(255, 255, 255, 0.6),
    inset 0 -4px 12px rgba(0, 0, 0, 0.2);
}
.modal-add-hero-content {
  position: relative;
  z-index: 2;
  padding: 18px 0 8px 20px;
  min-height: 110px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
}
.modal-add-hero-content h2, .modal-add-hero h2 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.4rem;
  margin: 0 0 2px;
  color: #fff;
  padding-right: 88px;
}
.modal-add-hero-content p, .modal-add-hero p { margin: 0; opacity: 0.9; font-size: 0.85rem; }
.modal-add-hero-content h2,
.modal-add-hero-content p { text-shadow: 0 1px 5px rgba(0, 0, 0, 0.35); }

.modal-header.over-hero {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 110px;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding: 0 18px;
  background: none;
  border-bottom: none;
  z-index: 3;
  gap: var(--space-2);
}

/* Item modal shell: form is a flex column that fills the sheet, so the
   body can grow and scroll while the hero and footer stay put. Without
   these the form collapsed to content height and nothing scrolled. */
#item-form {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}
#item-form .modal-body {
  flex: 1 1 auto;
  min-height: 0;
}
.icon-btn.on-photo.scan-highlight {
  background: rgba(255, 255, 255, 0.36);
  border-color: rgba(255, 255, 255, 0.65);
  box-shadow:
    var(--glass-photo-shadow),
    0 0 0 3px rgba(255, 255, 255, 0.16),
    inset 0 1px 1px rgba(255, 255, 255, 0.6);
  width: 64px;
  border-radius: var(--radius-pill);
}

/* Item Modal input chrome. */
#item-modal input[type="text"],
#item-modal input[type="number"],
#item-modal input[type="date"],
#item-modal select {
  background-color: var(--field-bg);
  border: 1px solid var(--field-border);
  border-radius: var(--radius-12);
  color: var(--ink);
}
#item-modal input:focus, #item-modal select:focus { outline: 2px solid var(--primary); background: var(--input-focus-bg); }

.or-divider { display: flex; align-items: center; gap: var(--space-2); margin: var(--space-4) 0; color: var(--muted); font-size: 0.78rem; }
.or-divider::before, .or-divider::after { content: ''; flex: 1; height: 1px; background: var(--border); }

.item-photo-row { display: flex; flex-direction: column; align-items: center; gap: var(--space-3); margin: var(--space-6) 0 var(--space-2); }
.item-photo-actions { display: flex; align-items: center; justify-content: center; gap: var(--space-4); }

.photo-action-btn {
  position: relative;
  isolation: isolate;
  width: 48px;
  height: 48px;
  color: var(--primary-ink);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.38) 0%, rgba(255, 255, 255, 0.08) 48%, rgba(255, 255, 255, 0) 52%),
    color-mix(in srgb, var(--primary) 82%, transparent);
  border: 1px solid rgba(255, 255, 255, 0.40);
  box-shadow:
    0 6px 16px color-mix(in srgb, var(--primary) 40%, transparent),
    0 1px 3px rgba(0, 0, 0, 0.25),
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 -3px 6px rgba(0, 0, 0, 0.18);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.photo-action-btn::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  z-index: -1;
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
}
.photo-action-btn:active { transform: scale(0.92); }
.photo-action-btn:disabled { opacity: 0.6; }
.photo-action-btn.is-loading:disabled { opacity: 1; }
.photo-action-btn.is-loading svg { visibility: hidden; }
.photo-action-btn.is-loading::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 22px;
  height: 22px;
  margin: -11px 0 0 -11px;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round'%3E%3Cpath d='M21 12a9 9 0 1 1-3.2-6.9'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round'%3E%3Cpath d='M21 12a9 9 0 1 1-3.2-6.9'/%3E%3C/svg%3E") center / contain no-repeat;
  animation: ptrSpin 0.8s linear infinite;
  will-change: transform;
}

.item-thumb-wrap.large {
  width: 42%;
  max-width: 160px;
  height: auto;
  aspect-ratio: 1;
  margin: 0 auto;
  padding: var(--space-4);
  border-radius: var(--radius-24);
  background: var(--panel-solid-bg);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-card-shadow);
}
.item-thumb-wrap.large.is-pending {
  position: relative;
  border-color: var(--primary);
  box-shadow:
    0 0 0 3px color-mix(in srgb, var(--primary) 30%, transparent),
    var(--glass-card-shadow);
}
.item-thumb-wrap.large.is-pending::after {
  content: 'New — save to keep';
  position: absolute;
  top: 10px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--primary);
  color: var(--primary-ink);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  white-space: nowrap;
  z-index: 2;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
  pointer-events: none;
}
.item-thumb-wrap.large .item-thumb { object-fit: contain; }
#item-scan-section { margin-top: 0; }
#item-scan-status:empty { display: none; }

/* Add-mode photo row: elevated card on the left, two buttons stacked on the right. */
#item-photo-row.add-mode {
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 20px;
  margin: var(--space-5) 0 var(--space-2);
}
#item-photo-row.add-mode .item-thumb-wrap.large {
  width: 160px;
  max-width: none;
  aspect-ratio: 1;
  margin: 0;
  padding: 0;
  border: none;
  background: var(--surface);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-radius: var(--radius-20);
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.05),
    0 4px 12px rgba(0, 0, 0, 0.06);
}
[data-theme="dark"] #item-photo-row.add-mode .item-thumb-wrap.large {
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.35),
    0 4px 12px rgba(0, 0, 0, 0.40);
}
#item-photo-row.add-mode .item-thumb-wrap.large::before {
  content: 'Photo (optional)';
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0 12px;
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 600;
  line-height: 1.3;
  pointer-events: none;
}
#item-photo-row.add-mode .item-thumb-wrap.large:has(img[src])::before { display: none; }
#item-photo-row.add-mode .item-thumb-wrap.large .item-thumb {
  width: 100%;
  height: 100%;
  padding: var(--space-2-5);
  box-sizing: border-box;
  object-fit: contain;
}
#item-photo-row.add-mode .item-thumb-wrap.large .item-thumb:not([src]),
#item-photo-row.add-mode .item-thumb-wrap.large .item-thumb[src=""] {
  display: none;
}
#item-photo-row.add-mode .item-photo-actions {
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  height: 160px;
}

/* Duplicate-name hint. */
.add-name-hint { font-size: 0.72rem; color: var(--muted); margin: 4px 0 0; font-weight: 500; }


/* ============================================================================
   9. CONSUME STOCK PAGE
   ============================================================================ */
#tab-consume .consume-header {
  position: relative;
  padding-bottom: 32px;
}
.consume-hero-search {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: 0;
}
.consume-hero-search .search-pill-wrap {
  flex: 1 1 auto;
  margin-bottom: 0;
}
.consume-hero-search .search-pill-wrap input[type="search"],
.consume-hero-search .search-pill-wrap input[type="text"] {
  background: var(--surface);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.10);
  border: none;
}
.consume-hero-search .search-pill-icon { color: var(--primary); }
.consume-hero-search .consume-search-results {
  position: absolute;
  left: 0;
  right: calc(46px + var(--space-3));
  top: calc(100% + 6px);
}

/* Shopping tab has two buttons beside the search pill (add-low +
   scan-receipt), so the results dropdown stops short of both. Consume
   stays at one button (the 46px base rule above). */
#tab-shopping .consume-hero-search .consume-search-results {
  right: calc(2 * 46px + 2 * var(--space-3));
}
.consume-hero-select {
  width: 46px;
  height: 46px;
  flex-shrink: 0;
}
.consume-hero-select:disabled {
  opacity: 0.4;
  pointer-events: none;
}
.consume-select-pill {
  width: 46px;
  height: 46px;
  flex-shrink: 0;
  color: var(--primary-ink);
  background: var(--glass-photo-bg);
  border: 1px solid var(--glass-photo-border);
  box-shadow: var(--glass-photo-shadow);
  transition: transform 0.24s var(--ease-ios), background 0.15s ease;
}
.consume-select-pill:active { transform: scale(0.9); }
.consume-select-pill.active {
  background: var(--primary);
  color: var(--primary-ink);
  border-color: var(--primary);
}

/* Scroll lock: hero stays put, only the body scrolls. */
html:has(body[data-tab="tab-consume"]) {
  overflow: hidden;
  height: 100dvh;
}
body[data-tab="tab-consume"] {
  padding-bottom: 0;
  height: 100dvh;
  overflow: hidden;
}
body[data-tab="tab-consume"] main {
  height: 100%;
  min-height: 0;
  padding-bottom: 0;
  display: flex;
  flex-direction: column;
}
body[data-tab="tab-consume"] #tab-consume {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  animation: none;
}
body[data-tab="tab-consume"] .consume-header { flex: 0 0 auto; }
body[data-tab="tab-consume"] .consume-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  padding-bottom: calc(120px + var(--safe-bottom));
  margin-top: -20px;
  z-index: 3;
  background: var(--bg);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}

/* Row press state + qty focus micro-animation. */
.consume-row.pressed .consume-row-content {
  background: color-mix(in srgb, var(--row-card-bg) 94%, var(--ink) 6%);
}
.consume-row.pressed:not(.swiped) .consume-row-content {
  transform: scale(0.99);
  box-shadow: var(--row-highlight), 0 0 0 1px color-mix(in srgb, var(--primary) 22%, transparent);
}
.shop-qty-input {
  transition: transform 0.18s var(--ease-ios), box-shadow 0.18s ease, background 0.15s ease;
  position: relative;
}
.shop-qty-input:focus {
  transform: scale(1.06);
  box-shadow: 0 0 0 2px var(--primary), 0 4px 10px rgba(0, 0, 0, 0.10);
  background: var(--input-focus-bg);
  z-index: 2;
}

/* Submit-pill peek on tab switch in. */
@keyframes submitPillPeek {
  from { opacity: 0; transform: translateX(-50%) translateY(28px) scale(0.94); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
}
body[data-tab="tab-consume"] #consume-submit-btn,
body[data-tab="tab-shopping"] #shopping-submit-btn {
  animation: submitPillPeek 0.42s var(--ease-ios) 0.08s backwards;
}

/* Empty state. */
.consume-empty { text-align: center; padding: 28px 8px 12px; }
.consume-empty-illus { width: 96px; height: 96px; color: var(--muted); opacity: 0.55; margin: 0 0 14px; }
[data-theme="dark"] .consume-empty-illus { opacity: 0.72; }
.consume-empty-title { margin: 0 0 4px; font-weight: 700; font-size: 1rem; color: var(--ink); }
.consume-empty-sub { margin: 0 0 18px; font-size: 0.85rem; color: var(--muted); }
.consume-empty-chips { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
.consume-empty-chips .location-chip { height: 38px; padding: 0 16px; font-size: 0.85rem; }

/* Hide the Submit pill when the list is empty. */
body[data-tab="tab-consume"].consume-list-empty #consume-submit-btn {
  display: none;
}


/* ============================================================================
   10. SHOPPING LIST PAGE + BOUGHT OVERLAY
   ============================================================================ */
#tab-shopping .consume-header {
  position: relative;
  padding-bottom: 32px;
}
#tab-shopping .consume-hero-search { margin-top: 0; }
#tab-shopping .consume-body {
  margin-top: -20px;
  z-index: 3;
  background: var(--bg);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}
#tab-shopping .consume-header .shopping-progress { display: none; }


/* Scroll lock: hero stays put, only the body scrolls. */
html:has(body[data-tab="tab-shopping"]) {
  overflow: hidden;
  height: 100dvh;
}
body[data-tab="tab-shopping"] {
  padding-bottom: 0;
  height: 100dvh;
  overflow: hidden;
}
body[data-tab="tab-shopping"] main {
  height: 100%;
  min-height: 0;
  padding-bottom: 0;
  display: flex;
  flex-direction: column;
}
body[data-tab="tab-shopping"] #tab-shopping {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  animation: none;
}
body[data-tab="tab-shopping"] .consume-header { flex: 0 0 auto; }
body[data-tab="tab-shopping"] .consume-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  padding-bottom: calc(120px + var(--safe-bottom));
}

/* Two-line Bought pill. */
body[data-tab="tab-shopping"] #shopping-submit-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  padding: 10px 14px;
  line-height: 1.15;
}
#shopping-submit-btn .submit-btn-main { font-size: 0.95rem; font-weight: 700; }
#shopping-submit-btn .submit-btn-sub { font-size: 0.72rem; font-weight: 500; opacity: 0.9; }
#shopping-submit-btn .submit-btn-sub:empty { display: none; }
body[data-tab="tab-shopping"].shopping-list-empty #shopping-submit-btn { display: none; }

/* --- Shopping list rows --- */
#shopping-list .consume-row-content { transition: transform 0.15s ease, padding-left 0.15s ease, background 0.15s ease; }
#shopping-list .consume-row { user-select: none; -webkit-user-select: none; }

/* Right-swipe reveals a grey Mute button on the left of the row (the
   mirror of the delete reveal on the right). Only rendered on rows whose
   product could be auto-added by "Add Low-Stock Items" — see
   isProductLowOrZero() in app.js. */
#shopping-list .consume-row.swiped-right .consume-row-content { transform: translateX(72px); }
.consume-list .consume-row-mute-reveal {
  position: absolute;
  top: 0; left: 0; bottom: 0;
  width: 96px;
  border: none;
  border-radius: var(--radius-16);
  padding: 0 24px 0 0;
  background: color-mix(in srgb, var(--muted) 70%, transparent);
  color: #fff;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  cursor: pointer;
  z-index: 1;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
}
.consume-list .consume-row.swiped-right .consume-row-mute-reveal {
  opacity: 1;
  pointer-events: auto;
}
.consume-list .consume-row-mute-reveal svg,
.consume-list .consume-row-mute-reveal span { pointer-events: none; }
.consume-list .consume-row-mute-reveal span {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}
.consume-list .consume-row-mute-reveal svg { width: 20px; height: 20px; }
.consume-list .consume-row-mute-reveal:active { filter: brightness(0.92); }

.shop-tick {
  width: 24px; height: 24px;
  border-radius: 7px;
  border: 2px solid color-mix(in srgb, var(--muted) 60%, transparent);
  background: var(--input-bg);
  flex-shrink: 0;
  padding: 0;
  position: relative;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.shop-tick::before { content: ''; position: absolute; inset: -10px; }
.shop-tick.checked { background: var(--primary); border-color: var(--primary); }
.shop-tick.checked::after {
  content: '';
  position: absolute;
  left: 6px; top: 2px;
  width: 5px; height: 10px;
  border: solid #fff;
  border-width: 0 2.5px 2.5px 0;
  transform: rotate(45deg);
}
.shop-tick.pop { animation: tickPop 0.28s var(--ease-ios); }
#shopping-list .consume-thumb { transition: opacity 0.2s ease; }
#shopping-list .consume-row-name { transition: color 0.2s ease; }
#shopping-list .consume-row.is-done .consume-row-name,
#shopping-list .consume-row.is-done .consume-row-sub { text-decoration: line-through; color: var(--muted); }
#shopping-list .consume-row.is-done .consume-thumb { opacity: 0.45; }
#shopping-list .consume-row.is-done .shop-qty-input { opacity: 0.5; }
/* Hidden (not dimmed) until at least one item is picked. The selector
   is as specific as the display:flex rule above so it wins. */
body[data-tab="tab-shopping"] #shopping-submit-btn.is-empty { display: none; }

.shop-qty-input {
  width: 80px;
  flex-shrink: 0;
  text-align: center;
  font-weight: 700;
  padding: 8px 6px;
  border-radius: var(--radius-xs);
  user-select: text;
  -webkit-user-select: text;
  -moz-appearance: textfield;
  appearance: textfield;
}
.shop-qty-input::-webkit-outer-spin-button,
.shop-qty-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

.shop-fail-note { color: var(--danger); font-size: 0.75rem; font-weight: 600; margin-top: 2px; }
.or-divider { animation: dividerIn 0.24s var(--ease-ios); }
@keyframes dividerIn { from { opacity: 0; transform: scaleX(0.6); } to { opacity: 1; transform: none; } }

/* --- Bought overlay --- */
.bought-list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  position: relative;
  margin-top: -18px;
  z-index: 2;
  padding: var(--space-5) var(--space-5) calc(120px + var(--safe-bottom) + var(--kb));
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  background: var(--surface);
  box-shadow:
    0 -8px 18px rgba(0, 0, 0, 0.28),
    0 -1px 0 rgba(255, 255, 255, 0.55),
    inset 0 2px 0 rgba(255, 255, 255, 0.95);
}
/* Bought row: outer shell clips the swipe reveal, inner content slides. */
.bought-row {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-16);
  box-shadow: var(--row-shadow);
  margin-bottom: var(--space-3);
}
.bought-row-content {
  position: relative;
  z-index: 2;
  background: var(--row-card-bg);
  border: 1px solid var(--row-card-border);
  border-radius: var(--radius-16);
  box-shadow: var(--row-highlight);
  padding: 12px;
  transition: transform 0.15s ease;
}
.bought-row.pressed .bought-row-content {
  transform: scale(0.99);
  background: color-mix(in srgb, var(--row-card-bg) 94%, var(--ink) 6%);
}
.bought-row.swiped .bought-row-content {
  transform: translateX(-72px);
}
.bought-row-delete-reveal {
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: 96px;
  border: none;
  padding: 0 0 0 24px;
  background: var(--danger);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-16);
  cursor: pointer;
  z-index: 1;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.bought-row.swiped .bought-row-delete-reveal { opacity: 1; }
.bought-row-delete-reveal svg { width: 20px; height: 20px; pointer-events: none; }
.bought-row-delete-reveal:active { filter: brightness(0.92); }
.bought-row .consume-thumb { width: 44px; height: 44px; border-radius: var(--radius-12); overflow: hidden; flex-shrink: 0; background: var(--surface-alt); }
.bought-row .consume-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; border: 0; }
.bought-row .consume-thumb img:not([src]) { visibility: hidden; }

/* Both header variants share the same grid: thumb | content | pencil. */
.bought-row .bought-header {
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 8px;
  margin-bottom: 12px;
}
.bought-row-new .bought-header { margin-bottom: 8px; }

/* 2-line name block for existing rows. */
.bought-name-block {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  min-width: 0;
}
.bought-name-block .bought-name {
  font-size: 0.95rem;
  font-weight: 700;
  line-height: 1.2;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.bought-meta {
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--muted);
  line-height: 1.2;
  overflow-wrap: anywhere;
}
.bought-meta:empty { display: none; }

.bought-header-secondary {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr) minmax(0, 0.8fr);
  column-gap: 8px;
  align-items: end;
  margin-bottom: 12px;
}
.bought-fields {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr) minmax(0, 0.8fr);
  column-gap: 8px;
  align-items: end;
}
.bought-fields .bought-qty { padding: 0 6px; text-align: center; }
.bought-field { min-width: 0; }
.field-label,
.bought-field .field-label { font-size: 11px; }
.bought-field .field-label {
  display: block;
  margin: 0 0 6px;
  font-weight: 700;
  line-height: 1;
  color: var(--muted);
  text-transform: none;
  letter-spacing: 0;
}

/* "NEW" divider between sections. */
.bought-divider {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-3) 2px 6px;
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  color: var(--info);
}
.bought-divider::before,
.bought-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border);
}

/* Elevated fields inside Bought rows — 11px, transparent inner field. */
.bought-row .input-elevated { min-height: 44px; }
.bought-row .input-elevated input,
.bought-row .input-elevated select,
.bought-row .input-elevated .ui-dropdown-trigger {
  background: transparent;
  border: none;
  box-shadow: none;
  border-radius: 0;
  padding: 0;
  height: 44px;
  min-height: 0;
  font-size: 11px;
  font-family: var(--font-ui);
  font-weight: 500;
  color: var(--ink);
  line-height: 42px;
  text-align: left;
  margin: 0;
  min-width: 0;
  box-sizing: border-box;
  outline: none;
}
.bought-row .input-elevated select {
  padding-right: 26px;
  background-position: right 0 center;
  background-repeat: no-repeat;
}
.bought-row .input-elevated .ui-dropdown-trigger {
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.bought-row .input-elevated input:focus,
.bought-row .input-elevated select:focus,
.bought-row .input-elevated .ui-dropdown-trigger:focus {
  background: transparent;
  border: none;
  outline: none;
}
.bought-row .exp-field.has-leading-icon { min-height: 44px; }
.bought-row .exp-field.has-leading-icon input[type="date"] {
  background: transparent;
  border: none;
  border-radius: 0;
  padding: 0 0 0 30px;
  height: 44px;
  font-size: 11px;
}
.bought-row .exp-field.has-leading-icon input[type="date"]:focus {
  background: transparent;
  border: none;
  outline: none;
}

/* Row X (remove-from-batch) button. */
.bought-row-remove {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: none;
  background: var(--surface-alt);
  color: var(--muted);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.15s ease, transform 0.24s var(--ease-ios);
}
.bought-row-remove:active { transform: scale(0.88); }
.bought-row-remove svg { width: 14px; height: 14px; pointer-events: none; }

.bought-footer {
  position: absolute;
  left: 0; right: 0;
  bottom: max(calc(25px + var(--safe-bottom)), calc(var(--kb) + 12px));
  padding: 0 var(--space-5);
  z-index: 5;
}

/* What can I cook? — Generate footer only appears once something is ticked.
   Paint-only (opacity / transform / visibility); no layout or --kb changes. */
#recipe-ai-modal .bought-footer {
  transition: opacity 0.2s ease, transform 0.26s var(--ease-ios), visibility 0s linear 0s;
}
#recipe-ai-modal .bought-footer.is-empty {
  opacity: 0;
  transform: translateY(14px);
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.26s var(--ease-ios), visibility 0s linear 0.26s;
}

/* Bought modal header tweaks. */
#bought-modal .modal-add-hero-content {
  align-items: flex-start;
  text-align: left;
}
#bought-modal .modal-add-hero-content h2 {
  text-align: left;
  padding-right: 88px;
}
#bought-modal .icon-btn.on-photo { color: var(--primary-ink); }

/* Long-press drag-to-reorder. */
body.drag-active {
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}
body.drag-active #shopping-list .consume-row:not(.dragging) {
  pointer-events: none;
}
#shopping-list .consume-row.dragging {
  z-index: 1000;
  pointer-events: none;
  box-shadow:
    0 18px 40px rgba(0, 0, 0, 0.30),
    0 4px 12px rgba(0, 0, 0, 0.15);
  cursor: grabbing;
  will-change: top;
}
#shopping-list .consume-row.dragging .consume-row-content {
  background: var(--row-card-bg);
  border-color: var(--primary);
  box-shadow:
    0 0 0 2px var(--primary),
    0 8px 20px rgba(0, 0, 0, 0.15);
}
.shopping-drag-placeholder { visibility: hidden; }


/* ============================================================================
   11. SETTINGS
   ============================================================================ */
.settings-section {
  margin-bottom: var(--space-4);
  padding: var(--space-4);
  background: var(--panel-solid-bg);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--glass-card-shadow);
}
.settings-section-title {
  font-family: var(--font-ui);
  font-size: 1rem;
  font-weight: 700;
  color: var(--ink);
  margin: 0 0 4px;
}
.settings-slider-row { margin-top: var(--space-4); }
.settings-slider-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 6px;
}
.settings-slider-value {
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--primary);
}
.settings-slider-row input[type="range"] {
  width: 100%;
  accent-color: var(--primary);
}
.settings-info-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-3);
}
.settings-info-value { text-align: right; word-break: break-word; }
.theme-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); margin-top: var(--space-2-5); }
.theme-swatch {
  background: var(--tile-bg);
  box-shadow: var(--tile-shadow);
  border: 2px solid transparent;
  border-radius: var(--radius-xs);
  padding: var(--space-3);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  font-size: 0.82rem;
  color: var(--ink);
  font-weight: 600;
  transition: transform 0.24s var(--ease-ios), border-color 0.15s ease, box-shadow 0.15s ease;
}
.theme-swatch.active { border-color: var(--primary); box-shadow: var(--tile-selected-shadow); }
.theme-swatch:active { transform: scale(0.97); }
.swatch-preview { width: 44px; height: 44px; border-radius: 50%; display: block; }
.swatch-preview.fresh { background: linear-gradient(135deg, #51950A, #F6FBF4); }
.swatch-preview.ocean { background: linear-gradient(135deg, #25375A, #EDF0F8); }
.swatch-preview.warm { background: linear-gradient(135deg, #D46A34, #FBF3E7); }
.swatch-preview.dark { background: linear-gradient(135deg, #7A9A2F, #0A0E11); }
.swatch-preview.cartoon { background: linear-gradient(135deg, #8CBD42, #FBF3DF); }

/* Typography picker — 4 tiles, each previewing its typeface in large "Aa". */
.font-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
  margin-top: var(--space-2-5);
}
.font-swatch {
  background: var(--tile-bg);
  box-shadow: var(--tile-shadow);
  border: 2px solid transparent;
  border-radius: var(--radius-xs);
  padding: var(--space-3);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-ui);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  transition: transform 0.24s var(--ease-ios), border-color 0.15s ease, box-shadow 0.15s ease;
}
.font-swatch.active { border-color: var(--primary); box-shadow: var(--tile-selected-shadow); }
.font-swatch:active { transform: scale(0.97); }
.font-swatch-preview {
  font-size: 1.7rem;
  line-height: 1;
  color: var(--ink);
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.font-swatch[data-font="jakarta"] .font-swatch-preview { font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, sans-serif; }
.font-swatch[data-font="system"]  .font-swatch-preview { font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', sans-serif; }
.font-swatch[data-font="serif"]   .font-swatch-preview { font-family: ui-serif, 'New York', 'Iowan Old Style', Georgia, 'Times New Roman', serif; }
.font-swatch[data-font="mono"]    .font-swatch-preview { font-family: ui-monospace, 'SF Mono', Menlo, Monaco, 'Courier New', monospace; }


/* ============================================================================
   12. RECIPES REWORK
   ============================================================================ */
.recipes-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
  margin-top: var(--space-3);
  animation: gridIn 0.2s ease-out;
}
.recipes-grid[hidden] { display: none; }
.recipes-empty { text-align: center; padding: 40px 20px; }

/* Hero. */
.recipes-hero {
  padding: calc(22px + var(--safe-top)) var(--space-5) 32px;
}
.recipes-hero .consume-header-row { justify-content: flex-start; }
.recipes-hero-titles {
  flex: 1;
  text-align: left;
  padding-left: 4px;
}

/* Recipe tab: page scroll lock, same shape as Consume / Shopping. The hero
   (title + search) stays pinned to the top; the white body is the scroll
   container. Portrait mode keeps the page locked too — the horizontal card
   strip scrolls inside the body. */
html:has(body[data-tab="tab-recipes"]) {
  overflow: hidden;
  height: 100dvh;
}
body[data-tab="tab-recipes"] {
  padding-bottom: 0;
  height: 100dvh;
  overflow: hidden;
}
body[data-tab="tab-recipes"] main {
  height: 100%;
  min-height: 0;
  padding-bottom: 0;
  display: flex;
  flex-direction: column;
}
body[data-tab="tab-recipes"] #tab-recipes {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  animation: none;
}
body[data-tab="tab-recipes"] .consume-header { flex: 0 0 auto; }
body[data-tab="tab-recipes"] .consume-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  padding-top: 0px;
  padding-bottom: calc(120px + var(--safe-bottom));
  margin-top: -20px;
  z-index: 3;
  background: var(--bg);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}

/* The view-row (count + hearts + grid/portrait toggle) sticks to the top of
   the scrollable body so it stays visible as the grid scrolls. Negative
   margins bleed it edge-to-edge, matching the body's own full-bleed width. */
body[data-tab="tab-recipes"] .recipe-view-row {
  position: sticky;
  top: 0;
  z-index: 4;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  margin-left: calc(-1 * var(--space-4));
  margin-right: calc(-1 * var(--space-4));
  padding-left: calc(var(--space-4) + 2px);
  padding-right: calc(var(--space-4) + 2px);
}

/* Recipe hero-search is two buttons wide (AI + Add), not one like Consume /
   Shopping, so the results dropdown must stop short of the second button. */
#tab-recipes .consume-hero-search .consume-search-results {
  right: calc(2 * 46px + 2 * var(--space-3));
}
.recipes-hello {
  margin: 0 0 6px;
  font-family: var(--font-ui);
  font-size: 0.88rem;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.86);
  letter-spacing: 0.01em;
}
.recipes-hello-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.5rem;
  line-height: 1.2;
  color: #fff;
  letter-spacing: -0.01em;
}

/* View-toggle row. */
.recipe-view-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 4px 0 var(--space-4);
  padding: 0 2px;
}
.recipe-count {
  font-family: var(--font-ui);
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--ink);
  letter-spacing: -0.01em;
}
.recipe-view-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2-5);
  background: none;
  border: none;
  padding: 4px;
}
.recipe-view-btn {
  background: none;
  border: none;
  padding: 4px;
  cursor: pointer;
  color: color-mix(in srgb, var(--muted) 45%, transparent);
  transition: color 0.15s ease, transform 0.24s var(--ease-ios);
}
.recipe-view-btn svg { width: 20px; height: 20px; display: block; }
.recipe-view-btn.active { color: var(--ink); }
.recipe-view-btn:active { transform: scale(0.92); }
.recipe-view-actions {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3-5);
}
.recipe-fav-filter-btn.active { color: #e0463c; }
.recipe-fav-filter-btn.active svg { fill: currentColor; }

html.recipes-portrait-mode { overflow-y: hidden; }

/* Portrait mode: the card strip is vertically centered inside the
   scrollable body. The body becomes a flex column; the strip's auto
   top/bottom margins absorb the free space above and below it, so it
   sits mid-way between the sticky view-row and the body's bottom edge.
   The strip keeps flex-shrink: 0 so it never squashes on short screens
   -- below its own min-height (340px) the body simply starts scrolling. */
html.recipes-portrait-mode body[data-tab="tab-recipes"] .consume-body {
  display: flex;
  flex-direction: column;
}
html.recipes-portrait-mode body[data-tab="tab-recipes"] .recipes-portrait {
  margin-top: auto;
  margin-bottom: auto;
  flex-shrink: 0;
}

/* Portrait (wheel) view. */
.recipes-portrait {
  display: flex;
  flex-wrap: nowrap;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  height: clamp(340px, calc((100svh / var(--app-scale)) - 360px), 640px);
  padding: 16px 6% 48px;
  margin: 0 calc(-1 * var(--space-4));
  gap: 16px;
  align-items: stretch;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  overscroll-behavior-x: contain;
}
.recipes-portrait[hidden] { display: none; }
.recipes-portrait::-webkit-scrollbar { display: none; }

.portrait-card {
  flex: 0 0 auto;
  height: 100%;
  aspect-ratio: 3 / 4;
  scroll-snap-align: center;
  border-radius: var(--radius-lg);
  overflow: hidden;
  position: relative;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  will-change: transform, opacity;
  transition: transform 0.18s ease-out, opacity 0.18s ease-out;
  transform: scale(0.92);
  opacity: 0.75;
  box-shadow:
    0 2px 4px rgba(0, 0, 0, 0.12),
    0 8px 20px rgba(0, 0, 0, 0.16),
    0 14px 32px rgba(0, 0, 0, 0.08);
}
[data-theme="dark"] .portrait-card {
  box-shadow:
    0 2px 4px rgba(0, 0, 0, 0.5),
    0 8px 20px rgba(0, 0, 0, 0.55),
    0 14px 32px rgba(0, 0, 0, 0.35);
}
.portrait-card-inner {
  position: absolute;
  inset: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--surface-alt);
  cursor: pointer;
}

/* Broken-image placeholder for portrait cards. Same striped pattern the
   grid cards use via .item-thumb-wrap.img-failed, adapted to the portrait
   inner wrapper which has no .item-thumb-wrap ancestor. */
.portrait-card-inner.img-failed {
  background:
    repeating-linear-gradient(45deg,
      var(--surface-alt),
      var(--surface-alt) 8px,
      color-mix(in srgb, var(--surface-alt) 82%, var(--ink)) 8px,
      color-mix(in srgb, var(--surface-alt) 82%, var(--ink)) 16px);
}
.portrait-card-inner.img-failed .portrait-card-img {
  visibility: hidden;
}
.portrait-card-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  pointer-events: none;
  -webkit-user-drag: none;
  user-drag: none;
}
.portrait-card-shade {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
    rgba(0, 0, 0, 0.10) 0%,
    rgba(0, 0, 0, 0.00) 40%,
    rgba(0, 0, 0, 0.55) 78%,
    rgba(0, 0, 0, 0.78) 100%);
  pointer-events: none;
  z-index: 1;
}
.portrait-card-text {
  position: absolute;
  left: 18px;
  right: 18px;
  bottom: 20px;
  z-index: 2;
  color: #fff;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.5);
}
.portrait-card-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.15rem;
  line-height: 1.25;
  margin: 0 0 6px;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.portrait-card-meta {
  font-family: var(--font-ui);
  font-size: 0.82rem;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.85);
}

.recipe-fav-btn {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 3;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: none;
  background: rgba(255, 255, 255, 0.38);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
  transition: background 0.15s ease, transform 0.24s var(--ease-ios);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.22);
}
.recipe-fav-btn:active { transform: scale(0.82); }
.recipe-fav-btn.active {
  background: rgba(255, 255, 255, 0.96);
  color: #e0463c;
}
.recipe-fav-btn svg { width: 20px; height: 20px; }

.recipe-grid-item .recipe-fav-btn {
  top: 8px;
  right: 8px;
  width: 32px;
  height: 32px;
}
.recipe-grid-item .recipe-fav-btn svg { width: 18px; height: 18px; }

/* Recipe cards adopt .grid-item styles from Home. */
.recipe-grid-item { cursor: pointer; }
.recipe-grid-item .item-thumb-wrap { background: var(--surface-alt); }
.recipe-grid-item .item-thumb-wrap:empty,
.recipe-grid-item .item-thumb-wrap.no-img {
  background:
    repeating-linear-gradient(45deg,
      var(--surface-alt),
      var(--surface-alt) 8px,
      color-mix(in srgb, var(--surface-alt) 82%, var(--ink)) 8px,
      color-mix(in srgb, var(--surface-alt) 82%, var(--ink)) 16px);
}
.recipe-grid-item .name {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-left: 4px;
}
.recipe-grid-item .grid-amount { margin-left: 4px; }
.recipe-card-wrap {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-20);
  background: var(--glass-bg);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-card-shadow);
}
.recipe-card-wrap .grid-item {
  position: relative;
  z-index: 2;
  width: 100%;
  /* Fill the wrapper so margin-top: auto on .recipe-card-bottom has
     the card's full height to push against. Without this, cards in
     the same row align their bottom rows at different heights when
     one name is 1 line and another is 2. */
  height: 100%;
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border: none;
  box-shadow: none;
  border-radius: var(--radius-20);
  transition: transform 0.15s ease;
}

/* Recipe detail overlay — full page. */
.recipe-detail-sheet { padding: 0; }
#recipe-detail-modal { background: var(--bg); }
#recipe-detail-modal > .modal-sheet.recipe-detail-sheet {
  height: calc(100dvh / var(--app-scale));
  max-height: calc(100dvh / var(--app-scale));
  border-radius: 0;
  position: relative;
  overflow: hidden;
}
.recipe-detail-hero {
  position: sticky;
  top: calc(-1 * var(--hero-d, 0px));
  height: calc(var(--hero-rest, 70%) + 22px);
  background: var(--surface-alt);
  overflow: hidden;
  z-index: 2;
}
.recipe-detail-sheet.no-image .recipe-detail-hero {
  max-height: 62px;
  min-height: 62px;
  background: transparent;
}
.recipe-detail-sheet.no-image .recipe-detail-hero-shade { display: none; }
.recipe-detail-sheet.no-image .recipe-detail-icon-btn {
  background: var(--glass-bg);
  border-color: var(--glass-border);
  color: var(--ink);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  box-shadow: var(--glass-card-shadow);
}
.recipe-detail-sheet.no-image .recipe-detail-icon-btn svg { stroke: var(--ink); }
.recipe-detail-hero.refetching::before {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  width: 36px; height: 36px;
  margin: -18px 0 0 -18px;
  border: 3px solid rgba(255, 255, 255, 0.35);
  border-top-color: #fff;
  border-radius: 50%;
  animation: ptrSpin 0.8s linear infinite;
  z-index: 6;
  pointer-events: none;
}
.recipe-detail-hero.refetching::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.35);
  z-index: 5;
  pointer-events: none;
}
.recipe-detail-img { width: 100%; height: 100%; object-fit: cover; display: block; will-change: transform; }
.recipe-detail-hero-shade {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(0,0,0,0.35) 0%, transparent 40%, transparent 70%, rgba(0,0,0,0.25) 100%);
  pointer-events: none;
}

/* Downward chevron at the bottom of the hero, hinting that the page
   scrolls. Fades out via JS as the user begins to scroll. */
.recipe-detail-scroll-hint {
  position: absolute;
  left: 50%;
  bottom: 56px;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  z-index: 3;
  filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.55));
  animation: recipeScrollHintBounce 1.6s ease-in-out infinite;
  pointer-events: none;
  opacity: 0.9;
  will-change: opacity;
}
.recipe-detail-sheet.no-image .recipe-detail-scroll-hint { display: none; }
@keyframes recipeScrollHintBounce {
  0%, 100% { transform: translate(-50%, 0); }
  50%      { transform: translate(-50%, 6px); }
}
.recipe-detail-top-actions {
  position: absolute;
  top: calc(14px + var(--safe-top));
  left: 0;
  right: 0;
  padding: 0 14px;
  z-index: 6;
  display: flex;
  justify-content: flex-end;
  pointer-events: none;
}
.recipe-detail-top-actions > * { pointer-events: auto; }
.recipe-detail-top-actions-right { display: flex; gap: 8px; }
.recipe-detail-top-actions .recipe-detail-icon-btn {
  position: static;
  width: 40px;
  height: 40px;
}
#recipe-detail-fav-btn.active { color: #e0463c; }
#recipe-detail-fav-btn.active svg { fill: currentColor; }
.recipe-detail-body {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  overflow-anchor: none;
  background: var(--bg);
  padding: 0;
}
.recipe-detail-main {
  position: relative;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  margin-top: -42px;
  height: var(--rd-main, auto);
}
.recipe-detail-sheet.no-image .recipe-detail-main { margin-top: -20px; }
.recipe-detail-card {
  position: sticky;
  top: calc(var(--hero-min, 0px) - 20px);
  z-index: 3;
  display: flow-root;
  box-sizing: border-box;
  padding: 26px 20px 0;
  background: var(--bg);
  border-radius: 22px 22px 0 0;
  box-shadow:
    0 -12px 24px -12px rgba(0, 0, 0, 0.42),
    0 -3px 6px -3px rgba(0, 0, 0, 0.22),
    0 -1px 0 rgba(255, 255, 255, 0.55);
  flex: 0 0 auto;
}
.recipe-detail-rest { will-change: transform; }
.recipe-detail-list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  padding: 4px 20px calc(110px + var(--safe-bottom) + var(--kb));
}
.recipe-detail-body:not(.rd-on) .recipe-tabs-reveal { pointer-events: none; }
.recipe-detail-body:not(.rd-open) .recipe-detail-list { pointer-events: none; }

.recipe-detail-title-wrap { margin: 0 0 16px; }
.recipe-detail-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 2.5rem;
  transform-origin: 0 0;
  line-height: 1.2;
  margin: 0;
  color: var(--ink);
  will-change: transform;
}

/* Inline "By <source>" tag that renders as the first span of the
   description paragraph. Bolder than the description so it reads as
   metadata, not prose. */
.recipe-detail-source-inline {
  font-weight: 700;
  color: var(--ink);
  opacity: 0.85;
  margin-right: 4px;
}
.recipe-detail-meta {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-2-5);
  margin-bottom: 16px;
  align-items: start;
}
.recipe-meta-col {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.recipe-meta-col:nth-child(1) { align-items: flex-start; text-align: left; }
.recipe-meta-col:nth-child(2) { align-items: center; text-align: center; }
.recipe-meta-col:nth-child(3) { align-items: flex-end; text-align: right; }
.recipe-meta-label {
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  line-height: 1;
}
.recipe-meta-col-accent .recipe-meta-label { color: var(--primary); }
.recipe-meta-value {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--ink);
  line-height: 1.15;
  overflow-wrap: anywhere;
}
.recipe-servings { display: flex; align-items: center; gap: 6px; }
.recipe-servings button {
  width: 22px; height: 22px;
  border-radius: 50%;
  border: 1px solid var(--input-border);
  background: var(--input-bg);
  color: var(--ink);
  font-size: 0.9rem; font-weight: 700;
  line-height: 1;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  padding: 0;
  flex-shrink: 0;
}
.recipe-servings button:active { transform: scale(0.9); }
.recipe-servings span { font-weight: 700; color: var(--ink); font-size: 1.05rem; }
.recipe-detail-description {
  color: var(--muted);
  font-size: 0.9rem;
  margin: 0 0 var(--space-4);
  line-height: 1.4;
}
.recipe-detail-description.clamped {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.recipe-description-toggle {
  background: none;
  border: none;
  color: var(--primary);
  font-family: var(--font-ui);
  font-size: inherit;
  font-weight: 700;
  padding: 0;
  margin-left: 4px;
  cursor: pointer;
  text-decoration: none;
}
.recipe-description-toggle:active { opacity: 0.7; }

/* Segmented control. */
.recipe-tabs {
  position: relative;
  display: flex;
  gap: 6px;
  background: color-mix(in srgb, var(--surface-2) 82%, transparent);
  backdrop-filter: blur(8px) saturate(1.1);
  -webkit-backdrop-filter: blur(8px) saturate(1.1);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.05);
  border-radius: var(--radius-pill);
  padding: 4px;
  margin-bottom: var(--space-4);
}
[data-theme="dark"] .recipe-tabs {
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.35);
}
.recipe-tab {
  position: relative;
  z-index: 1;
  flex: 1;
  background: none;
  border: none;
  padding: 8px 12px;
  border-radius: var(--radius-pill);
  font-family: var(--font-ui);
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--muted);
  cursor: pointer;
  transition: color 0.2s ease;
}
.recipe-tab.active { color: var(--ink); }
.recipe-tab-indicator {
  position: absolute;
  top: 4px;
  bottom: 4px;
  left: 4px;
  width: calc(50% - 4px);
  background: var(--surface);
  border-radius: var(--radius-pill);
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.10),
    0 4px 12px rgba(0, 0, 0, 0.06);
  transition: transform 0.28s var(--ease-ios);
  z-index: 0;
  pointer-events: none;
}
[data-theme="dark"] .recipe-tab-indicator {
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.5),
    0 4px 12px rgba(0, 0, 0, 0.35);
}
.recipe-tabs[data-active="instructions"] .recipe-tab-indicator { transform: translateX(100%); }

/* Sliding pane wrapper. */
.recipe-tab-content-wrap { overflow: hidden; position: relative; width: 100%; }
.recipe-tab-panes {
  display: flex;
  flex-wrap: nowrap;
  transform: translateX(0);
  transition: transform 0.36s var(--ease-ios);
}
.recipe-tab-panes[data-active="instructions"] { transform: translateX(-100%); }
.recipe-tab-pane {
  flex: 0 0 100%;
  min-width: 100%;
  max-width: 100%;
  box-sizing: border-box;
  align-self: flex-start;
}

.recipe-ingredients { display: flex; flex-direction: column; gap: 8px; }
.recipe-ing-row {
  display: grid;
  grid-template-columns: 12px minmax(0, 1fr) 84px 92px;
  column-gap: 12px;
  align-items: center;
  padding: 12px 14px;
  background: var(--row-card-bg);
  border: 1px solid var(--row-card-border);
  border-radius: var(--radius-sm);
}
.recipe-ing-dot { width: 12px; height: 12px; border-radius: 50%; flex-shrink: 0; }
.recipe-ing-dot.ok { background: var(--primary); }
.recipe-ing-dot.partial { background: var(--warn); }
.recipe-ing-dot.out { background: var(--danger); }
.recipe-ing-dot.low { background: var(--warn); }
.recipe-ing-dot.none { background: var(--muted); opacity: 0.5; }

/* Dim rows for ingredients we have none of. Keeps the list scannable
   while making it obvious which rows need shopping. */
.recipe-ing-row.is-out { opacity: 0.55; }
.recipe-ing-row.is-out .recipe-ing-name { color: var(--muted); }

/* Summary line above the ingredient list: "7 of 10 ingredients in
   stock". Muted, small, sits as the first flex child of .recipe-ingredients. */
.recipe-ing-summary {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--muted);
  padding: 0 2px 6px;
}
.recipe-ing-name { font-weight: 600; font-size: 0.9rem; color: var(--ink); }
.recipe-ing-qty { justify-self: end; text-align: right; color: var(--muted); font-weight: 600; font-size: 0.85rem; }
.recipe-ing-note { justify-self: start; text-align: left; color: var(--muted); font-size: 0.75rem; }
.recipe-ing-name-block { min-width: 0; }
.recipe-ing-name-block .recipe-ing-name { display: block; }
.recipe-ing-note-below {
  display: block;
  font-size: 0.72rem;
  color: var(--muted);
  font-weight: 500;
  margin-top: 2px;
  line-height: 1.3;
  overflow-wrap: anywhere;
}

.recipe-instructions {
  display: flex;
  flex-direction: column;
  gap: var(--space-2-5);
  color: var(--ink);
}
.recipe-step {
  display: flex;
  gap: 12px;
  padding: 12px 14px;
  background: var(--row-card-bg);
  border: 1px solid var(--row-card-border);
  border-radius: var(--radius-sm);
  box-shadow: var(--row-shadow), var(--row-highlight);
}
.recipe-step-num {
  width: 28px; height: 28px;
  border-radius: 50%;
  background: var(--primary);
  color: var(--primary-ink);
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 0.85rem;
  flex-shrink: 0;
}
.recipe-step-text {
  flex: 1;
  font-size: 0.92rem;
  line-height: 1.45;
  padding-top: 4px;
}

/* AI picker. */
.recipe-ai-cat-chips {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  padding-bottom: var(--space-2-5);
  margin-bottom: 6px;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.recipe-ai-cat-chips::-webkit-scrollbar { display: none; }
.recipe-ai-body .search-pill-wrap { margin-bottom: 12px; }
.recipe-ai-cat-chip {
  flex: 1 1 0;
  min-width: 56px;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-pill);
  padding: 6px 4px;
  font-family: var(--font-ui);
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--muted);
  cursor: pointer;
  white-space: nowrap;
  text-align: center;
  box-shadow: var(--glass-card-shadow);
  transition: background 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}
.recipe-ai-cat-chip.active {
  background: var(--primary);
  color: var(--primary-ink);
  border-color: var(--primary);
  box-shadow: 0 4px 14px color-mix(in srgb, var(--primary) 28%, transparent);
}
.recipe-ai-cat-chip:active { transform: scale(0.92); }

/* AI picker tick — same pop the shopping tick uses, so the whole app
   has one "selected" animation language. */
.recipe-ai-row.checked .recipe-ai-check {
  animation: tickPop 0.28s var(--ease-ios);
}

/* Cook button footer. */
.recipe-cook-footer {
  position: absolute;
  left: 0; right: 0;
  bottom: max(calc(25px + var(--safe-bottom)), calc(var(--kb) + 12px));
  padding: 0 var(--space-5);
  z-index: 5;
  opacity: 0;
  pointer-events: none;
  will-change: opacity;
  display: flex;
  gap: 12px;
  align-items: stretch;
}
.recipe-cook-footer::before {
  content: '';
  position: absolute;
  left: 0; right: 0;
  top: -36px;
  bottom: calc(-1 * max(calc(25px + var(--safe-bottom)), calc(var(--kb) + 12px)));
  background: linear-gradient(180deg, transparent 0%, var(--bg) 55%);
  pointer-events: none;
  z-index: -1;
}
.recipe-cook-footer .recipe-cook-btn {
  flex: 1 1 auto;
  pointer-events: none;
}
.recipe-cook-footer.on .recipe-cook-btn { pointer-events: auto; }

/* Cook overlay rows. */
.recipe-cook-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.recipe-cook-dot.ok { background: var(--primary); }
.recipe-cook-dot.low { background: var(--warn); }
.recipe-cook-dot.none { background: var(--muted); opacity: 0.5; }

/* Cook row layout under the shared .consume-row pattern. */
#recipe-cook-list .consume-row-content {
  gap: 12px;
  padding: 8px 12px 8px 8px;
  background: var(--row-card-bg);
  border: 1px solid var(--row-card-border);
  border-radius: var(--radius-16);
  box-shadow: var(--row-highlight);
}
#recipe-cook-list .consume-thumb {
  width: 56px;
  height: 56px;
  border-radius: var(--radius-12);
  overflow: hidden;
  flex-shrink: 0;
  background: var(--row-thumb-bg);
}
#recipe-cook-list .consume-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cook-qty-warning {
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--warn);
  margin-top: 2px;
}
#recipe-cook-list .recipe-cook-qty.blank {
  border-color: var(--warn);
  background: color-mix(in srgb, var(--warn) 8%, var(--input-inset-bg));
}

/* AI picker body. */
.recipe-ai-body { padding: var(--space-4) 18px calc(100px + var(--safe-bottom) + var(--kb)); }

/* AI picker has no hero. Reset the base .modal-body rules that exist to
   overlap a hero image (negative top margin + inner top shadow), so the
   white body sits flush under the plain .modal-header. */
#recipe-ai-modal .modal-body {
  margin-top: 0;
  border-radius: 0;
  box-shadow: none;
  padding-top: 0;
}
#recipe-ai-modal .modal-header {
  border-bottom: none;
}

/* Search + chips stay stuck to the top of the scrollable body. Glass
   treatment matches the app's other sticky search bars. Negative
   horizontal margins bleed the glass edge to edge so list content
   scrolling behind it isn't visible in the side margins. */
.recipe-ai-sticky {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  margin-left: -18px;
  margin-right: -18px;
  padding: var(--space-4) 18px 0;
}

/* Search pill inside the picker gets the same lift as the Consume /
   Shopping hero search pill, so the picker reads as an equally-raised
   surface. Background stays solid so the pill separates from the chips
   directly beneath it. */
#recipe-ai-modal .search-pill-wrap input[type="text"] {
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.10);
}

/* Search-focus dropdown for the AI picker. Sits directly under the
   search pill, overlaying the chips + list. Shown only while the field
   is focused and empty. */
.recipe-ai-suggestions {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  z-index: 8;
  background: var(--popup-bg);
  border: 1px solid var(--popup-edge);
  box-shadow: var(--popup-shadow);
  border-radius: var(--radius-sm);
  padding: 6px;
  max-height: 260px;
  overflow-y: auto;
  animation: popIn 0.15s var(--ease-ios);
}
.recipe-ai-suggestions[hidden] { display: none; }
.recipe-ai-suggestion-heading {
  font-family: var(--font-ui);
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--warn);
  padding: 8px 10px 4px;
}
.recipe-ai-suggestion-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2-5);
  padding: 10px 10px;
  border-radius: var(--radius-xs);
  cursor: pointer;
  transition: background 0.15s ease;
}
.recipe-ai-suggestion-row:active { background: var(--surface-2); }
.recipe-ai-suggestion-row.ticked { background: var(--pill-active-bg); }
.recipe-ai-suggestion-name {
  font-family: var(--font-ui);
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--ink);
  min-width: 0;
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.recipe-ai-suggestion-badge {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--warn);
  background: var(--warn-bg);
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  flex-shrink: 0;
}
.recipe-ai-suggestion-row.ticked .recipe-ai-suggestion-badge {
  color: var(--primary);
  background: var(--pill-active-bg);
}
.recipe-ai-select-all { margin-bottom: var(--space-2-5); }
.recipe-ai-list { display: flex; flex-direction: column; gap: 6px; }
.recipe-ai-row {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-12);
  touch-action: pan-y;
  box-shadow: var(--row-shadow);
}
.recipe-ai-row-content {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  background: var(--row-card-bg);
  border: 1px solid var(--row-card-border);
  border-radius: var(--radius-12);
  box-shadow: var(--row-highlight);
  cursor: pointer;
  transition: transform 0.15s ease, background 0.15s ease, border-color 0.15s ease;
  box-sizing: border-box;
}
.recipe-ai-row.checked .recipe-ai-row-content {
  border-color: var(--primary);
  background: var(--pill-active-bg);
}
.recipe-ai-row.swiped .recipe-ai-row-content { transform: translateX(-84px); }
.recipe-ai-row.is-muted .recipe-ai-row-content {
  background: var(--surface-alt);
  border-color: transparent;
}
.recipe-ai-row.is-muted .recipe-ai-name,
.recipe-ai-row.is-muted .recipe-ai-qty { opacity: 0.55; }
.recipe-ai-row.is-muted .recipe-ai-name {
  text-decoration: line-through;
  text-decoration-thickness: 1.5px;
}

/* Swipe reveal: Mute (grey) / Unmute (blue). Matches the alerts panel's
   Running Low reveal so the app has one visual language for this gesture. */
.recipe-ai-mute-reveal {
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: 96px;
  border: none;
  border-radius: var(--radius-12);
  padding: 0 0 0 24px;
  background: color-mix(in srgb, var(--muted) 70%, transparent);
  color: #fff;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  cursor: pointer;
  z-index: 1;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
}
.recipe-ai-row.swiped .recipe-ai-mute-reveal {
  opacity: 1;
  pointer-events: auto;
}
.recipe-ai-mute-reveal.is-unmute { background: var(--info); }
.recipe-ai-mute-reveal svg,
.recipe-ai-mute-reveal span { pointer-events: none; }
.recipe-ai-mute-reveal svg { width: 20px; height: 20px; }
.recipe-ai-mute-reveal span { font-size: 0.7rem; font-weight: 700; letter-spacing: 0.02em; }
.recipe-ai-check {
  width: 22px; height: 22px;
  border-radius: 6px;
  border: 2px solid var(--muted);
  background: var(--input-bg);
  flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-size: 0.85rem;
}
.recipe-ai-row.checked .recipe-ai-check { background: var(--primary); border-color: var(--primary); }
.recipe-ai-info { flex: 1; min-width: 0; }
.recipe-ai-name { font-weight: 600; font-size: 0.9rem; }
.recipe-ai-qty { font-size: 0.76rem; color: var(--muted); }

/* Web results. */
.recipe-web-results {
  padding: var(--space-4) 18px calc(40px + var(--safe-bottom) + var(--kb));
  overflow-y: auto;
  flex: 1;
  min-height: 0;
}
.recipe-web-row {
  padding: 12px 14px;
  border-radius: var(--radius-sm);
  background: var(--row-card-bg);
  border: 1px solid var(--row-card-border);
  margin-bottom: 8px;
  cursor: pointer;
  box-shadow: var(--row-shadow), var(--row-highlight);
  transition: transform 0.24s var(--ease-ios), background 0.15s ease;
}
.recipe-web-row:active { background: var(--surface-2); transform: translateY(-1px); }
.recipe-web-title { font-weight: 700; font-size: 0.9rem; color: var(--ink); margin-bottom: 2px; }
.recipe-web-domain { color: var(--primary); font-size: 0.76rem; font-weight: 600; }
.recipe-web-snippet { color: var(--muted); font-size: 0.8rem; margin-top: 4px; line-height: 1.35; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

#recipe-search-results.consume-search-results { position: absolute; }

/* Preview modal. */
.recipe-preview-body { padding: var(--space-4) 18px calc(100px + var(--safe-bottom) + var(--kb)); }

/* The preview and web-search modals sit above the standard modal layer.
   They are declared EARLIER in the DOM than #recipe-add-modal and
   #recipe-url-modal, so at the shared --z-modal they lose the paint
   order fight and render *behind* the add sheet when opened on top
   of it. Bumping them clears that, and matches their logical role
   (they are always the top of the overlay stack when open). */
#recipe-preview-modal { z-index: calc(var(--z-modal) + 50); }
#recipe-web-modal     { z-index: calc(var(--z-modal) + 40); }

/* Recipe Preview is a floating card, not a bottom-anchored sheet.
   The overlay centers it with padding on all sides; the sheet keeps
   its rounded corners on every edge and caps its own height. The
   base body rules that overlap a hero are reset. */
#recipe-preview-modal {
  align-items: center;
  padding: var(--space-4);
  padding-top: max(var(--space-4), var(--safe-top));
  padding-bottom: max(var(--space-4), var(--safe-bottom));
}
#recipe-preview-modal > .modal-sheet {
  height: auto;
  max-height: 100%;
  border-radius: var(--radius-xl);
  max-width: 520px;
}
#recipe-preview-modal .modal-header {
  border-bottom: none;
  padding: 18px 20px 4px;
}
#recipe-preview-modal .modal-body {
  margin-top: 0;
  border-radius: 0;
  box-shadow: none;
  min-height: 0;
}
/* When the hero is present, the body curls up over the bottom of the
   photo with rounded top corners and a lift shadow, matching the
   recipe detail card's stacking. `:has()` scopes this to the
   has-image state only; the no-image variant stays flush under the
   plain header. */
#recipe-preview-modal:has(.recipe-preview-hero:not([hidden])) .modal-body {
  margin-top: -22px;
  border-radius: 22px 22px 0 0;
  box-shadow:
    0 -12px 24px -12px rgba(0, 0, 0, 0.42),
    0 -3px 6px -3px rgba(0, 0, 0, 0.22),
    0 -1px 0 rgba(255, 255, 255, 0.55);
  position: relative;
  z-index: 2;
}
[data-theme="dark"] #recipe-preview-modal:has(.recipe-preview-hero:not([hidden])) .modal-body {
  box-shadow:
    0 -12px 24px -12px rgba(0, 0, 0, 0.75),
    0 -3px 6px -3px rgba(0, 0, 0, 0.50),
    0 -1px 0 rgba(255, 255, 255, 0.08);
}
/* Preview hero (only shown when the recipe has an image). Sits at the
   top of the floating card with the title overlaid on a bottom-fade
   shade; the plain modal-header takes its place when there's no image. */
.recipe-preview-hero {
  position: relative;
  width: 100%;
  height: 220px;
  flex-shrink: 0;
  overflow: hidden;
  background: var(--surface-alt);
}
.recipe-preview-hero[hidden] { display: none; }
.recipe-preview-hero-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.recipe-preview-hero-shade {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
    rgba(0, 0, 0, 0.10) 0%,
    rgba(0, 0, 0, 0.00) 42%,
    rgba(0, 0, 0, 0.78) 100%);
  pointer-events: none;
}
.recipe-preview-hero-title {
  position: absolute;
  left: 20px;
  right: 20px;
  /* 38px clears the 22px body curl + a little breathing room. */
  bottom: 38px;
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.4rem;
  line-height: 1.2;
  color: #fff;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.55);
  overflow-wrap: anywhere;
}
/* Header is display: flex from the base rule, so [hidden] alone won't
   override it -- an explicit rule is needed to actually hide it. */
#recipe-preview-header[hidden] { display: none; }
.img-broken {
  background:
    repeating-linear-gradient(45deg,
      var(--surface-alt),
      var(--surface-alt) 8px,
      color-mix(in srgb, var(--surface-alt) 82%, var(--ink)) 8px,
      color-mix(in srgb, var(--surface-alt) 82%, var(--ink)) 16px);
  color: transparent;
  font-size: 0;
  text-indent: -9999px;
}
.recipe-preview-name { font-family: var(--font-display); font-weight: 700; font-size: 1.2rem; margin: 0 0 6px; }

/* Section heading used inside the Recipe Preview body (Ingredients /
   Instructions). The class existed in JS but had no CSS -- render was
   falling back to bare <h4> defaults. Styled here to match the Product
   Detail equivalent so both read as the same level. */
.detail-section-heading {
  font-family: var(--font-ui);
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--ink);
  margin: 20px 0 10px;
  letter-spacing: -0.01em;
}
.recipe-preview-meta { color: var(--muted); font-size: 0.85rem; margin-bottom: 12px; }
.recipe-preview-ing-list { margin: 0 0 16px; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 8px; }
.recipe-preview-ing-list li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  font-size: 0.88rem;
  background: var(--row-card-bg);
  border: 1px solid var(--row-card-border);
  border-radius: var(--radius-sm);
  box-shadow: var(--row-shadow), var(--row-highlight);
}
/* Preview footer: three round pills, centred. X (red-tinted), Refetch
   (glass, AI only), Save (primary green with a checkmark). */
.recipe-preview-footer {
  display: flex;
  gap: var(--space-3-5);
  align-items: center;
  justify-content: center;
}
.recipe-preview-pill {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  border: none;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  cursor: pointer;
  flex-shrink: 0;
  transition: transform 0.24s var(--ease-ios), box-shadow 0.2s var(--ease-ios), opacity 0.15s ease;
}
.recipe-preview-pill svg { display: block; }
.recipe-preview-pill:active { transform: scale(0.94); }
.recipe-preview-pill:disabled { opacity: 0.6; cursor: default; }
.recipe-preview-pill[hidden] { display: none; }
.recipe-preview-pill-x {
  background: color-mix(in srgb, var(--danger) 14%, var(--glass-bg));
  color: var(--danger);
  border: 1px solid color-mix(in srgb, var(--danger) 32%, transparent);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  box-shadow: var(--glass-card-shadow);
}
.recipe-preview-pill-refetch {
  background: var(--glass-bg);
  color: var(--ink);
  border: 1px solid var(--glass-border);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  box-shadow: var(--glass-card-shadow);
}
.recipe-preview-pill-save {
  background: var(--primary);
  color: var(--primary-ink);
  box-shadow:
    0 6px 16px color-mix(in srgb, var(--primary) 38%, transparent),
    inset 0 1px 0 rgba(255, 255, 255, 0.4);
}
.recipe-preview-pill.is-loading svg { visibility: hidden; }
.recipe-preview-pill.is-loading::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 20px;
  height: 20px;
  margin: -10px 0 0 -10px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: ptrSpin 0.7s linear infinite;
}

/* patched: pill buttons for discard + save (icon-only) */
.recipe-preview-pill.recipe-preview-pill-x,
.recipe-preview-pill.recipe-preview-pill-save {
  width: auto;
  height: 56px;
  min-width: 96px;
  padding: 0 24px;
  border-radius: var(--radius-pill);
}
/* Preview footer has 3 buttons in a row (2 pills + 1 circle) - tighten
   so they still fit alongside the 56px regenerate circle on narrow phones. */
.recipe-preview-footer .recipe-preview-pill.recipe-preview-pill-x,
.recipe-preview-footer .recipe-preview-pill.recipe-preview-pill-save {
  min-width: 84px;
  padding: 0 20px;
}

/* Add action sheet: floating card centered in the viewport, blurred
   backdrop behind (inherited from .modal-overlay). */
#recipe-add-modal {
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  padding-top: max(var(--space-4), var(--safe-top));
  padding-bottom: max(var(--space-4), var(--safe-bottom));
}
#recipe-add-modal > .modal-sheet {
  height: auto;
  max-height: 100%;
  border-radius: var(--radius-xl);
  max-width: 400px;
  animation: popIn 0.24s var(--ease-ios);
}
#recipe-add-modal.closing > .modal-sheet {
  animation: popOut 0.2s ease-in forwards;
}
#recipe-add-modal .modal-header {
  border-bottom: none;
  padding: 20px 22px 4px;
}
.recipe-add-sheet { height: auto; }
.recipe-add-body {
  padding: 6px 18px 20px;
  margin-top: 0;
  box-shadow: none;
}

/* Each option reads as a raised card: outer drop shadow + inset top
   highlight, chevron on the right, spring press. */
.recipe-add-option {
  display: flex;
  align-items: center;
  gap: var(--space-3-5);
  width: 100%;
  padding: 16px;
  border-radius: var(--radius-md);
  border: 1px solid var(--row-card-border);
  background: var(--row-card-bg);
  margin-bottom: 12px;
  text-align: left;
  font-family: var(--font-ui);
  cursor: pointer;
  box-shadow: var(--row-shadow), var(--row-highlight);
  transition:
    transform 0.24s var(--ease-ios),
    box-shadow 0.2s var(--ease-ios),
    background 0.15s ease;
}
.recipe-add-option:last-child { margin-bottom: 0; }
.recipe-add-option:active {
  transform: scale(0.97);
  background: color-mix(in srgb, var(--row-card-bg) 92%, var(--ink) 8%);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.10), var(--row-highlight);
}
.recipe-add-option::after {
  content: '';
  width: 9px;
  height: 9px;
  margin-left: auto;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  transform: rotate(-45deg);
  flex-shrink: 0;
  opacity: 0.55;
  transition: transform 0.24s var(--ease-ios), opacity 0.15s ease;
}
.recipe-add-option:active::after { transform: rotate(-45deg) translate(2px, -2px); opacity: 0.9; }

/* Icon sits in a rounded tile, matching the .input-icon / tile-visual
   language elsewhere in the app. */
.recipe-add-icon {
  font-size: 1.5rem;
  flex-shrink: 0;
  width: 46px;
  height: 46px;
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-2);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6);
}
[data-theme="dark"] .recipe-add-icon {
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
.recipe-add-label { font-weight: 700; font-size: 0.95rem; color: var(--ink); }
.recipe-add-hint { font-size: 0.78rem; color: var(--muted); margin-top: 2px; }

/* Manual entry sheet — floating card, elevated inputs with labels,
   sticky Name field, auto-resizing textareas, pill footer. Mirrors the
   Recipe Preview layout and reuses .input-elevated from the Add Product /
   Edit Stock forms. */
#recipe-manual-modal {
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  padding-top: max(var(--space-4), var(--safe-top));
  /* No --kb here: the overlay keeps its full-screen box. The keyboard
     just layers on top. This keeps the sheet's geometry stable -- it
     doesn't get squeezed or pushed up when a field is focused. The
     footer pill row and body padding-bottom handle the keyboard
     clearance instead. */
  padding-bottom: var(--space-4);
}
#recipe-manual-modal > .modal-sheet.recipe-manual-sheet {
  /* Fixed-height floating card. The overlay's padding makes it read as
     a card rather than a full-screen sheet, but the height itself is
     100% of the overlay's content box -- a value that does NOT change
     when the on-screen keyboard opens, so the sheet's geometry is
     stable across every focus change. Scrolling happens inside
     .recipe-manual-body. */
  height: 100%;
  max-height: 100%;
  border-radius: var(--radius-xl);
  max-width: 440px;
  padding: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
#recipe-manual-form {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}
#recipe-manual-modal .modal-header {
  border-bottom: none;
  padding: 22px 24px 4px;
  flex-shrink: 0;
}
.recipe-manual-body {
  /* The body scrolls internally. The --kb in padding-bottom gives iOS
     enough scroll room to bring the LAST field into view above the
     keyboard; without it the final textarea can't be fully raised
     above the keyboard. */
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  padding: 0 20px calc(120px + var(--safe-bottom) + var(--kb));
  margin-top: 0;
  border-radius: 0;
  box-shadow: none;
}

/* Each label + field pair. */
.recipe-manual-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 8px 0;
}
.recipe-manual-row .field-label {
  margin: 0 0 0 4px;
  font-size: 0.72rem;
  letter-spacing: 0.02em;
}

/* Sticky Name field. Extends horizontally into the body's side gutters so
   nothing scrolling behind it shows through. The negative top margin
   cancels the row's own padding-top so it sits flush at the top edge. */
.recipe-manual-sticky-name {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--surface);
  margin: 0 -20px;
  padding: 14px 20px 10px;
}

/* Force 11px inside every elevated field — matches Edit Stock. */
#recipe-manual-modal .input-elevated input,
#recipe-manual-modal .input-elevated textarea {
  font-size: 11px;
}

/* Two-column row for Cook Time / Servings. */
.recipe-manual-grid-2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2-5);
}

/* Textarea variant: icon top-aligned, textarea grows with content. The
   JS auto-resize sets an explicit height so overflow stays hidden --
   the classic "grow-to-fit" textarea pattern. */
.input-elevated-textarea {
  align-items: flex-start;
  padding: 10px 8px;
}
.input-elevated-textarea .input-icon {
  margin-top: 3px;
}
.input-elevated-textarea textarea {
  flex: 1 1 auto;
  min-width: 0;
  border: none;
  background: transparent;
  box-shadow: none;
  padding: 0;
  margin: 0;
  min-height: 140px;
  line-height: 1.45;
  font-family: var(--font-ui);
  color: var(--ink);
  outline: none;
  resize: none;
  overflow: hidden;
  box-sizing: border-box;
}
.input-elevated-textarea textarea::placeholder {
  color: color-mix(in srgb, var(--muted) 75%, transparent);
}

/* Live ingredient count under the ingredients textarea. */
.recipe-manual-optional {
  font-weight: 500;
  color: var(--muted);
  font-size: 0.66rem;
  letter-spacing: 0.01em;
  opacity: 0.75;
}

.recipe-manual-count {
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--muted);
  margin: 2px 0 0 4px;
  min-height: 1em;
}

/* Pill footer. Overrides .bought-footer's bottom formula so the row
   is PINNED to the sheet's bottom edge and does not follow the
   keyboard. The buttons hide behind the on-screen keyboard while
   typing (matching native iOS behaviour) and reappear when the
   keyboard closes. */
.recipe-manual-footer {
  bottom: max(calc(25px + var(--safe-bottom)), 12px);
  display: flex;
  gap: var(--space-3-5);
  align-items: center;
  justify-content: center;
}
.recipe-preview-pill-save:disabled {
  opacity: 0.4;
  cursor: default;
}

.recipe-spinner {
  display: inline-block;
  width: 16px; height: 16px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: ptrSpin 0.7s linear infinite;
  vertical-align: middle;
  margin-right: 6px;
}

/* Prompt sheet + reading overlay + URL error. */
.recipe-prompt-overlay {
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
}
.recipe-prompt-sheet {
  height: auto;
  max-height: 100%;
  border-radius: var(--radius-xl);
  width: 100%;
  max-width: 340px;
  min-height: 520px;
  padding: 52px 28px 32px;
  display: flex;
  flex-direction: column;
  text-align: center;
  background: var(--popup-bg);
  border: 1px solid var(--popup-edge);
  box-shadow:
    0 28px 64px rgba(0, 0, 0, 0.30),
    0 10px 24px rgba(0, 0, 0, 0.18),
    0 2px 6px rgba(0, 0, 0, 0.10),
    inset 0 1px 0 rgba(255, 255, 255, 0.9);
  animation: popIn 0.24s var(--ease-ios);
}
[data-theme="dark"] .recipe-prompt-sheet {
  box-shadow:
    0 28px 64px rgba(0, 0, 0, 0.75),
    0 10px 24px rgba(0, 0, 0, 0.55),
    0 2px 6px rgba(0, 0, 0, 0.35),
    inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
.recipe-prompt-overlay.closing > .recipe-prompt-sheet { animation: popOut 0.2s ease-in forwards; }
.recipe-prompt-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.35rem;
  color: var(--ink);
  margin: 0 0 8px;
  letter-spacing: -0.01em;
  line-height: 1.2;
}
.recipe-prompt-hint {
  font-size: 0.9rem;
  color: var(--muted);
  margin: 0 0 24px;
  line-height: 1.45;
}
.recipe-prompt-input-wrap { margin-bottom: 12px; }
.recipe-prompt-input-wrap input[type="text"] {
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.10);
}
.recipe-prompt-error {
  color: var(--danger);
  font-size: 0.82rem;
  font-weight: 600;
  margin: 0 0 12px;
  line-height: 1.35;
  text-align: center;
}
.recipe-prompt-actions {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: auto;
  padding-top: 24px;
}

/* URL prompt: top icon, matching the success popup's icon treatment. */
.recipe-url-icon {
  width: 84px;
  height: 84px;
  border-radius: 50%;
  margin: 0 auto 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-2);
  color: var(--primary);
  flex-shrink: 0;
}
[data-theme="dark"] .recipe-url-icon {
  background: color-mix(in srgb, var(--primary) 18%, var(--surface-2));
}

/* Paste button inside the URL field. Input padding-right reserves the
   space so typed text never sits under it. */
#recipe-url-input {
  padding-right: 72px;
}
.recipe-url-paste {
  position: absolute;
  right: 8px;
  top: 50%;
  transform: translateY(-50%);
  padding: 6px 12px;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  color: var(--primary);
  font-family: var(--font-ui);
  font-size: 0.75rem;
  font-weight: 700;
  cursor: pointer;
  transition: background 0.15s ease, transform 0.24s var(--ease-ios);
}
.recipe-url-paste:active {
  transform: translateY(-50%) scale(0.94);
  background: var(--pill-active-bg);
}

/* Live validation tint: field reads red the moment the text stops looking
   like a URL. Applies while the field has content; the empty state stays
   neutral so the first keystroke doesn't flash red. */
#recipe-url-input.invalid {
  border-color: var(--danger);
  background: color-mix(in srgb, var(--danger) 6%, var(--input-bg));
}
#recipe-url-input.invalid:focus {
  border-color: var(--danger);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--danger) 22%, transparent);
}

/* Domain preview under the field: shows the site a valid URL points at. */
.recipe-url-domain {
  min-height: 1.1em;
  margin: -4px 0 12px;
  font-family: var(--font-ui);
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--primary);
  text-align: center;
  overflow-wrap: anywhere;
}
.recipe-url-domain[hidden] { display: none; }

/* Live status line under the URL field: small pulsing dot + message. */



/* Primary pill -- matches .success-modal-primary. */
.recipe-prompt-primary {
  display: block;
  width: 100%;
  padding: 15px 20px;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--primary);
  color: var(--primary-ink);
  font-family: var(--font-ui);
  font-size: 0.95rem;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 6px 16px color-mix(in srgb, var(--primary) 34%, transparent);
  transition: transform 0.24s var(--ease-ios), box-shadow 0.2s var(--ease-ios);
  position: relative;
}
.recipe-prompt-primary:active {
  transform: scale(0.97);
  box-shadow: 0 3px 10px color-mix(in srgb, var(--primary) 34%, transparent);
}
.recipe-prompt-primary:disabled { opacity: 0.7; cursor: default; }

/* Secondary text button -- matches .success-modal-secondary. */
.recipe-prompt-secondary {
  display: block;
  width: 100%;
  padding: 14px 20px;
  border: none;
  background: none;
  font-family: var(--font-ui);
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--muted);
  cursor: pointer;
  border-radius: var(--radius-pill);
  transition: color 0.15s ease, background 0.15s ease;
}
.recipe-prompt-secondary:active { background: var(--surface-2); color: var(--ink); }

.recipe-reading-sheet {
  height: auto;
  max-height: 40vh;
  border-radius: var(--radius-lg);
  width: 100%;
  max-width: 320px;
  padding: 32px 22px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3-5);
  text-align: center;
}
.recipe-reading-spinner {
  width: 26px;
  height: 26px;
  border-width: 2.5px;
  margin: 0;
}
.recipe-reading-text {
  font-family: var(--font-ui);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--ink);
}
.recipe-reading-dot {
  display: inline-block;
  animation: recipeReadingDot 1.2s infinite;
  opacity: 0;
}
.recipe-reading-dot:nth-of-type(2) { animation-delay: 0.15s; }
.recipe-reading-dot:nth-of-type(3) { animation-delay: 0.3s; }
@keyframes recipeReadingDot {
  0%, 60%, 100% { opacity: 0; transform: translateY(0); }
  30%           { opacity: 1; transform: translateY(-2px); }
}

/* Recipe source label -- sits on the left of .recipe-card-bottom. */
.recipe-source {
  font-family: var(--font-ui);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--muted);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Bottom row of a recipe grid card: source label left, cook-time / ing
   count right. The name above wraps to two lines independently. */
.recipe-grid-item .recipe-card-bottom {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin: auto 4px 0;
  padding-top: 8px;
}
.recipe-grid-item .recipe-card-meta {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--amount-ink);
  white-space: nowrap;
  flex: 0 0 auto;
  text-align: right;
}

/* --- Recipe edit page — floating card with a hero image --- */
#recipe-edit-modal {
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  padding-top: max(var(--space-4), var(--safe-top));
  padding-bottom: var(--space-4);
}
#recipe-edit-modal > .modal-sheet.recipe-edit-sheet {
  /* Explicit 100% width: without it, the sheet's width is derived from
     its content's intrinsic size, and grids with 1fr columns report a
     tiny preferred width, shrinking the whole card. */
  width: 100%;
  height: 100%;
  max-height: 100%;
  border-radius: var(--radius-xl);
  max-width: 440px;
  padding: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Hero — the recipe photo, refetch button top-right. No title. */
.recipe-edit-hero {
  position: relative;
  width: 100%;
  height: 170px;
  flex-shrink: 0;
  overflow: hidden;
  background: var(--surface-alt);
}
.recipe-edit-hero-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.recipe-edit-hero-shade {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
    rgba(0, 0, 0, 0.10) 0%,
    rgba(0, 0, 0, 0.00) 42%,
    rgba(0, 0, 0, 0.55) 100%);
  pointer-events: none;
}

/* ID-scoped so .icon-btn.on-photo cannot override position/size/colour. */
#recipe-edit-modal .recipe-edit-hero-refetch,
#recipe-edit-modal .recipe-edit-hero-delete {
  position: absolute;
  top: 14px;
  width: 40px;
  height: 40px;
  z-index: 4;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  cursor: pointer;
  transition: transform 0.24s var(--ease-ios);
}

/* Refetch: glass pill, kept as-is. */
#recipe-edit-modal .recipe-edit-hero-refetch {
  right: 62px;
  color: var(--ink);
  background: var(--glass-photo-bg);
  border: 1px solid var(--glass-photo-border);
  backdrop-filter: blur(var(--glass-photo-blur));
  -webkit-backdrop-filter: blur(var(--glass-photo-blur));
  box-shadow: var(--glass-photo-shadow);
}

/* Delete: glass pill with red tint. */
#recipe-edit-modal .recipe-edit-hero-delete {
  right: 14px;
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 30%, var(--glass-photo-bg));
  border: 1px solid color-mix(in srgb, var(--danger) 45%, var(--glass-photo-border));
  backdrop-filter: blur(var(--glass-photo-blur));
  -webkit-backdrop-filter: blur(var(--glass-photo-blur));
  box-shadow: var(--glass-photo-shadow);
}

#recipe-edit-modal .recipe-edit-hero-refetch:active,
#recipe-edit-modal .recipe-edit-hero-delete:active { transform: scale(0.92); }

#recipe-edit-modal .recipe-edit-hero-refetch.is-loading svg { visibility: hidden; }
#recipe-edit-modal .recipe-edit-hero-refetch.is-loading::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 18px;
  height: 18px;
  margin: -9px 0 0 -9px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: ptrSpin 0.7s linear infinite;
}

/* Body curls up over the hero, same shape as Recipe Preview. */
.recipe-edit-body {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  overflow-x: hidden;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  padding: 22px 20px calc(160px + var(--safe-bottom) + var(--kb));
  margin-top: -22px;
  border-radius: 22px 22px 0 0;
  background: var(--surface);
  box-shadow:
    0 -12px 24px -12px rgba(0, 0, 0, 0.42),
    0 -3px 6px -3px rgba(0, 0, 0, 0.22),
    0 -1px 0 rgba(255, 255, 255, 0.55);
  position: relative;
  z-index: 2;
}
[data-theme="dark"] .recipe-edit-body {
  box-shadow:
    0 -12px 24px -12px rgba(0, 0, 0, 0.75),
    0 -3px 6px -3px rgba(0, 0, 0, 0.50),
    0 -1px 0 rgba(255, 255, 255, 0.08);
}

/* Every form text in the edit page is 11px. */
#recipe-edit-modal input,
#recipe-edit-modal textarea,
#recipe-edit-modal select,
#recipe-edit-modal .field-label {
  font-size: 11px;
}

/* Description box: 2 rows only. */
#recipe-edit-description {
  min-height: 72px;
  padding: 20px 10px;
  line-height: 1.45;
}

.recipe-edit-section-heading {
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--ink);
  margin: 24px 0 10px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}
.recipe-edit-add-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  margin-top: 16px;
  margin-left: var(--space-2-5);
  background: color-mix(in srgb, var(--primary) 12%, transparent);
  border: none;
  border-radius: var(--radius-xs);
  color: var(--primary);
  cursor: pointer;
  transition: background 0.15s ease, transform 0.24s var(--ease-ios);
}
.recipe-edit-add-btn:active {
  transform: scale(0.92);
  background: color-mix(in srgb, var(--primary) 20%, transparent);
}
.recipe-edit-add-btn svg { width: 16px; height: 16px; }

/* --- Undo bar (deleted-row recovery) ---------------------------------------
   Floating pill above the footer. Visible whenever there are pending deletes
   in the undo stack; hidden otherwise. */
.recipe-edit-undo-bar {
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: calc(96px + var(--safe-bottom));
  z-index: 30;
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  padding: 10px 10px 10px 16px;
  border-radius: var(--radius-pill);
  background: var(--popup-bg);
  border: 1px solid var(--popup-edge);
  box-shadow: var(--popup-shadow);
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 600;
  color: var(--ink);
  animation: popIn 0.22s var(--ease-ios);
}
.recipe-edit-undo-bar[hidden] { display: none; }
.recipe-edit-undo-text {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.recipe-edit-undo-btn {
  flex: 0 0 auto;
  padding: 6px 14px;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--primary);
  color: var(--primary-ink);
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  transition: transform 0.24s var(--ease-ios);
}
.recipe-edit-undo-btn:active { transform: scale(0.95); }
.recipe-edit-undo-dismiss {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--muted);
  font-family: var(--font-ui);
  font-size: 16px;
  font-weight: 400;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.recipe-edit-undo-dismiss:active {
  background: var(--surface-alt);
  color: var(--ink);
}

/* --- Ingredient drag-reorder visuals --------------------------------------
   Same language as the step-row drag (see .recipe-edit-step-row.dragging). */
.recipe-edit-ing-row.dragging {
  z-index: 1000;
  pointer-events: none;
  box-shadow:
    0 18px 40px rgba(0, 0, 0, 0.30),
    0 4px 12px rgba(0, 0, 0, 0.15);
}
.recipe-edit-ing-row.dragging .recipe-edit-ing-content {
  border-radius: var(--radius-sm);
  box-shadow: 0 0 0 2px var(--primary);
}
.recipe-edit-ing-placeholder {
  visibility: hidden;
  border-radius: var(--radius-sm);
  background: var(--row-card-bg);
  border: 1px dashed var(--primary);
}
body.recipe-ing-drag-active {
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}
body.recipe-ing-drag-active .recipe-edit-ing-row:not(.dragging) {
  pointer-events: none;
}

/* --- Ingredient rows: swipe RIGHT to reveal the link button on the LEFT;
   swipe LEFT to reveal the delete button on the RIGHT. Both reveals are
   absolutely positioned; the content sits on top (z 2) and slides aside. --- */
.recipe-edit-ingredients {
  display: flex;
  flex-direction: column;
  gap: var(--space-2-5);
  min-width: 0;
}
.recipe-edit-ing-row {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: var(--row-card-bg);
  border: 1px solid var(--row-card-border);
  box-shadow: var(--row-shadow), var(--row-highlight);
  touch-action: pan-y;
  min-height: 64px;
}
.recipe-edit-ing-content {
  position: relative;
  z-index: 2;
  min-width: 0;
  padding: var(--space-2-5);
  background: var(--row-card-bg);
  transition: transform 0.15s ease;
  box-sizing: border-box;
  border-radius: inherit;
}
.recipe-edit-ing-row.swiped-left  .recipe-edit-ing-content { transform: translateX(-72px); }
.recipe-edit-ing-row.swiped-right .recipe-edit-ing-content { transform: translateX(72px); }

.recipe-edit-ing-link-reveal,
.recipe-edit-ing-delete-reveal {
  position: absolute;
  top: 0; bottom: 0;
  width: 72px;
  z-index: 1;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
}
.recipe-edit-ing-link-reveal   { left: 0; }
.recipe-edit-ing-delete-reveal { right: 0; }
.recipe-edit-ing-row.swiped-right .recipe-edit-ing-link-reveal   { opacity: 1; pointer-events: auto; }
.recipe-edit-ing-row.swiped-left  .recipe-edit-ing-delete-reveal { opacity: 1; pointer-events: auto; }

.recipe-edit-ing-link,
.recipe-edit-ing-delete {
  width: 100%;
  height: 100%;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  cursor: pointer;
}
.recipe-edit-ing-link {
  background: var(--surface-alt);
  color: var(--muted);
  border-radius: 0 14px 14px 0;
}
.recipe-edit-ing-link.linked {
  background: color-mix(in srgb, var(--primary) 15%, var(--surface-alt));
  color: var(--primary);
}
.recipe-edit-ing-link:active { filter: brightness(0.95); }
.recipe-edit-ing-link svg { width: 20px; height: 20px; }

.recipe-edit-ing-delete {
  background: var(--danger);
  color: #fff;
  border-radius: 14px 0 0 14px;
}
.recipe-edit-ing-delete svg { width: 20px; height: 20px; pointer-events: none; }
.recipe-edit-ing-delete:active { filter: brightness(0.9); }

.recipe-edit-ing-main {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 72px;
  gap: 8px;
  align-items: center;
}
.recipe-edit-ing-name,
.recipe-edit-ing-qty {
  width: 100%;
  height: 34px;
  padding: 0 10px;
  border-radius: var(--radius-xs);
  background: var(--input-inset-bg);
  border: 1px solid var(--input-inset-border);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: 11px;
  box-sizing: border-box;
  min-width: 0;
}
.recipe-edit-ing-name:focus,
.recipe-edit-ing-qty:focus { outline: 2px solid var(--primary); outline-offset: -1px; }
.recipe-edit-ing-qty { text-align: center; padding: 0 6px; }
.recipe-edit-ing-match {
  margin-top: 6px;
  margin-left: var(--space-2-5);
  font-size: 11px;
  line-height: 1.3;
  min-height: 14px;
}

/* --- Instruction steps: num + textarea on the left; swipe left to
   reveal the delete button on the right. Long-press to drag. Grid
   (not flex) so the textarea column reliably takes remaining width. --- */
.recipe-edit-instructions {
  display: flex;
  flex-direction: column;
  gap: var(--space-2-5);
  min-width: 0;
}
.recipe-edit-step-row {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: var(--row-card-bg);
  border: 1px solid var(--row-card-border);
  box-shadow: var(--row-shadow), var(--row-highlight);
  touch-action: pan-y;
}
.recipe-edit-step-content {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: 32px minmax(0, 1fr);
  gap: 8px;
  align-items: center;
  padding: var(--space-2-5);
  background: var(--row-card-bg);
  border-radius: inherit;
  transition: transform 0.15s ease;
  box-sizing: border-box;
}
.recipe-edit-step-row.swiped .recipe-edit-step-content { transform: translateX(-72px); }
.recipe-edit-step-num {
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--primary);
  color: var(--primary-ink);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: 11px;
  margin-top: 0;
  flex-shrink: 0;
}
.recipe-edit-step-text {
  width: 100%;
  min-height: 60px;
  padding: 8px 10px;
  border-radius: var(--radius-xs);
  background: var(--input-inset-bg);
  border: 1px solid var(--input-inset-border);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: 11px;
  line-height: 1.4;
  resize: none;
  box-sizing: border-box;
  overflow: hidden;
  min-width: 0;
}
.recipe-edit-step-text:focus { outline: 2px solid var(--primary); outline-offset: -1px; }
.recipe-edit-step-reveal {
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: 72px;
  z-index: 1;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.recipe-edit-step-row.swiped .recipe-edit-step-reveal { opacity: 1; }
/* patched: step delete matches ingredient delete */
.recipe-edit-step-delete-reveal {
  width: 100%;
  height: 100%;
  border: none;
  background: var(--danger);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  cursor: pointer;
  border-radius: 14px 0 0 14px;
}
.recipe-edit-step-delete-reveal svg { width: 20px; height: 20px; pointer-events: none; }
.recipe-edit-step-delete-reveal:active { filter: brightness(0.9); }

/* Drag state: lifted row floats above the list with a stronger shadow. */
.recipe-edit-step-row.dragging {
  z-index: 1000;
  pointer-events: none;
  box-shadow:
    0 18px 40px rgba(0, 0, 0, 0.30),
    0 4px 12px rgba(0, 0, 0, 0.15);
}
.recipe-edit-step-row.dragging .recipe-edit-step-content {
  border-color: var(--primary);
  box-shadow: 0 0 0 2px var(--primary);
}
.recipe-edit-step-placeholder {
  visibility: hidden;
  border-radius: var(--radius-sm);
  background: var(--row-card-bg);
  border: 1px dashed var(--primary);
}
body.recipe-step-drag-active {
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}
body.recipe-step-drag-active .recipe-edit-step-row:not(.dragging) {
  pointer-events: none;
}

/* Delete recipe — danger-styled pill at the bottom of the form. */
.recipe-edit-delete-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  padding: var(--space-3-5);
  margin: 28px 0 0;
  background: color-mix(in srgb, var(--danger) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--danger) 35%, transparent);
  border-radius: var(--radius-pill);
  color: var(--danger);
  font-family: var(--font-ui);
  font-size: 0.9rem;
  font-weight: 700;
  cursor: pointer;
  transition: background 0.15s ease, transform 0.24s var(--ease-ios);
}
.recipe-edit-delete-btn:active {
  transform: scale(0.97);
  background: color-mix(in srgb, var(--danger) 15%, transparent);
}

/* Footer pill row pinned to the sheet's bottom edge. When the keyboard
   is open, the row drops by --kb so it sits behind the keyboard (native
   iOS behaviour) instead of crawling up with it. */
.recipe-edit-footer {
  display: flex;
  gap: var(--space-3-5);
  align-items: center;
  justify-content: center;
  bottom: calc(max(calc(25px + var(--safe-bottom)), 12px) - var(--kb));
}

.recipe-edit-match-label { display: inline-block; line-height: 1.35; }
.recipe-edit-match-label.linked { color: var(--primary); font-weight: 600; }
.recipe-edit-match-label.unlinked { color: var(--muted); font-style: italic; }
.recipe-edit-match-input-wrap { position: relative; }
.recipe-edit-match-input {
  width: 100%; height: 36px; padding: 0 10px; border-radius: var(--radius-xs);
  background: var(--input-inset-bg);
  border: 1px solid var(--primary);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: 0.85rem;
  box-sizing: border-box;
}
.recipe-edit-match-input:focus { outline: none; }
.recipe-edit-match-dropdown {
  position: absolute;
  top: calc(100% + 4px);
  left: 0; right: 0;
  max-height: 220px;
  overflow-y: auto;
  background: var(--popup-bg);
  border: 1px solid var(--popup-edge);
  box-shadow: var(--popup-shadow);
  border-radius: var(--radius-12);
  padding: 4px;
  z-index: 50;
  animation: popIn 0.14s var(--ease-ios);
}
.recipe-edit-match-option {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 10px 12px;
  border-radius: 8px;
  font-family: var(--font-ui);
  font-size: 0.88rem;
  color: var(--ink);
  cursor: pointer;
}
.recipe-edit-match-option:active { background: var(--surface-2); }
.recipe-edit-match-option.is-current {
  background: var(--pill-active-bg);
  color: var(--primary);
  font-weight: 700;
}
.recipe-edit-match-check { color: var(--primary); font-weight: 700; }
.recipe-edit-match-option.unlink {
  color: var(--danger);
  border-top: 1px solid var(--border);
  margin-top: 2px;
  padding-top: 12px;
  border-radius: 0 0 8px 8px;
}
.recipe-edit-match-empty {
  padding: 12px;
  text-align: center;
  color: var(--muted);
  font-size: 0.82rem;
}

.recipe-edit-step-actions {
  display: flex; flex-direction: column; gap: 4px; align-items: center;
}
.recipe-edit-step-action {
  width: 28px; height: 28px;
  border-radius: 8px;
  border: 1px solid var(--input-inset-border);
  background: var(--input-inset-bg);
  color: var(--muted);
  display: flex; align-items: center; justify-content: center;
  padding: 0; cursor: pointer;
  transition: transform 0.24s var(--ease-ios), background 0.15s ease;
}
.recipe-edit-step-action:active:not(:disabled) { transform: scale(0.9); }
.recipe-edit-step-action:disabled { opacity: 0.3; cursor: default; }
.recipe-edit-step-action.danger {
  background: var(--danger-bg);
  border-color: transparent;
  color: var(--danger);
}

/* --- Brandmark intro (one-shot on app load) --- */
@keyframes brandIconTilt {
  from { transform: rotate(-10deg) scale(0.7); opacity: 0; }
  60%  { transform: rotate(2deg)   scale(1.06); opacity: 1; }
  to   { transform: rotate(0)      scale(1);    opacity: 1; }
}
@keyframes brandTextFade {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
body.brand-intro .brand-mark { animation: brandIconTilt 0.7s var(--ease-ios) both; }
body.brand-intro .brand-name { animation: brandTextFade 0.5s ease-out 0.08s both; }
body.brand-intro .home-header { animation: brandTextFade 0.5s ease-out 0.16s both; }
body.brand-intro .home-header-sub { animation: brandTextFade 0.5s ease-out 0.22s both; }


/* ============================================================================
   13. PRODUCT DETAIL OVERLAY + SHEET STACK
   ============================================================================ */
#product-detail-modal { background: var(--bg); }

#product-detail-modal > .modal-sheet.product-detail-sheet {
  height: 100%;
  max-height: 100%;
  border-radius: 0;
  max-width: 100%;
  padding: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  position: relative;
  background: var(--primary-2);
}

/* Top actions: back top-left, action cluster right (centred against the photo). */
.product-detail-top-actions {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 34vh;
  min-height: 200px;
  padding: 0 14px;
  z-index: 6;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  pointer-events: none;
}
.product-detail-top-actions > * { pointer-events: auto; }
.product-detail-icon-btn { width: 40px; height: 40px; }
#product-detail-back {
  align-self: flex-start;
  margin-top: calc(14px + var(--safe-top));
}
.product-detail-top-actions-right {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-right: 6px;
}

/* Vertical action cluster — refetch / upload / delete. */
.pd-action-stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-3-5);
  align-items: center;
}
.pd-action-stack .product-detail-icon-btn {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  color: var(--primary-ink);
  border: 1px solid rgba(255, 255, 255, 0.40);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.38) 0%, rgba(255, 255, 255, 0.08) 48%, rgba(255, 255, 255, 0) 52%),
    color-mix(in srgb, var(--primary) 82%, transparent);
  box-shadow:
    0 6px 16px color-mix(in srgb, var(--primary) 40%, transparent),
    0 1px 3px rgba(0, 0, 0, 0.25),
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 -3px 6px rgba(0, 0, 0, 0.18);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  transition: transform 0.24s var(--ease-ios);
}
.pd-action-stack .product-detail-icon-btn:active { transform: scale(0.92); }
.pd-action-stack .product-detail-icon-btn:disabled { opacity: 0.6; }
.pd-action-stack .pd-delete-btn {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.38) 0%, rgba(255, 255, 255, 0.08) 48%, rgba(255, 255, 255, 0) 52%),
    color-mix(in srgb, var(--danger) 82%, transparent);
  border-color: rgba(255, 255, 255, 0.40);
  box-shadow:
    0 6px 16px color-mix(in srgb, var(--danger) 40%, transparent),
    0 1px 3px rgba(0, 0, 0, 0.25),
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 -3px 6px rgba(0, 0, 0, 0.18);
}

/* Cluster visibility — hidden unless the Edit sheet is open. */
#product-detail-modal #pd-photo-refetch,
#product-detail-modal #pd-photo-upload,
#product-detail-modal #pd-delete-item { display: none; }
#product-detail-modal.pd-edit-open #pd-photo-refetch,
#product-detail-modal.pd-edit-open #pd-photo-upload,
#product-detail-modal.pd-edit-open #pd-delete-item { display: flex; }

/* Body: pinned image + sheet stack. */
.product-detail-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background: var(--primary-2);
  padding: 0;
  margin: 0;
  border-radius: 0;
  box-shadow: none;
}

/* Pinned product image (never scrolls). */
.product-detail-pinned {
  flex: 0 0 auto;
  height: 36vh;
  min-height: 220px;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px 0 4px;
  position: relative;
}
.product-detail-hero-img {
  max-width: 60%;
  max-height: 100%;
  object-fit: contain;
  display: block;
}

/* Staged-photo preview badge. */
.product-detail-pinned.is-pending::after {
  content: 'New — save to keep';
  position: absolute;
  top: 12px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--primary);
  color: var(--primary-ink);
  font-family: var(--font-ui);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  padding: 5px 12px;
  border-radius: var(--radius-pill);
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.25);
  pointer-events: none;
  z-index: 5;
  white-space: nowrap;
}

/* --- Sheet stack (4 stacked sheets: Content / Add Stock / Edit / History) ---
   Each sheet is a flex column. JS writes the height inline (see pdLayout());
   the layout below defines how each layer looks when it's fully open. */
.pd-stack {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  position: relative;
  overflow: hidden;
  background: transparent;
}

.pd-sheet {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 0 0 auto;
  overflow: hidden;
  height: 0;
  transition: height 0.34s cubic-bezier(0.32, 0.72, 0, 1);
  will-change: height;
}
.pd-stack.dragging .pd-sheet { transition: none; }

/* Layer order: Content on top, History at the bottom. */
.pd-sheet[data-sheet="0"] { z-index: 4; }
.pd-sheet[data-sheet="1"] { z-index: 3; }
.pd-sheet[data-sheet="2"] { z-index: 2; }
.pd-sheet[data-sheet="3"] { z-index: 1; }

/* Peek cards tuck 32px under the card above. */
.pd-sheet[data-sheet="1"],
.pd-sheet[data-sheet="2"],
.pd-sheet[data-sheet="3"] { margin-top: -32px; }

/* Content sheet — white, curved bottom, no handle. */
.pd-sheet[data-sheet="0"] {
  background: var(--bg);
  border-radius: 0 0 32px 32px;
  box-shadow: 0 10px 18px -6px rgba(0, 0, 0, 0.55);
}
[data-theme="dark"] .pd-sheet[data-sheet="0"] {
  box-shadow:
    0 -1px 0 rgba(163, 193, 74, 0.35),
    0 10px 18px -6px rgba(0, 0, 0, 0.55);
}

/* Peek sheets — themed gradient ladder (derived from --primary and --primary-2
   so Ocean / Warm / Dark each get their own family automatically). */
.pd-sheet[data-sheet="1"] {
  background: linear-gradient(
    180deg,
    var(--primary) 0%,
    color-mix(in srgb, var(--primary) 67%, var(--primary-2)) 100%
  );
  border-radius: 0 0 32px 32px;
  box-shadow: 0 10px 18px -6px rgba(0, 0, 0, 0.55);
}
.pd-sheet[data-sheet="2"] {
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--primary) 67%, var(--primary-2)) 0%,
    color-mix(in srgb, var(--primary) 34%, var(--primary-2)) 100%
  );
  border-radius: 0 0 32px 32px;
  box-shadow: 0 10px 18px -6px rgba(0, 0, 0, 0.55);
}
.pd-sheet[data-sheet="3"] {
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--primary) 34%, var(--primary-2)) 0%,
    var(--primary-2) 100%
  );
  border-radius: 0;
  box-shadow: none;
  min-height: 56px;
}

/* Handle — 80px tall, title sits 32px down. Matches H=80 / O=32 in pdLayout(). */
.pd-sheet-handle {
  flex: 0 0 80px;
  height: 80px;
  box-sizing: border-box;
  padding: 32px 0 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: 0.9rem;
  letter-spacing: 0.01em;
  color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
  background: transparent;
  cursor: grab;
  touch-action: none;
}

/* Body — white, scrolls internally, below the handle. */
.pd-sheet-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  background: var(--bg);
  padding: 0 20px 24px;
}
.pd-sheet-body::-webkit-scrollbar { display: none; }

/* --- Content sheet (sheet 0) --- */
.product-detail-title-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3-5);
  margin-bottom: 32px;
}
.product-detail-title-block { flex: 1 1 auto; min-width: 0; }
.product-detail-title {
  font-family: var(--font-display);
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1.15;
  margin: 0 0 4px;
  color: var(--ink);
  letter-spacing: -0.01em;
  overflow-wrap: anywhere;
}
.product-detail-sub {
  color: var(--muted);
  font-size: 0.85rem;
  font-weight: 500;
}
.pd-section-heading {
  font-family: var(--font-ui);
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--ink);
  margin: 32px 0 10px;
  letter-spacing: -0.01em;
}
.pd-section-heading:first-of-type { margin-top: 0; }
.pd-description {
  color: var(--muted);
  font-size: 0.92rem;
  line-height: 1.55;
  margin: 0;
}
.nutrition-section { margin: 32px 0 0; }
.nutrition-section-header {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 0 0 12px;
}
.nutrition-section-header h3 {
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--ink);
  margin: 0;
}
.nutrition-subscript {
  font-size: 0.72rem;
  font-weight: 500;
  color: var(--muted);
}
.nutrition-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
  margin: 0;
}
@media (max-width: 340px) {
  .nutrition-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.nutrition-card {
  background: var(--nutri-card-bg);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--nutri-card-edge);
  box-shadow: var(--nutri-card-shadow);
  border-radius: var(--radius-md);
  padding: 12px 6px 10px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 5px;
  min-width: 0;
}
.nutrition-card .n-icon {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.nutrition-card .n-icon svg { width: 16px; height: 16px; display: block; }
.nutrition-card .n-value {
  font-family: var(--font-ui);
  font-weight: 800;
  font-size: 0.98rem;
  line-height: 1.05;
  color: var(--primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
  display: inline-flex;
  align-items: baseline;
  justify-content: center;
  gap: 2px;
}
.nutrition-card .n-value .n-unit {
  font-size: 0.66rem;
  font-weight: 700;
  opacity: 0.75;
}
.nutrition-card .n-label {
  font-size: 0.66rem;
  color: var(--muted);
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: 0.01em;
}
.n-icon.nutri-calories { background: var(--nutri-calorie-bg); color: var(--nutri-calorie-ink); }
.n-icon.nutri-protein  { background: var(--nutri-protein-bg);  color: var(--nutri-protein-ink); }
.n-icon.nutri-fat      { background: var(--nutri-fat-bg);      color: var(--nutri-fat-ink); }
.n-icon.nutri-carbs    { background: var(--nutri-carbs-bg);    color: var(--nutri-carbs-ink); }
.n-value.nutri-calories { color: var(--nutri-calorie-ink); }
.n-value.nutri-protein  { color: var(--nutri-protein-ink); }
.n-value.nutri-fat      { color: var(--nutri-fat-ink); }
.n-value.nutri-carbs    { color: var(--nutri-carbs-ink); }

/* Batch list (Content sheet, read-only). */
.detail-batches-list { display: flex; flex-direction: column; gap: var(--space-2-5); margin: 0; }
.detail-batch-row {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1.5fr) minmax(0, 1fr);
  gap: var(--space-3);
  align-items: center;
  background: var(--nutri-card-bg);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--nutri-card-edge);
  box-shadow: var(--nutri-card-shadow);
  border-radius: var(--radius-20);
  padding: 14px 12px;
}
.detail-batch-row.is-expired {
  border-color: color-mix(in srgb, var(--danger) 45%, var(--nutri-card-edge));
}
.detail-batch-row.is-expired .detail-batch-amount { color: var(--danger); }
.detail-batch-location {
  display: inline-block;
  justify-self: start;
  font-size: 10px;
  font-weight: 700;
  color: var(--muted);
  background: var(--surface-alt);
  padding: 3px 9px;
  border-radius: var(--radius-pill);
  white-space: nowrap;
}
.detail-batch-date {
  font-weight: 600;
  font-size: 11px;
  color: var(--ink);
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
}
.detail-batch-date svg { color: var(--danger); flex-shrink: 0; }
.detail-batch-date-text { display: flex; flex-direction: column; min-width: 0; }
.detail-batch-date-value { line-height: 1.2; }
.days-left {
  font-weight: 500;
  font-size: 10px;
  color: var(--muted);
  line-height: 1.2;
  margin-top: 1px;
}
.detail-batch-amount {
  justify-self: end;
  text-align: right;
  font-weight: 700;
  font-size: 12px;
  color: var(--primary);
  white-space: nowrap;
}

/* Empty-state card for the Product Detail sheets (Content + Edit).
   Replaces the old plain `muted small` text so "no stock" and
   "could not load" read as intentional states, not fallback text. */
.pd-empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 28px 16px;
  color: var(--muted);
  text-align: center;
}
.pd-empty-state svg {
  opacity: 0.45;
  color: var(--muted);
}
.pd-empty-state p {
  margin: 0;
  font-size: 0.85rem;
  font-weight: 500;
  line-height: 1.4;
}

/* --- Add Stock sheet (sheet 1) --- */
.pd-add-stock-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-3-5);
  padding-top: 18px;
}
.pd-add-stock-form .field-label { margin: 0 0 6px; }
.pd-add-stock-form .input-elevated { width: 100%; }

/* --- Edit sheet (sheet 2) --- */
.pd-edit-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-3-5);
  padding-top: 18px;
}
.pd-edit-form .field-label { margin: 0 0 6px; }
.pd-edit-form .input-elevated { width: 100%; }
.pd-edit-form .pd-section-heading { margin: 8px 0 4px; }
.pd-edit-form .field-grid-2 > div { min-width: 0; }

/* Batch rows inside the Edit sheet. */
.batch-list-scroll {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding-right: 2px;
}
.batch-row-editable {
  border-radius: var(--radius-12);
  user-select: none;
  -webkit-user-select: none;
  transition: opacity 0.2s ease;
}
.batch-row-editable.consume-row { overflow: visible; }
.batch-row-editable.batch-row-zero { opacity: 0.35; }
.batch-row-editable .batch-row-content {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1.5fr) minmax(0, 1fr);
  gap: var(--space-3);
  padding: 0;
}
.batch-row-editable input { user-select: text; -webkit-user-select: text; }
#item-modal .batch-row-editable input:focus { outline-offset: -2px; }

.batch-row-editable .consume-row-delete-reveal.batch-delete-reveal {
  all: unset;
  box-sizing: border-box;
  position: absolute;
  top: 50%;
  right: 16px;
  width: 36px;
  height: 36px;
  min-width: 36px;
  min-height: 36px;
  max-width: 36px;
  max-height: 36px;
  border-radius: 50%;
  background: var(--danger);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  opacity: 0;
  transform: translateY(-50%) scale(0.5);
  transition:
    opacity 0.18s ease,
    transform 0.24s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.batch-row-editable.swiped .consume-row-delete-reveal.batch-delete-reveal {
  opacity: 1;
  transform: translateY(-50%) scale(1);
}
.batch-row-editable .consume-row-delete-reveal.batch-delete-reveal svg {
  width: 16px;
  height: 16px;
  display: block;
  pointer-events: none;
}
.batch-row-editable .consume-row-delete-reveal.batch-delete-reveal::before { content: ''; position: absolute; inset: -4px; }
.batch-location-input {
  width: 100%;
  min-width: 0;
  background-color: var(--input-inset-bg);
  border: 1px solid var(--input-inset-border);
  border-radius: var(--radius-xs);
  padding: 7px 26px 7px 9px;
  font-size: 0.85rem;
}

/* Product Detail — fixed 11px on all sheet inputs. */
.pd-add-stock-form .input-elevated input,
.pd-add-stock-form .input-elevated select,
.pd-add-stock-form .input-elevated .ui-dropdown-trigger,
.pd-add-stock-form .input-elevated .ui-dd-value,
.pd-add-stock-form .exp-field.has-leading-icon input[type="date"],
.pd-add-stock-form .noexp-chip,
.pd-edit-form .input-elevated input,
.pd-edit-form .input-elevated select,
.pd-edit-form .input-elevated .ui-dropdown-trigger,
.pd-edit-form .input-elevated .ui-dd-value,
.pd-edit-form .exp-field.has-leading-icon input[type="date"],
.pd-edit-form .noexp-chip {
  font-size: 11px;
}
#pd-edit-batches .input-elevated input,
#pd-edit-batches .input-elevated select,
#pd-edit-batches .input-elevated .ui-dropdown-trigger {
  background-color: transparent;
  border: none;
  box-shadow: none;
  border-radius: 0;
  outline: none;
  font-size: 11px;
}
#pd-edit-batches .exp-field.has-leading-icon input[type="date"] {
  background: transparent;
  border: none;
  box-shadow: none;
  border-radius: 0;
  font-size: 11px;
}
#pd-edit-batches .noexp-chip { font-size: 11px; }

/* Sheet footers — 20px off the edge, only visible when the sheet is genuinely open. */
.pd-sheet[data-sheet="1"] .pd-sheet-body,
.pd-sheet[data-sheet="2"] .pd-sheet-body {
  /* + var(--kb): extra scroll room so the lowest field can be lifted
     above the on-screen keyboard (--kb is 0px when it is closed). */
  padding-bottom: calc(96px + var(--safe-bottom) + var(--kb, 0px));
}
.pd-sheet-footer {
  position: absolute;
  left: 0; right: 0;
  bottom: 0;
  padding: 14px 20px calc(20px + var(--safe-bottom));
  z-index: 4;
  pointer-events: none;
}
.pd-sheet-footer > * { pointer-events: auto; }
.pd-sheet:not(.is-open) .pd-sheet-footer { display: none; }

.pd-add-stock-btn {
  display: block;
  width: 100%;
  padding: var(--space-3-5);
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: 0.95rem;
  border: 1px solid rgba(255, 255, 255, 0.4);
  border-radius: var(--radius-pill);
  color: var(--primary-ink);
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--primary) 55%, white 15%) 0%,
    color-mix(in srgb, var(--primary) 70%, transparent) 100%
  );
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.5),
    inset 0 -1px 6px rgba(0, 0, 0, 0.08),
    0 8px 24px rgba(0, 0, 0, 0.15);
  cursor: pointer;
  transition: transform 0.24s var(--ease-ios);
}
.pd-add-stock-btn:active { transform: scale(0.96); }
.pd-add-stock-btn:disabled { opacity: 0.6; }

/* --- History sheet (sheet 3) --- */
.history-filter-row {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  justify-content: safe center;
}
.history-filter-row::-webkit-scrollbar { display: none; }
#pd-history-filter-row {
  padding: 16px 4px 14px;
}
#pd-history-filter-row .location-chip:last-child { margin-right: 4px; }
.history-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2-5);
}
.history-header {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 0.9fr);
  gap: 8px;
  padding: 0 14px 4px 18px;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--muted);
}
.history-header > span:first-child { padding-left: 32px; }
.history-header > span:nth-child(4) { text-align: right; }
.history-card {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 0.9fr);
  gap: 8px;
  align-items: center;
  padding: 12px 14px;
  border-radius: var(--radius-16);
  background: var(--nutri-card-bg);
  border: 1px solid var(--nutri-card-edge);
  border-left: 4px solid var(--muted);
  box-shadow: var(--nutri-card-shadow);
  font-size: 11px;
}
.history-card.add     { border-left-color: var(--primary); }
.history-card.bought  { border-left-color: var(--primary); }
.history-card.consume { border-left-color: var(--info); }
.history-card.toss    { border-left-color: var(--danger); }
.history-card.adjust  { border-left-color: var(--warn); }
.history-card.undone  { opacity: 0.55; }
.history-type {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.history-icon {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--surface-alt);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: var(--muted);
}
.history-icon svg { width: 14px; height: 14px; }
.history-card.add     .history-icon { background: color-mix(in srgb, var(--primary) 18%, var(--surface-alt)); color: var(--primary); }
.history-card.bought  .history-icon { background: color-mix(in srgb, var(--primary) 18%, var(--surface-alt)); color: var(--primary); }
.history-card.consume .history-icon { background: var(--info-bg); color: var(--info); }
.history-card.toss    .history-icon { background: var(--danger-bg); color: var(--danger); }
.history-card.adjust  .history-icon { background: var(--warn-bg); color: var(--warn); }
.history-label {
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.history-date,
.history-exp {
  color: var(--muted);
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.history-qty {
  justify-self: end;
  text-align: right;
  font-weight: 700;
  color: var(--primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.history-card.toss .history-qty { color: var(--danger); }
.history-load-more {
  display: block;
  margin: 12px 4px 4px auto;
  background: none;
  border: none;
  padding: 6px 2px;
  font-family: var(--font-ui);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--primary);
  cursor: pointer;
  min-height: 0;
}
.history-load-more:active { opacity: 0.6; }
.history-load-more[hidden] { display: none; }


/* ============================================================================
   14. ADD PRODUCT MODAL — ELEVATED INPUTS + CUSTOM DROPDOWN
   ============================================================================ */

/* Wrapper for inputs with a leading icon. */
.input-elevated {
  position: relative;
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--surface);
  border-radius: var(--radius-sm);
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.05),
    0 4px 12px rgba(0, 0, 0, 0.06);
  padding: 0 8px;
  transition: box-shadow 0.18s var(--ease-ios);
  min-height: 44px;
}
[data-theme="dark"] .input-elevated {
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.35),
    0 4px 12px rgba(0, 0, 0, 0.40);
}
.input-elevated:focus-within {
  box-shadow:
    0 0 0 3px color-mix(in srgb, var(--primary) 25%, transparent),
    0 1px 2px rgba(0, 0, 0, 0.05),
    0 4px 12px rgba(0, 0, 0, 0.06);
}
.input-elevated .input-icon {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  opacity: 0.75;
}
.input-elevated .input-icon svg { width: 16px; height: 16px; }
.input-elevated input,
.input-elevated select {
  flex: 1 1 auto;
  min-width: 0;
  border: none;
  background: transparent;
  box-shadow: none;
  padding: 0;
  height: 44px;
  font-size: 0.9rem;
  color: var(--ink);
  outline: none;
}
.input-elevated input:focus,
.input-elevated select:focus { outline: none; background: transparent; }
.input-elevated select {
  padding-right: 26px;
  background-position: right 0 center;
}
/* Higher specificity than #item-modal's field chrome. */
#item-modal .input-elevated input,
#item-modal .input-elevated select {
  background-color: transparent;
  border: none;
  box-shadow: none;
  border-radius: 0;
  outline: none;
}
#item-modal .input-elevated input:focus,
#item-modal .input-elevated select:focus {
  outline: none;
  background-color: transparent;
  border: none;
}

/* Date field with a leading icon — becomes the elevated card. */
.exp-field.has-leading-icon {
  position: relative;
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--surface);
  border-radius: var(--radius-sm);
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.05),
    0 4px 12px rgba(0, 0, 0, 0.06);
  padding: 0 8px;
  min-height: 44px;
  transition: box-shadow 0.18s var(--ease-ios);
}
[data-theme="dark"] .exp-field.has-leading-icon {
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.35),
    0 4px 12px rgba(0, 0, 0, 0.40);
}
.exp-field.has-leading-icon:focus-within {
  box-shadow:
    0 0 0 3px color-mix(in srgb, var(--primary) 25%, transparent),
    0 1px 2px rgba(0, 0, 0, 0.05),
    0 4px 12px rgba(0, 0, 0, 0.06);
}
.exp-field.has-leading-icon input[type="date"] {
  flex: 1 1 auto;
  min-width: 0;
  border: none;
  background: transparent;
  box-shadow: none;
  padding: 0 0 0 30px;
  height: 44px;
  font-size: 0.9rem;
  color: var(--ink);
  outline: none;
}
.exp-field.has-leading-icon input[type="date"]:focus { outline: none; }
#item-modal .exp-field.has-leading-icon input[type="date"] {
  background: transparent;
  border: none;
  box-shadow: none;
  border-radius: 0;
}
#item-modal .exp-field.has-leading-icon input[type="date"]:focus {
  outline: none;
  background: transparent;
  border: none;
}
.exp-field input[type="date"]::-webkit-calendar-picker-indicator,
.exp-field.has-leading-icon input[type="date"]::-webkit-calendar-picker-indicator {
  display: none;
  -webkit-appearance: none;
  appearance: none;
  opacity: 0;
  width: 0;
  height: 0;
  margin: 0;
  padding: 0;
  pointer-events: none;
  position: absolute;
}

/* --- Custom dropdown (replaces native <select>) --- */
.ui-dropdown {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  width: 100%;
}
.ui-dropdown > select {
  position: absolute;
  opacity: 0;
  pointer-events: none;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  border: 0;
}
.ui-dropdown-trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  background: transparent;
  border: none;
  padding: 0;
  font-family: var(--font-ui);
  font-size: 0.9rem;
  color: var(--ink);
  cursor: pointer;
  text-align: left;
  height: 44px;
}
.ui-dropdown-trigger .ui-dd-value {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ui-dropdown-trigger .ui-dd-chevron {
  flex: 0 0 auto;
  width: 14px; height: 14px;
  color: var(--muted);
  transition: transform 0.2s var(--ease-ios);
}
.ui-dropdown.open .ui-dropdown-trigger .ui-dd-chevron { transform: rotate(180deg); }
.ui-dropdown-menu {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  min-width: 100%;
  width: max-content;
  background: var(--popup-bg, var(--surface));
  border: 1px solid var(--popup-edge, transparent);
  box-shadow: var(--popup-shadow, 0 8px 24px rgba(0, 0, 0, 0.18));
  border-radius: var(--radius-sm);
  padding: 4px;
  z-index: 2100;
  display: none;
  max-height: 240px;
  overflow-y: auto;
  animation: popIn 0.15s var(--ease-ios);
}
.ui-dropdown-menu.open { display: block; }
.ui-dropdown-item {
  display: block;
  width: 100%;
  padding: 11px 12px;
  text-align: left;
  background: none;
  border: none;
  font-family: var(--font-ui);
  font-size: 0.9rem;
  color: var(--ink);
  border-radius: var(--radius-xs);
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ui-dropdown-item:active { background: var(--surface-2); }
.ui-dropdown-item.is-selected {
  background: var(--pill-active-bg);
  color: var(--primary);
  font-weight: 700;
}


/* ============================================================================
   15. RECEIPT SCAN
   ============================================================================ */
#receipt-modal {
  align-items: stretch;
  justify-content: stretch;
  padding: 0;
  background: #000;
}
#receipt-modal > .modal-sheet {
  height: calc(100vh / var(--app-scale));
  height: calc(100svh / var(--app-scale));
  max-height: calc(100vh / var(--app-scale));
  max-height: calc(100svh / var(--app-scale));
  max-width: 100%;
  border-radius: 0;
  background: #000;
}
#receipt-modal .modal-header {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 3;
  background: linear-gradient(180deg, rgba(0,0,0,0.7) 0%, transparent 100%);
  border-bottom: none;
  padding: calc(16px + var(--safe-top)) 18px 20px;
}
#receipt-modal .modal-header h2 {
  color: #fff;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}
#receipt-modal .modal-body.barcode-scan-body {
  flex: 1 1 auto;
  min-height: 0;
  height: auto;
  padding: 0;
  margin-top: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: #000;
  box-shadow: none;
  border-radius: 0;
}
.receipt-video-wrap {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  padding: calc(84px + var(--safe-top)) 16px 8px;
}
#receipt-video {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  background: #111;
  border: 2px solid rgba(255, 255, 255, 0.20);
  border-radius: var(--radius-md);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.55);
  display: block;
}
#receipt-status {
  text-align: center;
  color: rgba(255, 255, 255, 0.72);
  background: transparent;
  margin: 0;
  padding: 6px 16px 0;
  font-size: 0.82rem;
  min-height: 1.1em;
}
.receipt-camera-actions {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 28px;
  background: transparent;
  padding: 18px 16px calc(28px + var(--safe-bottom));
  flex-shrink: 0;
}
.receipt-action-btn { width: 64px; height: 64px; }
.receipt-action-btn svg { width: 26px; height: 26px; }

/* Receipt-loading overlay. Centered card, matches the recipe prompt
   sheet (rounded corners, soft shadow, spinner at top). The Cancel
   button reuses .recipe-prompt-secondary. */
.receipt-loading-overlay {
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
}
.receipt-loading-sheet {
  background: var(--popup-bg);
  border: 1px solid var(--popup-edge);
  box-shadow:
    0 28px 64px rgba(0, 0, 0, 0.30),
    0 10px 24px rgba(0, 0, 0, 0.18),
    0 2px 6px rgba(0, 0, 0, 0.10),
    inset 0 1px 0 rgba(255, 255, 255, 0.9);
  width: 100%;
  max-width: 320px;
  border-radius: var(--radius-xl);
  padding: 40px 26px 18px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  animation: popIn 0.24s var(--ease-ios);
}
[data-theme="dark"] .receipt-loading-sheet {
  box-shadow:
    0 28px 64px rgba(0, 0, 0, 0.75),
    0 10px 24px rgba(0, 0, 0, 0.55),
    0 2px 6px rgba(0, 0, 0, 0.35),
    inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
.receipt-loading-spinner {
  width: 32px;
  height: 32px;
  border-width: 3px;
  margin: 0 0 20px;
  color: var(--primary);
}
.receipt-loading-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.15rem;
  color: var(--ink);
  margin: 0 0 8px;
  letter-spacing: -0.01em;
}
/* Barcode number shown under the loading title. Monospace so digits
   are easy to read; word-break handles very long codes. */
.barcode-loading-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.85rem;
  color: var(--muted);
  margin: -2px 0 10px;
  letter-spacing: 0.03em;
  word-break: break-all;
}
.barcode-loading-code:empty { display: none; }

.receipt-loading-text {
  font-family: var(--font-ui);
  font-size: 0.9rem;
  color: var(--muted);
  margin: 0 0 20px;
  min-height: 1.4em;
  line-height: 1.4;
}

.receipt-pick-sheet {
  height: auto;
  max-height: calc(80dvh / var(--app-scale));
  display: flex;
  flex-direction: column;
}
.receipt-pick-body {
  padding: 16px 18px 18px;
  margin-top: 0;
  box-shadow: none;
  overflow-y: auto;
  min-height: 0;
  flex: 1 1 auto;
}
.receipt-pick-body .search-pill-wrap { margin-bottom: 12px; }
.receipt-pick-results {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.receipt-pick-row {
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  padding: 11px 12px;
  border-radius: var(--radius-12);
  background: var(--row-card-bg);
  border: 1px solid var(--row-card-border);
  cursor: pointer;
  transition: background 0.15s ease;
}
.receipt-pick-row:active { background: var(--surface-2); }
.receipt-pick-row.is-new { color: var(--info); font-weight: 700; justify-content: center; }
.receipt-pick-row.is-selected { border-color: var(--primary); background: var(--pill-active-bg); }
.receipt-pick-name {
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--ink);
  flex: 1;
  min-width: 0;
  overflow-wrap: anywhere;
}
.receipt-pick-section {
  font-size: 0.7rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  padding: 10px 2px 4px;
}

/* Barcode scanner overlay. */
.barcode-scan-overlay {
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
}
.barcode-scan-sheet {
  height: auto;
  max-height: 80vh;
  border-radius: var(--radius-lg);
  width: 100%;
  max-width: 380px;
  animation: popIn 0.22s var(--ease-ios);
}
.barcode-scan-overlay.closing > .barcode-scan-sheet { animation: popOut 0.2s ease-in forwards; }
.barcode-scan-body {
  padding: var(--space-3) var(--space-5) var(--space-4);
  margin-top: 0;
  box-shadow: none;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
}
/* Tighter status + toggle so the whole scanner UI fits one screen. */
.barcode-scan-body #item-scan-popup-status {
  margin: var(--space-3) 0 4px;
  text-align: center;
}
.barcode-scan-body .manual-barcode-toggle {
  margin: 4px auto 0;
}
.barcode-scan-body #item-reader-popup {
  position: relative;   /* positioning context for .barcode-scan-guide */
  max-width: 320px;
  margin: 0 auto;
  border-radius: var(--radius-xs);
  overflow: hidden;
}
/* Force a consistent landscape shape for the video display, so iOS
   and Android look identical. iOS delivers a portrait stream
   (1080×1920) and Android a landscape stream (1920×1080), and
   without a fixed aspect ratio the two produce visibly different
   crops of the same container size. aspect-ratio pins the visible
   shape regardless of the source. object-fit: cover fills that
   shape by cropping the overflow — middle strip of the frame
   either way, which is exactly where the barcode sits. */
.barcode-scan-body #item-reader-popup video {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  max-height: 45vh;
  object-fit: cover;
  object-position: center;
}
/* html5-qrcode sometimes layers a <canvas> over the video for its own
   guides. Hide it — we draw our own guide below. */
.barcode-scan-body #item-reader-popup canvas {
  display: none;
}
.barcode-header-actions { display: flex; align-items: center; gap: var(--space-2); }

/* Barcode scan alignment guide. A semi-transparent horizontal strip
   over the video element showing where the code should sit. The two
   edge brackets frame the strip; the label above it is small and
   low-contrast so it guides without dominating the viewfinder. */
.barcode-scan-guide {
  position: absolute;
  left: 8%;
  right: 8%;
  top: 50%;
  transform: translateY(-50%);
  height: 42%;
  pointer-events: none;
  z-index: 5;
}
.barcode-scan-guide::before,
.barcode-scan-guide::after {
  content: '';
  position: absolute;
  top: 0; bottom: 0;
  width: 26px;
  border: 3px solid rgba(255, 255, 255, 0.85);
  border-radius: 6px;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25), 0 2px 8px rgba(0, 0, 0, 0.35);
}
.barcode-scan-guide::before {
  left: 0;
  border-right: none;
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
.barcode-scan-guide::after {
  right: 0;
  border-left: none;
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}
.barcode-scan-guide-label {
  position: absolute;
  left: 50%;
  top: -28px;
  transform: translateX(-50%);
  color: #fff;
  font-family: var(--font-ui);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  white-space: nowrap;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.75);
}
.icon-btn.torch-on { background: #FFD54A; border-color: #FFC107; color: #4A3800; }

/* Barcode scan — loading block + manual entry. */
.scan-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  min-height: 240px;
  padding: 24px 16px;
  max-width: 320px;
  margin: 0 auto;
  border-radius: var(--radius-xs);
  background: var(--surface-alt);
  text-align: center;
}
.scan-loading[hidden] { display: none; }
.scan-loading .recipe-spinner {
  width: 32px;
  height: 32px;
  border-width: 3px;
  margin: 0;
}
.scan-loading p {
  margin: 0;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--ink);
}
.scan-loading code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.85rem;
  color: var(--muted);
  background: var(--surface);
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  letter-spacing: 0.02em;
}
.manual-barcode-toggle {
  display: block;
  margin: 12px auto 0;
  background: none;
  border: none;
  color: var(--primary);
  font-family: var(--font-ui);
  font-size: 0.85rem;
  font-weight: 700;
  padding: 8px 14px;
  cursor: pointer;
  border-radius: var(--radius-pill);
}
.manual-barcode-toggle:active { background: var(--surface-2); }
.manual-barcode-toggle[hidden] { display: none; }
.manual-barcode-form {
  display: flex;
  gap: 8px;
  align-items: stretch;
  max-width: 320px;
  margin: 10px auto 0;
}
.manual-barcode-form[hidden] { display: none; }
.manual-barcode-form input { flex: 1 1 auto; min-width: 0; }
.manual-barcode-form .btn { flex: 0 0 auto; min-height: 44px; }

/* Pack size badge. */
.pack-size {
  font-size: 0.72em;
  font-weight: 600;
  color: var(--muted);
  margin-left: 5px;
  white-space: nowrap;
  letter-spacing: 0.01em;
}
.grid-item .name .pack-size { display: inline-block; }


/* ============================================================================
   16. iOS 18 POLISH
   ============================================================================ */

/* Focus halo for inputs that aren't inside an elevated wrapper. */
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--primary) 22%, transparent);
}
.input-elevated input:focus,
.input-elevated select:focus,
.exp-field.has-leading-icon input:focus,
.exp-field.has-leading-icon input[type="date"]:focus {
  box-shadow: none;
  border-color: transparent;
}

/* Caret + text selection tint. */
input, textarea, [contenteditable="true"] { caret-color: var(--primary); }
::selection {
  background: color-mix(in srgb, var(--primary) 22%, transparent);
  color: var(--ink);
}

/* Spring press on shared buttons. */
.btn,
.icon-btn,
.add-btn-inline,
.consume-submit-btn,
.bought-submit-btn,
.pd-add-stock-btn {
  transition:
    transform 0.24s var(--ease-ios),
    background 0.15s ease,
    color 0.15s ease,
    border-color 0.15s ease,
    box-shadow 0.2s var(--ease-ios),
    opacity 0.15s ease;
}

/* Symbol weight swap on active tab. */
.tabbar .tab-btn svg {
  stroke-width: 1.6;
  transition: stroke-width 0.24s var(--ease-ios);
}
.tabbar .tab-btn.active svg { stroke-width: 2.4; }

/* --- Home grid long-press context menu --- */
.grid-context-menu {
  position: fixed;
  inset: 0;
  z-index: 2500;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
}
.grid-context-menu[hidden] { display: none; }
.grid-context-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.34);
  backdrop-filter: blur(22px) saturate(1.4);
  -webkit-backdrop-filter: blur(22px) saturate(1.4);
  animation: overlayFadeIn 0.2s ease-out;
}
.grid-context-list {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 320px;
  background: var(--popup-bg);
  border: 1px solid var(--popup-edge);
  box-shadow: var(--popup-shadow);
  border-radius: var(--radius-md);
  padding: 6px;
  animation: popIn 0.24s var(--ease-ios);
}
.grid-context-title {
  font-family: var(--font-ui);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 6px 12px 4px;
}
.grid-context-list button {
  display: block;
  width: 100%;
  padding: 14px 16px;
  text-align: left;
  font-family: var(--font-ui);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--ink);
  background: none;
  border: none;
  border-radius: var(--radius-12);
  cursor: pointer;
  transition: background 0.15s ease;
}
.grid-context-list button:active { background: var(--surface-2); }
.grid-context-list button.danger { color: var(--danger); }
.grid-context-list button[data-action="delete"] {
  margin-top: 4px;
  border-top: 1px solid var(--border);
  padding-top: 15px;
  border-radius: 0 0 12px 12px;
}

/* Heart pop on favourite toggle. */
@keyframes heartPop {
  0%   { transform: scale(1); }
  40%  { transform: scale(0.82); }
  70%  { transform: scale(1.18); }
  100% { transform: scale(1); }
}
.recipe-fav-btn.pop svg,
#recipe-detail-fav-btn.pop svg {
  animation: heartPop 0.48s var(--ease-ios);
}

/* Drag handles on Product Detail / Recipe Detail — steal the gesture. */
.product-detail-pinned,
.recipe-detail-hero {
  touch-action: none;
}
#product-detail-modal,
#recipe-detail-modal {
  touch-action: none;
}
#product-detail-modal .pd-sheet-body,
#recipe-detail-modal .recipe-detail-list,
#recipe-detail-modal .recipe-detail-body {
  touch-action: pan-y;
}


/* ============================================================================
   17. ITEM MODAL — pin every field to 11px
   ============================================================================ */
/* Source-order-last + high-specificity so nothing above (base input rules,
   .input-elevated overrides, .exp-field.has-leading-icon) can win. */
#item-modal input,
#item-modal select,
#item-modal textarea,
#item-modal .input-elevated input,
#item-modal .input-elevated select,
#item-modal .ui-dropdown-trigger,
#item-modal .ui-dd-value,
#item-modal .exp-field.has-leading-icon input[type="date"],
#item-modal .noexp-chip {
  font-size: 11px;
}

/* --- Long-press guard: suppress the OS callout on drag-able rows so a
   long-press starts a drag instead of Save Image / Copy / Look Up.
   Text fields opt back into selection. --- */
.recipe-edit-ing-row,
.recipe-edit-step-row {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}
.recipe-edit-ing-row input,
.recipe-edit-ing-row textarea,
.recipe-edit-step-row textarea {
  -webkit-user-select: text;
  user-select: text;
}


/* patched: recipe edit enhancement set */
.recipe-edit-ing-autocomplete {
  position: absolute;
  left: 10px;
  right: 10px;
  z-index: 60;
  background: var(--popup-bg);
  border: 1px solid var(--popup-edge);
  box-shadow: var(--popup-shadow);
  border-radius: var(--radius-12);
  padding: 4px;
  max-height: 220px;
  overflow-y: auto;
  animation: popIn 0.14s var(--ease-ios);
}
.recipe-edit-ing-autocomplete .recipe-edit-match-option {
  padding: 9px 10px;
  font-size: 11px;
}
.recipe-edit-unmatched-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2-5);
  padding: 9px 12px;
  margin-bottom: var(--space-2-5);
  border-radius: var(--radius-12);
  background: color-mix(in srgb, var(--warn) 12%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--warn) 35%, transparent);
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 600;
  color: var(--ink);
}
.recipe-edit-autolink-btn {
  flex-shrink: 0;
  padding: 6px 12px;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--primary);
  color: var(--primary-ink);
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 700;
  cursor: pointer;
  transition: transform 0.24s var(--ease-ios);
}
.recipe-edit-autolink-btn:active { transform: scale(0.95); }
#recipe-edit-save:disabled {
  opacity: 0.4;
  cursor: default;
  pointer-events: none;
}


/* patched: cook overlay = recipe-edit-style card */
#recipe-cook-modal {
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  padding-top: max(var(--space-4), var(--safe-top));
  padding-bottom: var(--space-4);
}
#recipe-cook-modal > .modal-sheet.recipe-cook-sheet {
  width: 100%;
  height: 100%;
  max-height: 100%;
  border-radius: var(--radius-xl);
  max-width: 440px;
  padding: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Hero — same dimensions/shade as .recipe-edit-hero, with title overlaid. */
.recipe-cook-hero {
  position: relative;
  width: 100%;
  height: 170px;
  flex-shrink: 0;
  overflow: hidden;
  background: var(--surface-alt);
}
.recipe-cook-hero-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.recipe-cook-hero-shade {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
    rgba(0, 0, 0, 0.10) 0%,
    rgba(0, 0, 0, 0.00) 42%,
    rgba(0, 0, 0, 0.55) 100%);
  pointer-events: none;
}
/* No-image fallback: shrink to a thin bar, hide shade + title (the sheet
   has nothing to say without a photo). */
#recipe-cook-modal .recipe-cook-sheet.no-image .recipe-cook-hero {
  height: 62px;
}
/* Body curls up over the hero, same shape as .recipe-edit-body. The
   base rule applies whenever the hero carries a photo; the .no-image
   override further down flattens it when the hero collapses to the
   62px bar. */
.recipe-cook-body {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  overflow-x: hidden;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  padding: 22px 20px calc(120px + var(--safe-bottom));
  margin-top: -22px;
  border-radius: 22px 22px 0 0;
  background: var(--surface);
  box-shadow:
    0 -12px 24px -12px rgba(0, 0, 0, 0.42),
    0 -3px 6px -3px rgba(0, 0, 0, 0.22),
    0 -1px 0 rgba(255, 255, 255, 0.55);
  position: relative;
  z-index: 2;
}
[data-theme="dark"] .recipe-cook-body {
  box-shadow:
    0 -12px 24px -12px rgba(0, 0, 0, 0.75),
    0 -3px 6px -3px rgba(0, 0, 0, 0.50),
    0 -1px 0 rgba(255, 255, 255, 0.08);
}
.recipe-cook-list {
  display: flex;
  flex-direction: column;
}

/* Footer pinned to the card's bottom edge. */
.recipe-cook-footer {
  position: absolute;
  left: 0; right: 0;
  bottom: max(20px, var(--safe-bottom));
  padding: 0 20px;
  z-index: 5;
}


/* patched: cook overlay v2 */
/* Body heading (moved out of the hero). */
.recipe-cook-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.1rem;
  color: var(--ink);
  margin: 0 0 12px;
  letter-spacing: -0.01em;
  line-height: 1.2;
}

/* No-image variant: hide the hero entirely, drop the body curl. */
#recipe-cook-modal .recipe-cook-sheet.no-image .recipe-cook-hero {
  display: none;
}
#recipe-cook-modal .recipe-cook-sheet.no-image .recipe-cook-body {
  margin-top: 0;
  border-radius: 0;
  box-shadow: none;
  padding-top: 28px;
}

/* Row swipe reveal — matches the Consume / Shopping slab look. */
#recipe-cook-list .consume-row-delete-reveal {
  top: 0; bottom: 0; right: 0;
  width: 96px;
  height: auto;
  padding: 0 0 0 24px;
  transform: none;
  border-radius: var(--radius-16);
  opacity: 0;
  transition: opacity 0.15s ease;
}
#recipe-cook-list .consume-row.swiped .consume-row-delete-reveal { opacity: 1; }
#recipe-cook-list .consume-row.swiped .consume-row-content { transform: translateX(-72px); }
#recipe-cook-list .consume-row {
  border-radius: var(--radius-16);
  box-shadow: var(--row-shadow);
  overflow: hidden;
}


/* patched: cook overlay v3 */
/* Gap between cook rows (matches .consume-list). */
.recipe-cook-list {
  gap: var(--space-2-5);
}

/* Row card chrome — the base .consume-row-content rule is bare; the
   Consume page styles are scoped to .consume-list, so the cook list
   needs its own copy to look the same. */
#recipe-cook-list .consume-row-content {
  gap: 12px;
  padding: 8px 12px 8px 8px;
  background: var(--row-card-bg);
  border: 1px solid var(--row-card-border);
  border-radius: var(--radius-16);
  box-shadow: var(--row-highlight);
}
#recipe-cook-list .consume-thumb {
  width: 56px;
  height: 56px;
  border-radius: var(--radius-12);
  overflow: hidden;
  flex-shrink: 0;
  background: var(--row-thumb-bg);
}
#recipe-cook-list .consume-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Footer: the base .recipe-cook-footer rule (used by the detail page's
   Cook This pill) sets opacity: 0 + pointer-events: none for its
   fade-in-on-scroll behaviour. Override both inside the cook overlay
   so the submit button is actually visible and tappable. Also kill the
   scroll-fade ::before gradient — that belongs to the detail page. */
#recipe-cook-modal .recipe-cook-footer {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: var(--space-3-5);
  opacity: 1;
  pointer-events: auto;
  bottom: max(20px, var(--safe-bottom));
  padding: 0 20px;
}
#recipe-cook-modal .recipe-cook-footer::before { display: none; }
#recipe-cook-modal .recipe-cook-footer .recipe-preview-pill { margin-bottom: 4px; }
#recipe-cook-modal #recipe-cook-submit { pointer-events: auto; }






/* patched: cook submit disabled gate */
/* The base .recipe-preview-pill:disabled is opacity 0.6. Give the cook
   tick a slightly stronger dim so the disabled state reads clearly,
   and kill the pointer so a hover on desktop doesn't glow. */
#recipe-cook-modal #recipe-cook-submit:disabled {
  opacity: 0.35;
  cursor: default;
  pointer-events: none;
  box-shadow: none;
}


.recipe-tm6-btn {
  flex: 0 0 auto;
  padding: 14px 20px;
  min-height: 48px;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-pill);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  box-shadow: var(--glass-card-shadow);
  cursor: pointer;
  transition: transform 0.24s var(--ease-ios);
}
.recipe-tm6-btn:active { transform: scale(0.96); }
.recipe-cook-footer.on .recipe-tm6-btn { pointer-events: auto; }
.recipe-cook-footer .recipe-tm6-btn { pointer-events: none; }

/* TM6 overlay - full-screen page, dark navy bottom, white hero top. */
.recipe-tm6-overlay {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  background: #0F1E3D;
  align-items: stretch;
  justify-content: stretch;
  padding: 0;
  padding-top: 0;
}
.recipe-tm6-overlay > .modal-sheet.recipe-tm6-sheet {
  position: relative;
  width: 100%;
  height: 100%;
  max-width: 100%;
  max-height: 100%;
  border-radius: 0;
  background: #0F1E3D;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}.recipe-tm6-dots {
  display: flex;
  gap: 8px;
  margin-bottom: 32px;
  align-items: center;
  justify-content: center;
  transition: margin 0.32s var(--ease-ios);
}
.recipe-tm6-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.32);
  transition: width 0.24s var(--ease-ios), background 0.2s;
}
.recipe-tm6-dot.active {
  width: 22px;
  border-radius: 5px;
  background: #fff;
}


/* patched: tm6 theme colors */
/* Body bg and Continue button follow the current theme's accent.
   Uses --primary-2 (the darker accent) for the background so the white
   title/subtitle/Continue retain the same contrast as the mockup, and
   --primary for the Continue text colour. Each theme gets its own
   palette automatically. */
.recipe-tm6-overlay { background: var(--primary-2); }
.recipe-tm6-overlay > .modal-sheet.recipe-tm6-sheet { background: var(--bg); }


/* Horizontal swipe track */
.recipe-tm6-track {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  overscroll-behavior-x: contain;
}
.recipe-tm6-track::-webkit-scrollbar { display: none; }

.recipe-tm6-slide {
  flex: 0 0 100%;
  width: 100%;
  height: 100%;
  scroll-snap-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 60px 24px 24px;
  box-sizing: border-box;
  overflow-y: auto;
  text-align: center;
}

/* Intro slide (keeps recipe photo on white) */
.recipe-tm6-slide-intro {
  padding: 0;
  justify-content: flex-start;
}
.recipe-tm6-slide-hero {
  position: relative;
  flex: 0 0 auto;
  width: 100%;
  height: 54vh;
  min-height: 260px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: var(--surface-alt);
  border-radius: 0 0 28px 28px;
  overflow: hidden;
}.recipe-tm6-slide-intro-body {
  flex: 1 1 auto;
  padding: 28px 24px 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.recipe-tm6-intro-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.5rem;
  line-height: 1.25;
  color: var(--ink);
  margin: 0 0 10px;
  letter-spacing: -0.01em;
}
.recipe-tm6-intro-sub {
  font-family: var(--font-ui);
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--muted);
  margin: 0;
}

/* Step slide */
.recipe-tm6-step-icon {
  width: 108px;
  height: 108px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--primary) 12%, var(--surface));
  color: var(--primary);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 26px;
  flex-shrink: 0;
}
.recipe-tm6-step-icon svg {
  width: 54px;
  height: 54px;
}
.recipe-tm6-step-text {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.55rem;
  line-height: 1.35;
  color: var(--ink);
  max-width: 340px;
  letter-spacing: -0.01em;
}
.recipe-tm6-step-num {
  font-family: var(--font-ui);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: var(--space-3-5);
}

/* CTA slide */
.recipe-tm6-slide-cta {
  gap: 20px;
}
.recipe-tm6-cta-icon {
  width: 108px;
  height: 108px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--primary) 14%, var(--surface));
  color: var(--primary);
  display: flex;
  align-items: center;
  justify-content: center;
}
.recipe-tm6-cta-icon svg { width: 56px; height: 56px; }
.recipe-tm6-cta-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.5rem;
  color: var(--ink);
  margin: 0;
}
.recipe-tm6-cta-sub {
  font-family: var(--font-ui);
  font-size: 0.9rem;
  color: var(--muted);
  margin: 0;
}
.recipe-tm6-cta-btn {
  margin-top: 12px;
  padding: 15px 40px;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--primary);
  color: var(--primary-ink);
  font-family: var(--font-ui);
  font-size: 0.98rem;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 6px 18px color-mix(in srgb, var(--primary) 40%, transparent);
  transition: transform 0.24s var(--ease-ios);
}
.recipe-tm6-cta-btn:active { transform: scale(0.96); }

/* Bottom panel */
.recipe-tm6-panel {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 5;
  background: var(--primary-2);
  padding: 22px 20px calc(16px + var(--safe-bottom));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
  border-radius: 22px 22px 0 0;
  transition: gap 0.32s var(--ease-ios), padding 0.32s var(--ease-ios);
}
.recipe-tm6-panel.has-controls {
  padding-top: 30px;
  padding-bottom: calc(38px + var(--safe-bottom));
  gap: 26px;
}
.recipe-tm6-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.32);
  transition: width 0.24s var(--ease-ios), background 0.2s ease;
}
.recipe-tm6-dot.active {
  width: 22px;
  border-radius: 5px;
  background: #fff;
}
.recipe-tm6-controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3-5);
  width: 100%;
  max-width: 420px;
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  transition: max-height 0.34s var(--ease-ios), opacity 0.24s ease;
}
.recipe-tm6-panel.has-controls .recipe-tm6-controls {
  max-height: 190px;
  opacity: 1;
}
.recipe-tm6-ctrl {
  flex: 1 1 0;
  text-align: center;
  color: #fff;
  min-width: 0;
}
.recipe-tm6-ctrl-label {
  display: block;
  font-family: var(--font-ui);
  font-size: 0.74rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.6;
  margin-bottom: 3px;
}
.recipe-tm6-ctrl-value {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  font-family: var(--font-ui);
  font-size: 1.5rem;
  font-weight: 700;
}
.recipe-tm6-reverse {
  font-size: 1.5rem;
  opacity: 0.85;
  margin-left: 6px;
  vertical-align: middle;
  line-height: 1;
}
/* Circular timer: SVG progress ring with the digital clock centred in it.
   The ring "head" (glowing dot) trails the arc as time runs out.
   Stropped (idle): dim ring, full circle, no dot.
   Running:        bright ring, arc shrinks, glowing dot at the tip. */
.recipe-tm6-play {
  position: relative;
  flex: 0 0 auto;
  width: 138px;
  height: 138px;
  border-radius: 50%;
  background: transparent;
  border: none;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: transform 0.24s var(--ease-ios);
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
.recipe-tm6-play:active { transform: scale(0.96); }

/* Finished state: the timer holds at 0:00 instead of the entry being
   auto-deleted, and the ring track pulses to draw the eye. Tap to
   restart from the top, long-press to clear. */
.recipe-tm6-play.is-finished .recipe-tm6-play-ring-track {
  animation: tm6FinishedPulse 1.4s ease-in-out infinite;
}
@keyframes tm6FinishedPulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.30; }
}

.recipe-tm6-play-ring {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: visible;
}
.recipe-tm6-play-ring-track {
  fill: none;
  stroke: rgba(255, 255, 255, 0.22);
  stroke-width: 8;
}
.recipe-tm6-play-ring-progress {
  fill: none;
  stroke: rgba(255, 255, 255, 0.55);
  stroke-width: 8;
  stroke-linecap: round;
  stroke-dasharray: 263.894;   /* 2 * PI * r(42) */
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 0.35s linear, stroke 0.2s ease;
}
.recipe-tm6-play-dot {
  fill: #fff;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.recipe-tm6-play-time {
  position: relative;
  z-index: 1;
  font-family: var(--font-ui);
  font-size: 1.45rem;
  font-weight: 800;
  line-height: 1;
  color: #fff;
  letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums;
  transition: opacity 0.2s ease;
}

/* No time configured (e.g. a pure "Add X" step with only temp/speed):
   hide the arc entirely so the ring reads as "empty". */
.recipe-tm6-play.is-empty .recipe-tm6-play-ring-progress { opacity: 0; }

/* Running: brighter ring + glowing tip dot. */
.recipe-tm6-play.is-running .recipe-tm6-play-ring-progress {
  stroke: #fff;
  filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.55));
}
.recipe-tm6-play.is-running .recipe-tm6-play-dot {
  opacity: 1;
  filter: drop-shadow(0 0 6px rgba(255, 255, 255, 0.75));
}

/* Paused: ring frozen at its current position, but dimmed back to
   the idle tone and the glow removed — reads as "held". */
.recipe-tm6-play.is-paused .recipe-tm6-play-ring-progress {
  stroke: rgba(255, 255, 255, 0.55);
  filter: none;
}
.recipe-tm6-play.is-paused .recipe-tm6-play-dot {
  opacity: 1;
  fill: rgba(255, 255, 255, 0.55);
  filter: none;
}
/* Breathing pulse on the paused ring tip so a paused timer is easy to
   spot at a glance among running and idle steps. */
@keyframes tm6PausedPulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.4; }
}
.recipe-tm6-play.is-paused .recipe-tm6-play-dot {
  animation: tm6PausedPulse 1.6s ease-in-out infinite;
}


/* patched: tm6 v3 (curved panel + taller) */
/* Panel pulls up over the slide area with a curved top edge — this is
   the white-to-green transition the mockup shows. */
/* Reserve room so slide content never sits under the overlaying panel. */
.recipe-tm6-slide { padding-bottom: calc(190px + var(--safe-bottom)); }
.recipe-tm6-slide-intro-body { padding-bottom: calc(190px + var(--safe-bottom)); }

/* Expanded controls get more vertical presence. *//* Small white pill on the flat part of the panel — matches the mockup's
   handle-like curved edge if you want to add one later. Currently unused
   but reserved. */
.recipe-tm6-panel::before {
  content: '';
  position: absolute;
  top: 8px;
  left: 50%;
  width: 40px;
  height: 4px;
  margin-left: -20px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.32);
  pointer-events: none;
}


/* patched: tm6 v4 (seconds + inline edit) */
.recipe-tm6-edit-overlay {
  align-items: center;
  justify-content: center;
  padding: 20px;
  z-index: calc(var(--z-modal) + 60);
}
.recipe-tm6-edit-sheet {
  width: 100%;
  max-width: 400px;
  background: var(--surface);
  border-radius: var(--radius-lg);
  padding: 22px 20px 18px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.35);
  animation: popIn 0.22s var(--ease-ios);
}
.recipe-tm6-edit-title {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--ink);
  margin: 0 0 14px;
}
.recipe-tm6-edit-row { margin-bottom: 12px; }
.recipe-tm6-edit-label {
  display: block;
  font-family: var(--font-ui);
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--muted);
  margin: 0 0 5px;
  letter-spacing: 0.02em;
}
.recipe-tm6-edit-textarea,
.recipe-tm6-edit-sheet input[type="number"] {
  width: 100%;
  box-sizing: border-box;
  background: var(--input-inset-bg);
  border: 1px solid var(--input-inset-border);
  border-radius: var(--radius-12);
  padding: 10px 12px;
  font-family: var(--font-ui);
  font-size: 0.88rem;
  color: var(--ink);
}
.recipe-tm6-edit-textarea { min-height: 62px; resize: vertical; line-height: 1.4; }
.recipe-tm6-edit-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-2-5);
  margin-bottom: var(--space-2-5);
}
.recipe-tm6-edit-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-ui);
  font-size: 0.82rem;
  color: var(--ink);
  padding: 8px 2px 12px;
}
.recipe-tm6-edit-toggle input { width: 18px; height: 18px; accent-color: var(--primary); }
.recipe-tm6-edit-actions { display: flex; gap: var(--space-2-5); }
.recipe-tm6-edit-btn {
  flex: 1 1 0;
  padding: 12px 16px;
  border: none;
  border-radius: var(--radius-pill);
  font-family: var(--font-ui);
  font-size: 0.88rem;
  font-weight: 700;
  cursor: pointer;
  transition: transform 0.24s var(--ease-ios);
}
.recipe-tm6-edit-btn:active { transform: scale(0.96); }
.recipe-tm6-edit-btn.ghost { background: var(--surface-alt); color: var(--ink); }
.recipe-tm6-edit-btn.primary { background: var(--primary); color: var(--primary-ink); }

/* Long-press hint on intro slide */
.recipe-tm6-hint {
  margin-top: var(--space-3-5);
  font-family: var(--font-ui);
  font-size: 0.74rem;
  color: var(--muted);
  opacity: 0.85;
}

/* Slide got touched — subtle press feedback */
.recipe-tm6-slide-step.is-pressing { transform: scale(0.985); transition: transform 0.15s ease; }

/* Play button running state is styled in the ring block above. */


/* patched: tm6 step mc pill + notes */
/* Meta row (step number + MC pill) replaces the bare .recipe-tm6-step-num. */

/* MC pill — same hue family as the icon accent so it reads as metadata,
   not a call-to-action. */
.recipe-tm6-mc-pill {
  font-family: var(--font-ui);
  font-size: 0.62rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--primary);
  background: color-mix(in srgb, var(--primary) 14%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--primary) 24%, transparent);
  padding: 3px 9px;
  border-radius: var(--radius-pill);
  line-height: 1;
  white-space: nowrap;
}

/* Notes line under the step text (doneness cue, scrape-down, etc). */
.recipe-tm6-step-notes {
  font-family: var(--font-ui);
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--muted);
  margin-top: 16px;
  max-width: 340px;
  text-align: center;
  font-style: italic;
}


/* patched: tm6 v5 batch */
/* MC pill now sits at the bottom of the card, under text + notes. */
.recipe-tm6-mc-pill {
  margin-top: 20px;
  display: inline-block;
}

/* Top-left action bar (ingredients book button). */
.recipe-tm6-topbar {
  position: absolute;
  top: calc(14px + var(--safe-top));
  left: 14px;
  z-index: 20;
  display: flex;
  gap: 8px;
}
.recipe-tm6-topbar-btn {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid var(--glass-border);
  background: var(--glass-bg);
  color: var(--ink);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: var(--glass-card-shadow);
  padding: 0;
}
.recipe-tm6-topbar-btn:active { transform: scale(0.92); }

/* Expanded green panel: taller, bigger timer. *//* Bigger reverse glyph. */
/* Ingredients modal. */
.recipe-tm6-ingredients-sheet {
  height: auto;
  max-height: 80dvh;
  display: flex;
  flex-direction: column;
}
#recipe-tm6-ingredients-list {
  overflow-y: auto;
  padding: 6px 20px 24px;
  min-height: 0;
  flex: 1 1 auto;
}
.recipe-tm6-ing-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 4px;
  border-bottom: 1px solid var(--border);
  font-family: var(--font-ui);
}
.recipe-tm6-ing-row:last-child { border-bottom: none; }
.recipe-tm6-ing-name {
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--ink);
  min-width: 0;
  overflow-wrap: anywhere;
}
.recipe-tm6-ing-qty {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
  flex-shrink: 0;
  text-align: right;
}

/* Edit modal: MC select + notes input + insert button. */
.recipe-tm6-edit-select,
.recipe-tm6-edit-input {
  width: 100%;
  box-sizing: border-box;
  background: var(--input-inset-bg);
  border: 1px solid var(--input-inset-border);
  border-radius: var(--radius-12);
  padding: 10px 12px;
  font-family: var(--font-ui);
  font-size: 0.88rem;
  color: var(--ink);
}
.recipe-tm6-edit-select { height: 44px; }


/* patched: tm6 v6 batch */
/* Intro slide hero — full-bleed image with a fade-to-bg blend at the bottom.
   The DDG recipe images are square; contain left whitespace on the sides.
   Cover fills the space cleanly. The shade layer blends into the intro body. */.recipe-tm6-hero-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  max-width: none;
  max-height: none;
}
/* No-image variant: keep the big icon centered with contain */
.recipe-tm6-slide-hero:not(.has-img) .recipe-tm6-step-icon {
  width: 140px;
  height: 140px;
}
.recipe-tm6-slide-hero:not(.has-img) .recipe-tm6-step-icon svg {
  width: 72px;
  height: 72px;
}



/* patched: tm6 icon picker */
.recipe-tm6-edit-icons {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding: 4px 0 8px;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.recipe-tm6-edit-icons::-webkit-scrollbar { display: none; }
.recipe-tm6-edit-icon-btn {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  width: 60px;
  padding: 8px 4px 6px;
  border-radius: var(--radius-12);
  border: 1px solid var(--input-inset-border);
  background: var(--input-inset-bg);
  color: var(--muted);
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease, transform 0.24s var(--ease-ios);
}
.recipe-tm6-edit-icon-btn:active { transform: scale(0.94); }
.recipe-tm6-edit-icon-btn.selected {
  border-color: var(--primary);
  background: color-mix(in srgb, var(--primary) 12%, var(--surface));
  color: var(--primary);
  box-shadow: 0 0 0 1px var(--primary);
}
.recipe-tm6-edit-icon-svg {
  width: 26px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.recipe-tm6-edit-icon-svg svg {
  width: 24px;
  height: 24px;
  display: block;
}
.recipe-tm6-edit-icon-name {
  font-family: var(--font-ui);
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}


/* patched: tm6 no-callout */
/* Suppress the native long-press popup (Save Image / Copy / Look Up) on
   every slide and inside the ingredients sheet. Long-press on a step slide
   opens the inline editor instead.
   touch-action stays pan-x so the swipe track still works. */
.recipe-tm6-track,
.recipe-tm6-slide,
.recipe-tm6-slide *,
.recipe-tm6-ingredients-list,
.recipe-tm6-ingredients-list * {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}
.recipe-tm6-track { touch-action: pan-x; }
/* Slides intentionally get no touch-action override — the track's pan-x
   applies via the intersection rule, so horizontal swipe scrolls the track
   and vertical gesture is cancelable for the sheet's drag-to-close. */

/* Ingredients sheet scrolls vertically as normal. */
.recipe-tm6-ingredients-list { touch-action: pan-y; }


/* patched: tm6 notes textarea */
.recipe-tm6-edit-textarea {
  min-height: 54px;
  line-height: 1.4;
  resize: vertical;
  font-family: var(--font-ui);
}


/* patched: tm6 reorder sheet */
/* Edit Slides panel — same floating-card presentation as the Edit
   step overlay: centred on screen, rounded on all corners, pop-in
   animation. Centring / padding / z-index come from the
   .recipe-tm6-edit-overlay class on the modal in the markup. */
.recipe-tm6-reorder-sheet {
  width: 100%;
  max-width: 400px;
  height: auto;
  /* Match the Edit step sheet's natural height so the two overlays
     present the same footprint. The list scrolls internally past this. */
  max-height: min(100%, 560px);
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.35);
  animation: popIn 0.22s var(--ease-ios);
  display: flex;
  flex-direction: column;
  padding: 0;
  overflow: hidden;
}

/* Navy: product-detail peek strips. The shared 67/33 + 34/66 mix lands
   on two nearly identical darks and the three-sheet cascade disappears.
   These overrides walk the strips down the palette so each reads as a
   separate layer against the #19253D sheet background. */
[data-theme="ocean"] .pd-sheet[data-sheet="1"] {
  background: linear-gradient(180deg, #344D7F 0%, #25375A 100%);
}
[data-theme="ocean"] .pd-sheet[data-sheet="2"] {
  background: linear-gradient(180deg, #25375A 0%, #1F2A48 100%);
}
[data-theme="ocean"] .pd-sheet[data-sheet="3"] {
  background: linear-gradient(180deg, #1F2A48 0%, #19253D 100%);
}
[data-theme="warm"] .pd-sheet[data-sheet="1"] {
  background: linear-gradient(180deg, #E88456 0%, #D46A34 100%);
}
[data-theme="warm"] .pd-sheet[data-sheet="2"] {
  background: linear-gradient(180deg, #D46A34 0%, #BE5A2A 100%);
}
[data-theme="warm"] .pd-sheet[data-sheet="3"] {
  background: linear-gradient(180deg, #BE5A2A 0%, #A84E22 100%);
}
[data-theme="dark"] .pd-sheet[data-sheet="1"] {
  background: linear-gradient(180deg, #8FB53D 0%, #7A9A2F 100%);
}
[data-theme="dark"] .pd-sheet[data-sheet="2"] {
  background: linear-gradient(180deg, #7A9A2F 0%, #6A8A28 100%);
}
[data-theme="dark"] .pd-sheet[data-sheet="3"] {
  background: linear-gradient(180deg, #6A8A28 0%, #55701D 100%);
}
.recipe-tm6-reorder-hint {
  font-family: var(--font-ui);
  font-size: 0.78rem;
  color: var(--muted);
  margin: 10px 20px 4px;
  text-align: center;
}
.recipe-tm6-reorder-list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 8px 16px calc(220px + var(--safe-bottom));
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.recipe-tm6-reorder-row {
  display: grid;
  grid-template-columns: 24px 26px 32px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-2-5);
  padding: 12px;
  background: var(--row-card-bg);
  border: 1px solid var(--row-card-border);
  border-radius: var(--radius-sm);
  box-shadow: var(--row-shadow), var(--row-highlight);
  touch-action: pan-y;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}
.recipe-tm6-reorder-handle {
  color: var(--muted);
  font-size: 1rem;
  letter-spacing: -0.25em;
  line-height: 1;
  text-align: center;
}
.recipe-tm6-reorder-num {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--primary);
  color: var(--primary-ink);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 700;
}
.recipe-tm6-reorder-icon {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--primary) 12%, var(--surface));
  color: var(--primary);
  display: flex;
  align-items: center;
  justify-content: center;
}
.recipe-tm6-reorder-icon svg { width: 18px; height: 18px; }
.recipe-tm6-reorder-text {
  font-family: var(--font-ui);
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--ink);
  overflow-wrap: anywhere;
  min-width: 0;
}
.recipe-tm6-reorder-meta {
  font-family: var(--font-ui);
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
  flex-shrink: 0;
}
.recipe-tm6-reorder-row.dragging {
  z-index: 1000;
  pointer-events: none;
  box-shadow:
    0 18px 40px rgba(0, 0, 0, 0.30),
    0 4px 12px rgba(0, 0, 0, 0.15);
}
.recipe-tm6-reorder-row.dragging .recipe-tm6-reorder-text { color: var(--ink); }
.recipe-tm6-reorder-placeholder {
  visibility: hidden;
  border-radius: var(--radius-sm);
  background: var(--row-card-bg);
  border: 1px dashed var(--primary);
}
body.tm6-reorder-drag-active {
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}
body.tm6-reorder-drag-active .recipe-tm6-reorder-row:not(.dragging) {
  pointer-events: none;
}
.recipe-tm6-reorder-footer {
  display: flex;
  gap: var(--space-3-5);
  align-items: center;
  justify-content: center;
}

/* Running dots pulse in the panel — driven by _tm6PulseDots(). */
.recipe-tm6-dot.running {
  animation: tm6DotPulse 1.2s ease-in-out infinite;
}
@keyframes tm6DotPulse {
  0%, 100% { transform: scale(1);   opacity: 1;   }
  50%      { transform: scale(1.5); opacity: 0.6; }
}


/* ============================================================================
   STICKY FLICKER GUARD (iOS Safari)
   ----------------------------------------------------------------------------
   When two position:sticky siblings stack directly and the top one has
   backdrop-filter, iOS re-samples the backdrop one frame late whenever the
   lower sibling changes size during scroll. The result is a 1-frame flash
   on both elements every time the promo banner toggles its shrink state.

   Two changes below 500px:
   1. Drop backdrop-filter from the sticky search bars and give them an
      opaque surface. This eliminates the cross-element backdrop sampling.
   2. Add backface-visibility: hidden to both sticky containers so iOS keeps
      them on their own compositor layers and stops re-rasterizing on every
      scroll frame. Unlike will-change: transform this does NOT create a new
      containing block, so sticky positioning is unaffected.

   The promo banner's own will-change: opacity is also relaxed — the 0.25s
   opacity transition runs fine without the permanent layer hint, and the
   hint was causing a second flash when the banner resized and the layer
   boundary had to be re-rasterized.
   ============================================================================ */
@media (max-width: 499px) {
  .search-row,
  .consume-search-wrap,
  .consume-search-sticky {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background: transparent;
  }

  .search-row,
  .sticky-filters {
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
  }

  .promo-banner-bg {
    will-change: auto;
  }
}


/* ============================================================================
   FAST-SCROLL FLICKER GUARD (iOS Safari, mobile only)
   ----------------------------------------------------------------------------
   Two sticky siblings + a resizable promo banner put too much work on iOS
   Safari's compositor during a fast scroll. Each frame it has to:
     1. Move the sticky search-row's position
     2. Move the sticky-filters' position
     3. Re-raster the promo banner because contain: paint gave it its own
        buffer and the buffer's viewport position just changed
     4. Blend the overlay's will-change: opacity layer at the new position
   If any of (1)-(4) miss the frame deadline, the whole sticky region shows
   one frame behind — visible as a whole-region flash, worst during fast
   scroll where there is less time per frame.

   Three reductions below 500px:
   - Drop contain: paint on the banner. It is not offering a layout saving
     (the banner has ~3 children) but IS forcing a per-frame re-raster.
   - Relax will-change: opacity on the overlay. The 0.25s fade runs fine
     without a permanent layer hint, and the hint was moving the layer
     boundary on every scroll frame.
   - Explicitly promote both sticky containers via translate3d(0,0,0).
     backface-visibility: hidden only hints; translate3d guarantees it.
     Sticky positioning is unaffected (transforms apply after layout).
   ============================================================================ */
@media (max-width: 499px) {
  .promo-banner {
    contain: none;
  }

  .promo-banner-overlay {
    will-change: auto;
  }

  .search-row,
  .sticky-filters {
    transform: translate3d(0, 0, 0);
  }
}


/* ALERTS-TOUCH-GUARD
   The swipe-to-close handler starts from the hero or the stat-card
   grid. Without touch-action: none, iOS Safari starts its own
   document-level rubber-band around 5px of movement — before the
   JS axis lock (12px) can call preventDefault. The result is that
   the home page behind the panel also moves with the drag.

   touch-action: none on those two elements tells iOS upfront
   not to consider a default gesture — the touch is handed to JS
   from the first pixel. Taps still work (touch-action only gates
   gestures, not click dispatch). The sections scroller is NOT
   covered, so list scrolling is unaffected. */
.alerts-hero,
.alert-stat-grid,
.alert-stat-card {
  touch-action: none;
}


/* PATCH 5: Add Product — hide Save until form is valid */
/* Mirrors body[data-tab="tab-shopping"] #shopping-submit-btn.is-empty
   — a full-width submit button that has no action yet is hidden
   rather than shown disabled. The button's own disabled flag stays
   set (see refreshAddSaveButtonState), so keyboard submit can't fire
   a save either. */
#item-save-btn.is-empty { display: none; }


/* ============================================================
   PATCH 32 — nested pill footer (Discard inside Save)
   Applies to .recipe-pillbar: Recipe Edit, Recipe Preview,
   Manual recipe, Confirm Cook. Save = full-width green glass
   pill (same recipe as Add Product / Add Stock / Bought).
   Discard = separate grey button laid inside its left end.
   ============================================================ */
.recipe-pillbar {
  display: flex;
  align-items: center;
  justify-content: stretch;
  gap: 0;
}

/* Save: full-width green glass pill */
.recipe-pillbar .recipe-preview-pill.recipe-preview-pill-save {
  flex: 1 1 auto;
  width: 100%;
  min-width: 0;
  height: 54px;
  margin: 0;
  /* left padding clears the Discard pill so the label centres in the
     remaining green area */
  padding: 0 0 0 50%;
  border-radius: var(--radius-pill);
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: 0.95rem;
  line-height: 1;
  color: var(--primary-ink);
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--primary) 55%, white 15%) 0%,
    color-mix(in srgb, var(--primary) 70%, transparent) 100%
  );
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  border: 1px solid rgba(255, 255, 255, 0.4);
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.5),
    inset 0 -1px 6px rgba(0, 0, 0, 0.08),
    0 8px 24px rgba(0, 0, 0, 0.15);
}
.recipe-pillbar .recipe-preview-pill.recipe-preview-pill-save:active { transform: scale(0.98); }

/* Discard: grey pill nested inside the left end of Save */
.recipe-pillbar .recipe-preview-pill.recipe-preview-pill-x {
  position: absolute;
  left: calc(var(--space-5) + 6px);
  top: 6px;
  z-index: 2;
  /* PATCH 33: ends at the centre of the bar (bar width = 100% - 2 x side padding) */
  width: calc((100% - 2 * var(--space-5)) / 2 - 6px);
  min-width: 0;
  height: 42px;
  margin: 0;
  padding: 0;
  border-radius: var(--radius-pill);
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: 0.85rem;
  line-height: 1;
  color: var(--ink);
  background: color-mix(in srgb, var(--ink) 12%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--ink) 14%, transparent);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.45),
    0 1px 3px rgba(0, 0, 0, 0.12);
}

/* Cook modal: base rule adds a 4px bottom margin to pills; keep both flush */
#recipe-cook-modal .recipe-pillbar .recipe-preview-pill { margin-bottom: 0; }

/* Disabled: only the green Save fades; Discard stays fully active */
.recipe-pillbar .recipe-preview-pill-save:disabled { opacity: 0.55; box-shadow: none; }
#recipe-cook-modal #recipe-cook-submit:disabled { opacity: 0.55; }

/* Loading: label hides, spinner takes its place (centred in the green area).
   JS only toggles .is-loading + disabled, so no script changes are needed. */
.recipe-pillbar .recipe-preview-pill-save.is-loading,
.recipe-pillbar .recipe-preview-pill-save.is-loading:disabled { color: transparent; opacity: 1; }
#recipe-cook-modal #recipe-cook-submit.is-loading:disabled { opacity: 1; }
.recipe-pillbar .recipe-preview-pill-save.is-loading::after {
  left: 75%;
  border-color: var(--primary-ink);
  border-top-color: transparent;
}
/* Discard is disabled while saving */
.recipe-pillbar:has(.recipe-preview-pill-save.is-loading) .recipe-preview-pill-x {
  pointer-events: none;
  opacity: 0.55;
}

/* PATCH 33 — Regenerate lives in the Preview's top area, on the recipe-name
   row, right-aligned. JS (renderRecipePreviewBody) moves the one button into
   the hero (photo) or the header (no photo). [hidden] still wins because no
   display is set here. */
.recipe-preview-hero > #recipe-preview-regenerate {
  position: absolute;
  right: 20px;
  bottom: 30px;
  z-index: 3;
  width: 44px;
  height: 44px;
  background: rgba(0, 0, 0, 0.38);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.35);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.25);
}
/* leave room for the button so a long name never runs under it */
.recipe-preview-hero:has(> #recipe-preview-regenerate:not([hidden])) .recipe-preview-hero-title {
  right: 76px;
}
#recipe-preview-header > #recipe-preview-regenerate {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  margin-left: 12px;
}

/* PATCH 33 applied — half/half pill + Regenerate in preview hero */


/* ============================================================
   PATCH 34 — uniform button system + Cook This | TM6 nested pill
   One set of tokens for every green glass button (3D highlights,
   shadow stack, press animation, disabled look) and one for the
   grey nested buttons (Discard, TM6). Paint / interaction only.
   ============================================================ */
:root {
  --btn-press-scale: 0.96;
  --btn-transition:
    transform 0.24s var(--ease-ios),
    background 0.15s ease,
    color 0.15s ease,
    border-color 0.15s ease,
    box-shadow 0.2s var(--ease-ios),
    opacity 0.15s ease;

  --btn-green-bg: linear-gradient(
    180deg,
    color-mix(in srgb, var(--primary) 55%, white 15%) 0%,
    color-mix(in srgb, var(--primary) 70%, transparent) 100%
  );
  --btn-green-border: 1px solid rgba(255, 255, 255, 0.4);
  --btn-green-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.5),
    inset 0 -1px 6px rgba(0, 0, 0, 0.08),
    0 8px 24px rgba(0, 0, 0, 0.15);
  --btn-green-shadow-pressed:
    inset 0 1px 1px rgba(255, 255, 255, 0.35),
    inset 0 -1px 6px rgba(0, 0, 0, 0.12),
    0 3px 10px rgba(0, 0, 0, 0.14);

  --btn-grey-bg: color-mix(in srgb, var(--ink) 12%, var(--surface));
  --btn-grey-border: 1px solid color-mix(in srgb, var(--ink) 14%, transparent);
  --btn-grey-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.45),
    0 1px 3px rgba(0, 0, 0, 0.12);
  --btn-grey-shadow-pressed:
    inset 0 1px 2px rgba(0, 0, 0, 0.10),
    0 0 1px rgba(0, 0, 0, 0.10);
}

/* ---- Green glass family ---- */
.consume-submit-btn,
#item-save-btn,
.bought-submit-btn,
.recipe-cook-btn,
.pd-add-stock-btn,
.recipe-pillbar .recipe-preview-pill.recipe-preview-pill-save {
  color: var(--primary-ink);
  font-weight: 700;
  background: var(--btn-green-bg);
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  border: var(--btn-green-border);
  box-shadow: var(--btn-green-shadow);
  transition: var(--btn-transition);
}
#item-save-btn:active,
.bought-submit-btn:active,
.recipe-cook-btn:active,
.pd-add-stock-btn:active,
.recipe-pillbar .recipe-preview-pill.recipe-preview-pill-save:active {
  transform: scale(var(--btn-press-scale));
}
.consume-submit-btn:active { transform: translateX(-50%) scale(var(--btn-press-scale)); }
.consume-submit-btn:active:not(:disabled),
#item-save-btn:active:not(:disabled),
.bought-submit-btn:active:not(:disabled),
.recipe-cook-btn:active:not(:disabled),
.pd-add-stock-btn:active:not(:disabled),
.recipe-pillbar .recipe-preview-pill.recipe-preview-pill-save:active:not(:disabled) {
  box-shadow: var(--btn-green-shadow-pressed);
}
/* Disabled: same dim + same shadow everywhere (loading keeps full opacity) */
.consume-submit-btn:disabled:not(.is-loading),
#item-save-btn:disabled:not(.is-loading),
.bought-submit-btn:disabled:not(.is-loading),
.recipe-cook-btn:disabled:not(.is-loading),
.pd-add-stock-btn:disabled:not(.is-loading),
.recipe-pillbar .recipe-preview-pill.recipe-preview-pill-save:disabled:not(.is-loading),
#recipe-cook-modal #recipe-cook-submit:disabled:not(.is-loading) {
  opacity: 0.6;
  box-shadow: var(--btn-green-shadow);
}

/* ---- Grey nested family (Discard, TM6) ---- */
.recipe-pillbar .recipe-preview-pill.recipe-preview-pill-x,
#recipe-cook-footer .recipe-tm6-btn {
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: 0.85rem;
  letter-spacing: 0;
  line-height: 1;
  color: var(--ink);
  background: var(--btn-grey-bg);
  border: var(--btn-grey-border);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: var(--btn-grey-shadow);
  transition: var(--btn-transition);
}
.recipe-pillbar .recipe-preview-pill.recipe-preview-pill-x:active,
#recipe-cook-footer .recipe-tm6-btn:active {
  transform: scale(var(--btn-press-scale));
  box-shadow: var(--btn-grey-shadow-pressed);
}

/* ---- Recipe detail footer: Cook This (full-width green) | TM6 (grey, 1/4) ---- */
#recipe-cook-footer {
  gap: 0;
  align-items: center;
}
#recipe-cook-footer .recipe-cook-btn {
  flex: 1 1 auto;
  width: 100%;
  max-width: none;
  margin: 0;
  height: 54px;
  min-height: 54px;
  /* right padding = 25% of the bar, so the label centres in the left 3/4 */
  padding: 0 25% 0 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-ui);
  font-size: 0.95rem;
  line-height: 1;
}
#recipe-cook-footer .recipe-tm6-btn {
  position: absolute;
  right: 26px;               /* 20px footer padding + 6px inset */
  top: 6px;
  z-index: 2;
  /* one quarter of the bar (bar = footer width - 2 x 20px), minus the inset */
  width: calc((100% - 40px) / 4 - 6px);
  min-width: 0;
  height: 42px;
  min-height: 0;
  margin: 0;
  padding: 0;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-pill);
}
/* PATCH 34 applied */


/* ============================================================
   PATCH 35 — TM6: "Edit Slide" badge, Edit Slides panel
   ============================================================ */
/* Small tinted badge button (shared by Edit Slide / + Add Slide) */
.recipe-tm6-badge {
  flex-shrink: 0;
  height: 28px;
  padding: 0 12px;
  border-radius: var(--radius-pill);
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: 0.02em;
  line-height: 1;
  white-space: nowrap;
  color: var(--primary);
  background: color-mix(in srgb, var(--primary) 12%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--primary) 30%, transparent);
  box-shadow: var(--btn-grey-shadow, inset 0 1px 1px rgba(255,255,255,0.45), 0 1px 3px rgba(0,0,0,0.12));
  cursor: pointer;
  transition: var(--btn-transition, transform 0.24s var(--ease-ios));
}
.recipe-tm6-badge:active {
  transform: scale(var(--btn-press-scale, 0.96));
  box-shadow: var(--btn-grey-shadow-pressed, inset 0 1px 2px rgba(0,0,0,0.10));
}

/* Edit overlay: badge pinned top-right, inline with the title */
.recipe-tm6-edit-sheet { position: relative; }
.recipe-tm6-edit-badge {
  position: absolute;
  top: 20px;
  right: 20px;
  z-index: 2;
}
.recipe-tm6-edit-sheet .recipe-tm6-edit-title { padding-right: 100px; }

/* Panel header: title left, Add Slide badge right */
#recipe-tm6-reorder-modal .modal-header { gap: 12px; }

/* Rows: extra column for the ✕ (meta cell is always rendered now) */
.recipe-tm6-reorder-row {
  grid-template-columns: 24px 26px 32px minmax(0, 1fr) auto 30px;
}
.recipe-tm6-reorder-del {
  width: 30px;
  height: 30px;
  padding: 0;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 10%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent);
  box-shadow: var(--btn-grey-shadow, inset 0 1px 1px rgba(255,255,255,0.45), 0 1px 3px rgba(0,0,0,0.12));
  cursor: pointer;
  transition: var(--btn-transition, transform 0.24s var(--ease-ios));
}
.recipe-tm6-reorder-del svg { display: block; pointer-events: none; }
.recipe-tm6-reorder-del:active {
  transform: scale(var(--btn-press-scale, 0.96));
  box-shadow: var(--btn-grey-shadow-pressed, inset 0 1px 2px rgba(0,0,0,0.10));
}

/* Newly added slide flashes once */
.recipe-tm6-reorder-row.is-new { animation: tm6RowFlash 1.2s ease-out; }
@keyframes tm6RowFlash {
  0%   { box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 55%, transparent); }
  100% { box-shadow: var(--row-shadow), var(--row-highlight); }
}


/* ============================================================
   PATCH 37 — TM6: collapsible panel without the drift
   The panel is absolutely positioned at the sheet's bottom edge
   (see the "tm6 phase b" block for its base rule), so its
   collapse / expand no longer reflows the track. Slides keep a
   stable size mid-swipe. Track / snap rules below are unchanged
   from PATCH 36.
   ============================================================ */

/* Track: instant programmatic scroll, strict one-slide snapping */
.recipe-tm6-track {
  scroll-behavior: auto;
  overscroll-behavior: contain;
}
.recipe-tm6-slide {
  scroll-snap-stop: always;
  overscroll-behavior: contain;
}


/* ============================================================
   Cartoon Images settings + overlay
   ============================================================ */
.settings-section-tappable {
  cursor: pointer;
  transition: transform 0.2s var(--ease-ios), box-shadow 0.2s var(--ease-ios);
}
.settings-section-tappable:active { transform: scale(0.985); }
.settings-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.settings-section-chevron {
  flex-shrink: 0;
  color: var(--muted);
  transition: transform 0.2s var(--ease-ios);
}
.settings-section-tappable:active .settings-section-chevron { transform: translateX(2px); }

.section-divider {
  height: 1px;
  background: var(--border);
  margin: 20px 0;
  opacity: 0.7;
}

.toggle-switch {
  position: relative;
  display: inline-block;
  width: 48px;
  height: 28px;
  flex-shrink: 0;
}
.toggle-switch input { opacity: 0; width: 0; height: 0; position: absolute; }
.toggle-track {
  position: absolute;
  inset: 0;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 14px;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.toggle-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 22px;
  height: 22px;
  background: #fff;
  border-radius: 50%;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
  transition: transform 0.22s var(--ease-ios);
}
.toggle-switch input:checked + .toggle-track {
  background: var(--primary);
  border-color: var(--primary);
}
.toggle-switch input:checked + .toggle-track .toggle-thumb { transform: translateX(20px); }


/* ============================================================
   Cartoon Images overlay — matches recipe-url prompt design
   ============================================================ */
.cartoon-prompt-sheet {
  min-height: 480px;
  position: relative;
}

/* Small X in the top-right corner of the floating card */
.cartoon-close-btn {
  position: absolute;
  top: 16px;
  right: 16px;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: none;
  background: var(--surface-2);
  color: var(--muted);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 2;
  padding: 0;
  transition: background 0.15s ease, color 0.15s ease, transform 0.24s var(--ease-ios);
}
.cartoon-close-btn:active {
  transform: scale(0.92);
  background: var(--pill-active-bg);
  color: var(--primary);
}

/* Toggle row inside the centered card */
.cartoon-toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 16px;
  background: var(--surface-2);
  border-radius: var(--radius-12);
  margin-bottom: 8px;
}
.cartoon-toggle-label {
  font-family: var(--font-ui);
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--ink);
}
.cartoon-toggle-hint {
  font-family: var(--font-ui);
  font-size: 0.78rem;
  color: var(--muted);
  margin: 0 4px 16px;
  text-align: left;
}
.cartoon-status {
  font-family: var(--font-ui);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--primary);
  text-align: center;
  padding: 8px 4px 12px;
}
.cartoon-tip {
  font-family: var(--font-ui);
  font-size: 0.82rem;
  color: var(--muted);
  line-height: 1.45;
  text-align: center;
  margin: 0 0 4px;
}
.cartoon-action-status {
  font-family: var(--font-ui);
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--primary);
  text-align: center;
  min-height: 1em;
  margin-top: 8px;
  overflow-wrap: anywhere;
}


/* ============================================================
   Typography picker — live preview block + 4 cartoon fonts
   ============================================================ */

/* Sample block above the picker. Renders in whatever font is currently
   applied, so tapping a swatch updates both the whole app and this
   block. Uses --font-display (which applyAppFont sets to the same
   value as --font-ui) so it inherits the user's choice automatically. */
.font-preview-block {
  margin: 12px 0 16px;
  padding: 16px;
  background: var(--surface-2);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
}
.font-preview-lg {
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.2;
  color: var(--ink);
  margin: 0 0 8px;
}
.font-preview-md {
  font-family: var(--font-ui);
  font-size: 0.95rem;
  font-weight: 500;
  line-height: 1.45;
  color: var(--ink);
  opacity: 0.85;
  margin: 0 0 10px;
}
.font-preview-sm {
  font-family: var(--font-ui);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--muted);
  margin: 0;
}

/* New swatch previews — same treatment as the existing four. */
.font-swatch[data-font="fredoka"] .font-swatch-preview {
  font-family: 'Fredoka', -apple-system, BlinkMacSystemFont, sans-serif;
}
.font-swatch[data-font="baloo"] .font-swatch-preview {
  font-family: 'Baloo 2', -apple-system, BlinkMacSystemFont, sans-serif;
}
.font-swatch[data-font="sniglet"] .font-swatch-preview {
  font-family: 'Sniglet', 'Comic Sans MS', cursive;
}
.font-swatch[data-font="grandstander"] .font-swatch-preview {
  font-family: 'Grandstander', 'Comic Sans MS', cursive;
}


/* ============================================================
   Collapsible settings sections
   Each .settings-section.settings-collapsible renders its header
   as a tappable button and its body inside .settings-collapsible-body.
   Body starts hidden; the header toggles aria-expanded + the hidden
   attribute together so assistive tech sees the state.
   ============================================================ */
.settings-collapsible-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 0;
  margin: 0;
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  color: inherit;
  font: inherit;
  -webkit-tap-highlight-color: transparent;
}
.settings-collapsible-header .settings-section-title { margin: 0; }
.settings-collapsible-chevron {
  flex-shrink: 0;
  color: var(--muted);
  transition: transform 0.24s var(--ease-ios);
}
.settings-collapsible-header[aria-expanded="true"] .settings-collapsible-chevron {
  transform: rotate(180deg);
}
.settings-collapsible-header:active .settings-collapsible-chevron {
  transform: translateY(1px);
}
.settings-collapsible-header[aria-expanded="true"]:active .settings-collapsible-chevron {
  transform: rotate(180deg) translateY(-1px);
}
.settings-collapsible-body {
  margin-top: 12px;
  animation: settingsBodyIn 0.22s var(--ease-ios);
}
.settings-collapsible-body[hidden] { display: none; }
@keyframes settingsBodyIn {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}

/* Cartoon theme: the search area (pill + dropdown) becomes a
   stacking context at z-index 100, so the results list paints
   above .consume-body. Only tabs with a search pill have
   .consume-hero-search (Consume / Shopping / Recipes); the rule
   is inert on Settings. */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .consume-hero-search {
  position: relative;
  z-index: 100;
  isolation: isolate;
}
/* Belt-and-braces: raise the dropdown itself above the body. */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .consume-search-results {
  z-index: 200;
}


/* ============================================================
   Per-product cartoon icon block (Product Detail → Edit sheet)
   Two secondary buttons that build a one-product prompt or accept
   a single-icon upload. Sits under the stock-on-hand list as a
   distinct action pair.
   ============================================================ */
.pd-cartoon-status {
  min-height: 1em;
  margin: 0 0 10px;
  line-height: 1.4;
}
.pd-cartoon-actions {
  display: grid;
  grid-template-columns: 1fr;
  gap: 8px;
  margin-bottom: 8px;
}
/* Destructive single action: red text, red border, no fill. */
.pd-cartoon-actions .pd-cartoon-delete-btn {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 40%, transparent);
}
.pd-cartoon-actions .pd-cartoon-delete-btn:active:not(:disabled) {
  background: color-mix(in srgb, var(--danger) 12%, transparent);
}
.pd-cartoon-actions .pd-cartoon-delete-btn:disabled {
  opacity: 0.4;
  color: var(--muted);
  border-color: var(--border);
}
.pd-cartoon-actions .btn {
  width: 100%;
  min-height: 40px;
  padding: 10px 12px;
  font-family: var(--font-ui);
  font-size: 0.82rem;
  font-weight: 700;
}
.pd-cartoon-actions .btn:disabled { opacity: 0.55; }

/* ============================================================
   PATCH 65 — Cartoon buttons
   Chunky marker-outline + hard-offset shadow on every button
   surface in the cartoon theme. SVG icons added via ::before
   + mask-image on primary text-only CTAs; icons inherit
   currentColor from the button so they recolour automatically.
   Applies ONLY in :is([data-theme="cartoon"], [data-theme="cbtl"]).
   ============================================================ */

/* ---- Icon asset library (mask shape; colour comes from the button) ---- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) {
  --cartoon-ico-plus:      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E");
  --cartoon-ico-check:     url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
  --cartoon-ico-x:         url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 6 6 18M6 6l12 12'/%3E%3C/svg%3E");
  --cartoon-ico-clipboard: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='8' y='3' width='8' height='4' rx='1'/%3E%3Cpath d='M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2'/%3E%3C/svg%3E");
  --cartoon-ico-upload:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4'/%3E%3Cpath d='M17 8l-5-5-5 5'/%3E%3Cpath d='M12 3v12'/%3E%3C/svg%3E");
  --cartoon-ico-flame:     url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 21c4 0 7-3 7-7 0-3-2-5-3.5-6.5S13 4.5 12 2c-1 2.5-2.5 4-4 5.5S5 11 5 14c0 4 3 7 7 7z'/%3E%3C/svg%3E");
}

/* ---- ::before base for every icon-bearing button ---- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) #item-save-btn::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #pd-add-stock-btn::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #bought-submit-btn::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #consume-submit-btn::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #pd-save-btn::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-pillbar .recipe-preview-pill-save::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-pillbar .recipe-preview-pill-x::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #cartoon-copy-btn::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #cartoon-upload-btn::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #pd-cartoon-prompt::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #pd-cartoon-upload::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-cook-btn::before {
  content: '';
  display: inline-block;
  width: 1.05em;
  height: 1.05em;
  flex-shrink: 0;
  background: currentColor;
  -webkit-mask-position: center;   mask-position: center;
  -webkit-mask-size: contain;      mask-size: contain;
  -webkit-mask-repeat: no-repeat;  mask-repeat: no-repeat;
  vertical-align: -0.15em;
}

/* ---- Icon assignment (which icon each button gets) ---- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) #item-save-btn::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #pd-add-stock-btn::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #bought-submit-btn::before {
  -webkit-mask-image: var(--cartoon-ico-plus); mask-image: var(--cartoon-ico-plus);
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) #consume-submit-btn::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #pd-save-btn::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-pillbar .recipe-preview-pill-save::before {
  -webkit-mask-image: var(--cartoon-ico-check); mask-image: var(--cartoon-ico-check);
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-pillbar .recipe-preview-pill-x::before {
  -webkit-mask-image: var(--cartoon-ico-x); mask-image: var(--cartoon-ico-x);
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) #cartoon-copy-btn::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #pd-cartoon-prompt::before {
  -webkit-mask-image: var(--cartoon-ico-clipboard); mask-image: var(--cartoon-ico-clipboard);
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) #cartoon-upload-btn::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #pd-cartoon-upload::before {
  -webkit-mask-image: var(--cartoon-ico-upload); mask-image: var(--cartoon-ico-upload);
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-cook-btn::before {
  -webkit-mask-image: var(--cartoon-ico-flame); mask-image: var(--cartoon-ico-flame);
}

/* ---- Flex layout so the icon sits inline with the text ---- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) #item-save-btn,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #pd-add-stock-btn,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #bought-submit-btn,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #pd-save-btn,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-pillbar .recipe-preview-pill-save,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-pillbar .recipe-preview-pill-x,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #cartoon-copy-btn,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #cartoon-upload-btn,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #pd-cartoon-prompt,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #pd-cartoon-upload,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-cook-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
/* The fixed-centered Consume / Shopping submit pill needs display: flex
   with higher specificity than body[data-tab="…"] #…-submit-btn. */
:is([data-theme="cartoon"], [data-theme="cbtl"]) body[data-tab="tab-consume"] #consume-submit-btn,
:is([data-theme="cartoon"], [data-theme="cbtl"]) body[data-tab="tab-shopping"] #shopping-submit-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

/* Hide the icon while a loading spinner is showing */
:is([data-theme="cartoon"], [data-theme="cbtl"]) #recipe-cook-submit.is-loading::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-preview-pill-save.is-loading::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #pd-save-btn.is-loading::before {
  display: none;
}

/* ---- Chunky outline + hard-offset shadow: primary green pills ---- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .btn,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .consume-submit-btn,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #item-save-btn,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .bought-submit-btn,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-cook-btn,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .pd-add-stock-btn,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-pillbar .recipe-preview-pill-save {
  background: var(--primary);
  color: var(--primary-ink);
  border: 2.5px solid var(--ink);
  box-shadow: 3px 3px 0 var(--ink);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  font-weight: 800;
  letter-spacing: 0.005em;
  transition:
    transform 0.10s ease-out,
    box-shadow 0.10s ease-out,
    background 0.15s ease,
    color 0.15s ease;
}

/* ---- Secondary / grey nested pills keep the flat cream fill ---- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .btn.secondary,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-pillbar .recipe-preview-pill-x {
  background: var(--surface-2);
  color: var(--ink);
}

:is([data-theme="cartoon"], [data-theme="cbtl"]) .btn.danger {
  background: var(--danger);
  color: #fff;
}

/* ---- Press-into-shadow animation ---- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .btn:active:not(:disabled),
:is([data-theme="cartoon"], [data-theme="cbtl"]) #item-save-btn:active:not(:disabled),
:is([data-theme="cartoon"], [data-theme="cbtl"]) .bought-submit-btn:active:not(:disabled),
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-cook-btn:active:not(:disabled),
:is([data-theme="cartoon"], [data-theme="cbtl"]) .pd-add-stock-btn:active:not(:disabled),
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-pillbar .recipe-preview-pill-save:active:not(:disabled),
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-pillbar .recipe-preview-pill-x:active:not(:disabled) {
  transform: translate(2px, 2px);
  box-shadow: 1px 1px 0 var(--ink);
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) .consume-submit-btn:active:not(:disabled) {
  transform: translateX(-50%) translate(2px, 2px);
  box-shadow: 1px 1px 0 var(--ink);
}

/* ---- Icon-only buttons: solid cream, chunky border + small offset ---- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .icon-btn,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .add-btn-inline,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .consume-hero-select,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-tm6-topbar-btn,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .cartoon-close-btn {
  background: var(--surface);
  color: var(--ink);
  border: 2px solid var(--ink);
  box-shadow: 2px 2px 0 var(--ink);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) .icon-btn:active,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .add-btn-inline:active,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .consume-hero-select:active,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-tm6-topbar-btn:active,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .cartoon-close-btn:active {
  transform: translate(1.5px, 1.5px);
  box-shadow: 0.5px 0.5px 0 var(--ink);
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) .add-btn-inline:active {
  transform: translate(2px, 2px);
  box-shadow: 0 0 0 var(--ink);
}

/* On-photo variant keeps a whisper of translucency so the button still
   reads as sitting on the image, but gets the chunky treatment. */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .icon-btn.on-photo {
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  color: var(--ink);
  border: 2px solid var(--ink);
  box-shadow: 2px 2px 0 var(--ink);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) .icon-btn.on-photo:active {
  transform: translate(1.5px, 1.5px);
  box-shadow: 0.5px 0.5px 0 var(--ink);
}

/* ---- Recipe "Add a recipe" action sheet ---- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-add-option {
  border: 2px solid var(--ink);
  box-shadow: 3px 3px 0 var(--ink);
  background: var(--surface);
  transition: transform 0.10s ease-out, box-shadow 0.10s ease-out;
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-add-option:active {
  transform: translate(2px, 2px);
  box-shadow: 1px 1px 0 var(--ink);
}

/* ---- Recipe-edit "+" add buttons ---- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-edit-add-btn {
  border: 2px solid var(--ink);
  box-shadow: 2px 2px 0 var(--ink);
  background: var(--surface-2);
  color: var(--ink);
  transition: transform 0.10s ease-out, box-shadow 0.10s ease-out;
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-edit-add-btn:active {
  transform: translate(1.5px, 1.5px);
  box-shadow: 0.5px 0.5px 0 var(--ink);
}

/* ---- Small flat secondary buttons: slightly chunkier text ---- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .btn.small { font-weight: 800; }

/* ---- Settings collapsible chevron stands out a touch more ---- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .settings-section-chevron {
  color: var(--primary-2);
  stroke-width: 2.6;
}


/* ============================================================================
   SETTINGS TAB — pinned hero + scrollable body
   Matches Consume / Shopping / Recipes. The hero stays fixed; only the
   white body below it scrolls. Scoped to [data-tab="tab-settings"] so
   nothing else changes.
   ============================================================================ */
#tab-settings .consume-header {
  position: relative;
  padding-bottom: 32px;
}
#tab-settings .consume-body {
  margin-top: -20px;
  z-index: 3;
  background: var(--bg);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}

html:has(body[data-tab="tab-settings"]) {
  overflow: hidden;
  height: 100dvh;
}
body[data-tab="tab-settings"] {
  padding-bottom: 0;
  height: 100dvh;
  overflow: hidden;
}
body[data-tab="tab-settings"] main {
  height: 100%;
  min-height: 0;
  padding-bottom: 0;
  display: flex;
  flex-direction: column;
}
body[data-tab="tab-settings"] #tab-settings {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  animation: none;
}
body[data-tab="tab-settings"] .consume-header { flex: 0 0 auto; }
body[data-tab="tab-settings"] .consume-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  padding-bottom: calc(110px + var(--safe-bottom));
}


/* ============================================================================
   PATCH 68 — Cartoon theme polish
   All rules scoped to :is([data-theme="cartoon"], [data-theme="cbtl"]) only.
   Undo notes:
     - Block 6   (hand-drawn borders): remove the first big rule.
     - Block 10  (wobble radii):       remove the "wobble corner radii" rule.
     - Block 11  (illustrations):      remove the empty-state illustration rules.
   ============================================================================ */

/* ---- Shared cartoon-only SVG assets (data URLs, single-quoted attrs) ---- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) {
  --cartoon-doodle-star: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12 2.5 L13.6 9.8 L21 11.8 L13.6 13.8 L12 21.2 L10.4 13.8 L3 11.8 L10.4 9.8 Z' fill='%238CBD42' stroke='%232E3A1F' stroke-width='1.6' stroke-linejoin='round'/></svg>");
  --cartoon-squiggle: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 6' preserveAspectRatio='none'><path d='M0 3 Q 5 0 10 3 T 20 3 T 30 3 T 40 3' fill='none' stroke='%23C4B88E' stroke-width='1.6' stroke-linecap='round'/></svg>");
  --cartoon-brush-circle: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><path d='M50 5 Q 68 3, 82 12 Q 96 22, 96 44 Q 96 68, 82 82 Q 68 96, 46 96 Q 22 96, 10 78 Q 0 60, 5 40 Q 10 18, 28 10 Q 40 4, 50 5 Z' fill='black'/></svg>");
  --cartoon-brush-ring: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><path d='M50 5 Q 68 3, 82 12 Q 96 22, 96 44 Q 96 68, 82 82 Q 68 96, 46 96 Q 22 96, 10 78 Q 0 60, 5 40 Q 10 18, 28 10 Q 40 4, 50 5 Z' fill='none' stroke='black' stroke-width='6' stroke-linejoin='round'/></svg>");
  --cartoon-brush-pill: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 30' preserveAspectRatio='none'><path d='M6 5 Q 20 1, 38 4 Q 58 0, 78 4 Q 92 2, 96 7 Q 100 12, 97 18 Q 94 25, 78 27 Q 58 30, 40 27 Q 22 29, 10 25 Q 1 22, 4 14 Q 5 8, 6 5 Z' fill='black'/></svg>");
  --cartoon-illus-basket: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'><path d='M40 90 L160 90 L145 170 Q142 180 132 180 L68 180 Q58 180 55 170 Z' fill='%238CBD42' stroke='%232E3A1F' stroke-width='5' stroke-linejoin='round'/><path d='M70 90 Q70 40 100 40 Q130 40 130 90' fill='none' stroke='%232E3A1F' stroke-width='5' stroke-linecap='round'/><circle cx='85' cy='125' r='5.5' fill='%232E3A1F'/><circle cx='115' cy='125' r='5.5' fill='%232E3A1F'/><path d='M82 145 Q100 158 118 145' fill='none' stroke='%232E3A1F' stroke-width='4.5' stroke-linecap='round'/><path d='M60 108 L140 108 M58 132 L142 132 M56 156 L144 156' stroke='%232E3A1F' stroke-width='2' opacity='0.35'/></svg>");
  --cartoon-illus-bag: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cdefs%3E%3Cfilter id='grain'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' seed='7'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0.99 0 0 0 0 0.96 0 0 0 0 0.88 0.18 0 0 0 -0.05'/%3E%3CfeComposite in2='SourceGraphic' operator='in'/%3E%3CfeBlend in2='SourceGraphic' mode='overlay'/%3E%3C/filter%3E%3C/defs%3E%3Cg transform='translate(12 12)' filter='url(%23grain)'%3E%3Cpath d='M65 82 Q68 25 100 22 Q132 25 135 82' fill='none' stroke='%23B07A4A' stroke-width='7' stroke-linecap='round'/%3E%3Cpath d='M78 84 Q80 38 100 35 Q120 38 122 84' fill='none' stroke='%23B07A4A' stroke-width='4' stroke-linecap='round'/%3E%3Cpath d='M42 82 L158 82 L152 178 Q150 188 142 188 L58 188 Q50 188 48 178 Z' fill='%23E89A4C'/%3E%3Cpath d='M42 82 L158 82 L156 100 Q100 106 44 100 Z' fill='%239B72C7'/%3E%3C/g%3E%3Cpath d='M65 82 Q68 25 100 22 Q132 25 135 82' fill='none' stroke='%232E3A1F' stroke-width='3.5' stroke-linecap='round'/%3E%3Cpath d='M78 84 Q80 38 100 35 Q120 38 122 84' fill='none' stroke='%232E3A1F' stroke-width='3' stroke-linecap='round'/%3E%3Cpath d='M42 82 L158 82 L152 178 Q150 188 142 188 L58 188 Q50 188 48 178 Z' fill='none' stroke='%232E3A1F' stroke-width='3.5' stroke-linejoin='round'/%3E%3Cpath d='M42 82 L158 82' stroke='%232E3A1F' stroke-width='3.5' stroke-linecap='round'/%3E%3Cpath d='M50 100 Q100 96 150 100' fill='none' stroke='%232E3A1F' stroke-width='1.5' opacity='0.4'/%3E%3Ccircle cx='85' cy='135' r='4' fill='%232E3A1F'/%3E%3Ccircle cx='115' cy='135' r='4' fill='%232E3A1F'/%3E%3Cpath d='M88 153 Q100 163 112 153' fill='none' stroke='%232E3A1F' stroke-width='3' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* ---- 2. Cartoon toast ------------------------------------------------ */
:is([data-theme="cartoon"], [data-theme="cbtl"]) #toast {
  background: var(--surface);
  color: var(--ink);
  border: 2.5px solid var(--ink);
  box-shadow: 3px 3px 0 var(--ink);
  border-radius: 14px;
  font-weight: 700;
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) #toast .toast-icon svg { filter: none; }

/* ---- 3. Section header doodles --------------------------------------- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .section-header h2::after,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .settings-section-title::after {
  content: '';
  display: inline-block;
  width: 16px;
  height: 16px;
  margin-left: 7px;
  vertical-align: -3px;
  background: var(--cartoon-doodle-star) center / contain no-repeat;
}
/* The Cartoon Images header has a chevron on the right — skip the
   doodle there so the row stays clean. */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .settings-section-header .settings-section-title::after { display: none; }

/* ---- 4. Wavy section dividers ---------------------------------------- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .or-divider::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .or-divider::after,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .bought-divider::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .bought-divider::after {
  background: var(--cartoon-squiggle) repeat-x left center;
  background-size: 40px 6px;
  height: 6px;
  border: none;
}

/* ---- 6. Hand-drawn card borders (UNDOABLE) --------------------------- */
/* Bump hairline borders to 2px and darken them. The sticker offset
   shadow from earlier patches stays. To undo: delete this rule. */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .grid-item,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .consume-list .consume-row-content,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .settings-section,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-card-wrap,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-ai-row-content,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .expiring-item-content,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .bought-row-content {
  border-width: 2px;
  border-color: rgba(46, 58, 31, 0.18);
}

/* ---- 7. Marker-style checkbox ---------------------------------------- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .shop-tick {
  width: 26px;
  height: 26px;
  border-radius: 8px;
  border: 2.5px solid var(--ink);
  background: var(--surface);
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) .shop-tick.checked {
  background: var(--primary);
  border-color: var(--ink);
  box-shadow: 1.5px 1.5px 0 var(--ink);
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) .shop-tick.checked::after {
  left: 6px;
  top: 1px;
  width: 6px;
  height: 12px;
  border-width: 0 3px 3px 0;
  border-color: #fff;
}

/* ---- 9. Sparkle animation (JS-driven; CSS defines the visuals) ------ */
.cartoon-sparkle {
  position: fixed;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  pointer-events: none;
  z-index: 9999;
  transform: translate(-50%, -50%);
  box-shadow: 0 0 0 1.5px rgba(46, 58, 31, 0.35);
  animation: cartoonSparkleFly 0.7s cubic-bezier(0.22, 0.9, 0.34, 1) forwards;
}
@keyframes cartoonSparkleFly {
  0%   { transform: translate(-50%, -50%) scale(0.6); opacity: 0; }
  20%  { transform: translate(-50%, -50%) scale(1.15); opacity: 1; }
  100% { transform: translate(calc(-50% + var(--sx, 0px)), calc(-50% + var(--sy, 0px))) scale(0.2); opacity: 0; }
}

/* ---- 10. Wobble corner radii (UNDOABLE) ------------------------------ */
/* Asymmetric radii so cards read as hand-drawn instead of machine-
   rounded. To undo: delete this rule. */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .grid-item { border-radius: 22px 18px 24px 20px; }
:is([data-theme="cartoon"], [data-theme="cbtl"]) .consume-list .consume-row-content { border-radius: 18px 22px 16px 20px; }
:is([data-theme="cartoon"], [data-theme="cbtl"]) .settings-section { border-radius: 22px 18px 24px 20px; }
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-card-wrap { border-radius: 24px 20px 22px 18px; }
:is([data-theme="cartoon"], [data-theme="cbtl"]) .banner-categories .category-chip .cc-emoji { border-radius: 50% 50% 48% 52%; }
/* .item-thumb-wrap was REMOVED from the wobble-radius group in
   PATCH 69 — this class wraps every product image, so making it
   circular clipped the image content on the Home grid, recipe grid,
   and every row thumbnail. */

/* ---- 11. Custom empty-state illustrations (UNDOABLE) ----------------- */
/* Hide the stroke-SVG and paint a bespoke cartoon illustration. To
   undo: delete this whole block. */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .consume-empty-illus { display: none; }
:is([data-theme="cartoon"], [data-theme="cbtl"]) .consume-empty::before {
  content: '';
  display: block;
  width: 140px;
  height: 140px;
  margin: 0 auto 14px;
  background: var(--cartoon-illus-basket) center / contain no-repeat;
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) #tab-shopping .consume-empty::before {
  background-image: var(--cartoon-illus-bag);
}

/* ---- A. Brush-stroke on selected filter chips ----------------------- */
/* Category chips: rough brush circle paints behind the emoji. */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .banner-categories .category-chip.active .cc-emoji {
  background: transparent;
  border-color: transparent;
  box-shadow: none;
  position: relative;
  isolation: isolate;
  /* Bypass the base `.cc-emoji { overflow: hidden }` — the ::before and
     ::after pseudos sit at inset: -2px, outside the parent box, and get
     clipped without this. */
  overflow: visible;
}
/* Dark ink RING, aligned. Uses the stroke-only mask so only the outline
   shows, not a filled disc. This is the "line plate". */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .banner-categories .category-chip.active .cc-emoji::before {
  content: '';
  position: absolute;
  inset: -2px;
  z-index: -1;
  background: var(--ink);
  -webkit-mask: var(--cartoon-brush-ring) center / 100% 100% no-repeat;
  mask: var(--cartoon-brush-ring) center / 100% 100% no-repeat;
}
/* Green FILLED disc, offset +4,+4. This is the "colour plate". Where it
   sits, it covers the ink ring; where it moves away (top-left) the ink
   ring is visible; where it moves past (bottom-right) it sticks out. */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .banner-categories .category-chip.active .cc-emoji::after {
  content: '';
  position: absolute;
  inset: -2px;
  transform: translate(4px, 4px);
  z-index: -2;
  background: var(--primary);
  -webkit-mask: var(--cartoon-brush-circle) center / 100% 100% no-repeat;
  mask: var(--cartoon-brush-circle) center / 100% 100% no-repeat;
}
/* Pill filter chips (Home location row, history filters, AI picker). */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .location-chip.active,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-ai-cat-chip.active {
  background: transparent;
  border-color: transparent;
  color: var(--primary-ink);
  position: relative;
  isolation: isolate;
  box-shadow: none;
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) .location-chip.active::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-ai-cat-chip.active::before {
  content: '';
  position: absolute;
  inset: -2px -4px;
  z-index: -1;
  background: var(--primary);
  -webkit-mask: var(--cartoon-brush-pill) center / 100% 100% no-repeat;
  mask: var(--cartoon-brush-pill) center / 100% 100% no-repeat;
}


/* ============================================================================
   PATCH 69 — Cartoon portrait card polish
   Three changes scoped to :is([data-theme="cartoon"], [data-theme="cbtl"]):
     1. Sticker frame     — hard ink border + offset shadow, matching every
                            other card in the cartoon theme.
     2. Brush-circle heart — rough-edged green brush behind the filled
                            favourite heart, same technique as the selected
                            filter chips.
     3. Squiggle underline — a small wavy line beneath the title so the
                            title / meta bands read as two clear layers.
   All three are pure CSS. To undo any of them: delete its rule.
   ============================================================================ */

/* ---- 1. Sticker frame on portrait cards (UNDOABLE) ------------------- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .portrait-card {
  border: 3px solid var(--ink);
  box-shadow: 4px 4px 0 var(--ink);
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) .portrait-card-inner {
  border-radius: calc(var(--radius-lg) - 3px);
}

/* ---- 2. Brush-circle behind the active favourite heart (UNDOABLE) ---- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .portrait-card .recipe-fav-btn.active {
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: none;
  color: #fff;
  isolation: isolate;
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) .portrait-card .recipe-fav-btn.active::before {
  content: '';
  position: absolute;
  inset: -2px;
  z-index: -1;
  background: var(--primary);
  -webkit-mask: var(--cartoon-brush-circle) center / 100% 100% no-repeat;
  mask: var(--cartoon-brush-circle) center / 100% 100% no-repeat;
}

/* Same treatment for the grid-view heart, so the two card styles stay
   in visual lockstep when the user switches views. */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-grid-item .recipe-fav-btn.active {
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: none;
  color: #fff;
  isolation: isolate;
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-grid-item .recipe-fav-btn.active::before {
  content: '';
  position: absolute;
  inset: -2px;
  z-index: -1;
  background: var(--primary);
  -webkit-mask: var(--cartoon-brush-circle) center / 100% 100% no-repeat;
  mask: var(--cartoon-brush-circle) center / 100% 100% no-repeat;
}

/* ---- 3. Squiggle underline beneath the portrait title (UNDOABLE) ----- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .portrait-card-title::after {
  content: '';
  display: block;
  width: 46px;
  height: 6px;
  margin-top: 8px;
  background: var(--cartoon-squiggle) no-repeat left center;
  background-size: 46px 6px;
  opacity: 0.85;
}



/* ============================================================================
   PATCH 70 — Cartoon theme polish (icons + illustrations)
   ----------------------------------------------------------------------------
   Full cartoon treatment for the remaining icon surfaces:
     • Hero title emoji (Consume / Shopping / Recipes / Settings / greeting)
     • Alert-panel 4-card stat icons
     • Nutrition card icons (calories / protein / fat / carbs)
     • Recipes empty-state illustration
     • Alerts empty-state illustration
     • Star doodles on Product Detail + Recipe Edit section headings
     • Chunkier stroke on Product Detail empty-state SVGs

   Every rule is scoped to :is([data-theme="cartoon"], [data-theme="cbtl"]). Under any other theme, the
   original emoji and stroke SVGs render exactly as before — nothing changes.
   No HTML or JS edits; switching back to a non-cartoon theme simply un-hides
   the emoji, since the emoji is still in the DOM.

   Style: bright saturated fills + chunky ink outlines + kawaii faces (dot
   eyes, small smiles, blush circles) — matching the tab-bar icon set and the
   Consume / Shopping empty-state illustrations.

   UNDO: delete this entire block, from the header comment through the final
   .pd-empty-state svg rule.
   ============================================================================ */

/* ---- Shared cartoon-only SVG assets ---- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) {
  /* Hero title icons — colourful, chunky, kawaii. */
  --cartoon-hero-basket: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cpath d='M6 15 H34 L31 30 Q30 33 27 33 H13 Q10 33 9 30 Z' fill='%238CBD42' stroke='%232E3A1F' stroke-width='2.6' stroke-linejoin='round'/%3E%3Cpath d='M5 15 H35' stroke='%232E3A1F' stroke-width='3' stroke-linecap='round'/%3E%3Cpath d='M14 6 Q16 9 14 12 M22 6 Q24 9 22 12' fill='none' stroke='%232E3A1F' stroke-width='2' stroke-linecap='round' opacity='0.75'/%3E%3Ccircle cx='14' cy='22' r='2' fill='%23FBF3DF'/%3E%3Ccircle cx='26' cy='22' r='2' fill='%23FBF3DF'/%3E%3Cpath d='M15 27 Q20 25 25 27' fill='none' stroke='%23FBF3DF' stroke-width='2.2' stroke-linecap='round'/%3E%3C/svg%3E");
  --cartoon-hero-bag:    url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cpath d='M6 10 L10 30 H32 L36 10 Z' fill='%23F5A623' stroke='%232E3A1F' stroke-width='2.6' stroke-linejoin='round'/%3E%3Cpath d='M6 10 L2 4' stroke='%232E3A1F' stroke-width='2.6' stroke-linecap='round'/%3E%3Ccircle cx='12' cy='32' r='2.6' fill='%232E3A1F'/%3E%3Ccircle cx='30' cy='32' r='2.6' fill='%232E3A1F'/%3E%3Ccircle cx='17' cy='18' r='1.7' fill='%23FBF3DF'/%3E%3Ccircle cx='25' cy='18' r='1.7' fill='%23FBF3DF'/%3E%3Cpath d='M17 22 Q21 24 25 22' fill='none' stroke='%23FBF3DF' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
  --cartoon-hero-chef:   url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cpath d='M12 22c-5-1-7-6-2.5-8.5 0-4.5 5-7 8-4.5 1-2.5 6-2.5 7 0 3-2.5 8 0 8 4.5 4.5 2.5 2.5 7.5-2.5 8.5z' fill='%23FBF3DF' stroke='%232E3A1F' stroke-width='2.5' stroke-linejoin='round'/%3E%3Crect x='12' y='22' width='16' height='9' rx='2' fill='%238CBD42' stroke='%232E3A1F' stroke-width='2.5'/%3E%3Ccircle cx='16.5' cy='26' r='1.4' fill='%232E3A1F'/%3E%3Ccircle cx='23.5' cy='26' r='1.4' fill='%232E3A1F'/%3E%3Cpath d='M17.5 29c.6.8 1.4 1.2 2.5 1.2s1.9-.4 2.5-1.2' fill='none' stroke='%232E3A1F' stroke-width='1.3' stroke-linecap='round'/%3E%3C/svg%3E");
  --cartoon-hero-gear:   url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cpath d='M17 3h6l1.5 4 3.5-1.5 4 4-1.5 3.5 4 1.5v6l-4 1.5 1.5 3.5-4 4-3.5-1.5-1.5 4h-6l-1.5-4-3.5 1.5-4-4 1.5-3.5-4-1.5v-6l4-1.5L6.5 9.5l4-4L14 7z' fill='%238CBD42' stroke='%232E3A1F' stroke-width='2.2' stroke-linejoin='round'/%3E%3Ccircle cx='20' cy='20' r='7.5' fill='%23FBF3DF' stroke='%232E3A1F' stroke-width='2'/%3E%3Ccircle cx='17' cy='19' r='1.3' fill='%232E3A1F'/%3E%3Ccircle cx='23' cy='19' r='1.3' fill='%232E3A1F'/%3E%3Cpath d='M17.5 22c1 1.2 2 1.6 2.5 1.6s1.5-.4 2.5-1.6' fill='none' stroke='%232E3A1F' stroke-width='1.3' stroke-linecap='round'/%3E%3C/svg%3E");
  --cartoon-hero-wave:   url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Crect x='10' y='16' width='20' height='18' rx='7' fill='%23F5A623' stroke='%232E3A1F' stroke-width='2.5'/%3E%3Crect x='12.5' y='8' width='4.5' height='13' rx='2.2' fill='%23F5A623' stroke='%232E3A1F' stroke-width='2'/%3E%3Crect x='18' y='6' width='4.5' height='15' rx='2.2' fill='%23F5A623' stroke='%232E3A1F' stroke-width='2'/%3E%3Crect x='23.5' y='8' width='4.5' height='13' rx='2.2' fill='%23F5A623' stroke='%232E3A1F' stroke-width='2'/%3E%3Cpath d='M10 22 Q4 22 5 17 Q6 13.5 10 15' fill='%23F5A623' stroke='%232E3A1F' stroke-width='2.5' stroke-linejoin='round'/%3E%3C/svg%3E");

  /* Alert stat icons — bright stickers with kawaii faces. */
  --cartoon-alert-expired: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cpath d='M20 4 L36 33 Q37 36 34 36 H6 Q3 36 4 33 Z' fill='%23E5484D' stroke='%232E3A1F' stroke-width='2.6' stroke-linejoin='round'/%3E%3Ccircle cx='16' cy='24' r='1.8' fill='%23FBF3DF'/%3E%3Ccircle cx='24' cy='24' r='1.8' fill='%23FBF3DF'/%3E%3Cpath d='M15 30 Q20 27 25 30' fill='none' stroke='%23FBF3DF' stroke-width='1.9' stroke-linecap='round'/%3E%3C/svg%3E");
  --cartoon-alert-soon:    url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Ccircle cx='20' cy='20' r='15' fill='%23F5A623' stroke='%232E3A1F' stroke-width='2.6'/%3E%3Ccircle cx='20' cy='20' r='11.5' fill='%23FBF3DF'/%3E%3Cpath d='M20 12v8l6 4' fill='none' stroke='%232E3A1F' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3Ccircle cx='20' cy='20' r='1.7' fill='%232E3A1F'/%3E%3C/svg%3E");
  --cartoon-alert-low:     url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cpath d='M5 13h30v19a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2z' fill='%234A90D9' stroke='%232E3A1F' stroke-width='2.6' stroke-linejoin='round'/%3E%3Cpath d='M5 13 L9 5 H31 L35 13 Z' fill='%236BA3DB' stroke='%232E3A1F' stroke-width='2.6' stroke-linejoin='round'/%3E%3Cline x1='5' y1='13' x2='35' y2='13' stroke='%232E3A1F' stroke-width='2.6'/%3E%3Ccircle cx='15' cy='23' r='1.8' fill='%23FBF3DF'/%3E%3Ccircle cx='25' cy='23' r='1.8' fill='%23FBF3DF'/%3E%3Cpath d='M15 30 Q20 28 25 30' fill='none' stroke='%23FBF3DF' stroke-width='1.9' stroke-linecap='round'/%3E%3C/svg%3E");
  --cartoon-alert-zero:    url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cpath d='M5 13h30v19a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2z' fill='%23D4CAA8' stroke='%232E3A1F' stroke-width='2.6' stroke-linejoin='round'/%3E%3Cpath d='M5 13 L9 5 H31 L35 13 Z' fill='%23E8DFC0' stroke='%232E3A1F' stroke-width='2.6' stroke-linejoin='round'/%3E%3Cline x1='5' y1='13' x2='35' y2='13' stroke='%232E3A1F' stroke-width='2.6'/%3E%3Ccircle cx='15' cy='23' r='1.8' fill='%232E3A1F'/%3E%3Ccircle cx='25' cy='23' r='1.8' fill='%232E3A1F'/%3E%3Cellipse cx='20' cy='29' rx='3' ry='2.2' fill='%232E3A1F'/%3E%3C/svg%3E");

  /* Nutrition icons — multi-colour flat, chunky. */
  --cartoon-nutri-calories: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2c-1.5 4.5-5 6.5-5 11a5 5 0 0 0 10 0c0-3.5-2-5-3-7 0 1.5-1 2.5-2 2.5 0-2 0-4.5 0-6.5z' fill='%23E8622C' stroke='%232E3A1F' stroke-width='1.5' stroke-linejoin='round'/%3E%3Cpath d='M12 18.5c1.7 0 2.8-1.2 2.8-2.8 0-1.6-1.2-2.6-2.8-3.7-1.6 1.1-2.8 2.1-2.8 3.7s1.1 2.8 2.8 2.8z' fill='%23F5A623'/%3E%3C/svg%3E");
  --cartoon-nutri-protein:  url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6 7c0-2.5 2-4.5 5-4.5 2.5 0 4.5 1.5 4.5 3.5 0 1-.5 2-1.2 2.7l-4.3 7.3-3.5-2.5L9 10C7.5 9.2 6 8.2 6 7z' fill='%23D64550' stroke='%232E3A1F' stroke-width='1.5' stroke-linejoin='round'/%3E%3Ccircle cx='18.5' cy='18' r='3' fill='%23FBF3DF' stroke='%232E3A1F' stroke-width='1.5'/%3E%3Cpath d='M15.5 18 L12.5 15.5' stroke='%23FBF3DF' stroke-width='3.5' stroke-linecap='round'/%3E%3C/svg%3E");
  --cartoon-nutri-fat:      url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2c-4.5 5.5-7 9.5-7 13a7 7 0 0 0 14 0c0-3.5-2.5-7.5-7-13z' fill='%23D9A02A' stroke='%232E3A1F' stroke-width='1.5' stroke-linejoin='round'/%3E%3Ccircle cx='10' cy='15' r='1.4' fill='%23FBF3DF' opacity='0.7'/%3E%3C/svg%3E");
  --cartoon-nutri-carbs:    url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 22V8' stroke='%235C8B4A' stroke-width='2.2' stroke-linecap='round'/%3E%3Cellipse cx='12' cy='4.5' rx='2.6' ry='3.2' fill='%23D9A02A' stroke='%232E3A1F' stroke-width='1.2'/%3E%3Cpath d='M9.5 10 L12 12 L14.5 10' stroke='%23D9A02A' stroke-width='2.2' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M9.5 14 L12 16 L14.5 14' stroke='%23D9A02A' stroke-width='2.2' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M9.5 18 L12 20 L14.5 18' stroke='%23D9A02A' stroke-width='2.2' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");

  /* Empty-state illustrations. */
  --cartoon-illus-chef:  url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cdefs%3E%3Cfilter id='grain'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' seed='7'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0.99 0 0 0 0 0.96 0 0 0 0 0.88 0.18 0 0 0 -0.05'/%3E%3CfeComposite in2='SourceGraphic' operator='in'/%3E%3CfeBlend in2='SourceGraphic' mode='overlay'/%3E%3C/filter%3E%3C/defs%3E%3Cg transform='translate(12 12)' filter='url(%23grain)'%3E%3Cpath d='M50 115 Q28 115 28 90 Q28 65 50 65 Q52 32 82 30 Q100 20 120 30 Q150 32 152 65 Q172 65 172 90 Q172 115 152 115 Z' fill='%23FBF3DF'/%3E%3Cpath d='M46 115 L154 115 L154 158 Q154 164 148 164 L52 164 Q46 164 46 158 Z' fill='%23FBF3DF'/%3E%3C/g%3E%3Cpath d='M50 115 Q28 115 28 90 Q28 65 50 65 Q52 32 82 30 Q100 20 120 30 Q150 32 152 65 Q172 65 172 90 Q172 115 152 115' fill='none' stroke='%232E3A1F' stroke-width='3.5' stroke-linejoin='round'/%3E%3Cpath d='M46 115 L154 115 L154 158 Q154 164 148 164 L52 164 Q46 164 46 158 Z' fill='none' stroke='%232E3A1F' stroke-width='3.5' stroke-linejoin='round'/%3E%3Cline x1='46' y1='132' x2='154' y2='132' stroke='%232E3A1F' stroke-width='2' opacity='0.5'/%3E%3Ccircle cx='85' cy='148' r='4' fill='%232E3A1F'/%3E%3Ccircle cx='115' cy='148' r='4' fill='%232E3A1F'/%3E%3Cpath d='M88 156 Q100 164 112 156' fill='none' stroke='%232E3A1F' stroke-width='3' stroke-linecap='round'/%3E%3C/svg%3E");
  --cartoon-illus-clean: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cdefs%3E%3Cfilter id='grain'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' seed='7'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0.99 0 0 0 0 0.96 0 0 0 0 0.88 0.18 0 0 0 -0.05'/%3E%3CfeComposite in2='SourceGraphic' operator='in'/%3E%3CfeBlend in2='SourceGraphic' mode='overlay'/%3E%3C/filter%3E%3C/defs%3E%3Cg transform='translate(12 12)' filter='url(%23grain)'%3E%3Ccircle cx='96' cy='96' r='76' fill='%238CBD42'/%3E%3C/g%3E%3Ccircle cx='96' cy='96' r='76' fill='none' stroke='%232E3A1F' stroke-width='4'/%3E%3Cpath d='M62 98 L86 122 L134 72' fill='none' stroke='%23FBF3DF' stroke-width='14' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* ---- Hero title emoji → cartoon icon -------------------------------------
   The emoji is a direct child <span> in each hero title (and a .wave span
   inside the dynamic greeting). Hiding it here; the ::after rule paints the
   bespoke icon. Under non-cartoon themes, neither rule matches, so the emoji
   displays exactly as it does today. */
:is([data-theme="cartoon"], [data-theme="cbtl"]) #consume-heading > span,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #shopping-heading > span,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #settings-heading > span,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipes-hello > span,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #home-greeting .wave {
  display: none;
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) #consume-heading::after,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #shopping-heading::after,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #settings-heading::after,
:is([data-theme="cartoon"], [data-theme="cbtl"]) #home-greeting::after {
  content: '';
  display: inline-block;
  width: 34px;
  height: 34px;
  margin-left: 10px;
  vertical-align: middle;
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipes-hello::after {
  content: '';
  display: inline-block;
  width: 34px;
  height: 34px;
  margin-left: 8px;
  vertical-align: middle;
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) #consume-heading::after  { background-image: var(--cartoon-hero-basket); }
:is([data-theme="cartoon"], [data-theme="cbtl"]) #shopping-heading::after { background-image: var(--cartoon-hero-bag); }
:is([data-theme="cartoon"], [data-theme="cbtl"]) #settings-heading::after { background-image: var(--cartoon-hero-gear); }
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipes-hello::after    { background-image: var(--cartoon-hero-chef); }
:is([data-theme="cartoon"], [data-theme="cbtl"]) #home-greeting::after    { background-image: var(--cartoon-hero-wave); }

/* ---- Alert-panel stat icons ----------------------------------------------
   font-size: 0 collapses the inline emoji without disrupting the flex
   layout of .alert-stat-icon (its own size comes from width/height).
   overflow: hidden clips any sub-pixel emoji residue. */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .alert-stat-icon {
  font-size: 0;
  line-height: 0;
  overflow: hidden;
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) .alert-stat-icon::before {
  content: '';
  display: block;
  width: 32px;
  height: 32px;
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) .alert-stat-card[data-alert-filter="expired"]      .alert-stat-icon::before { background-image: var(--cartoon-alert-expired); }
:is([data-theme="cartoon"], [data-theme="cbtl"]) .alert-stat-card[data-alert-filter="expires-soon"] .alert-stat-icon::before { background-image: var(--cartoon-alert-soon); }
:is([data-theme="cartoon"], [data-theme="cbtl"]) .alert-stat-card[data-alert-filter="running-low"]  .alert-stat-icon::before { background-image: var(--cartoon-alert-low); }
:is([data-theme="cartoon"], [data-theme="cbtl"]) .alert-stat-card[data-alert-filter="zero-balance"] .alert-stat-icon::before { background-image: var(--cartoon-alert-zero); }

/* ---- Nutrition card icons ------------------------------------------------ */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .nutrition-card .n-icon > svg { display: none; }
:is([data-theme="cartoon"], [data-theme="cbtl"]) .nutrition-card .n-icon::before {
  content: '';
  display: block;
  width: 100%;
  height: 100%;
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
}
:is([data-theme="cartoon"], [data-theme="cbtl"]) .n-icon.nutri-calories::before { background-image: var(--cartoon-nutri-calories); }
:is([data-theme="cartoon"], [data-theme="cbtl"]) .n-icon.nutri-protein::before  { background-image: var(--cartoon-nutri-protein); }
:is([data-theme="cartoon"], [data-theme="cbtl"]) .n-icon.nutri-fat::before      { background-image: var(--cartoon-nutri-fat); }
:is([data-theme="cartoon"], [data-theme="cbtl"]) .n-icon.nutri-carbs::before    { background-image: var(--cartoon-nutri-carbs); }

/* ---- Recipes empty-state illustration ------------------------------------ */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipes-empty::before {
  content: '';
  display: block;
  width: 140px;
  height: 140px;
  margin: 0 auto 14px;
  background: var(--cartoon-illus-chef) center / contain no-repeat;
}

/* ---- Alerts empty-state illustration ------------------------------------- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .alerts-empty::before {
  content: '';
  display: block;
  width: 120px;
  height: 120px;
  margin: 0 auto 12px;
  background: var(--cartoon-illus-clean) center / contain no-repeat;
}

/* ---- Star doodles on section headings (Product Detail + Recipe Edit) ----- */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .pd-section-heading::after,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-edit-section-heading::after {
  content: '';
  display: inline-block;
  width: 14px;
  height: 14px;
  margin-left: 6px;
  vertical-align: -2px;
  background: var(--cartoon-doodle-star) center / contain no-repeat;
}

/* ---- Product Detail empty-state icons get the chunky ink look ------------ */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .pd-empty-state svg {
  opacity: 1;
  color: var(--ink);
  stroke-width: 2.4;
}


/* ============================================================================
   Hide-when-empty final overrides
   ----------------------------------------------------------------------------
   The cartoon theme's button override in PATCH 65 forces display: flex on
   both submit buttons so the chunky icon layout works. Its specificity
   (1,2,1) beats the earlier "hide when empty" rules (0,2,1 and 1,1,1), so
   the buttons stayed visible even when the list was empty. These rules
   duplicate the hide conditions with the cartoon prefix, and sit at the
   very end of the file so source order resolves the tie in favour of
   hiding. Non-cartoon themes match the unprefixed selectors here too, so
   the intent stays in one place.
   ============================================================================ */

body[data-tab="tab-consume"].consume-list-empty #consume-submit-btn,
:is([data-theme="cartoon"], [data-theme="cbtl"]) body[data-tab="tab-consume"].consume-list-empty #consume-submit-btn {
  display: none;
}

body[data-tab="tab-shopping"] #shopping-submit-btn.is-empty,
:is([data-theme="cartoon"], [data-theme="cbtl"]) body[data-tab="tab-shopping"] #shopping-submit-btn.is-empty,
body[data-tab="tab-shopping"].shopping-list-empty #shopping-submit-btn,
:is([data-theme="cartoon"], [data-theme="cbtl"]) body[data-tab="tab-shopping"].shopping-list-empty #shopping-submit-btn {
  display: none;
}


/* ============================================================================
   Cartoon — #item-save-btn hide when form incomplete
   ----------------------------------------------------------------------------
   PATCH 65 forces display: inline-flex on #item-save-btn for the chunky
   icon layout. That ties the specificity of PATCH 5's hide rule
   (#item-save-btn.is-empty, both 1,1,0) and wins by source order, so the
   Add button stayed visible in cartoon theme even when the form was empty.
   This rule adds a second class to the selector — higher specificity than
   either — so it wins regardless of order.
   ============================================================================ */
:is([data-theme="cartoon"], [data-theme="cbtl"]) #item-save-btn.is-empty {
  display: none;
}


/* ============================================================================
   Riso basket — Consume empty-state
   ----------------------------------------------------------------------------
   Two-plate riso + soft grain (no hard patches).
     Colour plate — offset down-right, per-element variable
     Line plate   — aligned, black marker outlines
     Grain        — single fractal noise (0.35, 4 octaves) mapped to a
                    smooth cream-alpha curve (no threshold). Density
                    fades organically from nearly-solid to visible grain.
   Scoped to #tab-consume, cartoon theme only.
   ============================================================================ */
:is([data-theme="cartoon"], [data-theme="cbtl"]) #tab-consume .consume-empty::before {
  background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cdefs%3E%3Cfilter id='grain'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' seed='7'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0.99 0 0 0 0 0.96 0 0 0 0 0.88 0.18 0 0 0 -0.05'/%3E%3CfeComposite in2='SourceGraphic' operator='in'/%3E%3CfeBlend in2='SourceGraphic' mode='overlay'/%3E%3C/filter%3E%3C/defs%3E%3Cg transform='translate(12 12)' filter='url(%23grain)'%3E%3Cpath d='M65 80 Q68 25 100 22 Q132 25 135 80' fill='none' stroke='%23B07A4A' stroke-width='7' stroke-linecap='round'/%3E%3Cpath d='M78 82 Q80 38 100 35 Q120 38 122 82' fill='none' stroke='%23B07A4A' stroke-width='4' stroke-linecap='round'/%3E%3Cpath d='M30 95 L48 175 Q100 182 152 175 L170 95 Z' fill='%23B07A4A'/%3E%3Cpath d='M22 75 Q30 70 100 70 Q170 70 178 75 L178 95 Q170 100 100 100 Q30 100 22 95 Z' fill='%23B07A4A'/%3E%3Cpath d='M20 78 L42 72 Q62 78 70 96 L60 118 Q44 122 30 116 Q20 106 20 92 Z' fill='%239B72C7'/%3E%3C/g%3E%3Cpath d='M65 80 Q68 25 100 22 Q132 25 135 80' fill='none' stroke='%232E3A1F' stroke-width='3' stroke-linecap='round'/%3E%3Cpath d='M78 82 Q80 38 100 35 Q120 38 122 82' fill='none' stroke='%232E3A1F' stroke-width='3' stroke-linecap='round'/%3E%3Cpath d='M30 95 L48 175 Q100 182 152 175 L170 95' fill='none' stroke='%232E3A1F' stroke-width='3' stroke-linejoin='round'/%3E%3Cline x1='30' y1='95' x2='170' y2='95' stroke='%232E3A1F' stroke-width='3'/%3E%3Cpath d='M22 75 Q30 70 100 70 Q170 70 178 75' fill='none' stroke='%232E3A1F' stroke-width='3' stroke-linecap='round'/%3E%3Cpath d='M22 95 Q30 100 100 100 Q170 100 178 95' fill='none' stroke='%232E3A1F' stroke-width='3' stroke-linecap='round'/%3E%3Cline x1='22' y1='75' x2='22' y2='95' stroke='%232E3A1F' stroke-width='3'/%3E%3Cline x1='178' y1='75' x2='178' y2='95' stroke='%232E3A1F' stroke-width='3'/%3E%3Cpath d='M36 118 Q100 116 164 118' fill='none' stroke='%232E3A1F' stroke-width='1.4' opacity='0.35'/%3E%3Cpath d='M40 140 Q100 138 160 140' fill='none' stroke='%232E3A1F' stroke-width='1.4' opacity='0.35'/%3E%3Cpath d='M44 160 Q100 158 156 160' fill='none' stroke='%232E3A1F' stroke-width='1.4' opacity='0.35'/%3E%3Cpath d='M60 98 L56 176' fill='none' stroke='%232E3A1F' stroke-width='1.4' opacity='0.35'/%3E%3Cpath d='M80 98 L78 178' fill='none' stroke='%232E3A1F' stroke-width='1.4' opacity='0.35'/%3E%3Cpath d='M100 98 L100 180' fill='none' stroke='%232E3A1F' stroke-width='1.4' opacity='0.35'/%3E%3Cpath d='M120 98 L122 178' fill='none' stroke='%232E3A1F' stroke-width='1.4' opacity='0.35'/%3E%3Cpath d='M140 98 L144 176' fill='none' stroke='%232E3A1F' stroke-width='1.4' opacity='0.35'/%3E%3Cpath d='M20 78 L42 72 Q62 78 70 96 L60 118 Q44 122 30 116 Q20 106 20 92 Z' fill='none' stroke='%232E3A1F' stroke-width='3' stroke-linejoin='round'/%3E%3Cpath d='M38 76 Q42 96 40 116' fill='none' stroke='%232E3A1F' stroke-width='1.6' opacity='0.5'/%3E%3Ccircle cx='120' cy='148' r='3.2' fill='%232E3A1F'/%3E%3Ccircle cx='142' cy='148' r='3.2' fill='%232E3A1F'/%3E%3Cpath d='M124 160 Q131 166 138 160' fill='none' stroke='%232E3A1F' stroke-width='2.8' stroke-linecap='round'/%3E%3C/svg%3E");
  width: 220px;
  height: 220px;
  background-size: contain;
  background-position: center;
  background-repeat: no-repeat;
}


/* ============================================================================
   Riso hero icons — Consume (v2)
   ----------------------------------------------------------------------------
   Two-plate riso amber bowl, taller steam wisps, shifted down 4 units so the
   wisps fit inside the viewBox. Offset +3,+2 down-right.
   ============================================================================ */
:is([data-theme="cartoon"], [data-theme="cbtl"]) {
  --cartoon-hero-basket: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cg transform='translate(4 4)'%3E%3Cpath d='M6 20 Q5 24 6 28 Q8 34 14 35 Q20 36 26 35 Q32 34 34 28 Q35 24 34 20 Z' fill='%23E89A4C'/%3E%3C/g%3E%3Cpath d='M6 20 Q5 24 6 28 Q8 34 14 35 Q20 36 26 35 Q32 34 34 28 Q35 24 34 20' fill='none' stroke='%232E3A1F' stroke-width='2.2' stroke-linejoin='round'/%3E%3Cpath d='M4 20 Q20 17 36 20' fill='none' stroke='%232E3A1F' stroke-width='2.2' stroke-linecap='round'/%3E%3Cpath d='M14 16 Q10 11 14 6 Q17 3 14 1' fill='none' stroke='%232E3A1F' stroke-width='1.8' stroke-linecap='round'/%3E%3Cpath d='M22 16 Q26 11 22 6 Q19 3 22 1' fill='none' stroke='%232E3A1F' stroke-width='1.8' stroke-linecap='round'/%3E%3Ccircle cx='15' cy='26' r='1.2' fill='%232E3A1F'/%3E%3Ccircle cx='25' cy='26' r='1.2' fill='%232E3A1F'/%3E%3Cpath d='M16 30 Q20 32 24 30' fill='none' stroke='%232E3A1F' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E");
}


/* ============================================================================
   Riso hero icons — Shopping (v3, purple)
   ----------------------------------------------------------------------------
   Cart in riso purple #9B72C7 — matches the cloth from the empty-state basket,
   and distinct from the amber Consume bowl. Two-plate offset +3,+2.
   ============================================================================ */
:is([data-theme="cartoon"], [data-theme="cbtl"]) {
  --cartoon-hero-bag: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cg transform='translate(4 4)'%3E%3Cpath d='M10 13 Q9 22 11 29 Q12 31 15 31 L31 31 Q34 31 35 29 Q36 22 34 13 Z' fill='%239B72C7'/%3E%3Ccircle cx='16' cy='34' r='2.4' fill='%239B72C7'/%3E%3Ccircle cx='29' cy='34' r='2.4' fill='%239B72C7'/%3E%3C/g%3E%3Cpath d='M10 13 Q9 22 11 29 Q12 31 15 31 L31 31 Q34 31 35 29 Q36 22 34 13' fill='none' stroke='%232E3A1F' stroke-width='2.2' stroke-linejoin='round'/%3E%3Cpath d='M10 13 L5 7' fill='none' stroke='%232E3A1F' stroke-width='2.2' stroke-linecap='round'/%3E%3Cpath d='M9 13 L34 13' fill='none' stroke='%232E3A1F' stroke-width='2.2' stroke-linecap='round'/%3E%3Ccircle cx='16' cy='34' r='2.4' fill='%232E3A1F'/%3E%3Ccircle cx='29' cy='34' r='2.4' fill='%232E3A1F'/%3E%3Ccircle cx='17' cy='20' r='1.3' fill='%232E3A1F'/%3E%3Ccircle cx='27' cy='20' r='1.3' fill='%232E3A1F'/%3E%3Cpath d='M18 24 Q22 26 26 24' fill='none' stroke='%232E3A1F' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* ============================================================================
   Riso hero icons — Settings (v4, soft pink)
   ----------------------------------------------------------------------------
   Pastel pink #F4A5C0 — playful cartoon feel. Distinct from amber Consume,
   purple Shopping, and reads as "cute" rather than bold.
   ============================================================================ */
/* ============================================================================
   Riso hero icons — Settings (v5)
   ----------------------------------------------------------------------------
   viewBox expanded to 36x36, gear scaled up, offset bumped to +4,+4
   to match the other hero icons.
   ============================================================================ */
:is([data-theme="cartoon"], [data-theme="cbtl"]) {
  --cartoon-hero-gear: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 36 36'%3E%3Cg transform='translate(4 4)'%3E%3Cpath d='M16 2 L18 2 Q19.1 2 19.4 3.1 L19.8 5.7 Q21.8 6 23.4 6.8 L25.1 4.9 Q26 4 27.1 5 L28.3 6.2 Q29.3 7.3 28.4 8.2 L26.5 9.9 Q27.3 11.5 27.6 13.5 L30.2 13.9 Q31.4 14.1 31.4 15.4 L31.4 17.2 Q31.4 18.5 30.2 18.7 L27.6 19.1 Q27.3 21.1 26.5 22.7 L28.4 24.4 Q29.3 25.3 28.3 26.4 L27.1 27.6 Q26 28.6 25.1 27.7 L23.4 25.8 Q21.8 26.6 19.8 26.9 L19.4 29.5 Q19.1 30.6 18 30.6 L16 30.6 Q14.9 30.6 14.6 29.5 L14.2 26.9 Q12.2 26.6 10.6 25.8 L8.9 27.7 Q8 28.6 6.9 27.6 L5.7 26.4 Q4.7 25.3 5.6 24.4 L7.5 22.7 Q6.7 21.1 6.4 19.1 L3.8 18.7 Q2.6 18.5 2.6 17.2 L2.6 15.4 Q2.6 14.1 3.8 13.9 L6.4 13.5 Q6.7 11.5 7.5 9.9 L5.6 8.2 Q4.7 7.3 5.7 6.2 L6.9 5 Q8 4 8.9 4.9 L10.6 6.8 Q12.2 6 14.2 5.7 L14.6 3.1 Q14.9 2 16 2 Z' fill='%23F4A5C0'/%3E%3C/g%3E%3Cpath d='M16 2 L18 2 Q19.1 2 19.4 3.1 L19.8 5.7 Q21.8 6 23.4 6.8 L25.1 4.9 Q26 4 27.1 5 L28.3 6.2 Q29.3 7.3 28.4 8.2 L26.5 9.9 Q27.3 11.5 27.6 13.5 L30.2 13.9 Q31.4 14.1 31.4 15.4 L31.4 17.2 Q31.4 18.5 30.2 18.7 L27.6 19.1 Q27.3 21.1 26.5 22.7 L28.4 24.4 Q29.3 25.3 28.3 26.4 L27.1 27.6 Q26 28.6 25.1 27.7 L23.4 25.8 Q21.8 26.6 19.8 26.9 L19.4 29.5 Q19.1 30.6 18 30.6 L16 30.6 Q14.9 30.6 14.6 29.5 L14.2 26.9 Q12.2 26.6 10.6 25.8 L8.9 27.7 Q8 28.6 6.9 27.6 L5.7 26.4 Q4.7 25.3 5.6 24.4 L7.5 22.7 Q6.7 21.1 6.4 19.1 L3.8 18.7 Q2.6 18.5 2.6 17.2 L2.6 15.4 Q2.6 14.1 3.8 13.9 L6.4 13.5 Q6.7 11.5 7.5 9.9 L5.6 8.2 Q4.7 7.3 5.7 6.2 L6.9 5 Q8 4 8.9 4.9 L10.6 6.8 Q12.2 6 14.2 5.7 L14.6 3.1 Q14.9 2 16 2 Z' fill='none' stroke='%232E3A1F' stroke-width='1.9' stroke-linejoin='round'/%3E%3Ccircle cx='17' cy='16' r='6' fill='none' stroke='%232E3A1F' stroke-width='1.9'/%3E%3Ccircle cx='14.8' cy='15.5' r='1.1' fill='%232E3A1F'/%3E%3Ccircle cx='19.2' cy='15.5' r='1.1' fill='%232E3A1F'/%3E%3Cpath d='M15.3 18 Q17 19.7 18.7 18' fill='none' stroke='%232E3A1F' stroke-width='1.3' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* ============================================================================
   Riso hero icons — Recipes (v2, bigger hat)
   ----------------------------------------------------------------------------
   Chef hat now fills the viewBox (30 units out of 40) instead of 19.
   Cream #FBF3DF fill, kawaii face on the brim.
   ============================================================================ */
:is([data-theme="cartoon"], [data-theme="cbtl"]) {
  --cartoon-hero-chef: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cg transform='translate(4 4)'%3E%3Cpath d='M10 24 Q4 24 4 18 Q4 12 10 12 Q10 5 17 5 Q21 3 25 5 Q32 5 32 12 Q36 12 36 18 Q36 24 30 24 Z' fill='%23FBF3DF'/%3E%3Crect x='9' y='24' width='22' height='9' rx='2.5' fill='%23FBF3DF'/%3E%3C/g%3E%3Cpath d='M10 24 Q4 24 4 18 Q4 12 10 12 Q10 5 17 5 Q21 3 25 5 Q32 5 32 12 Q36 12 36 18 Q36 24 30 24' fill='none' stroke='%232E3A1F' stroke-width='2.2' stroke-linejoin='round'/%3E%3Crect x='9' y='24' width='22' height='9' rx='2.5' fill='none' stroke='%232E3A1F' stroke-width='2.2'/%3E%3Cline x1='9' y1='28' x2='31' y2='28' stroke='%232E3A1F' stroke-width='1.5'/%3E%3Ccircle cx='16' cy='30.5' r='1.1' fill='%232E3A1F'/%3E%3Ccircle cx='24' cy='30.5' r='1.1' fill='%232E3A1F'/%3E%3Cpath d='M17 32.5 Q20 34 23 32.5' fill='none' stroke='%232E3A1F' stroke-width='1.3' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* ============================================================================
   Riso hero icons — Greeting
   ----------------------------------------------------------------------------
   Waving hand, skin-tone tan #E8B48C. Four fingers + thumb + palm with a
   kawaii face. Two-plate offset +3,+2.
   ============================================================================ */
:is([data-theme="cartoon"], [data-theme="cbtl"]) {
  --cartoon-hero-wave: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cg transform='translate(4 4)'%3E%3Cpath d='M10 16 Q8 20 9 26 Q10 32 15 34 Q20 36 25 34 Q30 32 31 26 Q32 20 30 16 Z' fill='%23E8B48C'/%3E%3Crect x='11' y='6' width='4' height='12' rx='2' fill='%23E8B48C'/%3E%3Crect x='16' y='4' width='4' height='14' rx='2' fill='%23E8B48C'/%3E%3Crect x='21' y='5' width='4' height='13' rx='2' fill='%23E8B48C'/%3E%3Crect x='26' y='7' width='4' height='11' rx='2' fill='%23E8B48C'/%3E%3Cpath d='M10 18 Q6 16 5 20 Q4 24 8 26' fill='%23E8B48C'/%3E%3C/g%3E%3Cpath d='M10 16 Q8 20 9 26 Q10 32 15 34 Q20 36 25 34 Q30 32 31 26 Q32 20 30 16' fill='none' stroke='%232E3A1F' stroke-width='2' stroke-linejoin='round'/%3E%3Crect x='11' y='6' width='4' height='12' rx='2' fill='none' stroke='%232E3A1F' stroke-width='1.8'/%3E%3Crect x='16' y='4' width='4' height='14' rx='2' fill='none' stroke='%232E3A1F' stroke-width='1.8'/%3E%3Crect x='21' y='5' width='4' height='13' rx='2' fill='none' stroke='%232E3A1F' stroke-width='1.8'/%3E%3Crect x='26' y='7' width='4' height='11' rx='2' fill='none' stroke='%232E3A1F' stroke-width='1.8'/%3E%3Cpath d='M10 18 Q6 16 5 20 Q4 24 8 26' fill='none' stroke='%232E3A1F' stroke-width='1.8' stroke-linejoin='round'/%3E%3Ccircle cx='17' cy='24' r='1.1' fill='%232E3A1F'/%3E%3Ccircle cx='23' cy='24' r='1.1' fill='%232E3A1F'/%3E%3Cpath d='M17.5 28 Q20 30 22.5 28' fill='none' stroke='%232E3A1F' stroke-width='1.3' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* ============================================================================
   Riso alert stat icons (v2)
   ----------------------------------------------------------------------------
   Offset bumped from +2,+2 to +4,+4 — visibly more misregistration.
   viewBox expanded to 36x36 so the shifted colour plate stays inside.
   ============================================================================ */
:is([data-theme="cartoon"], [data-theme="cbtl"]) {
  --cartoon-alert-expired: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 36 36'%3E%3Cg transform='translate(4 4)'%3E%3Cpath d='M16 4 Q17 4 17.5 5 L29 26 Q29.5 27.5 28 27.5 L4 27.5 Q2.5 27.5 3 26 L14.5 5 Q15 4 16 4 Z' fill='%23E5484D'/%3E%3C/g%3E%3Cpath d='M16 4 Q17 4 17.5 5 L29 26 Q29.5 27.5 28 27.5 L4 27.5 Q2.5 27.5 3 26 L14.5 5 Q15 4 16 4 Z' fill='none' stroke='%232E3A1F' stroke-width='1.8' stroke-linejoin='round'/%3E%3Ccircle cx='13' cy='19' r='1.2' fill='%232E3A1F'/%3E%3Ccircle cx='19' cy='19' r='1.2' fill='%232E3A1F'/%3E%3Cpath d='M13.5 23 Q16 21 18.5 23' fill='none' stroke='%232E3A1F' stroke-width='1.3' stroke-linecap='round'/%3E%3C/svg%3E");
  --cartoon-alert-soon:    url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 36 36'%3E%3Cg transform='translate(4 4)'%3E%3Ccircle cx='15' cy='15' r='11.5' fill='%23F5A623'/%3E%3C/g%3E%3Ccircle cx='15' cy='15' r='11.5' fill='none' stroke='%232E3A1F' stroke-width='1.8'/%3E%3Cpath d='M15 8 L15 15 L21 19' fill='none' stroke='%232E3A1F' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3Ccircle cx='15' cy='15' r='1.4' fill='%232E3A1F'/%3E%3C/svg%3E");
  --cartoon-alert-low:     url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 36 36'%3E%3Cg transform='translate(4 4)'%3E%3Cpath d='M5 10 L27 10 L25 27 L7 27 Z' fill='%234A90D9'/%3E%3Cpath d='M5 10 L9 5 L23 5 L27 10 Z' fill='%233A7AB8'/%3E%3C/g%3E%3Cpath d='M5 10 L27 10 L25 27 L7 27 Z' fill='none' stroke='%232E3A1F' stroke-width='1.8' stroke-linejoin='round'/%3E%3Cpath d='M5 10 L9 5 L23 5 L27 10' fill='none' stroke='%232E3A1F' stroke-width='1.8' stroke-linejoin='round'/%3E%3Cline x1='5' y1='10' x2='27' y2='10' stroke='%232E3A1F' stroke-width='1.8'/%3E%3Ccircle cx='13' cy='19' r='1.1' fill='%232E3A1F'/%3E%3Ccircle cx='19' cy='19' r='1.1' fill='%232E3A1F'/%3E%3Cpath d='M13.5 22 Q16 24 18.5 22' fill='none' stroke='%232E3A1F' stroke-width='1.2' stroke-linecap='round'/%3E%3C/svg%3E");
  --cartoon-alert-zero:    url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 36 36'%3E%3Cg transform='translate(4 4)'%3E%3Cpath d='M7 10 L25 10 L27 24 Q27 27 24 27 L8 27 Q5 27 5 24 Z' fill='%23E8A44C'/%3E%3C/g%3E%3Cpath d='M7 10 L25 10 L27 24 Q27 27 24 27 L8 27 Q5 27 5 24 Z' fill='none' stroke='%232E3A1F' stroke-width='1.8' stroke-linejoin='round'/%3E%3Cpath d='M9 10 Q10 6 16 6 Q22 6 23 10' fill='none' stroke='%232E3A1F' stroke-width='1.8' stroke-linecap='round'/%3E%3Ccircle cx='13' cy='19' r='1.1' fill='%232E3A1F'/%3E%3Ccircle cx='19' cy='19' r='1.1' fill='%232E3A1F'/%3E%3Cpath d='M14 22 Q16 20 18 22' fill='none' stroke='%232E3A1F' stroke-width='1.2' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* ============================================================================
   Riso nutrition icons + riso empty states
   ----------------------------------------------------------------------------
   Three more riso-ified elements, all using the same two-plate construction
   as the hero icons: colour plate offset +4,+4 (nutrition) or +12,+12 (empty
   states), black line plate aligned, hand-drawn wobble.

     Nutrition icons  — flame, drumstick, droplet, wheat
     Recipes empty    — puffy cream chef hat with kawaii face
     Alerts empty     — green circle with cream tick
   ============================================================================ */
:is([data-theme="cartoon"], [data-theme="cbtl"]) {
  --cartoon-nutri-calories: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 36 36'%3E%3Cg transform='translate(4 4)'%3E%3Cpath d='M14 3 Q15 8 18 12 Q22 17 22 22 Q22 28 16 28 Q10 28 10 22 Q10 18 12 14 Q13 17 15 15 Q16 12 14 3 Z' fill='%23E8622C'/%3E%3Cpath d='M16 14 Q20 18 20 22 Q20 26 16 26 Q13 26 13 22 Q13 19 15 16 Z' fill='%23F5A623'/%3E%3C/g%3E%3Cpath d='M14 3 Q15 8 18 12 Q22 17 22 22 Q22 28 16 28 Q10 28 10 22 Q10 18 12 14 Q13 17 15 15 Q16 12 14 3 Z' fill='none' stroke='%232E3A1F' stroke-width='1.7' stroke-linejoin='round'/%3E%3Ccircle cx='14.5' cy='22' r='0.9' fill='%232E3A1F'/%3E%3Ccircle cx='17.5' cy='22' r='0.9' fill='%232E3A1F'/%3E%3C/svg%3E");
  --cartoon-nutri-protein:  url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 36 36'%3E%3Cg transform='translate(4 4)'%3E%3Cpath d='M10 8 Q8 6 10 4 Q14 3 16 6 Q20 10 20 16 Q20 24 14 26 Q8 28 6 22 Q4 16 8 12 Q8 10 10 8 Z' fill='%23D64550'/%3E%3Cpath d='M6 14 L2 10' stroke='%23FBF3DF' stroke-width='2.4' stroke-linecap='round' fill='none'/%3E%3C/g%3E%3Cpath d='M10 8 Q8 6 10 4 Q14 3 16 6 Q20 10 20 16 Q20 24 14 26 Q8 28 6 22 Q4 16 8 12 Q8 10 10 8 Z' fill='none' stroke='%232E3A1F' stroke-width='1.7' stroke-linejoin='round'/%3E%3Cpath d='M6 14 L2 10' stroke='%232E3A1F' stroke-width='2.2' stroke-linecap='round' fill='none'/%3E%3Ccircle cx='12' cy='18' r='0.9' fill='%232E3A1F'/%3E%3Ccircle cx='15' cy='18' r='0.9' fill='%232E3A1F'/%3E%3C/svg%3E");
  --cartoon-nutri-fat:      url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 36 36'%3E%3Cg transform='translate(4 4)'%3E%3Cpath d='M16 3 Q10 14 8 20 Q6 26 10 28 Q14 30 16 30 Q18 30 22 28 Q26 26 24 20 Q22 14 16 3 Z' fill='%23D9A02A'/%3E%3C/g%3E%3Cpath d='M16 3 Q10 14 8 20 Q6 26 10 28 Q14 30 16 30 Q18 30 22 28 Q26 26 24 20 Q22 14 16 3 Z' fill='none' stroke='%232E3A1F' stroke-width='1.7' stroke-linejoin='round'/%3E%3Ccircle cx='13.5' cy='22' r='0.9' fill='%232E3A1F'/%3E%3Ccircle cx='18.5' cy='22' r='0.9' fill='%232E3A1F'/%3E%3Cpath d='M14 25 Q16 26.5 18 25' fill='none' stroke='%232E3A1F' stroke-width='1.1' stroke-linecap='round'/%3E%3C/svg%3E");
  --cartoon-nutri-carbs:    url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 36 36'%3E%3Cg transform='translate(4 4)'%3E%3Cpath d='M16 30 L16 8' stroke='%235C8B4A' stroke-width='2' fill='none'/%3E%3Cellipse cx='16' cy='7' rx='3' ry='4' fill='%23D9A02A'/%3E%3Cellipse cx='12' cy='14' rx='2.5' ry='3.5' fill='%23D9A02A' transform='rotate(-25 12 14)'/%3E%3Cellipse cx='20' cy='14' rx='2.5' ry='3.5' fill='%23D9A02A' transform='rotate(25 20 14)'/%3E%3Cellipse cx='12' cy='21' rx='2.5' ry='3.5' fill='%23D9A02A' transform='rotate(-25 12 21)'/%3E%3Cellipse cx='20' cy='21' rx='2.5' ry='3.5' fill='%23D9A02A' transform='rotate(25 20 21)'/%3E%3C/g%3E%3Cpath d='M16 30 L16 8' stroke='%232E3A1F' stroke-width='1.7' stroke-linecap='round' fill='none'/%3E%3Cellipse cx='16' cy='7' rx='3' ry='4' fill='none' stroke='%232E3A1F' stroke-width='1.5'/%3E%3Cellipse cx='12' cy='14' rx='2.5' ry='3.5' fill='none' stroke='%232E3A1F' stroke-width='1.5' transform='rotate(-25 12 14)'/%3E%3Cellipse cx='20' cy='14' rx='2.5' ry='3.5' fill='none' stroke='%232E3A1F' stroke-width='1.5' transform='rotate(25 20 14)'/%3E%3Cellipse cx='12' cy='21' rx='2.5' ry='3.5' fill='none' stroke='%232E3A1F' stroke-width='1.5' transform='rotate(-25 12 21)'/%3E%3Cellipse cx='20' cy='21' rx='2.5' ry='3.5' fill='none' stroke='%232E3A1F' stroke-width='1.5' transform='rotate(25 20 21)'/%3E%3C/svg%3E");
  --cartoon-illus-chef:     url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cg transform='translate(12 12)'%3E%3Cpath d='M50 120 Q30 120 30 96 Q30 72 50 72 Q50 40 80 40 Q100 28 120 40 Q150 40 150 72 Q170 72 170 96 Q170 120 150 120 Z' fill='%23FBF3DF'/%3E%3Crect x='46' y='120' width='108' height='40' rx='10' fill='%23FBF3DF'/%3E%3C/g%3E%3Cpath d='M50 120 Q30 120 30 96 Q30 72 50 72 Q50 40 80 40 Q100 28 120 40 Q150 40 150 72 Q170 72 170 96 Q170 120 150 120' fill='none' stroke='%232E3A1F' stroke-width='6' stroke-linejoin='round'/%3E%3Crect x='46' y='120' width='108' height='40' rx='10' fill='none' stroke='%232E3A1F' stroke-width='6'/%3E%3Cline x1='46' y1='138' x2='154' y2='138' stroke='%232E3A1F' stroke-width='4'/%3E%3Ccircle cx='84' cy='148' r='4' fill='%232E3A1F'/%3E%3Ccircle cx='116' cy='148' r='4' fill='%232E3A1F'/%3E%3Cpath d='M90 156 Q100 164 110 156' fill='none' stroke='%232E3A1F' stroke-width='4.5' stroke-linecap='round'/%3E%3C/svg%3E");
  --cartoon-illus-clean:    url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cg transform='translate(12 12)'%3E%3Ccircle cx='94' cy='94' r='76' fill='%238CBD42'/%3E%3C/g%3E%3Ccircle cx='94' cy='94' r='76' fill='none' stroke='%232E3A1F' stroke-width='6'/%3E%3Cpath d='M60 94 L84 118 L128 74' fill='none' stroke='%23FBF3DF' stroke-width='14' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* ============================================================
   Riso pill-chip ring — appended override
   Gives the location-row and AI-category pills the same two-plate
   look the circle chips have: green fill offset down-right, ink
   ring aligned and on top.
   ============================================================ */
:is([data-theme="cartoon"], [data-theme="cbtl"]) {
  --cartoon-brush-pill-ring: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 30' preserveAspectRatio='none'><path d='M6 5 Q 20 1, 38 4 Q 58 0, 78 4 Q 92 2, 96 7 Q 100 12, 97 18 Q 94 25, 78 27 Q 58 30, 40 27 Q 22 29, 10 25 Q 1 22, 4 14 Q 5 8, 6 5 Z' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'/></svg>");
}

/* Green plate: offset down-right, sits BEHIND the ink ring. */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .location-chip.active::before,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-ai-cat-chip.active::before {
  transform: translate(4px, 3px);
  z-index: -2;
}

/* Ink ring: aligned, painted ON TOP of the green plate. */
:is([data-theme="cartoon"], [data-theme="cbtl"]) .location-chip.active::after,
:is([data-theme="cartoon"], [data-theme="cbtl"]) .recipe-ai-cat-chip.active::after {
  content: '';
  position: absolute;
  inset: -2px -4px;
  z-index: -1;
  background: var(--ink);
  -webkit-mask: var(--cartoon-brush-pill-ring) center / 100% 100% no-repeat;
  mask: var(--cartoon-brush-pill-ring) center / 100% 100% no-repeat;
}

[data-theme="cbtl"] {
  --bg: #F0E8DC;
  --surface: #FFFFFF;
  --surface-2: #E5D8C2;
  --surface-alt: #EBE0CD;
  --border: #D4C4AB;
  --ink: #4A2E22;
  --muted: #8A7660;
  --primary: #B8A5D0;
  --primary-2: #4A2A5C;
  --primary-ink: #3A1F4A;
  --warn: #B8833B;
  --warn-bg: #F5E4C8;
  --danger: #B83B3B;
  --danger-bg: #F5DADA;
  --hero-grad: linear-gradient(135deg, #A88CC0 0%, #6A4C8A 100%);
  --pill-bg: #EBE0CD;
  --pill-active-bg: #D9C7E0;
  --info: #8B6BAF;
  --info-bg: #ECE4F2;
  --cat-veg-bg: #E4E2D0;
  --cat-fruit-bg: #F0DCC8;
  --cat-meat-bg: #EDD0CC;
  --cat-dairy-bg: #E5D9EE;
  --cat-bakery-bg: #EBD8BE;
  --cat-grain-bg: #E8DCC5;
  --cat-drink-bg: #DCD5E0;
  --cat-snack-bg: #EDDCC5;
  --cat-frozen-bg: #DBD6E3;
  --cat-sauce-bg: #ECD0CC;
  --cat-spice-bg: #E5D5CD;
  --cat-instant-bg: #EBD9BE;
  --cat-clean-bg: #DDD9CB;
  --cat-default-bg: #E8DCC5;
  --nutri-calorie-bg: #F0DCC8;
  --nutri-calorie-ink: #B85A2E;
  --nutri-protein-bg: #F0D3CF;
  --nutri-protein-ink: #B83B3B;
  --nutri-fat-bg: #F5E4C8;
  --nutri-fat-ink: #A87A28;
  --nutri-carbs-bg: #E4E2D0;
  --nutri-carbs-ink: #5C6B3E;
  --glass-bg: color-mix(in srgb, var(--surface) 70%, transparent);
  --glass-border: rgba(255, 255, 255, 0.5);
  --glass-blur: 10px;
  --hero-photo: url('Image/rain-cartoon.jpg');
  --hero-pos: center 40%;
  --hero-shade: 0.45;
  --field-bg: #F0E8DC;
  --field-border: #D4C4AB;
  --card-edge: rgba(255, 255, 255, 0.7);
  --hairline: rgba(0, 0, 0, 0.04);
  --sheet-under: color-mix(in srgb, var(--primary-2) 55%, #000);
  --banner-overlay: linear-gradient(120deg, rgba(50, 25, 60, 0.82) 0%, rgba(80, 45, 90, 0.55) 55%, rgba(50, 25, 60, 0.82) 100%);
  --topbar-strong: 42%;
  --topbar-soft: 18%;
  --tile-edge: color-mix(in srgb, var(--primary) 10%, transparent);
  --tile-shadow: 0 4px 10px color-mix(in srgb, var(--primary) 14%, transparent);
  --body-rim-shadow: 0 -4px 12px rgba(0, 0, 0, 0.06);
  --body-glow-shadow: none;
  --popup-bg: var(--surface);
  --popup-edge: transparent;
  --popup-shadow: 0 16px 40px rgba(0, 0, 0, 0.18), inset 0 1px 0 rgba(255, 255, 255, 0.9);
  --tile-bg: var(--surface);
  --tile-selected-bg: var(--surface-2);
  --tile-selected-shadow: 0 4px 14px color-mix(in srgb, var(--primary) 28%, transparent);
  --amount-ink: #8A3B2E;
  --tabbar-inactive: #B0A08A;
  --fab-ring: var(--surface);
  --input-bg: var(--surface);
  --input-inset-bg: var(--surface-alt);
  --input-focus-bg: var(--surface);
  --input-border: var(--border);
  --input-inset-border: transparent;
  --input-search-border: var(--hairline);
  --row-card-bg: var(--surface);
  --row-card-border: var(--hairline);
  --row-thumb-bg: var(--surface-alt);
  --row-shadow: 0 8px 22px rgba(0, 0, 0, 0.09), 0 1px 3px rgba(0, 0, 0, 0.05);
  --row-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.9);
  --nutri-card-bg: var(--row-card-bg);
  --nutri-card-edge: var(--card-edge);
  --nutri-card-shadow: var(--glass-card-shadow);
  --toast-icon-success: #7ED321;
  --toast-icon-error:   #FF6B6B;
  --toast-icon-warn:    #FFD93D;
}
.swatch-preview.cbtl {
  background: linear-gradient(135deg, #4A2A5C, #F0E8DC);
}

/* CBTL FAB — light purple */
[data-theme="cbtl"] .fab-btn {
  background-color: var(--primary);
  color: var(--primary-ink);
  box-shadow: 0 6px 16px color-mix(in srgb, var(--primary) 45%, transparent);
}
[data-theme="cbtl"] .fab-btn.tab-btn.active {
  color: var(--primary-ink);
}


/* CBTL — promo banner readability */
[data-theme="cbtl"] .banner-categories .category-chip span:last-child {
  color: #FFFFFF;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.70), 0 0 2px rgba(0, 0, 0, 0.55);
}
[data-theme="cbtl"] .banner-categories .category-chip.active .cc-emoji {
  filter:
    drop-shadow(0 0 1.5px #F0E8DC)
    drop-shadow(0 0 1.5px #F0E8DC)
    drop-shadow(0 2px 5px rgba(0, 0, 0, 0.35));
}

/* Cartoon + CBTL — Home search: white pill, no glass wrapper */
/* The wrapping .search-row is a sticky glass band on the base stylesheet.
   Turn it off in these two themes so the pill sits on the page background. */
[data-theme="cartoon"] .search-row,
[data-theme="cbtl"]    .search-row {
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: none;
}
[data-theme="cartoon"] .search-row .search-pill-wrap,
[data-theme="cbtl"]    .search-row .search-pill-wrap {
  background: transparent;
}
/* Pill stays white. */
[data-theme="cartoon"] .search-row .search-pill-wrap input,
[data-theme="cbtl"]    .search-row .search-pill-wrap input {
  background: var(--surface);
  border: none;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.06);
}
[data-theme="cartoon"] .search-row .search-pill-wrap input:focus,
[data-theme="cbtl"]    .search-row .search-pill-wrap input:focus {
  background: var(--surface);
  border: none;
  box-shadow:
    0 0 0 3px color-mix(in srgb, var(--primary) 30%, transparent),
    0 4px 14px rgba(0, 0, 0, 0.06);
  outline: none;
}

/* Cartoon + CBTL — hand-drawn hero/body boundary (v2: full edge) */
[data-theme="cartoon"],
[data-theme="cbtl"] {
  --cartoon-hand-drawn-top: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 36' preserveAspectRatio='none'><path d='M 0 36 L 0 4 Q 4 0, 14 2 Q 32 8, 52 4 Q 72 0, 92 4 Q 112 8, 132 4 Q 152 0, 172 4 Q 190 8, 200 2 L 200 36 Z' fill='black'/></svg>");
  --hd-mask-img: var(--cartoon-hand-drawn-top), linear-gradient(#000, #000);
  --hd-mask-sz:  100% 36px, 100% calc(100% - 36px);
  --hd-mask-pos: top left, left 36px;
  --hd-mask-rep: no-repeat, no-repeat;
}

/* Tab-level panels (Home has no such boundary; Consume / Shopping /
   Recipes / Settings all share .consume-body under their gradient hero). */
[data-theme="cartoon"] .consume-body,
[data-theme="cbtl"]    .consume-body,

/* Modal bodies directly under .modal-add-hero (Item / Bought / Receipt
   Match). Selector uses the sibling combinator so nothing else is hit. */
[data-theme="cartoon"] .modal-add-hero + .modal-body,
[data-theme="cbtl"]    .modal-add-hero + .modal-body,
[data-theme="cartoon"] .modal-add-hero + .bought-list,
[data-theme="cbtl"]    .modal-add-hero + .bought-list,

/* Recipe edit / cook bodies (photo hero above, body curls over it). */
[data-theme="cartoon"] .recipe-edit-body,
[data-theme="cbtl"]    .recipe-edit-body,
[data-theme="cartoon"] .recipe-cook-body,
[data-theme="cbtl"]    .recipe-cook-body,

/* Recipe detail card (sticky white card under the recipe hero photo). */
[data-theme="cartoon"] .recipe-detail-card,
[data-theme="cbtl"]    .recipe-detail-card {
  border-radius: 0;
  box-shadow: none;
  -webkit-mask-image:    var(--hd-mask-img);
  mask-image:            var(--hd-mask-img);
  -webkit-mask-size:     var(--hd-mask-sz);
  mask-size:             var(--hd-mask-sz);
  -webkit-mask-position: var(--hd-mask-pos);
  mask-position:         var(--hd-mask-pos);
  -webkit-mask-repeat:   var(--hd-mask-rep);
  mask-repeat:           var(--hd-mask-rep);
  -webkit-mask-composite: source-over;
  mask-composite:         add;
}

/* Cartoon + CBTL — alerts hero: corners rise up, middle dips */
[data-theme="cartoon"],
[data-theme="cbtl"] {
  /* viewBox 200x24. Corners shallow (y=2). Middle dips deep (y=6..20). */
  --cartoon-hero-rise-wave: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 24' preserveAspectRatio='none'><path d='M 0 0 L 200 0 L 200 2 Q 188 20, 170 14 Q 146 6, 120 12 Q 100 20, 80 12 Q 54 6, 30 14 Q 12 20, 0 2 Z' fill='black'/></svg>");
}

[data-theme="cartoon"] .alerts-hero,
[data-theme="cbtl"]    .alerts-hero {
  border-radius: 0;
  -webkit-mask-image: linear-gradient(#000, #000), var(--cartoon-hero-rise-wave);
          mask-image: linear-gradient(#000, #000), var(--cartoon-hero-rise-wave);
  -webkit-mask-size: 100% calc(100% - 22px), 100% 22px;
          mask-size: 100% calc(100% - 22px), 100% 22px;
  -webkit-mask-position: top left, bottom left;
          mask-position: top left, bottom left;
  -webkit-mask-repeat: no-repeat, no-repeat;
          mask-repeat: no-repeat, no-repeat;
  -webkit-mask-composite: source-over;
          mask-composite: add;
}

/* CBTL — FAB badge readability */
[data-theme="cbtl"] .fab-badge {
  color: #FFFFFF;
  background-color: var(--danger);
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25);
}
[data-theme="cbtl"] .alerts-pill-count,
[data-theme="cbtl"] .topbar-badge {
  color: #FFFFFF;
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25);
}

/* Cartoon + CBTL — success / alert modal: sticker treatment */
/* Sheets: chunky ink border + hard offset shadow. */
[data-theme="cartoon"] .success-modal-sheet,
[data-theme="cbtl"]    .success-modal-sheet,
[data-theme="cartoon"] .alert-modal-sheet,
[data-theme="cbtl"]    .alert-modal-sheet {
  border: 2.5px solid var(--ink);
  box-shadow: 4px 4px 0 var(--ink);
}

/* Icon circles: sticker outline + offset shadow. */
[data-theme="cartoon"] .success-modal-icon,
[data-theme="cbtl"]    .success-modal-icon,
[data-theme="cartoon"] .alert-modal-icon,
[data-theme="cbtl"]    .alert-modal-icon {
  border: 2.5px solid var(--ink);
  box-shadow: 3px 3px 0 var(--ink);
}

/* Primary CTAs inside those popups. */
[data-theme="cartoon"] .success-modal-primary,
[data-theme="cbtl"]    .success-modal-primary,
[data-theme="cartoon"] .recipe-prompt-primary,
[data-theme="cbtl"]    .recipe-prompt-primary {
  background: var(--primary);
  color: var(--primary-ink);
  border: 2.5px solid var(--ink);
  box-shadow: 3px 3px 0 var(--ink);
  font-weight: 800;
  letter-spacing: 0.005em;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  transition: transform 0.10s ease-out, box-shadow 0.10s ease-out;
}
[data-theme="cartoon"] .success-modal-primary:active,
[data-theme="cbtl"]    .success-modal-primary:active,
[data-theme="cartoon"] .recipe-prompt-primary:active,
[data-theme="cbtl"]    .recipe-prompt-primary:active {
  transform: translate(2px, 2px);
  box-shadow: 1px 1px 0 var(--ink);
}

/* Secondary text buttons — subtle, but keep weight in line. */
[data-theme="cartoon"] .success-modal-secondary,
[data-theme="cbtl"]    .success-modal-secondary,
[data-theme="cartoon"] .recipe-prompt-secondary,
[data-theme="cbtl"]    .recipe-prompt-secondary,
[data-theme="cartoon"] .alert-modal-ok-btn,
[data-theme="cbtl"]    .alert-modal-ok-btn,
[data-theme="cartoon"] .alert-modal-cancel-btn,
[data-theme="cbtl"]    .alert-modal-cancel-btn {
  font-weight: 700;
}

/* Cartoon + CBTL — bespoke icons for success + alert modals */

/* Per-theme SVG asset variables. */
[data-theme="cartoon"] {
  --popup-icon-chef: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><path d='M22 62 Q10 62, 10 48 Q10 34, 22 34 Q22 14, 42 14 Q50 8, 58 14 Q78 14, 78 34 Q90 34, 90 48 Q90 62, 78 62 Z' fill='%23FBF3DF' stroke='%232E3A1F' stroke-width='4.5' stroke-linejoin='round'/><rect x='22' y='62' width='56' height='22' rx='4' fill='%23FBF3DF' stroke='%232E3A1F' stroke-width='4.5'/><path d='M34 42 L46 54 L68 32' fill='none' stroke='%238CBD42' stroke-width='8' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  --popup-icon-tick: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><circle cx='50' cy='50' r='40' fill='%238CBD42' stroke='%232E3A1F' stroke-width='7'/><path d='M32 52 L45 65 L70 38' fill='none' stroke='%23FBF3DF' stroke-width='10' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  --popup-icon-err:  url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><circle cx='50' cy='50' r='40' fill='%23E5484D' stroke='%232E3A1F' stroke-width='7'/><path d='M50 28 L50 58' stroke='%23FBF3DF' stroke-width='10' stroke-linecap='round'/><circle cx='50' cy='72' r='5' fill='%23FBF3DF'/></svg>");
}
[data-theme="cbtl"] {
  --popup-icon-chef: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><path d='M22 62 Q10 62, 10 48 Q10 34, 22 34 Q22 14, 42 14 Q50 8, 58 14 Q78 14, 78 34 Q90 34, 90 48 Q90 62, 78 62 Z' fill='%23FBF3DF' stroke='%234A2E22' stroke-width='4.5' stroke-linejoin='round'/><rect x='22' y='62' width='56' height='22' rx='4' fill='%23FBF3DF' stroke='%234A2E22' stroke-width='4.5'/><path d='M34 42 L46 54 L68 32' fill='none' stroke='%23B8A5D0' stroke-width='8' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  --popup-icon-tick: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><circle cx='50' cy='50' r='40' fill='%23B8A5D0' stroke='%234A2E22' stroke-width='7'/><path d='M32 52 L45 65 L70 38' fill='none' stroke='%23FBF3DF' stroke-width='10' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  --popup-icon-err:  url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><circle cx='50' cy='50' r='40' fill='%23B83B3B' stroke='%234A2E22' stroke-width='7'/><path d='M50 28 L50 58' stroke='%23FBF3DF' stroke-width='10' stroke-linecap='round'/><circle cx='50' cy='72' r='5' fill='%23FBF3DF'/></svg>");
}

/* --- Success modal (chef hat) --- */
[data-theme="cartoon"] .success-modal-icon > svg,
[data-theme="cbtl"]    .success-modal-icon > svg { display: none; }
[data-theme="cartoon"] .success-modal-icon,
[data-theme="cbtl"]    .success-modal-icon {
  background-image: var(--popup-icon-chef);
  background-position: center;
  background-repeat: no-repeat;
  background-size: 76px 76px;
}

/* --- Alert modal (tick / exclamation) --- */
[data-theme="cartoon"] .alert-modal-icon > svg,
[data-theme="cbtl"]    .alert-modal-icon > svg { display: none; }
[data-theme="cartoon"] .alert-modal-icon,
[data-theme="cbtl"]    .alert-modal-icon {
  background-position: center;
  background-repeat: no-repeat;
  background-size: 38px 38px;
}
[data-theme="cartoon"] .alert-modal-icon.success,
[data-theme="cbtl"]    .alert-modal-icon.success {
  background-image: var(--popup-icon-tick);
}
[data-theme="cartoon"] .alert-modal-icon.error,
[data-theme="cbtl"]    .alert-modal-icon.error {
  background-image: var(--popup-icon-err);
}


/* ============================================================================
   Bought overlay — pencil reassign + auto badge + as-printed line
   ============================================================================ */

/* Pencil button (3rd column in .bought-header). Reassign an existing row to
   a different product, or a new row to an existing product. */
.bought-pencil {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid var(--input-border);
  background: var(--surface-alt);
  color: var(--muted);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, transform 0.2s var(--ease-ios);
}
.bought-pencil:active {
  transform: scale(0.9);
  background: var(--surface-2);
  color: var(--ink);
}
.bought-pencil svg { width: 14px; height: 14px; pointer-events: none; }

/* Auto badge — shown while a receipt-scanned row is still at its original
   auto-match. Cleared the moment the user reassigns via the pencil. */
.bought-auto-badge {
  display: inline-block;
  margin-left: 6px;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 2px 6px;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--primary) 15%, transparent);
  color: var(--primary);
  vertical-align: middle;
}

/* "as printed: AYAM 1KG" — the raw text from the receipt, shown only when
   it differs from the current (English or reassigned) name. */
.bought-as-printed {
  font-size: 0.72rem;
  color: var(--muted);
  margin-top: 2px;
  font-style: italic;
  line-height: 1.3;
  overflow-wrap: anywhere;
}
