/* ============================================================
   Fridgit · Cold Labs
   Stylesheet for the 5.5" landscape display
   ============================================================ */

/* @property registrations let CSS custom properties animate
   smoothly via transition (instead of hard-snapping). Used by the
   score ring's conic angle. */
@property --score-angle {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

/* ─── Performance overrides ─────────────────────────────────
   Three hard rules that drop Chrome's compositor cost from "thrashing"
   to "calm":

   1. When any overlay is active, hide the entire page tree behind
      it. Chrome stops painting, shadowing, blurring it altogether.
      The .has-overlay class is toggled on .device by ui.show /
      ui.hide in app.js.

   2. Drop backdrop-filter from every internal surface. The .device
      background is a uniform near-black gradient — blurring it
      produces an imperceptible difference, but each backdrop-filter
      forces Chrome to allocate a separate compositor layer and
      re-blur whenever anything in front invalidates. Backdrop-filter
      is now retained ONLY on .overlay, where it visibly dims the
      content underneath.

   3. Containment on every repeated glass surface. `contain: layout
      style` tells Chrome that internal layout/style changes don't
      affect ancestors — so changing one item in the inventory grid
      doesn't trigger a re-layout of the entire grid. We deliberately
      OMIT `paint` containment because that would clip the box-shadow
      that gives tiles their elevation. */
.device.has-overlay .pages,
.device.has-overlay .top-tile-wrap {
  visibility: hidden;
}

/* Backdrop-filter — kept only on .overlay (where it actually dims
   page contents while a modal is up). Every other surface had it
   for "premium feel" but produced no visible effect against the
   uniform dark device background. */
.tile,
.top-tile,
.inv-cell,
.cat-tile,
.modal-close,
.pill,
.pill.primary,
.pill.danger,
.more-pill,
.close-pill,
.top-voice {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/* Paint isolation — each repeated tile becomes its own layer. */
.tile,
.inv-cell,
.cat-tile,
.cart-row,
.row,
.cell,
.modal {
  contain: layout style;
}

/* ─── Tokens ─────────────────────────────────────────────────
   Design language: Apple-inspired display gravitas. Deep ink base
   (true near-black with the faintest cool undertone, no navy/violet
   tint), high-contrast type, and proper glassmorphism — meaning
   layered translucency with a wet top-edge highlight, inset
   bottom darkening, and a close + far floating shadow so tiles
   read as physical glass plates over the surface. */
:root {
  /* Base — display ink. The old palette read as a generic violet
     dark-mode web theme; this reads as "premium hardware". */
  --bg-deep:       #030408;
  --bg-mid:        #07080C;
  --bg-violet:     #0B0C12;   /* token name kept for compat */

  /* Type — slightly warmer white than pure #fff (Apple's "Display
     White") + tighter alpha steps for confident hierarchy. */
  --text:          rgba(245, 245, 247, 0.98);
  --text-muted:    rgba(235, 235, 240, 0.58);
  --text-faint:    rgba(235, 235, 240, 0.34);

  /* Accents — a touch more saturated to hold their weight against
     the deeper black. Coral is more "Display P3 red", amber more
     "system orange", frost slightly cooler. */
  --frost:         rgba(170, 213, 235, 0.96);
  --frost-soft:    rgba(170, 213, 235, 0.10);
  --amber:         rgba(255, 176, 64, 0.95);
  --coral:         rgba(255, 88, 84, 0.96);

  /* Card material — one canonical recipe shared across every
     elevated surface (page tiles, inventory cells, multi-item
     assistant cards, modals' inner cards). Three rules:
       1. A very faint plate fill (just enough to lift off black).
       2. A hairline border 1px below max-vibrance.
       3. A grounded shadow + a thin top-edge inset highlight so
          the card looks like real glass catching one overhead
          light, not like a flat png with a drop shadow.
     Surfaces that need MORE elevation (modals, the device frame)
     use --glass-shadow-lg; surfaces that need less (compact rows,
     pills) use --glass-shadow-sm. */
  --glass-fill:       rgba(255, 255, 255, 0.045);
  --glass-border:     rgba(255, 255, 255, 0.07);
  --glass-highlight:  inset 0 1px 0 rgba(255, 255, 255, 0.04);
  --glass-shadow:     var(--glass-highlight), 0 12px 32px rgba(0, 0, 0, 0.32);
  --glass-shadow-sm:  var(--glass-highlight), 0 6px 18px rgba(0, 0, 0, 0.22);
  --glass-shadow-lg:  var(--glass-highlight), 0 24px 64px rgba(0, 0, 0, 0.45);

  /* Geometry — 5:3 aspect (1280×768) matches the Raspberry Pi 7" DSI
     panel (800×480) exactly, so kiosk scale-to-fit fills it edge-to-edge
     with no letterbox or distortion. */
  --device-w:      1280px;
  --device-h:      768px;
  --page-pad-x:    28px;
  --page-pad-top:  20px;
  --page-pad-bot:  36px;   /* breathing room at the bottom of each page */
  --header-y:      30px;
  --gutter:        18px;
  /* Bezel buffer — the physical 7" panel has a thicker top bezel than
     expected, so everything pinned to the top edge gets pushed down by
     this much to stay comfortably clear of it. */
  --top-safe:      30px;

  /* Type */
  --font-stack:    'Inter', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;

  /* Motion — Apple's standard physics curve. Used everywhere instead
     of the generic linear/ease so transitions feel like real objects
     responding to a user gesture, not CSS animations. */
  --ease-apple:    cubic-bezier(0.32, 0.72, 0, 1);
  --ease-out:      cubic-bezier(0.22, 1, 0.36, 1);
}

/* ─── Reset ──────────────────────────────────────────────── */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

/* ONE font, everywhere. No mono, no mixed system stacks — the whole UI
   reads in a single typeface (Inter). !important so it wins over every
   per-component font-family and `font:` shorthand. Emoji still render
   via the browser's emoji fallback. */
*, *::before, *::after {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, system-ui, sans-serif !important;
}

html, body {
  font-family: var(--font-stack);
  font-feature-settings: "tnum" 1, "cv11" 1, "ss01" 1;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

body {
  /* True black surround for max contrast against the device frame.
     Replaces the slightly-blue #040712 which was reading as "screen
     in dark mode" rather than "device on a black surface". */
  background: #000;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  padding: 36px;
}

button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
button:focus-visible { outline: 2px solid var(--frost); outline-offset: 2px; }

/* ─── Device frame ───────────────────────────────────────── */
.device {
  width: var(--device-w);
  height: var(--device-h);
  /* The device is a flex child of the column-flex <body>. Without this,
     default flex-shrink:1 collapses its height to the viewport on a
     short screen (e.g. the Pi's 480px) — the internal grid then computes
     against the squished height and the billboard hero text overflows
     its cell. Keep the layout box at the full design size; fitDevice()
     scales it to fit the panel instead. */
  flex-shrink: 0;
  border-radius: 36px;
  overflow: hidden;
  position: relative;
  /* Layered shadow: long ambient drop, plus a hairline outer ring
     that reads as the chamfered bezel of a real device. */
  box-shadow:
    0 80px 160px rgba(0,0,0,0.85),
    0 30px 60px rgba(0,0,0,0.55),
    0 0 0 1px rgba(255,255,255,0.05),
    inset 0 0 0 1px rgba(255,255,255,0.025);
  /* Background: nearly-pure ink with one very subtle warm bloom
     from the upper-right (suggests a soft overhead light source).
     The two competing red+blue radials of the old design read as
     "designed", not "atmospheric" — one quiet point of light is
     more cinematic. */
  background:
    radial-gradient(ellipse 900px 620px at 80% -10%, rgba(255, 230, 200, 0.05) 0%, transparent 55%),
    radial-gradient(ellipse 1000px 700px at 0% 110%, rgba(170, 213, 235, 0.04) 0%, transparent 60%),
    linear-gradient(170deg, var(--bg-deep) 0%, var(--bg-mid) 55%, var(--bg-violet) 100%);
}

/* ─── Kiosk / real-device mode ───────────────────────────────
   On the actual hardware (Raspberry Pi 7" DSI, 800×480 — or any
   viewport smaller than the 1280×768 design) app.js adds `.kiosk`
   to <body> and `.fit` to the device, and sets a transform: scale()
   so the UI fills the panel edge-to-edge. We drop the desktop
   "floating device" chrome (padding, rounded corners, drop shadow)
   so nothing peeks past the screen edges. Desktop browsers keep the
   mockup frame. */
body.kiosk {
  padding: 0;
  gap: 0;
  height: 100vh;
  min-height: 100vh;
  overflow: hidden;
}
.device.fit {
  border-radius: 0;
  box-shadow: none;
  /* transform is set inline by app.js fitDevice(); scale from the
     center so flex-centering keeps it filling the viewport. */
  transform-origin: center center;
  will-change: transform;
}

/* ─── Views ──────────────────────────────────────────────── */
.view {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease;
}
.view.active { opacity: 1; pointer-events: auto; }
.view-pages     { display: flex; flex-direction: column; }
.view-inventory { display: flex; flex-direction: column; }

/* ─── Top tile ───────────────────────────────────────────────
   Time + weather inline horizontally on the left; clean mic button
   on the right (no decorative orb). All baseline-aligned so the eye
   reads it as one calm strip. */
/* The header floats above the pages (absolute, not in flex flow) so it
   can slide up + fade out in lock-step with a swipe to the camera page
   without leaving a gap or jumping. Content pages pad their top to
   clear it; the camera page goes full-bleed underneath. Opacity +
   transform are driven by scroll position in app.js. */
.top-tile-wrap {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 20;
  padding: var(--top-safe) 28px 0;
  will-change: transform, opacity;
}
.top-tile {
  background: var(--glass-fill);
  border: 1px solid var(--glass-border);
  border-radius: 36px;
  padding: 12px 14px 12px 28px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  height: 78px;
  position: relative;
  box-shadow: var(--glass-shadow-sm);
}

.top-info {
  display: inline-flex;
  align-items: center;
  gap: 18px;
  min-width: 0;
}
/* Ambient clock — Apple uses weight 200 for clocks/glances so the
   number reads as "always there", not as a label. Slightly smaller
   too since the top tile is now 68px tall. */
/* Clock — native SF Pro Display on Apple devices for a refined,
   crisp render that Inter can't match at these mid sizes. */
.top-time {
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Inter', system-ui, sans-serif;
  font-feature-settings: "tnum" 1, "ss01" 1;
  font-variant-numeric: tabular-nums;
  font-size: 28px;
  font-weight: 400;
  color: var(--text);
  letter-spacing: -0.022em;
  line-height: 1;
}
/* Hairline · weather strip — sits beside the clock with a soft
   middot separator. Native SF Pro Display on Apple devices renders
   far cleaner than Inter at these small sizes (Inter at 14-15px
   reads chunky/blocky on dark; SF Pro is tighter and more refined). */
.top-meta {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', system-ui, sans-serif;
  font-size: 16px;
  font-weight: 400;
  color: rgba(235, 235, 240, 0.62);
  letter-spacing: -0.012em;
  font-feature-settings: "tnum" 1;
  font-variant-numeric: tabular-nums;
  padding-left: 16px;
}
.top-meta .weather-glyph {
  width: 15px; height: 15px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: rgba(235, 235, 240, 0.55);
}
.top-meta .weather-glyph svg { width: 15px; height: 15px; }
.top-meta .dot-sep {
  color: rgba(235, 235, 240, 0.30);
  margin: 0 2px;
}

/* "Hey Frost" — frost-tinted glass pill. The wet top highlight
   matches the global tile language; on hover the frost saturates
   slightly without changing scale, the way Apple's prominent
   buttons treat hover. */
/* Pill-shaped Hey Frost button — fully rounded, refined frost wash,
   subtle ambient halo on hover so it feels alive without screaming. */
.top-voice {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: rgba(170, 213, 235, 0.07);
  border: 1px solid rgba(170, 213, 235, 0.20);
  border-radius: 999px;
  padding: 0 20px 0 16px;
  cursor: pointer;
  transition: background 0.20s var(--ease-apple), border-color 0.20s var(--ease-apple), box-shadow 0.20s var(--ease-apple), transform 0.10s var(--ease-apple);
  -webkit-tap-highlight-color: transparent;
  flex-shrink: 0;
  height: 58px;
  padding: 0 24px 0 18px;
}
.top-voice:hover {
  background: rgba(170, 213, 235, 0.13);
  border-color: rgba(170, 213, 235, 0.40);
  box-shadow: 0 0 24px -4px rgba(170, 213, 235, 0.30);
}
.top-voice:active { transform: scale(0.97); }
.top-voice-icon { color: rgba(170, 213, 235, 0.95); }
.top-voice-icon svg { width: 18px; height: 18px; }
.top-voice-title {
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Inter', system-ui, sans-serif;
  font-size: 15px;
  font-weight: 500;
  color: var(--text);
  letter-spacing: -0.012em;
  line-height: 1;
}
/* (Earlier .top-voice-icon / .top-voice-title rules were duplicated
   above — consolidated; older block removed.) */
.top-voice-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.status,
.status > * { pointer-events: auto; }
.status {
  display: flex;
  align-items: center;
  gap: 18px;
  font-family: 'Geist Mono', 'SF Mono', ui-monospace, 'Menlo', monospace;
  font-size: 13px;
  font-weight: 400;
  letter-spacing: 0.02em;
  color: var(--text-muted);
  font-feature-settings: "tnum" 1;
}

.weather-glyph {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px;
  margin-right: -6px;
}
.weather-glyph svg { width: 14px; height: 14px; }

/* ─── Pages container ────────────────────────────────────── */
.pages {
  width: 100%;
  flex: 1;
  min-height: 0;
  display: flex; flex-direction: row; flex-wrap: nowrap;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
}
.pages::-webkit-scrollbar { display: none; }

.page {
  flex: 0 0 100%;
  width: 100%; height: 100%;
  scroll-snap-align: start;
  padding: var(--page-pad-top) var(--page-pad-x) var(--page-pad-bot);
  display: flex; flex-direction: column;
  position: relative;
  min-width: 0;
}
/* The header now floats over the deck (absolute), so content pages pad
   their top to clear it: bezel buffer + taller tile (78) + a gap. */
.page.expiring,
.page.fridge,
.page.restock { padding-top: calc(var(--top-safe) + 96px); }
/* Pages share the device background — no per-page color tint.
   The accent color comes from content (urgent items, primary actions),
   not from the canvas itself. */

/* ─── Full-screen camera page ────────────────────────────────
   Leftmost page in the deck (swipe-right reveals it). The live feed
   bleeds edge-to-edge; controls float over it. The header slides up +
   fades out in sync with the swipe (see syncHeaderToScroll), so the
   camera is truly full-screen with no jump. */
.page-camera {
  padding: 0;
  overflow: hidden;
  background: #000;
}
.page-cam-feed {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  background: #000;
}
/* Top + bottom scrims so white controls stay legible over any feed. */
.page-cam-scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to bottom,
      rgba(0,0,0,0.50) 0%,
      rgba(0,0,0,0)   20%,
      rgba(0,0,0,0)   55%,
      rgba(0,0,0,0.72) 100%);
}
/* Shutter feedback — a soft, quick bloom (not a harsh white slam) that
   confirms the frame was grabbed. */
.page-cam-flash {
  position: absolute;
  inset: 0;
  z-index: 5;
  background: radial-gradient(circle at 50% 46%, rgba(255,255,255,0.92), rgba(255,255,255,0.6) 55%, rgba(255,255,255,0) 78%);
  opacity: 0;
  pointer-events: none;
}
.page-cam-flash.fire { animation: cam-flash 0.5s cubic-bezier(.34,1,.5,1); }
@keyframes cam-flash {
  0%   { opacity: 0;    transform: scale(1.04); }
  16%  { opacity: 0.5;  transform: scale(1);    }
  100% { opacity: 0;    transform: scale(1);    }
}
/* "Looking" state — drifting dot-field + a soft blue edge bloom, the
   same scanning language as the mobile vision lens. The feed dims so the
   dots read clearly. All opacity-driven so it fades in/out as one. */
.page-cam-dots,
.page-cam-glow {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.34s var(--ease-apple);
}
.page-cam-glow {
  box-shadow:
    inset 0 0  90px  16px rgba(170, 213, 235, 0.12),
    inset 0 0 220px  60px rgba(170, 213, 235, 0.06),
    inset 0 0 360px 110px rgba(120, 180, 220, 0.03);
}
.page-camera.scanning .page-cam-dots,
.page-camera.scanning .page-cam-glow { opacity: 1; }
.page-camera.scanning .page-cam-glow { animation: cam-glow-pulse 3.2s ease-in-out 0.34s infinite; }
@keyframes cam-glow-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.6; } }
.page-camera.scanning .page-cam-feed { filter: brightness(0.32) saturate(0.7); transition: filter 0.34s var(--ease-apple); }
.page-cam-feed { transition: filter 0.34s var(--ease-apple); }
.page-cam-back {
  position: absolute;
  top: var(--top-safe); left: 24px;
  z-index: 3;
  width: 64px; height: 64px;
  border-radius: 999px;
  background: rgba(0,0,0,0.32);
  border: 1px solid rgba(255,255,255,0.18);
  backdrop-filter: blur(8px);
  color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.12s var(--ease-apple), background 0.2s var(--ease-apple);
}
.page-cam-back svg { width: 26px; height: 26px; }
.page-cam-back:active { transform: scale(0.92); }

/* Remove / Add segmented toggle, top-center. */
.page-cam-modes {
  position: absolute;
  top: var(--top-safe); left: 50%;
  transform: translateX(-50%);
  z-index: 3;
  display: inline-flex;
  padding: 5px;
  gap: 5px;
  border-radius: 999px;
  background: rgba(0,0,0,0.34);
  border: 1px solid rgba(255,255,255,0.16);
  backdrop-filter: blur(8px);
}
.page-cam-mode {
  border: 0;
  background: transparent;
  color: rgba(255,255,255,0.74);
  font: 600 18px/1 'Inter', system-ui, sans-serif;
  letter-spacing: -0.01em;
  padding: 15px 30px;
  border-radius: 999px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.2s var(--ease-apple), color 0.2s var(--ease-apple);
}
.page-cam-mode.on {
  background: #fff;
  color: #111;
}

/* Identified items, sitting just above the foot controls. Sized big —
   this is a 7" screen viewed from across the kitchen. */
.page-cam-review {
  position: absolute;
  left: 24px; right: 24px;
  bottom: 232px;
  z-index: 3;
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  justify-content: center;
}
.page-cam-review:empty { display: none; }
.page-cam-review .scan-cam-item {
  gap: 14px;
  padding: 16px 28px;
  font-size: 28px;
  border-radius: 999px;
  background: rgba(0,0,0,0.42);
  border-color: rgba(255,255,255,0.18);
}
.page-cam-review .scan-cam-emoji { font-size: 32px; }
.page-cam-review .scan-cam-qty   { font-size: 22px; }

/* Bottom control cluster: hint, shutter, post-capture actions. */
.page-cam-foot {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 3;
  padding: 0 24px 40px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
}
.page-cam-hint {
  font: 600 28px/1.3 'Inter', system-ui, sans-serif;
  color: #fff;
  text-shadow: 0 1px 10px rgba(0,0,0,0.9);
  letter-spacing: -0.01em;
  text-align: center;
}
/* Big post-capture action buttons (Retake / Add / Remove). */
.page-cam-actions .pill {
  font-size: 22px;
  padding: 18px 34px;
  height: auto;
}
.page-cam-shutter {
  width: 92px; height: 92px;
  border-radius: 999px;
  background: #fff;
  border: 6px solid rgba(255,255,255,0.45);
  background-clip: padding-box;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.12s var(--ease-apple), opacity 0.2s var(--ease-apple);
  flex-shrink: 0;
}
.page-cam-shutter:active { transform: scale(0.92); }
.page-cam-shutter.busy {
  opacity: 0.5;
  pointer-events: none;
  animation: cam-pulse 1s ease-in-out infinite;
}
@keyframes cam-pulse { 50% { opacity: 0.85; } }
.page-cam-actions {
  display: flex;
  gap: 12px;
  justify-content: center;
}
.page-cam-actions:empty { display: none; }

/* ─── Surface ────────────────────────────────────────────────
   A tile is a flat dark material with one very subtle wash of
   light at the top and a hairline border. No multi-stop gradients,
   no wet-edge specular highlights, no ::before pseudo-elements.
   The surface is intentionally quiet so the typography can carry
   the design. This is the actual Apple approach — the type IS
   the gravitas; the surface just gets out of the way. */
.tile {
  background: var(--glass-fill);
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-shadow);
  border-radius: 38px;
  padding: 32px 36px;
  position: relative;
  overflow: hidden;
  display: flex; flex-direction: column;
  min-height: 0;
}
/* ─── Pills ──────────────────────────────────────────────────
   Apple's actual button language is more restrained than glass-
   capsule pills. Secondary actions are nearly flat — a faint fill,
   a hairline border, no inset highlights. Primary is a confident
   white plate. Danger is coral text on a faint coral wash.
   Removed: 3-stop gradient fills, inset top highlights, multi-
   layer shadows. The pills now look like Apple, not "web app". */
.pill {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  /* Touch target: ~52px design → ~33px (≈6.5mm) on the Pi panel. */
  min-height: 52px;
  padding: 12px 24px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 999px;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.005em;
  color: var(--text);
  white-space: nowrap;
  transition: background 0.18s var(--ease-apple), border-color 0.18s var(--ease-apple), transform 0.12s var(--ease-apple);
}
.pill:hover  {
  background: rgba(255, 255, 255, 0.10);
  border-color: rgba(255, 255, 255, 0.18);
}
.pill:active { transform: scale(0.97); }

/* Primary — Apple's "filled prominent" — a confident white plate
   with crisp seating shadow. Used for the single most important
   action on a view. */
.pill.primary {
  background: #FFFFFF;
  color: #0A0B0F;
  border-color: transparent;
  padding: 12px 22px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
}
.pill.primary:hover {
  background: #F5F5F7;
}

/* Danger — coral text on a faint coral wash, hairline coral border.
   No "tinted glass" gradient — flat material, just like Apple's
   destructive system buttons. */
.pill.danger {
  background: rgba(255, 88, 84, 0.10);
  border-color: rgba(255, 88, 84, 0.32);
  color: rgba(255, 138, 134, 1);
}
.pill.danger:hover {
  background: rgba(255, 88, 84, 0.16);
  border-color: rgba(255, 88, 84, 0.50);
}

.pill .arrow { font-weight: 400; opacity: 0.9; }

/* "More" pill (entry to inventory) — same recipe as .pill but
   heftier padding so it reaches the 44px tap target for fingers
   on a kiosk-class fridge display. */
.more-pill {
  margin-top: auto;
  align-self: flex-start;
  padding: 13px 24px;
  min-height: 50px;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 999px;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.005em;
  color: var(--text);
  display: inline-flex; align-items: center; gap: 8px;
  transition: background 0.18s var(--ease-apple), border-color 0.18s var(--ease-apple), transform 0.12s var(--ease-apple);
}
.more-pill:hover {
  background: rgba(255, 255, 255, 0.10);
  border-color: rgba(255, 255, 255, 0.18);
  transform: translateY(-1px);
}
.more-pill .arrow { opacity: 0.6; font-size: 13px; font-weight: 300; }

.close-pill {
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 999px;
  padding: 10px 16px 10px 18px;
  font-size: 14px;
  font-weight: 500;
  color: var(--text);
  display: inline-flex; align-items: center; gap: 8px;
  transition: background 0.18s var(--ease-apple), border-color 0.18s var(--ease-apple);
}
.close-pill:hover {
  background: rgba(255, 255, 255, 0.10);
  border-color: rgba(255, 255, 255, 0.18);
}
.close-pill .x { font-size: 16px; line-height: 1; opacity: 0.6; font-weight: 300; }

/* ─── Page 1 layout ──────────────────────────────────────────
   Two-column page: HERO (left, the most-urgent item at billboard
   scale) and the QUEUE (right, what's next). */
.grid-split {
  flex: 1;
  display: grid;
  grid-template-columns: 1.45fr 1fr;
  grid-template-rows: 1fr;
  grid-template-areas: "hero queue";
  gap: var(--gutter);
  min-height: 0;
}
.grid-split .hero    { grid-area: hero; }
.grid-split .queue   { grid-area: queue; }
.grid-split .insight { grid-area: insight; }

.hero {
  display: flex;
  flex-direction: column;
  cursor: pointer;
}
/* Featured-item composition: emoji anchor → name → countdown, with the
   actions pinned to the bottom so the tall card fills intentionally. */
.hero-emoji {
  font-size: 116px;
  line-height: 1;
  margin-bottom: 20px;
}
.hero-count {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin-top: 26px;
  color: var(--text);
}
.hero-count.warn { color: var(--amber); }
.hero-count.crit { color: var(--coral); }
.hero-count-num {
  font-size: 84px; font-weight: 400; line-height: 0.85;
  letter-spacing: -0.05em; font-feature-settings: "tnum" 1;
}
.hero-count-unit {
  font-size: 26px; font-weight: 500; opacity: 0.75;
}
.hero-count-word {
  font-size: 60px; font-weight: 500; letter-spacing: -0.02em; line-height: 0.9;
}
.hero-grid {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: end;
  column-gap: 32px;
  min-height: 0;
}
.hero-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.hero-word {
  font-weight: 500;
  font-size: 108px;
  line-height: 0.88;
  letter-spacing: -0.052em;
  color: var(--text);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.hero-sub {
  font-size: 17px;
  font-weight: 400;
  color: rgba(245, 245, 247, 0.62);
  letter-spacing: -0.005em;
  margin-top: 12px;
  min-height: 22px;
}

/* Typographic countdown. The number IS the design — no icon, no
   box. Color carries urgency. */
.hero-time {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  text-align: right;
  flex-shrink: 0;
  color: var(--text);
}
.hero-time.warn { color: var(--amber); }
.hero-time.crit { color: var(--coral); }
/* Hero numerals — Apple Watch / Apple Health hero readouts use
   weight 380-440 at very-large sizes. The sheer size + heavier
   weight combination is what creates "display gravitas" — thin
   weights at this scale read anorexic on dark backgrounds. */
.hero-time-num {
  font-weight: 400;
  line-height: 0.85;
  letter-spacing: -0.06em;
  color: currentColor;
  font-feature-settings: "tnum" 1;
}
.hero-time-unit {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0;
  color: rgba(245, 245, 247, 0.50);
  margin-top: 12px;
}

.hero-actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: auto;   /* pinned to the bottom of the tall hero card */
  padding-top: 28px;
  flex-shrink: 0;
}
/* Bigger tap targets on the hero actions — fridge UI with possibly
   wet/gloved fingers needs the Apple HIG 44px minimum. */
.hero-actions .pill {
  padding: 12px 22px;
  font-size: 16px;
}

/* ─── Insight tile (bottom-left of home) ─────────────────────
   "$N at risk" — the dollar value of food expiring within the
   week. Compact, single-glance, two actions (Cook / Browse).
   Empty state turns into a quiet "All clear" + activity recap. */
.insight-tile {
  /* Match the horizontal padding of the other tiles (.tile = 32 36) so
     "$N at risk" left-aligns with the hero word directly above it. */
  padding: 26px 36px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 12px;
}
.insight-eyebrow {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--text-faint);
}
.insight-main {
  display: flex;
  align-items: baseline;
  gap: 14px;
  min-width: 0;
}
/* Dollar value stays NEUTRAL — it's information, not a warning.
   Coral / amber are reserved for time/urgency cues. */
.insight-value {
  font-size: 64px;
  font-weight: 500;
  letter-spacing: -0.045em;
  line-height: 1;
  color: var(--text);
  font-feature-settings: "tnum" 1;
}
.insight-sub {
  font-size: 16px;
  font-weight: 400;
  color: rgba(245, 245, 247, 0.62);
  letter-spacing: -0.005em;
}
.insight-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.insight-actions .pill { font-size: 14px; padding: 8px 16px; }
.insight-actions .pill.primary { padding: 9px 18px; }

/* ─── Lists ──────────────────────────────────────────────── */
.rows { display: flex; flex-direction: column; }
/* In the Up-Next queue, rows take the available space and clip cleanly,
   so the "+N more" pill is ALWAYS pinned + visible at the bottom (it was
   getting pushed off the short 800×400 panel). */
.queue .rows { flex: 1; min-height: 0; overflow: hidden; }
.queue .more-pill { flex-shrink: 0; }
/* Clean rows: name (+ owner) on the left, expiry on the right. No
   dividers, no lifeline bars — urgency is carried by the expiry color. */
.row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 18px;
  margin: 0 -12px;
  border-radius: 20px;
  cursor: pointer;
  transition: background 0.15s ease;
}
.row:hover { background: rgba(255,255,255,0.04); }
.row-head { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
.row-name { font-size: 24px; font-weight: 500; color: var(--text); letter-spacing: -0.012em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Communal ownership indicator — who stocked the item (shared fridge). */
.row-owner {
  flex-shrink: 0;
  font-size: 16px; font-weight: 500;
  color: rgba(170, 213, 235, 0.88);
  white-space: nowrap;
}
.row-meta {
  flex-shrink: 0;
  font-size: 19px; font-weight: 500;
  color: rgba(245, 245, 247, 0.55);
  font-feature-settings: "tnum" 1;
}
.row-meta.warn { color: var(--amber); }
.row-meta.crit { color: var(--coral); }

.more-line {
  margin-top: auto;
  padding-top: 14px;
  font-size: 15px;
  color: var(--text-faint);
  font-weight: 500;
}

/* ─── Page 2 — fridge overview ─────────────────────────────────
   A freshness summary (total + a segmented health bar + legend)
   above an information-dense category grid. The summary turns the
   single most important fact — how much food is at risk — into a
   glanceable, color-coded bar instead of grey subtext. */
.fridge-summary {
  background: var(--glass-fill);
  border: 1px solid var(--glass-border);
  border-radius: 28px;
  padding: 18px 26px 20px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  flex-shrink: 0;
  box-shadow: var(--glass-shadow-sm);
}
.fs-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.fs-headline {
  font-size: 30px;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.02em;
  line-height: 1;
}
.fs-unit { font-weight: 500; font-size: 21px; color: rgba(245, 245, 247, 0.5); }

/* Segmented freshness bar — flex weights set inline make each segment
   proportional to its count. */
.fs-bar {
  display: flex;
  gap: 4px;
  height: 24px;
  border-radius: 999px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.05);
}
.fs-seg { display: block; min-width: 8px; border-radius: 999px; }
.fs-seg.fresh { background: var(--frost); }
.fs-seg.warn  { background: var(--amber); }
.fs-seg.crit  { background: var(--coral); }

.fs-legend { display: flex; align-items: center; gap: 20px; }
.fs-leg {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 14px;
  font-weight: 500;
  color: rgba(245, 245, 247, 0.6);
  letter-spacing: -0.005em;
}
.fs-dot { width: 8px; height: 8px; border-radius: 999px; flex-shrink: 0; }
.fs-dot.fresh { background: var(--frost); }
.fs-dot.warn  { background: var(--amber); }
.fs-dot.crit  { background: var(--coral); }

/* ─── Page 2 layout — category tiles ──────────────────────── */
.grid-banner-cats {
  flex: 1;
  display: grid;
  grid-template-rows: auto 1fr;
  gap: var(--gutter);
  min-height: 0;
}

/* 3×2 grid — exactly six cells, fills the available area below the
   banner. Each row gets equal flex height so the 6 cards fill the
   page evenly without auto-rows producing variable heights. */
.cat-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(2, 1fr);
  gap: 18px;
  min-height: 0;
}

/* Category cell — emoji + count/label, with a two-tier water fill
   that visualises urgency:
     • RED (coral) at the bottom for items ≤ 1 day.
     • YELLOW (amber) stacked on top for items 1-3 days.
   Heights driven by --crit-fill and --warn-fill custom properties
   set inline by the template. Both fills fade to transparent at
   their top edge so they blend seamlessly into each other and into
   the dark cell above. */
/* Horizontal hero composition: a large emoji tile on the left, the
   count + label beside it, and the urgency chip on the right edge —
   all vertically centered so the card fills its space instead of
   stranding tiny elements in the corners. Clean glass, soft top edge. */
.cat-tile {
  background:
    linear-gradient(180deg, rgba(255,255,255,0.055) 0%, rgba(255,255,255,0.018) 60%, rgba(255,255,255,0.012) 100%);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 32px;
  padding: 0 27px;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 20px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  text-align: left;
  box-shadow: var(--glass-shadow-sm), inset 0 1px 0 rgba(255,255,255,0.05);
  transition: border-color 0.25s var(--ease-apple), transform 0.2s var(--ease-apple), box-shadow 0.25s var(--ease-apple);
}
/* Status dots — fresh / use-soon / expired counts, top-right corner. */
.cat-dots {
  position: absolute;
  top: 18px; right: 20px;
  display: flex;
  gap: 7px;
}
.cat-dot {
  min-width: 30px;
  height: 30px;
  padding: 0 8px;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font: 700 15px/1 'Inter', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
.cat-dot.fresh { color: #0e1a22; background: var(--frost); }
.cat-dot.warn  { color: #1c1305; background: var(--amber); }
.cat-dot.crit  { color: #fff;    background: var(--coral); }

/* ─── Page 3 — People ─────────────────────────────────────────
   Household roster as profile cards: who owns what + how much is at
   risk. Tap to drill into that person's items. */
.people-page {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.people-title {
  display: flex; align-items: baseline; gap: 12px;
  font-size: 30px; font-weight: 600; color: var(--text); letter-spacing: -0.02em;
  flex-shrink: 0;
}
.people-title-count { font-size: 20px; font-weight: 500; color: rgba(245,245,247,0.4); }
.people-grid {
  flex: 1;
  min-height: 0;
  display: grid;
  /* Cap card width so a 1-2 person household reads as tidy cards, not
     stretched full-width voids; center the roster. */
  grid-template-columns: repeat(auto-fit, minmax(340px, 460px));
  grid-auto-rows: 210px;
  justify-content: center;
  align-content: center;
  gap: 22px;
  overflow: hidden;
}
.person-card {
  background:
    linear-gradient(180deg, rgba(255,255,255,0.055) 0%, rgba(255,255,255,0.018) 60%, rgba(255,255,255,0.012) 100%);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 32px;
  padding: 30px 32px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 18px;
  cursor: pointer;
  text-align: left;
  box-shadow: var(--glass-shadow-sm), inset 0 1px 0 rgba(255,255,255,0.05);
  transition: border-color 0.25s var(--ease-apple), transform 0.2s var(--ease-apple);
}
.person-card:hover  { border-color: rgba(255,255,255,0.16); transform: translateY(-1px); }
.person-card:active { transform: translateY(0); }
.person-head { display: flex; align-items: center; gap: 16px; min-width: 0; }
.person-avatar {
  width: 64px; height: 64px;
  flex-shrink: 0;
  border-radius: 999px;
  background: rgba(170, 213, 235, 0.16);
  border: 1px solid rgba(170, 213, 235, 0.28);
  color: #dcefff;
  display: inline-flex; align-items: center; justify-content: center;
  font: 600 28px/1 'Inter', system-ui, sans-serif;
}
.person-id { min-width: 0; flex: 1; }
.person-name {
  font-size: 26px;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.person-role {
  font-size: 15px;
  font-weight: 500;
  color: rgba(245, 245, 247, 0.45);
  letter-spacing: 0.02em;
}
.person-chevron { flex-shrink: 0; color: rgba(245,245,247,0.3); display: inline-flex; }
.person-foot {
  display: flex;
  align-items: center;
  gap: 12px;
}
.person-count { display: inline-flex; align-items: baseline; gap: 8px; flex-shrink: 0; }
.person-count-num {
  font-size: 46px;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.035em;
  line-height: 0.9;
  font-variant-numeric: tabular-nums;
}
.person-count-unit { font-size: 18px; font-weight: 500; color: rgba(245,245,247,0.5); }
.person-risk    { font-size: 16px; font-weight: 500; color: var(--amber); letter-spacing: -0.005em; }
.person-allgood { font-size: 16px; font-weight: 500; color: rgba(170,213,235,0.85); letter-spacing: -0.005em; }
.person-dots { display: flex; gap: 7px; margin-left: auto; flex-shrink: 0; }
.people-hint {
  flex-shrink: 0;
  text-align: center;
  font-size: 17px;
  font-weight: 500;
  color: rgba(245, 245, 247, 0.4);
  letter-spacing: -0.005em;
  padding: 4px 0;
}
.cat-tile:hover {
  border-color: rgba(255, 255, 255, 0.16);
  transform: translateY(-1px);
}
.cat-tile:active { transform: translateY(0); }

/* Large emoji tile — a real focal element, not a corner sticker. */
.cat-emoji {
  width: 112px; height: 112px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 58px;
  line-height: 1;
  border-radius: 28px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.06);
  user-select: none;
}
.cat-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.cat-count {
  font-size: 64px;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.035em;
  line-height: 0.95;
  font-feature-settings: "tnum" 1;
  font-variant-numeric: tabular-nums;
}
.cat-label {
  font-size: 22px;
  font-weight: 500;
  color: rgba(245, 245, 247, 0.62);
  letter-spacing: -0.012em;
  white-space: nowrap;          /* "Meat & Fish" stays on one line */
}

/* ─── Page 3 layout — Restock as a single tile ──────────────── */
.restock-page {
  flex: 1;
  display: flex;
  min-height: 0;
}
.restock-tile {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: 26px 32px;
  gap: 8px;
}
.restock-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding: 6px 14px 22px;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  margin-bottom: 8px;
}
.restock-head-text { display: flex; flex-direction: column; gap: 4px; }
.restock-headline {
  font-size: 34px;
  font-weight: 500;
  color: var(--text);
  letter-spacing: -0.024em;
  line-height: 1.05;
}
.restock-sub {
  font-size: 15px;
  color: rgba(245, 245, 247, 0.58);
  letter-spacing: -0.005em;
  font-weight: 400;
  margin-top: 4px;
}
.restock-empty {
  text-align: center;
  color: var(--text-muted);
  font-size: 18px;
  padding: 60px 0;
}

.cart-list { display: flex; flex-direction: column; }
.ad-tag {
  font-size: 13px;
  font-weight: 500;
  color: rgba(244,241,235,0.45);
  letter-spacing: 0;
}

/* ─── Inventory view ─────────────────────────────────────── */
.view-inventory { padding: calc(var(--top-safe) + 6px) var(--page-pad-x) 32px; }

.inv-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 4px 14px 22px;
  gap: 24px;
}
.inv-title {
  margin: 0;
  font-size: 32px;
  font-weight: 500;
  color: var(--text);
  letter-spacing: -0.022em;
}
/* Count sits beside the title — slightly thinner color treatment so
   it reads as "X items in this category" rather than "Dairy 5th". */
.inv-count {
  font-weight: 400;
  color: rgba(245, 245, 247, 0.45);
  margin-left: 16px;
  font-feature-settings: "tnum" 1;
  font-size: 26px;
}
.inv-status {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', system-ui, sans-serif;
  font-size: 16px;
  font-weight: 400;
  letter-spacing: -0.012em;
  color: rgba(245, 245, 247, 0.65);
  font-feature-settings: "tnum" 1;
  font-variant-numeric: tabular-nums;
}
.inv-status .weather-glyph {
  width: 15px; height: 15px;
  color: rgba(245, 245, 247, 0.55);
}
.inv-status .weather-glyph svg { width: 15px; height: 15px; }
.inv-status .dot-sep {
  color: rgba(245, 245, 247, 0.25);
}
.inv-status .close-pill {
  margin-left: 12px;
}

/* ─── Inventory carousel ────────────────────────────────────
   Horizontal swipe carousel — iPod / Apple Music album art style.
   The user pushes through items left-to-right with the same
   inertia + scroll-snap as flipping through albums on a Touch
   wheel. Each card is a tall vertical block with a giant emoji
   anchor at the top and the item meta below.

   Implementation: simple flex row with `scroll-snap-type: x mandatory`,
   each card `scroll-snap-align: center`. Padding-left/right on the
   container so first and last cards can center on the screen
   (otherwise the first card pins to the left, the last pins right). */
/* A dense, scrollable grid — many items visible at once and the whole
   panel is used (the old cover-flow carousel wasted the left half when
   there were only a few items). 4 columns on the 7" panel. */
.inv-grid {
  flex: 1;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  align-content: start;
  padding: 4px var(--page-pad-x) 24px;
  scrollbar-width: none;
}
.inv-grid::-webkit-scrollbar { display: none; }

/* Compact card: emoji anchor top-left, name + expiry along the bottom. */
.inv-cell {
  height: 196px;
  background: var(--glass-fill);
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-shadow-sm);
  border-radius: 28px;
  padding: 20px 22px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.18s var(--ease-apple), transform 0.12s var(--ease-apple);
}
.inv-cell:hover  { background: rgba(255, 255, 255, 0.06); }
.inv-cell:active { transform: scale(0.97); }

.cell-art {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  font-size: 64px;
  line-height: 1;
  user-select: none;
  filter: drop-shadow(0 6px 16px rgba(0, 0, 0, 0.35));
}
/* (Legacy SVG icon mount — kept off; we now use emoji art instead.) */
.cell-icon { display: none; }

.cell-content {
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex-shrink: 0;
}
.cell-name {
  font-size: 24px;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.018em;
  line-height: 1.05;
  text-transform: capitalize;   /* tidy "CORN" → "Corn" */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cell-line {
  display: flex;
  align-items: baseline;
  gap: 12px;
  font-size: 18px;
  font-weight: 500;
  color: rgba(245, 245, 247, 0.55);
  letter-spacing: -0.005em;
  font-feature-settings: "tnum" 1;
  font-variant-numeric: tabular-nums;
}
.cell-expiry { font-weight: 600; }
.cell-owner {
  color: rgba(170, 213, 235, 0.88);
  font-weight: 500;
}
.cell-owner::before { content: "· "; color: rgba(245,245,247,0.4); }
.cell-line.warn .cell-expiry { color: var(--amber); }
.cell-line.crit .cell-expiry { color: var(--coral); }

/* Quantity pill — top-right corner. Refined glass capsule, medium
   size: not so tiny it disappears, not so big it competes with the
   emoji. Uses the same flat-material recipe as the rest of the
   pill family (.pill, .more-pill, .close-pill) so it reads as a
   sibling, not a one-off chip. */
.cell-qty-pill {
  position: absolute;
  top: 18px;
  right: 18px;
  z-index: 2;
  background: rgba(255, 255, 255, 0.10);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 999px;
  padding: 6px 14px;
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Inter', system-ui, sans-serif;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.005em;
  color: var(--text);
  font-feature-settings: "tnum" 1;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  pointer-events: none;
  user-select: none;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}

/* ─── Overlays + Modals ──────────────────────────────────────
   Overlay = the dimmed backdrop (its blur affects what's behind).
   Modal  = the floating glass plate sitting on top of the overlay.
   The modal is its own glass surface — slightly darker than the
   tile glass so it reads as "in front" rather than "alongside". */
.overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.62);
  backdrop-filter: blur(20px) saturate(1.15);
  -webkit-backdrop-filter: blur(20px) saturate(1.15);
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
  z-index: 100;
}
.overlay.active { opacity: 1; pointer-events: auto; }

/* Modal — premium dark glass plate. The fill is bumped to
   fully-opaque (was 86-92%) which lets us drop backdrop-filter
   entirely — Chrome was re-blurring it on every paint of the
   wave canvas inside, even though the blur contributed nothing
   visible at near-opaque alpha. The visual is identical and the
   compositor cost drops dramatically. */
.modal {
  background:
    linear-gradient(180deg,
      rgba(14, 15, 20, 1) 0%,
      rgba(8, 9, 13, 1) 100%);
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 46px;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.10),
    0 30px 80px rgba(0, 0, 0, 0.60);
  padding: calc(var(--top-safe) + 8px) 44px 36px;
  position: relative;
  width: calc(100% - 36px);
  height: calc(100% - 36px);
  max-width: none;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transform: scale(0.98);
  transition: transform 0.22s ease;
}
.overlay.active .modal { transform: scale(1); }
/* Wet top-edge specular highlight — brighter than tiles since the
   modal is the focal surface. */
/* Modal top-edge specular line removed — was reading as glassmorphism
   cliché. The modal's hairline border + dim overlay above is enough. */

.modal-close {
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: 50%;
  width: 60px; height: 60px;
  display: flex; align-items: center; justify-content: center;
  color: rgba(245, 245, 247, 0.65);
  font-size: 24px; font-weight: 300;
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition:
    background 0.2s var(--ease-apple),
    border-color 0.2s var(--ease-apple),
    color 0.2s var(--ease-apple),
    transform 0.2s var(--ease-apple);
}
.modal-close:hover {
  background: rgba(255, 255, 255, 0.09);
  border-color: rgba(255, 255, 255, 0.14);
  color: var(--text);
  transform: scale(1.04);
}
.modal-close:active {
  transform: scale(0.96);
}

/* Detail modal — flex column so .detail-stack can flex:1 between head
   and actions. Inherits full-modal sizing from base .modal */
/* Detail modal — uses the whole panel (it's a 7" screen, not a phone).
   Content is distributed top→bottom and everything is large + easy to tap. */
.modal-detail { gap: 0; }
.detail-head {
  display: flex;
  align-items: center;
  gap: 22px;
  flex-shrink: 0;
}
.detail-icon { display: none; }
.detail-title { flex: 1; min-width: 0; }
.detail-name {
  font-size: 58px;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.024em;
  line-height: 1.0;
}
.detail-sub {
  font-size: 22px;
  font-weight: 500;
  color: rgba(245, 245, 247, 0.55);
  margin-top: 8px;
  letter-spacing: -0.005em;
}

/* The stack fills the space between the title and the actions, with the
   headline / stepper / snooze chips spread out generously. */
.detail-stack {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 44px;
  padding: 16px 0;
}
/* Quantity + Snooze sit side-by-side as labeled groups so the wide
   panel is used and the controls aren't crammed into one column. */
.detail-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 56px;
  width: 100%;
}
.detail-group {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 22px;
}
.detail-group-label {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(245, 245, 247, 0.4);
}
.detail-divider {
  width: 1px;
  align-self: stretch;
  margin: 6px 0;
  background: rgba(255, 255, 255, 0.08);
}
.detail-headline {
  font-size: 84px;
  font-weight: 600;
  letter-spacing: -0.028em;
  color: var(--text);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  line-height: 1.05;
}
/* Time urgency stays color-coded — coral for critical, amber for warn.
   Price-trend tints (below/above) are now NEUTRAL — they were tinting
   the dollar value frost-blue (or amber for above) which broke the
   "color = urgency only" rule the rest of the UI follows. The trend
   info still appears in the subtitle as text. */
.detail-headline.crit { color: var(--coral); }
.detail-headline.warn { color: var(--amber); }
.detail-headline-date {
  font-size: 24px;
  font-weight: 500;
  color: rgba(245, 245, 247, 0.55);
  letter-spacing: -0.005em;
}

.detail-stepper {
  display: inline-flex;
  align-items: center;
  gap: 28px;
}
.qty-btn {
  width: 92px; height: 92px;
  border-radius: 50%;
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255, 255, 255, 0.10);
  color: var(--text);
  font-size: 40px;
  font-weight: 400;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 0.18s var(--ease-apple), transform 0.10s var(--ease-apple);
  line-height: 1;
}
.qty-btn:hover  { background: rgba(255,255,255,0.10); border-color: rgba(255,255,255,0.18); }
.qty-btn:active { transform: scale(0.94); }
.qty-display {
  font-size: 60px;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.012em;
  min-width: 110px;
  text-align: center;
  font-feature-settings: "tnum" 1;
}

.detail-chips {
  display: inline-flex;
  gap: 16px;
  flex-wrap: wrap;
  justify-content: center;
}
.snooze-btn {
  padding: 20px 36px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 999px;
  font-size: 24px;
  font-weight: 500;
  color: var(--text);
  transition: background 0.18s var(--ease-apple), border-color 0.18s var(--ease-apple);
}
.snooze-btn:hover { background: rgba(255, 255, 255, 0.10); border-color: rgba(255, 255, 255, 0.18); }

.detail-actions {
  display: flex;
  gap: 20px;
  justify-content: center;
  flex-shrink: 0;
}
.detail-actions .pill {
  flex: 1;
  max-width: 420px;
  min-height: 88px;
  padding: 0 32px;
  font-size: 26px;
  justify-content: center;
}
.detail-actions .pill.primary { padding: 0 32px; }

/* Add modal + keyboard */
.modal-add {
  gap: 22px;
}
.add-header { display: flex; justify-content: space-between; align-items: center; }
.add-title { font-size: 26px; font-weight: 500; color: var(--text); letter-spacing: -0.01em; }
.add-head-actions { display: inline-flex; align-items: center; gap: 12px; }

/* Mode pill — switches the sheet between voice and keyboard input. */
.add-mode-toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 44px;
  padding: 0 18px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: var(--text);
  font: 600 15px/1 'Inter', system-ui, sans-serif;
  letter-spacing: -0.01em;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.2s var(--ease-apple), transform 0.12s var(--ease-apple);
}
.add-mode-toggle:hover  { background: rgba(255, 255, 255, 0.11); }
.add-mode-toggle:active { transform: scale(0.95); }

/* Communal "Adding as" picker — only rendered when >1 member. */
.add-as { position: relative; }
.add-as:empty { display: none; }
.add-as-btn {
  display: inline-flex; align-items: center; gap: 10px;
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 999px;
  padding: 10px 18px;
  cursor: pointer;
}
.add-as-pre   { font-size: 14px; color: rgba(245,245,247,0.45); letter-spacing: -0.005em; }
.add-as-name  { font-size: 15px; font-weight: 600; color: var(--text); letter-spacing: -0.01em; }
.add-as-caret { font-size: 13px; color: rgba(245,245,247,0.55); }
.add-as-menu {
  position: absolute; top: calc(100% + 8px); left: 0; z-index: 30;
  min-width: 220px;
  background: #1c1f24;
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 24px;
  padding: 6px;
  box-shadow: 0 18px 48px rgba(0,0,0,0.5);
  display: flex; flex-direction: column; gap: 2px;
}
.add-as-menu[hidden] { display: none; }
.add-as-opt {
  text-align: left;
  font-size: 16px; font-weight: 500; color: var(--text);
  background: transparent; border: 0; border-radius: 999px;
  padding: 13px 15px; cursor: pointer;
}
.add-as-opt:hover { background: rgba(255,255,255,0.06); }
.add-as-opt.on {
  background: rgba(120,170,255,0.16);
  color: #cfe0ff;
}

/* Add-item autocomplete suggestions */
.add-suggest {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: 10px;
  flex: 1;                 /* fill the space between input and keyboard */
  min-height: 1px;
  overflow-y: auto;
  scrollbar-width: none;
}
.add-suggest::-webkit-scrollbar { display: none; }
.add-suggest-label {
  flex-basis: 100%;
  font: 600 13px/1 -apple-system, system-ui, sans-serif;
  letter-spacing: 0;
  color: rgba(245, 245, 247, 0.42);
  margin-bottom: 4px;
}
.add-suggest:empty { display: none; }
.add-suggest-chip {
  display: inline-flex; align-items: center; gap: 9px;
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 999px;
  padding: 13px 20px;
  min-height: 50px;
  font: 500 17px/1 -apple-system, system-ui, sans-serif;
  color: var(--text);
  letter-spacing: -0.01em;
  cursor: pointer;
}
.add-suggest-chip:active { background: rgba(255,255,255,0.12); }
.add-suggest-emoji { font-size: 19px; }

/* "Who's adding this?" prompt */
.modal-who { gap: 22px; max-width: 520px; }
.who-title {
  font: 600 24px/1.2 -apple-system, system-ui, sans-serif;
  color: var(--text);
  letter-spacing: -0.015em;
  text-align: center;
}
.who-list { display: flex; flex-direction: column; gap: 10px; }
.who-opt {
  display: flex; align-items: center; gap: 16px;
  background: rgba(255,255,255,0.05);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 999px;
  padding: 16px 20px;
  cursor: pointer;
}
.who-opt:active { background: rgba(255,255,255,0.10); }
.who-avatar {
  width: 44px; height: 44px; flex: 0 0 44px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: rgba(120,170,255,0.22);
  color: #cfe0ff;
  font: 600 19px/1 -apple-system, system-ui, sans-serif;
}
.who-name {
  font: 500 19px/1.2 -apple-system, system-ui, sans-serif;
  color: var(--text);
  letter-spacing: -0.01em;
}
.add-input-wrap {
  background: rgba(0,0,0,0.22);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 999px;
  padding: 14px 16px 14px 26px;
  min-height: 76px;
  display: flex; align-items: center;
  gap: 16px;
}
/* Quantity stepper inside the add field. */
.add-qty {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  background: rgba(255,255,255,0.05);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 999px;
  padding: 5px;
}
.add-qty-btn {
  width: 48px; height: 48px;
  border-radius: 999px;
  border: 0;
  background: transparent;
  color: var(--text);
  font-size: 30px;
  font-weight: 400;
  line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.15s var(--ease-apple), transform 0.1s var(--ease-apple);
}
.add-qty-btn:hover  { background: rgba(255,255,255,0.10); }
.add-qty-btn:active { transform: scale(0.92); }
.add-qty-val {
  min-width: 40px;
  text-align: center;
  font-size: 26px;
  font-weight: 600;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.add-input {
  flex: 1;
  font-size: 28px; font-weight: 400;
  color: var(--text);
  letter-spacing: -0.005em;
  min-height: 34px;
  line-height: 1.2;
  word-break: break-word;
}
.add-input:empty::before { content: 'Type item name'; color: rgba(244,241,235,0.30); }
.add-input.voice:empty::before { content: 'Say an item name…'; }
.add-cursor {
  display: inline-block;
  width: 2px; height: 30px;
  background: rgba(157,203,226,0.85);
  margin-left: 2px;
  vertical-align: middle;
  animation: blink 1s steps(2) infinite;
}
@keyframes blink { 50% { opacity: 0; } }

/* ─── Voice add panel ─────────────────────────────────────────
   The default add mode: a big tappable mic, a status line, and a
   commit button that appears once a name is heard. Sits at the
   bottom of the sheet (where the keyboard would be) for thumb reach. */
.voice-panel {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  padding: 16px 0 8px;
}
.voice-mic {
  position: relative;
  width: 108px;
  height: 108px;
  border-radius: 999px;
  border: 1px solid rgba(170, 213, 235, 0.22);
  background: rgba(170, 213, 235, 0.08);
  color: rgba(220, 240, 255, 0.95);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.2s var(--ease-apple), transform 0.12s var(--ease-apple), border-color 0.2s var(--ease-apple);
}
.voice-mic:active { transform: scale(0.95); }
/* Listening — coral wash + an expanding pulse ring. */
.voice-mic.listening {
  background: rgba(232, 74, 71, 0.16);
  border-color: rgba(232, 74, 71, 0.45);
  color: #ffd9d7;
}
.voice-mic-ring {
  position: absolute;
  inset: -4px;
  border-radius: 999px;
  border: 2px solid rgba(232, 74, 71, 0.5);
  opacity: 0;
  pointer-events: none;
}
.voice-mic.listening .voice-mic-ring { animation: voice-pulse 1.4s ease-out infinite; }
@keyframes voice-pulse {
  0%   { transform: scale(1);    opacity: 0.7; }
  100% { transform: scale(1.45); opacity: 0;   }
}
.voice-status {
  font: 500 18px/1.2 'Inter', system-ui, sans-serif;
  color: rgba(245, 245, 247, 0.62);
  letter-spacing: -0.005em;
}
.voice-commit {
  min-width: 220px;
  justify-content: center;
}

/* Keyboard sticks to the bottom of the modal — closer to the touch
   surface, less reach for the user. The flex parent (.modal) is a
   column, so margin-top: auto pushes the keyboard down and lets the
   input + title sit at the top where they're most visible. */
.keyboard {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: auto;
}
.kb-row { display: flex; gap: 8px; justify-content: center; }
.kb-key {
  flex: 1;
  height: 62px;
  background: rgba(255,255,255,0.05);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 999px;
  color: rgba(244,241,235,0.92);
  font-size: 19px; font-weight: 400;
  display: flex; align-items: center; justify-content: center;
  transition: all 0.12s;
  user-select: none;
}
.kb-key:hover  { background: rgba(255,255,255,0.08); }
.kb-key:active { background: rgba(255,255,255,0.16); transform: scale(0.95); }
.kb-key.wide   { flex: 1.5; }
.kb-key.space  { flex: 5; }
.kb-key.enter {
  background: rgba(244,241,235,0.92);
  color: var(--bg-deep);
  font-weight: 500;
  flex: 1.5;
}
.kb-key.enter:hover { background: rgba(244,241,235,1); }
.kb-key.del svg { width: 20px; height: 20px; }

/* ═══════════════════════════════════════════════════════════
   Assistant — visual stage (one query, one big visual response,
   no chat-log accumulation). Tokens used: --frost (cool), --coral
   (urgent), --amber (warn). Layout fills the modal and the
   centerpiece dominates.
   ═══════════════════════════════════════════════════════════ */
.modal-assistant {
  padding: 0;
  display: flex;
  flex-direction: column;
}

.modal-assistant-inner {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: calc(var(--top-safe) + 6px) 36px 24px;
  gap: 12px;
  min-height: 0;
}

.ax-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-shrink: 0;
}

.ax-state {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0;
  color: rgba(245, 245, 247, 0.55);
  padding: 6px 14px 6px 12px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 999px;
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition: color 0.2s var(--ease-apple), background 0.2s var(--ease-apple);
}
.ax-state-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: rgba(244, 241, 235, 0.30);
  flex-shrink: 0;
}
.ax-state.listening { color: var(--text); }
.ax-state.listening .ax-state-dot {
  background: var(--coral);
  box-shadow: 0 0 12px rgba(232,74,71,0.55);
  animation: ax-pulse 1s ease-in-out infinite;
}
.ax-state.thinking { color: var(--text); }
.ax-state.thinking .ax-state-dot {
  background: var(--frost);
  box-shadow: 0 0 10px rgba(157,203,226,0.55);
  animation: ax-pulse 0.6s ease-in-out infinite;
}
.ax-state.speaking { color: var(--text); }
.ax-state.speaking .ax-state-dot {
  background: var(--frost);
  box-shadow: 0 0 12px rgba(157,203,226,0.55);
  animation: ax-pulse 0.4s ease-in-out infinite;
}
@keyframes ax-pulse {
  0%, 100% { transform: scale(1);   opacity: 1;   }
  50%      { transform: scale(1.6); opacity: 0.45; }
}

/* Body — clean text-only layout. No orb. When there's no result yet,
   the query (or live transcript) is centered. When a result is showing,
   query goes small at top, visual takes the middle, reply caption sits
   below. The padding-top transition is animated so the layout shift
   when a result lands feels like a settle, not a jump. */
.ax-body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  padding: 12px 8px;
  transition: padding-top 0.32s cubic-bezier(.2,.8,.2,1);
}
.modal-assistant-inner.no-result .ax-body {
  justify-content: center;
}
.modal-assistant-inner.has-result .ax-body {
  justify-content: flex-start;
  padding-top: 22px;
}

/* Query line — what the user said. Big and centered when there's no
   result yet (focal element); compact at the top when a result is
   showing. */
/* Query line — what the user said. Bumped to weight 500 because at
   thin weights against pure black on a kiosk display, Inter renders
   washed out. 500 is still ambient (not bold) but actually legible. */
.ax-query {
  font-size: 28px;
  font-weight: 500;
  letter-spacing: -0.018em;
  color: var(--text);
  text-align: center;
  line-height: 1.2;
  min-height: 32px;
  max-width: 88%;
  transition:
    opacity 0.28s var(--ease-apple),
    color 0.28s var(--ease-apple),
    font-size 0.32s var(--ease-apple),
    font-weight 0.28s var(--ease-apple);
}
.ax-query.live    { color: var(--text); font-weight: 500; }
/* The "what the user said" caption above a tool result — lighter
   color + weight so it reads as memory of the question, not as a
   competing heading. */
.ax-query.static  {
  color: rgba(245, 245, 247, 0.50);
  font-size: 19px;
  font-weight: 400;
  letter-spacing: -0.010em;
}
.ax-query.hidden  { opacity: 0; }
.modal-assistant-inner.no-result .ax-query.live    { font-size: 48px; font-weight: 500; }
.modal-assistant-inner.no-result .ax-query.hidden  { display: none; }

/* Centerpiece — visual response area. Always present (empty when
   there's no result) so the layout doesn't reflow as visuals come
   and go and the canvas elements above/below stay anchored.
   `overflow: hidden` is a hard backstop against tall content (long
   lists) bleeding up into the query line or down into the reply. */
.ax-visual {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
  width: 100%;
  padding: 8px 0;
  overflow: hidden;
}
.ax-visual:empty { flex: 0; padding: 0; }
/* Visuals bloom into place — scale + opacity, not translate — so
   the halo feels like it's lighting up rather than sliding in
   from below. easeOutQuint curve: fast-out / slow-settle, the
   premium-feeling entrance Apple uses on sheet presentations. */
.ax-visual > * { animation: ax-bloom 0.6s cubic-bezier(.22, 1, .36, 1) both; }
@keyframes ax-bloom {
  from { opacity: 0; transform: scale(0.88); filter: blur(6px);  }
  to   { opacity: 1; transform: scale(1);    filter: blur(0);     }
}

.ax-spinner {
  width: 60px; height: 60px;
  border-radius: 50%;
  border: 3px solid rgba(157,203,226,0.20);
  border-top-color: var(--frost);
  animation: ax-orb-spin 0.9s linear infinite;
}
@keyframes ax-orb-spin { to { transform: rotate(360deg); } }

/* Tool-result answer card.

   Two flavors share the .ax-card frame:

   - default (action / status confirmation): refined column stack
     — small glyph, eyebrow verb in caps, focal name, single-line
     meta. Used for "Added eggs", undo confirmations, empties,
     pick-of-the-day, etc.

   - .ax-card--stat (data answer with a hero numeral): 2-column
     baseline-aligned grid — small glyph above a hero numeric on
     the left, name + meta stacked to the right. Used for
     find_item counts, time, weather, running timers.

   Numerics are always white at any size; accent colors (frost /
   coral / amber) move to the eyebrow and glyph. The hero number
   is the answer — never tinted, so it never reads as "label". */
.ax-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 4px;
  padding: 4px 32px;
  width: 100%;
  max-width: 620px;
}

/* ──────────────────────────────────────────────────────────
   Halo layout — food + weather answer cards.

   The visual stage is a soft radial halo with the food/weather
   glyph floating in the middle. The halo carries the data:
     - SIZE  encodes how much shelf life remains (big = lots,
             small = nearly gone).
     - COLOR encodes the urgency tier on a continuous green →
             amber → red gradient, computed in JS so an 8-day
             item is visibly different from a 12-day item.
   A small numeric + caps name sits below the halo. The text
   bands above (query) and below (spoken reply) already say the
   answer; the halo's job is to make you FEEL it.

   Both --halo-size and --halo-color are set as inline custom
   properties on .ax-halo by the template — see haloFor() in
   app.js. The ::before pseudo paints the bloom; the emoji /
   SVG sits on top in normal flow.
   ────────────────────────────────────────────────────────── */
.ax-card--halo {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 0;
  width: 100%;
  max-width: none;
  gap: 22px;
}
/* Caption under the glyph tile — eyebrow verb + item name. Gives the
   center real composition instead of a lone floating emoji. */
.ax-halo-cap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  text-align: center;
}
.ax-halo-eyebrow {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--frost);
}
.ax-halo-eyebrow.tier-green { color: rgba(120, 217, 122, 0.95); }
.ax-halo-eyebrow.tier-amber { color: var(--amber); }
.ax-halo-eyebrow.tier-coral { color: var(--coral); }
.ax-halo-eyebrow.tier-frost { color: var(--frost); }
.ax-halo-title {
  font-size: 34px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text);
  line-height: 1.05;
}

/* Halo stage. Three layers of light render the bloom:

     - ::before  →  the CORE bloom: a tight radial gradient with
                    three color stops (bright core → mid wash →
                    soft outer fade → transparent). Drives the
                    sense of color intensity right next to the
                    subject. Blurred ~22px for a clean fade with
                    no banding.

     - ::after   →  the AMBIENT bloom: a much wider, much softer
                    gradient at low opacity, blurred ~52px. Gives
                    the halo reach without making the core hotter.
                    It's what makes the glow feel atmospheric
                    instead of stuck to the subject.

     - subject  →  emoji or SVG at premium scale, drop-shadowed
                    so it grounds against the bloom rather than
                    floating on top of it.

   Both bloom layers breathe on a slow 6s cycle — never noticeable
   directly, but the screen stops feeling like a static png.

   Color stops are passed in as inline custom properties:
     --halo-size    radius of the core bloom (relative to the box)
     --halo-bright  innermost color, ~0.8 alpha
     --halo-mid     mid-stop color, ~0.5 alpha
     --halo-soft    outer color, ~0.18 alpha
   See haloFor() / staticHalo() / weatherHalo() in app.js. */
/* Glyph tile — the subject sits in a clean, contained rounded tile
   with a soft tier-tinted aura (no giant raw radial cloud). --halo-mid
   carries the tier color from JS (haloFor / staticHalo). Deliberate
   and well-proportioned, not a sticker floating in fog. */
.ax-halo {
  --halo-mid: rgba(157, 203, 226, 0.32);
  position: relative;
  width:  208px;
  height: 208px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 52px;
  /* Neutral hairline (no glowing colored outline that "cut off"), and a
     gentle inner wash that fades almost to the edge so there's no hard
     ring. The hue lives mostly in the soft outer aura below. */
  background:
    radial-gradient(circle at 50% 46%, var(--halo-mid) 0%, transparent 92%),
    rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.07);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
/* Soft outer aura — wide, heavily blurred, low opacity → the glow has no
   visible boundary; it just melts into the black. */
.ax-halo::after {
  content: '';
  position: absolute;
  inset: -28%;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%, var(--halo-mid) 0%, transparent 62%);
  filter: blur(46px);
  opacity: 0.5;
  z-index: -1;
  pointer-events: none;
  animation: ax-halo-breathe 6s ease-in-out infinite;
}
@keyframes ax-halo-breathe {
  0%, 100% { opacity: 0.44; transform: scale(1.00); }
  50%      { opacity: 0.56; transform: scale(1.06); }
}
.ax-halo.pulse::after { animation: ax-halo-breathe 1.5s ease-in-out infinite; }

.ax-halo__emoji {
  position: relative;
  z-index: 1;
  font-size: 96px;
  line-height: 1;
  user-select: none;
  filter: drop-shadow(0 8px 20px rgba(0, 0, 0, 0.45));
}
.ax-halo__emoji:has(> svg) { filter: none; }
.ax-halo__emoji svg {
  width:  84px;
  height: 84px;
  stroke-width: 1.5;
  color: rgba(245, 245, 247, 0.96);
}
/* Inline time inside the halo (for axTimer countdown) — replaces
   the emoji, sized to fit the same footprint. */
.ax-halo__time {
  position: relative;
  z-index: 1;
  font-size: 72px;
  font-weight: 300;
  letter-spacing: -0.03em;
  line-height: 1;
  color: var(--text);
  font-feature-settings: "tnum" 1, "ss01" 1;
  white-space: nowrap;
  user-select: none;
}

/* (Legacy .ax-card--stat, .ax-card__*, .ax-icon-xl, .ax-item-*,
    .ax-answer* removed — every tool-call template now uses the
    .ax-card--halo layout above.) */

/* List visual (expiring, category). Capped at 6 in a 3-col grid so
   each cell breathes on a 5.5" panel. The optional list-title was
   colliding with the static query line above it — dropped, since
   the query text already names the topic ("what's expiring soon"). */
/* List tile sizes were too large for the visual area (visible top/
   bottom clipping with 6 items in a 3-col grid). Tightened across
   the board so 6 tiles in 2 rows comfortably fit. */
.ax-list {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}
.ax-list-title {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--text-faint);
}
.ax-list-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
  width: 100%;
  max-width: 920px;
}
.ax-list.ax-list-3 .ax-list-grid {
  grid-template-columns: repeat(3, 1fr);
  max-width: 760px;
  gap: 10px;
}
.ax-list.ax-list-2 .ax-list-grid {
  grid-template-columns: repeat(2, 1fr);
  max-width: 560px;
  gap: 12px;
}
.ax-tile {
  background: var(--glass-fill);
  border: 1px solid var(--glass-border);
  border-radius: 28px;
  padding: 24px 18px 18px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  text-align: center;
  min-height: 0;
  position: relative;
  box-shadow: var(--glass-shadow-sm);
  transition: transform 0.25s var(--ease-apple),
              border-color 0.25s var(--ease-apple),
              background 0.25s var(--ease-apple),
              box-shadow 0.25s var(--ease-apple);
}
.ax-tile.warn {
  border-color: rgba(255, 176, 64, 0.22);
  background: linear-gradient(180deg, rgba(255, 176, 64, 0.06), rgba(255, 176, 64, 0.02));
}
.ax-tile.crit {
  border-color: rgba(255, 88, 84, 0.26);
  background: linear-gradient(180deg, rgba(255, 88, 84, 0.07), rgba(255, 88, 84, 0.02));
}
/* Small Apple-emoji icon for assistant list / suggestion tiles. The
   container holds a single emoji character at ~50px so it reads as
   real artwork (not generic line-art) in the same family as the
   inventory carousel. */
.ax-tile-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 56px;
  line-height: 1;
  user-select: none;
  filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.4));
  margin-bottom: 4px;
}
.ax-tile-icon svg { width: 36px; height: 36px; stroke-width: 1.5; }
.ax-tile-name {
  font-size: 18px;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.012em;
  line-height: 1.1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.ax-tile-meta {
  font-size: 16px;
  font-weight: 500;
  color: rgba(245, 245, 247, 0.55);
  font-feature-settings: "tnum" 1;
  letter-spacing: -0.005em;
}
.ax-tile-meta.warn { color: var(--amber); }
.ax-tile-meta.crit { color: var(--coral); }
.ax-list-more {
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  background: rgba(255,255,255,0.05);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 999px;
  padding: 8px 18px;
  cursor: pointer;
  transition: background 0.2s, color 0.2s;
}
.ax-list-more:hover { background: rgba(255,255,255,0.10); color: var(--text); }

/* Recipes visual — name + time/count on top, ingredient chips
   below colored by have/missing. Answers "can I make this" at a
   glance instead of just naming a dish. */
.ax-recipes {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 14px;
  width: 100%;
  max-width: 920px;
}
.ax-recipe {
  background: var(--glass-fill);
  border: 1px solid var(--glass-border);
  border-radius: 26px;
  padding: 22px 26px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  box-shadow: var(--glass-shadow-sm);
}
.ax-recipe-status {
  margin-top: 6px;
  font-size: 17px;
  font-weight: 500;
  color: rgba(232, 165, 71, 0.95);
  letter-spacing: -0.005em;
}
.ax-recipe-status.ready { color: var(--frost); }
.ax-recipe.ready {
  border-color: rgba(157,203,226,0.30);
  background: linear-gradient(180deg, rgba(157,203,226,0.10), rgba(157,203,226,0.02));
  box-shadow:
    inset 0 1px 0 rgba(157, 203, 226, 0.10),
    0 10px 32px rgba(157, 203, 226, 0.08);
}
.ax-recipe-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.ax-recipe-name {
  font-size: 22px;
  font-weight: 500;
  color: var(--text);
  letter-spacing: -0.01em;
}
.ax-recipe-meta {
  font-size: 15px;
  color: var(--text-muted);
  letter-spacing: -0.005em;
  font-feature-settings: "tnum" 1;
  flex-shrink: 0;
}
.ax-recipe.ready .ax-recipe-meta { color: var(--frost); }
.ax-ingr-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.ax-ingr {
  font-size: 13px;
  font-weight: 500;
  letter-spacing: -0.005em;
  padding: 5px 11px;
  border-radius: 999px;
  border: 1px solid transparent;
}
.ax-ingr.have    { background: rgba(157,203,226,0.12); border-color: rgba(157,203,226,0.25); color: var(--text); }
.ax-ingr.missing { background: rgba(232,165,71,0.10);  border-color: rgba(232,165,71,0.22); color: rgba(232,165,71,0.95); }

.ax-empty {
  font-size: 24px;
  font-weight: 300;
  color: var(--text-muted);
  letter-spacing: -0.01em;
  text-align: center;
}

/* ───── Lifeline bar — small frost/amber/coral progress bar shown
   on the find-card and the single-item list-card. Visualizes the
   fraction of expiry life remaining at a glance. ───── */
.ax-lifeline {
  margin-top: 10px;
  height: 4px;
  width: 100%;
  max-width: 320px;
  background: rgba(255,255,255,0.08);
  border-radius: 2px;
  overflow: hidden;
}
.ax-lifeline-fill {
  height: 100%;
  background: var(--frost);
  border-radius: 2px;
  transition: width 0.4s cubic-bezier(.2,.8,.2,1);
}
.ax-lifeline-fill.warn { background: var(--amber); }
.ax-lifeline-fill.crit { background: var(--coral); box-shadow: 0 0 12px rgba(232,74,71,0.45); }

/* List 2-col modifier (for n=2 case). 3-col modifier already exists
   above. */
.ax-list.ax-list-2 .ax-list-grid {
  grid-template-columns: repeat(2, 1fr);
  max-width: 720px;
  gap: 18px;
}

/* ───── Stats visual — fridge overview at a glance. Big total on
   the left; three urgency-coded counters on the right; a footer
   line with $ at risk this week. Single card so the layout reads
   as one answer, not three. ───── */
.ax-stats {
  width: 100%;
  max-width: 820px;
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-rows: auto auto;
  grid-template-areas:
    "main counters"
    "footer footer";
  gap: 20px 40px;
  padding: 28px 36px;
  background: var(--glass-fill);
  border: 1px solid var(--glass-border);
  border-radius: 32px;
  align-items: center;
  box-shadow: var(--glass-shadow);
}
.ax-stats-main {
  grid-area: main;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.ax-stats-total {
  font-size: 110px;
  font-weight: 400;
  letter-spacing: -0.05em;
  line-height: 0.92;
  color: var(--text);
  font-feature-settings: "tnum" 1;
}
.ax-stats-total-label {
  font-size: 17px;
  color: var(--text-muted);
  letter-spacing: -0.005em;
  margin-top: 6px;
}
.ax-stats-counters {
  grid-area: counters;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  align-content: center;
}
.ax-stats-cell {
  background: var(--glass-fill);
  border: 1px solid var(--glass-border);
  border-radius: 22px;
  padding: 18px 14px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  box-shadow: var(--glass-highlight);
}
.ax-stats-cell.warn {
  border-color: rgba(255, 176, 64, 0.22);
  background: linear-gradient(180deg, rgba(255, 176, 64, 0.07), rgba(255, 176, 64, 0.02));
}
.ax-stats-cell.crit {
  border-color: rgba(255, 88, 84, 0.26);
  background: linear-gradient(180deg, rgba(255, 88, 84, 0.08), rgba(255, 88, 84, 0.02));
}
.ax-stats-num {
  font-size: 40px;
  font-weight: 500;
  letter-spacing: -0.034em;
  line-height: 1;
  color: var(--text);
  font-feature-settings: "tnum" 1;
  font-variant-numeric: tabular-nums;
}
.ax-stats-cell.warn .ax-stats-num { color: var(--amber); }
.ax-stats-cell.crit .ax-stats-num { color: var(--coral); }
.ax-stats-cell-label {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--text-muted);
}
.ax-stats-footer {
  grid-area: footer;
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding-top: 14px;
  border-top: 1px solid rgba(255,255,255,0.08);
  font-size: 16px;
  color: var(--text-muted);
}
.ax-stats-footer-value {
  font-size: 26px;
  font-weight: 400;
  letter-spacing: -0.015em;
  color: var(--text);
  font-feature-settings: "tnum" 1;
}
.ax-stats-footer-label {
  letter-spacing: -0.005em;
}
.ax-stats-footer.ok {
  color: var(--frost);
  font-weight: 500;
  letter-spacing: -0.005em;
  justify-content: center;
}

/* ───── Cart visual — shopping-list overview with prices and
   total. Big total on top, item rows below. ───── */
.ax-cart {
  width: 100%;
  max-width: 760px;
  background: var(--glass-fill);
  border: 1px solid var(--glass-border);
  border-radius: 32px;
  padding: 26px 32px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  box-shadow: var(--glass-shadow);
}
.ax-cart-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 12px;
  border-bottom: 1px solid rgba(255,255,255,0.08);
}
.ax-cart-total {
  font-size: 42px;
  font-weight: 300;
  letter-spacing: -0.025em;
  line-height: 1;
  color: var(--text);
  font-feature-settings: "tnum" 1;
}
.ax-cart-count {
  font-size: 16px;
  color: var(--text-muted);
  letter-spacing: -0.005em;
}
.ax-cart-list {
  display: flex;
  flex-direction: column;
}
.ax-cart-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 8px 0;
}
.ax-cart-row + .ax-cart-row {
  border-top: 1px solid rgba(255,255,255,0.05);
}
.ax-cart-icon {
  width: 32px; height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  line-height: 1;
  flex-shrink: 0;
  user-select: none;
  flex-shrink: 0;
}
.ax-cart-icon svg { width: 22px; height: 22px; }
.ax-cart-name {
  flex: 1;
  font-size: 18px;
  font-weight: 500;
  color: var(--text);
  letter-spacing: -0.005em;
  display: flex;
  align-items: baseline;
  gap: 10px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ax-cart-qty {
  font-size: 14px;
  font-weight: 400;
  color: var(--text-muted);
}
.ax-cart-price {
  font-size: 17px;
  font-weight: 500;
  color: var(--text);
  font-feature-settings: "tnum" 1;
  flex-shrink: 0;
}
.ax-cart-more {
  font-size: 13px;
  letter-spacing: 0;
  color: var(--text-faint);
  text-align: center;
  padding-top: 4px;
}

/* (Legacy .ax-time-big removed — axTime is now a clock-face SVG
   inside .ax-card--halo; axTimer running uses .ax-halo__time
   defined alongside the halo rules above.) */

/* ───── Compare visual — two items side-by-side, "vs" divider in
   the middle. Whichever expires first carries a coral "EXPIRES
   FIRST" tag so the answer is glanceable. ───── */
.ax-compare {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: stretch;
  gap: 14px;
  width: 100%;
  max-width: 880px;
}
.ax-compare-cell {
  background: var(--glass-fill);
  border: 1px solid var(--glass-border);
  border-radius: 26px;
  padding: 24px 22px 20px;
  box-shadow: var(--glass-shadow-sm);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  position: relative;
  text-align: center;
}
.ax-compare-cell.first {
  border-color: rgba(232,74,71,0.45);
  background: linear-gradient(180deg, rgba(232,74,71,0.10), rgba(232,74,71,0.02));
}
.ax-compare-cell.ax-amber.first {
  border-color: rgba(232,165,71,0.45);
  background: linear-gradient(180deg, rgba(232,165,71,0.10), rgba(232,165,71,0.02));
}
/* Tag is in normal flow (was absolute, which clipped against
   ax-visual's overflow:hidden). The slot reserves height even when
   empty, so both compare cells stay vertically aligned. */
.ax-compare-tag-slot {
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.ax-compare-tag {
  background: var(--coral);
  color: #1a0606;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0;
  padding: 4px 10px;
  border-radius: 999px;
  white-space: nowrap;
}
.ax-compare-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 72px;
  line-height: 1;
  user-select: none;
  filter: drop-shadow(0 6px 16px rgba(0, 0, 0, 0.45));
}
.ax-compare-icon svg { width: 50px; height: 50px; stroke-width: 1.5; }
.ax-compare-cell.ax-coral .ax-compare-icon { color: var(--coral); }
.ax-compare-cell.ax-amber .ax-compare-icon { color: var(--amber); }
.ax-compare-cell.ax-frost .ax-compare-icon { color: var(--frost); }
.ax-compare-name {
  font-size: 22px;
  font-weight: 500;
  color: var(--text);
  letter-spacing: -0.01em;
}
.ax-compare-qty {
  font-size: 14px;
  color: var(--text-muted);
  letter-spacing: -0.005em;
}
.ax-compare-expiry {
  font-size: 18px;
  font-weight: 600;
  color: var(--text);
  font-feature-settings: "tnum" 1;
}
.ax-compare-expiry.warn { color: var(--amber); }
.ax-compare-expiry.crit { color: var(--coral); }
.ax-compare-cell .ax-lifeline { margin-top: 4px; max-width: 240px; }
.ax-compare-vs {
  align-self: center;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--text-faint);
  padding: 0 4px;
}

/* ───── Plan visual — Breakfast / Lunch / Dinner stack. Each row:
   meal label (left, fixed width), recipe card (flex-1), readiness
   badge (right, fixed). Ready meals get a frost-tinted background. */
.ax-plan {
  width: 100%;
  max-width: 760px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.ax-plan-row {
  display: grid;
  grid-template-columns: 110px 1fr 64px;
  align-items: center;
  gap: 16px;
  background: var(--glass-fill);
  border: 1px solid var(--glass-border);
  border-radius: 22px;
  padding: 16px 20px;
  box-shadow: var(--glass-shadow-sm);
}
.ax-plan-row.ready {
  background: linear-gradient(180deg, rgba(157,203,226,0.10), rgba(157,203,226,0.02));
  border-color: rgba(157,203,226,0.30);
}
.ax-plan-meal {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--text-muted);
}
.ax-plan-row.ready .ax-plan-meal { color: var(--frost); }
.ax-plan-recipe { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ax-plan-recipe-name {
  font-size: 22px;
  font-weight: 500;
  color: var(--text);
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ax-plan-recipe-meta {
  font-size: 14px;
  color: var(--text-muted);
  letter-spacing: -0.005em;
  font-feature-settings: "tnum" 1;
}
.ax-plan-status {
  font-size: 16px;
  font-weight: 600;
  color: var(--text-muted);
  text-align: center;
  font-feature-settings: "tnum" 1;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 36px;
  border-radius: 999px;
}
.ax-plan-status.ready {
  color: var(--frost);
  background: rgba(157,203,226,0.18);
}
.ax-plan-status.partial {
  color: var(--text);
  background: rgba(255,255,255,0.06);
}
.ax-plan-status svg { width: 22px; height: 22px; }

/* ───── Score visual — 0-100 fridge health score in a circular
   meter. Score number lives in the center; the ring fills a fraction
   based on the score; tier color (frost/amber/coral) is applied to
   both the ring fill and the side-label.  ───── */
.ax-score {
  display: flex;
  align-items: center;
  gap: 40px;
  width: 100%;
  max-width: 760px;
  padding: 18px 28px;
}
.ax-score-ring {
  --ring-size: 220px;
  --ring-thick: 14px;
  --ring-color: var(--frost);
  width: var(--ring-size);
  height: var(--ring-size);
  border-radius: 50%;
  background:
    conic-gradient(var(--ring-color) var(--score-angle, 0deg), rgba(255,255,255,0.08) 0deg);
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  flex-shrink: 0;
}
.ax-score-ring.tier-frost  { --ring-color: var(--frost); }
.ax-score-ring.tier-amber  { --ring-color: var(--amber); }
.ax-score-ring.tier-coral  { --ring-color: var(--coral); }
/* Inner mask for the conic-gradient ring. Uses the modal's gradient
   stops as a near-match so the ring reads as a true ring on the
   actual modal background, not a sticker on a different surface. */
.ax-score-inner {
  width: calc(var(--ring-size) - var(--ring-thick) * 2);
  height: calc(var(--ring-size) - var(--ring-thick) * 2);
  border-radius: 50%;
  background: linear-gradient(180deg, rgba(28,32,58,1), rgba(20,24,46,1));
  border: 1px solid rgba(255,255,255,0.06);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.04);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
}
.ax-score-num {
  font-size: 78px;
  font-weight: 400;
  letter-spacing: -0.04em;
  line-height: 1;
  color: var(--text);
  font-feature-settings: "tnum" 1;
}
.ax-score-of {
  font-size: 13px;
  color: var(--text-faint);
  letter-spacing: 0;
  font-weight: 500;
}
.ax-score-side {
  display: flex;
  flex-direction: column;
  gap: 14px;
  flex: 1;
  min-width: 0;
}
.ax-score-label {
  font-size: 32px;
  font-weight: 300;
  letter-spacing: -0.02em;
  color: var(--text);
  line-height: 1.1;
}
.ax-score-label.tier-frost { color: var(--frost); }
.ax-score-label.tier-amber { color: var(--amber); }
.ax-score-label.tier-coral { color: var(--coral); }
.ax-score-stats {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 16px;
  color: var(--text);
  letter-spacing: -0.005em;
}
.ax-score-stat {
  display: flex;
  align-items: center;
  gap: 10px;
}
.ax-score-stat .dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}
.ax-score-stat .dot.fresh { background: rgba(157,203,226,0.85); }
.ax-score-stat .dot.warn  { background: var(--amber); }
.ax-score-stat .dot.crit  { background: var(--coral); }
.ax-score-stat .dot.dim   { background: rgba(244,241,235,0.30); }

/* ───── Bulk action visual — confirms a many-at-once toss. Big
   number on top, thumbnail strip of icons below, names ribbon. */
.ax-bulk {
  width: 100%;
  max-width: 760px;
  padding: 28px 34px;
  background: linear-gradient(180deg, rgba(255, 88, 84, 0.08), rgba(255, 88, 84, 0.02));
  border: 1px solid rgba(255, 88, 84, 0.22);
  border-radius: 32px;
  display: flex;
  flex-direction: column;
  gap: 18px;
  box-shadow:
    inset 0 1px 0 rgba(255, 88, 84, 0.10),
    0 12px 36px rgba(255, 88, 84, 0.08);
}
.ax-bulk-head {
  display: flex;
  align-items: baseline;
  gap: 14px;
}
.ax-bulk-num {
  font-size: 110px;
  font-weight: 400;
  letter-spacing: -0.05em;
  line-height: 0.92;
  color: var(--coral);
  font-feature-settings: "tnum" 1;
}
.ax-bulk-verb {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--coral);
}
.ax-bulk-thumbs {
  display: flex;
  gap: 8px;
  flex-wrap: nowrap;
  overflow: hidden;
}
.ax-bulk-thumb {
  width: 56px; height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 44px;
  line-height: 1;
  user-select: none;
  filter: drop-shadow(0 3px 8px rgba(0, 0, 0, 0.4));
}
.ax-bulk-thumb svg { width: 30px; height: 30px; stroke-width: 1.5; }
.ax-bulk-thumb.more {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-muted);
}
.ax-bulk-names {
  font-size: 14px;
  color: var(--text-muted);
  letter-spacing: -0.005em;
  line-height: 1.4;
}

/* ───── History visual — recent waste timeline. Big $ value on top,
   compact rows with icon + name + when + line value below. */
.ax-history {
  width: 100%;
  max-width: 760px;
  padding: 24px 30px;
  background: var(--glass-fill);
  border: 1px solid var(--glass-border);
  border-radius: 32px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  box-shadow: var(--glass-shadow);
}
.ax-history-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding-bottom: 12px;
  border-bottom: 1px solid rgba(255,255,255,0.08);
}
.ax-history-total {
  font-size: 42px;
  font-weight: 300;
  letter-spacing: -0.025em;
  line-height: 1;
  color: var(--coral);
  font-feature-settings: "tnum" 1;
}
.ax-history-meta {
  font-size: 14px;
  color: var(--text-muted);
  letter-spacing: -0.005em;
}
.ax-history-list {
  display: flex;
  flex-direction: column;
}
.ax-history-row {
  display: grid;
  grid-template-columns: 28px 1fr auto auto;
  align-items: center;
  gap: 12px;
  padding: 7px 0;
  font-size: 16px;
  letter-spacing: -0.005em;
}
.ax-history-row + .ax-history-row {
  border-top: 1px solid rgba(255,255,255,0.05);
}
.ax-history-icon {
  width: 32px; height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  line-height: 1;
  flex-shrink: 0;
  user-select: none;
}
.ax-history-icon svg { width: 22px; height: 22px; }
.ax-history-name { color: var(--text); font-weight: 500; }
.ax-history-when { color: var(--text-muted); font-size: 13px; }
.ax-history-val {
  color: var(--coral);
  font-weight: 500;
  font-feature-settings: "tnum" 1;
  min-width: 56px;
  text-align: right;
}
.ax-history-more {
  font-size: 13px;
  letter-spacing: 0;
  color: var(--text-faint);
  text-align: center;
  padding-top: 4px;
}

/* (Legacy .ax-pick-recipe / .ax-pick-tag removed — axPick is now a
   halo card; recipe pairing is spoken in the reply caption.) */

/* ───── Nutrition visual — single horizontal stacked bar showing
   distribution across categories, with a legend below. ───── */
.ax-nutri {
  width: 100%;
  max-width: 820px;
  padding: 24px 30px;
  background: var(--glass-fill);
  border: 1px solid var(--glass-border);
  border-radius: 32px;
  display: flex;
  flex-direction: column;
  gap: 20px;
  box-shadow: var(--glass-shadow);
}
.ax-nutri-head {
  display: flex;
  align-items: baseline;
  gap: 14px;
}
.ax-nutri-total {
  font-size: 56px;
  font-weight: 400;
  letter-spacing: -0.04em;
  line-height: 0.95;
  color: var(--text);
  font-feature-settings: "tnum" 1;
}
.ax-nutri-total-label {
  font-size: 15px;
  color: var(--text-muted);
  letter-spacing: -0.005em;
}
.ax-nutri-bar {
  display: flex;
  height: 14px;
  border-radius: 999px;
  overflow: hidden;
  background: rgba(255,255,255,0.05);
  box-shadow: inset 0 1px 0 rgba(0,0,0,0.2);
}
.ax-nutri-seg {
  height: 100%;
  transition: width 0.35s ease;
}
.ax-nutri-seg + .ax-nutri-seg { box-shadow: inset 1px 0 0 rgba(0,0,0,0.2); }
.ax-nutri-legend {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px 24px;
}
.ax-nutri-legend-row {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 14px;
  color: var(--text);
  letter-spacing: -0.005em;
}
.ax-nutri-swatch {
  width: 10px; height: 10px;
  border-radius: 3px;
  flex-shrink: 0;
}
.ax-nutri-legend-name { flex: 1; }
.ax-nutri-legend-count {
  color: var(--text-muted);
  font-feature-settings: "tnum" 1;
}
.ax-nutri-missing-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  font-size: 14px;
  color: var(--text-muted);
  padding-top: 12px;
  border-top: 1px solid rgba(255,255,255,0.06);
}
.ax-nutri-missing-label {
  font-weight: 600;
  letter-spacing: 0;
  font-size: 13px;
  color: var(--text-faint);
}
.ax-nutri-missing {
  font-size: 13px;
  font-weight: 500;
  letter-spacing: -0.005em;
  padding: 4px 11px;
  border-radius: 999px;
  background: rgba(232,165,71,0.10);
  border: 1px solid rgba(232,165,71,0.25);
  color: var(--amber);
}

/* Countdown number — tabular-nums so digits don't shift width on
   tick (visual jitter when 9→10 transitions). Selector hook also
   used by the timer-tick interval to find the live element. */
.ax-timer-num {
  font-variant-numeric: tabular-nums;
}

/* ───── Timer list — multiple active timers in compact rows. ───── */
.ax-timers {
  width: 100%;
  max-width: 720px;
  background: var(--glass-fill);
  border: 1px solid var(--glass-border);
  border-radius: 32px;
  padding: 22px 28px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  box-shadow: var(--glass-shadow);
}
.ax-timers-head {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--text-muted);
  padding-bottom: 10px;
  border-bottom: 1px solid rgba(255,255,255,0.08);
}
.ax-timers-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.ax-timer-row {
  display: grid;
  grid-template-columns: 28px 1fr auto;
  align-items: center;
  gap: 14px;
  padding: 10px 0;
}
.ax-timer-row + .ax-timer-row {
  border-top: 1px solid rgba(255,255,255,0.05);
}
.ax-timer-row-icon { color: var(--frost); }
.ax-timer-row.is-alarm .ax-timer-row-icon { color: var(--amber); }
.ax-timer-row-icon svg { width: 22px; height: 22px; }
.ax-timer-row-label {
  font-size: 17px;
  font-weight: 500;
  color: var(--text);
  letter-spacing: -0.005em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ax-timer-row-num {
  font-size: 22px;
  font-weight: 400;
  color: var(--text);
  font-feature-settings: "tnum" 1;
  letter-spacing: -0.01em;
}

/* ───── Recipe steps — numbered cooking instructions on the left,
   ingredient checklist on the right. Two-column layout fits the
   landscape modal cleanly without scrolling for typical 5-step
   recipes. ───── */
.ax-recipe-steps {
  width: 100%;
  max-width: 920px;
  background: var(--glass-fill);
  border: 1px solid var(--glass-border);
  border-radius: 32px;
  padding: 22px 28px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  box-shadow: var(--glass-shadow);
}
.ax-rs-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 12px;
  border-bottom: 1px solid rgba(255,255,255,0.08);
}
.ax-rs-name {
  font-size: 22px;
  font-weight: 500;
  color: var(--text);
  letter-spacing: -0.01em;
}
.ax-rs-meta {
  font-size: 14px;
  color: var(--text-muted);
  letter-spacing: -0.005em;
  font-feature-settings: "tnum" 1;
}
.ax-rs-body {
  display: grid;
  grid-template-columns: 1.6fr 1fr;
  gap: 28px;
  min-height: 0;
}
.ax-rs-steps {
  display: flex;
  flex-direction: column;
  gap: 10px;
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: step;
}
.ax-step {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  font-size: 14px;
  line-height: 1.45;
  color: var(--text);
  letter-spacing: -0.005em;
}
.ax-step-num {
  flex-shrink: 0;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: rgba(157,203,226,0.15);
  border: 1px solid rgba(157,203,226,0.30);
  color: var(--frost);
  font-size: 13px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 2px;
}
.ax-step-text { flex: 1; min-width: 0; }
.ax-rs-ingr {
  display: flex;
  flex-direction: column;
  gap: 6px;
  list-style: none;
  margin: 0;
  padding: 12px 0 0;
  border-left: 1px solid rgba(255,255,255,0.06);
  padding-left: 18px;
}
.ax-ingr-li {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 14px;
  letter-spacing: -0.005em;
}
.ax-ingr-mark {
  flex-shrink: 0;
  width: 18px; height: 18px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 700;
}
.ax-ingr-li.have    { color: var(--text); }
.ax-ingr-li.missing { color: var(--text-muted); }
.ax-ingr-li.have    .ax-ingr-mark { background: rgba(157,203,226,0.20); color: var(--frost); }
.ax-ingr-li.missing .ax-ingr-mark { background: rgba(232,165,71,0.15);  color: var(--amber); }
.ax-ingr-li-name { flex: 1; min-width: 0; }

/* ───── Suggest visual — "did you mean" when find_item missed.
   Left-anchored heading + eyebrow over a row of suggestion tiles.
   No paragraph echoing what the tiles already say. ───── */
.ax-suggest {
  width: 100%;
  max-width: 720px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 22px;
  padding: 0 8px;
}
.ax-suggest-head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}
.ax-suggest-title {
  font-size: 28px;
  font-weight: 500;
  letter-spacing: -0.022em;
  color: var(--text);
  line-height: 1.1;
}
.ax-suggest-eyebrow {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0;
  color: var(--text-faint);
  line-height: 1;
}
.ax-suggest-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 220px));
  gap: 14px;
  width: 100%;
}

/* Bottom: assistant reply caption. Constrained to a single line on
   the modal width so it can't grow into the visual area; the system
   prompt already enforces short replies, this is a hard backstop. */
.ax-reply {
  font-size: 23px;
  font-weight: 500;
  letter-spacing: -0.012em;
  color: var(--text);
  text-align: center;
  line-height: 1.35;
  min-height: 28px;
  max-height: 66px;
  opacity: 0;
  transition: opacity 0.25s var(--ease-apple);
  padding: 0 32px;
  flex-shrink: 0;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.ax-reply.shown { opacity: 1; }

/* Canvas-based audio waveform + ambient glow. The waveform is the
   primary visual; the glow sits behind it to give the modal "alive"
   ambient lighting that pulses with audio energy. */
.modal-assistant-inner {
  position: relative;
  overflow: hidden;
}
/* The wave glow is now done by the GPU compositor via this CSS
   filter, instead of canvas-level shadowBlur=52px which was
   Chrome/Skia's worst-case CPU bloom path. The drop-shadow color
   is set inline by the canvas draw loop when activity flips
   (frost ↔ coral) — costs nothing per frame. */
.ax-wave {
  width: 100%;
  height: 220px;
  margin-top: auto;
  display: block;
  pointer-events: none;
  position: relative;
  z-index: 1;
  transition: height 0.32s cubic-bezier(.2,.8,.2,1);
  filter: drop-shadow(0 0 12px rgba(157, 203, 226, 0.55));
}
.modal-assistant-inner.has-result .ax-wave { height: 110px; }

.ax-head, .ax-body { position: relative; z-index: 2; }

/* Scan overlay */
.scan-content {
  display: flex; flex-direction: column;
  align-items: center;
  gap: 32px;
  width: 60%; max-width: 600px;
}
.scan-label {
  font-size: 14px;
  letter-spacing: 0;
  color: rgba(244,241,235,0.65);
  font-weight: 500;
}
.scan-receipt {
  width: 100%; height: 140px;
  background: rgba(244,241,235,0.05);
  border: 1px solid rgba(244,241,235,0.10);
  border-radius: 22px;
  position: relative;
  overflow: hidden;
  display: flex; flex-direction: column; justify-content: center;
  padding: 20px;
}
.scan-receipt-line {
  height: 6px;
  background: rgba(244,241,235,0.10);
  margin-bottom: 8px;
  border-radius: 2px;
}
.scan-receipt-line:nth-child(1) { width: 60%; }
.scan-receipt-line:nth-child(2) { width: 40%; }
.scan-receipt-line:nth-child(3) { width: 75%; }
.scan-receipt-line:nth-child(4) { width: 50%; }
.scan-receipt-line:nth-child(5) { width: 65%; }
.scan-sweep {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 3px;
  background: linear-gradient(90deg, transparent, rgba(157,203,226,0.95) 50%, transparent);
  box-shadow: 0 0 18px rgba(157,203,226,0.6);
  animation: scan-down 1.6s linear infinite;
}
@keyframes scan-down {
  0%   { top: 0; }
  50%  { top: calc(100% - 3px); }
  100% { top: 0; }
}
.scan-items {
  display: flex; flex-direction: column;
  gap: 6px;
  width: 100%;
  font-size: 18px;
  font-weight: 400;
  color: rgba(244,241,235,0.85);
}
.scan-item {
  opacity: 0;
  display: flex; align-items: center; gap: 10px;
  animation: scan-fade 0.35s ease forwards;
}
@keyframes scan-fade {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
.scan-item .check { color: var(--frost); font-weight: 500; }

/* ─── Toast ──────────────────────────────────────────────── */
.toast {
  position: absolute;
  bottom: 28px;
  left: 50%;
  transform: translateX(-50%) translateY(20px);
  background: rgba(20, 24, 32, 0.82);
  border: 1px solid rgba(255, 255, 255, 0.10);
  backdrop-filter: blur(30px) saturate(1.4);
  -webkit-backdrop-filter: blur(30px) saturate(1.4);
  border-radius: 999px;
  padding: 13px 26px;
  font-size: 15px;
  font-weight: 500;
  color: var(--text);
  letter-spacing: -0.005em;
  opacity: 0;
  z-index: 200;
  pointer-events: none;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.10),
    0 16px 40px rgba(0, 0, 0, 0.50);
  white-space: nowrap;
  transition: opacity 0.3s var(--ease-out), transform 0.3s var(--ease-out);
}
/* Actionable toast (Undo) — bigger, tappable, with a frost action. */
.toast.has-undo {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 20px;
  padding: 14px 16px 14px 28px;
  font-size: 18px;
}
.toast-undo {
  border: 0;
  background: rgba(170, 213, 235, 0.16);
  color: var(--frost);
  font: 700 17px/1 'Inter', system-ui, sans-serif;
  letter-spacing: -0.01em;
  padding: 12px 24px;
  border-radius: 999px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.toast-undo:active { transform: scale(0.95); }
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.toast.success { border-color: rgba(157,203,226,0.30); }
.toast.error   { border-color: rgba(232,74,71,0.30); }

/* ─── Hint footer ─────────────────────────────────────────── */
.hint {
  font-size: 13px;
  letter-spacing: 0;
  color: rgba(255,255,255,0.45);
  font-weight: 500;
}
.hint kbd {
  display: inline-block;
  padding: 3px 7px;
  margin: 0 4px;
  border: 1px solid rgba(255,255,255,0.20);
  border-radius: 5px;
  font-size: 13px;
  color: rgba(255,255,255,0.70);
}

/* ═══════════════════════════════════════════════════════════
   Restock cart — one row, one line.
   Tap a row → cart-detail modal (qty / remove / history).
   Hairline divider between rows so the list reads as discrete
   items, not one wall of text. Inset the divider so it doesn't
   reach the rounded modal edges.
   ═══════════════════════════════════════════════════════════ */
.cart-row {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 18px 18px;
  margin: 0 -14px;
  border-radius: 20px;
  cursor: pointer;
  transition: background 0.15s ease;
  position: relative;
}
.cart-row + .cart-row::before {
  content: '';
  position: absolute;
  top: 0;
  left: 14px;
  right: 14px;
  height: 1px;
  background: rgba(255, 255, 255, 0.05);
}
.cart-row:hover {
  background: rgba(255, 255, 255, 0.04);
}
.cart-row:hover::before,
.cart-row:hover + .cart-row::before { opacity: 0; }

.cart-icon { display: none; }

.cart-name {
  flex: 1;
  min-width: 0;
  font-size: 22px;
  font-weight: 500;
  color: var(--text);
  letter-spacing: -0.012em;
  display: flex;
  align-items: baseline;
  gap: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Quantity / unit text — bumped readability so "1 gal" / "1 dz"
   actually communicates rather than ghosting out at 0.55 alpha. */
.cart-qty-inline {
  font-size: 16px;
  font-weight: 400;
  color: rgba(245, 245, 247, 0.62);
  letter-spacing: -0.005em;
}
.cart-price {
  font-size: 22px;
  font-weight: 500;
  color: var(--text);
  font-feature-settings: "tnum" 1;
  min-width: 80px;
  text-align: right;
}
.cart-row .pill {
  font-size: 16px;
  padding: 10px 20px;
}

/* ═══════════════════════════════════════════════════════════
   Confirm modal — title + sub + 2 buttons centered vertically.
   Stack is centered in the full-screen modal so the dialog
   reads as a clear focused question, not a form crammed at top.
   ═══════════════════════════════════════════════════════════ */
.modal-confirm {
  position: relative;
  align-items: center;
  justify-content: center;
}
.confirm-x {
  position: absolute !important;
  top: 28px;
  right: 28px;
}
.confirm-stack {
  max-width: 760px;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 18px;
}
.confirm-title {
  font-size: 36px;
  font-weight: 500;
  color: var(--text);
  letter-spacing: -0.02em;
  line-height: 1.2;
}
.confirm-body {
  font-size: 19px;
  color: var(--text-muted);
  letter-spacing: -0.005em;
  line-height: 1.4;
  max-width: 580px;
}
.confirm-actions {
  display: flex;
  gap: 14px;
  margin-top: 14px;
}
.confirm-actions .pill {
  padding: 14px 32px;
  font-size: 17px;
  min-width: 140px;
  justify-content: center;
}
.confirm-actions .pill.primary { padding: 15px 36px; }

/* ═══════════════════════════════════════════════════════════
   Recipe modal — 2-col ingredient grid, no eyebrow labels.
   Each ingredient is a single tag-style row: check + name.
   ═══════════════════════════════════════════════════════════ */
.modal-recipe {
  gap: 24px;
}
.recipe-grid {
  flex: 1;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  align-content: center;
  min-height: 0;
}
.recipe-ingr {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 18px 24px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 999px;
  font-size: 22px;
  font-weight: 500;
  color: var(--text);
  letter-spacing: -0.005em;
}
.recipe-ingr.have    { border-color: rgba(157,203,226,0.25); }
.recipe-ingr.missing { border-color: rgba(232,165,71,0.22); background: linear-gradient(180deg, rgba(232,165,71,0.06), rgba(232,165,71,0.02)); }
.recipe-mark {
  width: 30px; height: 30px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-size: 16px;
  flex-shrink: 0;
}
.recipe-ingr.have .recipe-mark    { background: rgba(157,203,226,0.18); color: var(--frost); }
.recipe-ingr.missing .recipe-mark { background: rgba(232,165,71,0.18); color: var(--amber); }
.recipe-ingr-name { font-weight: 500; }

/* ─── Inventory toolbar ─── search input + filter chips. Rendered
   only when the user is in inventory view (Render.inventory inserts
   the markup). The toolbar lives above the grid; grid has
   overflow-y: auto so the chips stay sticky on long lists. */
.inv-toolbar:empty { display: none; }
.inv-toolbar {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 0 18px 12px;
}
.inv-search-wrap {
  position: relative;
  display: flex;
  align-items: center;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 999px;
  padding: 0 16px 0 38px;
  height: 46px;
  transition: border-color .15s ease, background .15s ease;
}
.inv-search-wrap:focus-within {
  background: rgba(255, 255, 255, 0.09);
  border-color: rgba(157, 203, 226, 0.45);
}
.inv-search-icon {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  width: 16px; height: 16px;
  color: rgba(245, 245, 247, 0.55);
}
.inv-search {
  flex: 1;
  background: transparent;
  border: 0;
  outline: none;
  color: var(--text);
  font: 500 16px/1.2 -apple-system, BlinkMacSystemFont, system-ui;
  letter-spacing: -0.005em;
}
.inv-search::placeholder { color: rgba(245, 245, 247, 0.40); }
.inv-search-clear {
  width: 24px; height: 24px;
  border-radius: 50%;
  border: 0;
  background: rgba(255, 255, 255, 0.10);
  color: var(--text);
  font: 500 16px/1 -apple-system, system-ui;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  padding: 0;
}
.inv-search-clear:active { transform: scale(.94); }
.inv-chips {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
  padding-bottom: 2px;
}
.inv-chips::-webkit-scrollbar { display: none; }
.inv-chip {
  flex: 0 0 auto;
  padding: 8px 14px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(255, 255, 255, 0.04);
  color: rgba(245, 245, 247, 0.85);
  font: 500 13px/1 -apple-system, system-ui;
  letter-spacing: -0.005em;
  white-space: nowrap;
  cursor: pointer;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.inv-chip:active { transform: scale(.97); }
.inv-chip.active {
  background: rgba(157, 203, 226, 0.18);
  color: var(--frost);
  border-color: rgba(157, 203, 226, 0.42);
}

/* Empty-state inside the inventory grid. The grid is a flex column;
   width:100% + text-align:center keeps the empty message centered. */
.inv-empty {
  width: 100%;
  text-align: center;
  color: var(--text-muted);
  font-size: 18px;
  padding: 80px 24px;
}

/* ════════════════════════════════════════════════════════════════
   QR pairing screen
   Mirrors the mobile auth screen: deep blue radial backdrop, single
   glassmorphic card. Wordmark, tagline, QR, six-digit code. Nothing
   else — no eyebrow, no instruction paragraph, no status pill, no
   refresh footer, no kbd-styled chips. The card breathes; the QR
   speaks for itself.
   ════════════════════════════════════════════════════════════════ */
.qr-screen {
  position: fixed;
  inset: 0;
  z-index: 999;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px;
  overflow: hidden;
  background: #04060d;
  animation: qr-screen-fade 0.32s cubic-bezier(0.32, 0.72, 0, 1) both;
}
.qr-screen[hidden] { display: none !important; }
@keyframes qr-screen-fade { from { opacity: 0; } to { opacity: 1; } }
.qr-screen.is-leaving {
  opacity: 0;
  transition: opacity 0.42s cubic-bezier(0.32, 0.72, 0, 1);
  pointer-events: none;
}

/* Drifting blue light-sources behind the glass card. */
.qr-screen-bg {
  position: absolute;
  inset: -10%;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 800px 700px at 22% 28%,
        rgba( 64, 132, 246, 0.55) 0%, rgba( 64, 132, 246, 0) 60%),
    radial-gradient(ellipse 600px 600px at 78% 18%,
        rgba(120, 180, 255, 0.32) 0%, rgba(120, 180, 255, 0) 60%),
    radial-gradient(ellipse 800px 700px at 60% 90%,
        rgba( 30,  90, 200, 0.50) 0%, rgba( 30,  90, 200, 0) 60%),
    linear-gradient(180deg, #050a18 0%, #02040a 100%);
  filter: saturate(1.05);
  animation: qr-bg-drift 18s ease-in-out infinite;
}
@keyframes qr-bg-drift {
  0%, 100% { transform: translate(0,    0)   scale(1.00); }
  33%      { transform: translate(2%,  -2%)  scale(1.02); }
  66%      { transform: translate(-3%,  1%)  scale(1.01); }
}

.qr-screen-card {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 460px;
  padding: 44px 36px 32px;
  border-radius: 32px;
  text-align: center;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.14);
  -webkit-backdrop-filter: blur(40px) saturate(150%);
  backdrop-filter: blur(40px) saturate(150%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.20),
    inset 0 -1px 0 rgba(255, 255, 255, 0.04),
    0 30px 80px rgba(0, 0, 0, 0.50),
    0 0 0 1px rgba(255, 255, 255, 0.04);
  animation: qr-card-in 0.52s cubic-bezier(0.32, 0.72, 0, 1) both;
}
@keyframes qr-card-in {
  from { opacity: 0; transform: translateY(12px) scale(0.96); }
  to   { opacity: 1; transform: translateY(0)    scale(1); }
}

.qr-screen-mark {
  font: 600 40px/1 -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Inter', system-ui, sans-serif;
  letter-spacing: -0.030em;
  color: rgba(255, 255, 255, 0.98);
  margin-bottom: 8px;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.20);
}
.qr-screen-tag {
  font: 500 15px/1 -apple-system, BlinkMacSystemFont, 'SF Pro Display', system-ui, sans-serif;
  letter-spacing: -0.005em;
  color: rgba(255, 255, 255, 0.55);
  margin-bottom: 28px;
}

.qr-screen-frame {
  position: relative;
  display: inline-block;
  background: #fff;
  padding: 14px;
  border-radius: 22px;
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.08),
    0 0 50px 6px rgba(170, 213, 235, 0.18),
    0 18px 36px rgba(0, 0, 0, 0.45);
}
.qr-screen-qr {
  width: 260px;
  height: 260px;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Rounded corners that match the frame's interior curve; the
     QR canvas has a baked-in 10px white margin so this clipping
     never touches the QR cells. */
  border-radius: 14px;
  overflow: hidden;
  transition: opacity 0.32s cubic-bezier(0.32, 0.72, 0, 1);
}
.qr-screen-qr svg,
.qr-screen-qr canvas {
  width: 100%;
  height: 100%;
  display: block;
}

.qr-screen-code {
  margin-top: 22px;
  font: 600 22px/1 -apple-system, system-ui, sans-serif;
  letter-spacing: 0;
  font-feature-settings: "tnum" 1;
  color: rgba(255, 255, 255, 0.78);
}

/* Success overlay — green ring + check drawn over the QR frame
   for the brief moment between claim and signed-in. */
.qr-screen-success {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #fff;
  border-radius: inherit;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.32s cubic-bezier(0.32, 0.72, 0, 1);
}
.qr-screen-card.is-paired .qr-screen-qr { opacity: 0; }
.qr-screen-card.is-paired .qr-screen-success { opacity: 1; }
.qr-screen-card.is-paired .qr-success-ring {
  animation: qr-success-ring-draw 0.5s cubic-bezier(0.32, 0.72, 0, 1) 0.10s both;
}
.qr-screen-card.is-paired .qr-success-tick {
  animation: qr-success-tick-draw 0.34s cubic-bezier(0.32, 0.72, 0, 1) 0.40s both;
}
@keyframes qr-success-ring-draw { to { stroke-dashoffset: 0; } }
@keyframes qr-success-tick-draw { to { stroke-dashoffset: 0; } }

/* ════════════════════════════════════════════════════════════════
   Settings — view, top-cog button, vertical category cards, pair card
   ════════════════════════════════════════════════════════════════ */

/* The settings cog sits to the LEFT of the Hey Frost pill.
   Same visual register as the pill — frost-tinted glass — but
   icon-only and slightly smaller so the pill stays the focal point. */
.top-actions {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}
/* Add item — global header action, available on every page. White pill
   so it reads as the primary "do something" affordance. */
.top-add {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  height: 58px;
  padding: 0 26px 0 22px;
  border-radius: 999px;
  background: rgba(170, 213, 235, 0.14);
  border: 1px solid rgba(170, 213, 235, 0.34);
  color: rgba(224, 242, 255, 0.98);
  font: 600 18px/1 'Inter', system-ui, sans-serif;
  letter-spacing: -0.01em;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition:
    background 0.20s var(--ease-apple),
    border-color 0.20s var(--ease-apple),
    transform 0.12s var(--ease-apple),
    box-shadow 0.20s var(--ease-apple);
}
.top-add:hover {
  background: rgba(170, 213, 235, 0.20);
  border-color: rgba(170, 213, 235, 0.5);
  box-shadow: 0 0 20px -4px rgba(170, 213, 235, 0.35);
}
.top-add:active { transform: scale(0.95); }
.top-cog {
  width: 58px;
  height: 58px;
  border-radius: 999px;
  background: rgba(170, 213, 235, 0.06);
  border: 1px solid rgba(170, 213, 235, 0.18);
  color: rgba(220, 240, 255, 0.92);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition:
    background 0.20s cubic-bezier(0.32, 0.72, 0, 1),
    border-color 0.20s cubic-bezier(0.32, 0.72, 0, 1),
    transform 0.12s cubic-bezier(0.32, 0.72, 0, 1),
    box-shadow 0.20s cubic-bezier(0.32, 0.72, 0, 1);
}
.top-cog:hover {
  background: rgba(170, 213, 235, 0.11);
  border-color: rgba(170, 213, 235, 0.34);
  box-shadow: 0 0 18px -4px rgba(170, 213, 235, 0.28);
}
.top-cog:active { transform: scale(0.93); }

/* ─── Camera food-scan overlay ──────────────────────────────── */
.modal-scan-cam {
  gap: 18px;
  width: 760px;
  max-width: 92%;
}
.scan-cam-stage {
  position: relative;
  width: 100%;
  height: 380px;
  border-radius: 22px;
  overflow: hidden;
  background: #000;
  display: flex; align-items: center; justify-content: center;
}
#cam-feed {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.scan-cam-hint {
  position: absolute;
  left: 0; right: 0; bottom: 16px;
  text-align: center;
  font: 500 17px/1.3 -apple-system, system-ui, sans-serif;
  color: #fff;
  text-shadow: 0 1px 6px rgba(0,0,0,0.8);
  letter-spacing: -0.005em;
  pointer-events: none;
}
.scan-cam-review {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
  min-height: 1px;
}
.scan-cam-review:empty { display: none; }
.scan-cam-item {
  display: inline-flex; align-items: center; gap: 9px;
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 999px;
  padding: 11px 18px;
  font: 500 18px/1 -apple-system, system-ui, sans-serif;
  color: var(--text);
  letter-spacing: -0.01em;
}
.scan-cam-emoji { font-size: 20px; }
.scan-cam-qty   { color: rgba(245,245,247,0.55); font-size: 15px; }
.scan-cam-actions {
  display: flex; gap: 10px; justify-content: center;
}
.scan-cam-actions:empty { display: none; }

/* ─── Settings view ─── */
.view-settings {
  display: none;
  flex-direction: column;
  background:
    radial-gradient(ellipse at 50% -10%, rgba(170, 213, 235, 0.05) 0%, transparent 50%),
    #0a0b0f;
}
.view-settings.active { display: flex; }

.set-head {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: calc(var(--top-safe) + 4px) 24px 10px;
}
.set-back {
  width: 56px;
  height: 56px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
  color: rgba(245, 245, 247, 0.85);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition:
    background 0.18s cubic-bezier(0.32, 0.72, 0, 1),
    transform  0.12s cubic-bezier(0.32, 0.72, 0, 1);
}
.set-back:hover  { background: rgba(255, 255, 255, 0.07); }
.set-back:active { transform: scale(0.93); }
.set-title {
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Inter', system-ui, sans-serif;
  font-size: 32px;
  font-weight: 600;
  letter-spacing: -0.022em;
  color: var(--text, #f1f3f6);
  margin: 0;
  flex: 1;
}
.set-head-spacer { width: 40px; }       /* visual symmetry with .set-back */

.set-body {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 8px 28px 32px;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  -webkit-overflow-scrolling: touch;
}
.set-body::-webkit-scrollbar { width: 0; height: 0; }

/* ─── Tabbed settings ─────────────────────────────────────────── */
.set-shell { display: flex; flex-direction: column; gap: 22px; flex: 1; min-height: 0; }
.set-tabs {
  display: inline-flex;
  align-self: center;
  gap: 4px;
  padding: 5px;
  border-radius: 999px;
  background: rgba(255,255,255,0.05);
  border: 1px solid rgba(255,255,255,0.08);
}
.set-tab {
  border: 0;
  background: transparent;
  color: rgba(245,245,247,0.6);
  font: 600 18px/1 'Inter', system-ui, sans-serif;
  letter-spacing: -0.01em;
  padding: 13px 28px;
  border-radius: 999px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.2s var(--ease-apple), color 0.2s var(--ease-apple);
}
.set-tab.on { background: #fff; color: #111; }
.set-panel { flex: 1; min-height: 0; display: flex; flex-direction: column; }

/* Preference rows */
.set-rows {
  display: flex; flex-direction: column; gap: 14px;
  width: 100%; max-width: 720px; align-self: center;
}
.set-row {
  display: flex; align-items: center; justify-content: space-between; gap: 24px;
  background: var(--glass-fill);
  border: 1px solid var(--glass-border);
  border-radius: 22px;
  padding: 22px 26px;
}
.set-row-text { min-width: 0; }
.set-row-label { font-size: 21px; font-weight: 500; color: var(--text); letter-spacing: -0.01em; }
.set-row-sub   { font-size: 15px; font-weight: 400; color: rgba(245,245,247,0.45); margin-top: 4px; }
.set-row-val   { font-size: 18px; font-weight: 500; color: rgba(245,245,247,0.6); }
/* Segmented control (°F / °C) */
.set-segctl {
  display: inline-flex; gap: 4px; padding: 4px;
  background: rgba(255,255,255,0.05);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 999px; flex-shrink: 0;
}
.set-seg {
  border: 0; background: transparent; color: rgba(245,245,247,0.6);
  font: 600 18px/1 'Inter', system-ui, sans-serif;
  padding: 12px 22px; border-radius: 999px; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.18s var(--ease-apple), color 0.18s var(--ease-apple);
}
.set-seg.on { background: #fff; color: #111; }
/* Toggle switch */
.set-switch {
  width: 76px; height: 44px; flex-shrink: 0;
  border-radius: 999px; border: 1px solid rgba(255,255,255,0.12);
  background: rgba(255,255,255,0.08); cursor: pointer; position: relative;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.2s var(--ease-apple), border-color 0.2s var(--ease-apple);
}
.set-switch.on { background: var(--frost); border-color: var(--frost); }
.set-switch-knob {
  position: absolute; top: 3px; left: 3px;
  width: 36px; height: 36px; border-radius: 999px; background: #fff;
  box-shadow: 0 2px 6px rgba(0,0,0,0.35);
  transition: transform 0.22s var(--ease-apple);
}
.set-switch.on .set-switch-knob { transform: translateX(32px); }

/* Pair tab + about reuse the existing pair/owner styles, just centered. */
.set-pair-tab, .set-about { display: flex; flex-direction: column; align-items: center; gap: 16px; }
.set-pair-tab { justify-content: center; flex: 1; }

/* The entire settings UI is one tight column. No section eyebrows,
   no dividers, no destructive-button chrome — just the things you
   actually need: who you're linked to, the QR, the code, and a
   quiet way out. */
.set-stack {
  width: 100%;
  max-width: 360px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  padding: 16px 0;
}

/* Account header — the fridge's identity. Empty by default;
   populated by cloud.populateOwner() reading /meta. */
.set-owner {
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  margin-bottom: 8px;
  min-height: 1px;
}
.set-owner:empty { display: none; }
.set-owner-label {
  font: 600 13px/1 -apple-system, system-ui, sans-serif;
  letter-spacing: 0;
  color: rgba(170, 213, 235, 0.62);
  margin-bottom: 6px;
}
.set-owner-name {
  font: 600 18px/1.2 -apple-system, BlinkMacSystemFont, 'SF Pro Display', system-ui, sans-serif;
  letter-spacing: -0.012em;
  color: var(--text, #f1f3f6);
}
.set-owner-email {
  font: 500 13px/1.3 -apple-system, system-ui, sans-serif;
  color: rgba(245, 245, 247, 0.45);
  letter-spacing: -0.005em;
}
/* Communal roster — everyone sharing this fridge. */
.set-owner-roster {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  margin-top: 14px;
}
.set-owner-member {
  font: 500 14px/1.3 -apple-system, system-ui, sans-serif;
  color: rgba(245, 245, 247, 0.62);
  letter-spacing: -0.005em;
}

/* Helper line beneath the QR — tells the user what to do, in
   one short sentence. */
.set-helper {
  font: 500 14px/1.4 -apple-system, system-ui, sans-serif;
  color: rgba(245, 245, 247, 0.55);
  letter-spacing: -0.005em;
  text-align: center;
}

/* Unpair as a quiet text link, not a screaming button. Same
   color register as the auth-error pill, but no border or fill —
   the destructive-ness is implied by the color and the word. */
.set-unpair-link {
  margin-top: 24px;
  background: none;
  border: none;
  color: rgba(255, 130, 126, 0.85);
  font: 500 14px/1 -apple-system, system-ui, sans-serif;
  letter-spacing: -0.005em;
  padding: 8px 12px;
  cursor: pointer;
  transition: color 0.18s cubic-bezier(0.32, 0.72, 0, 1);
}
.set-unpair-link:hover { color: rgba(255, 138, 134, 1); }
.set-unpair-link:active { color: rgba(255, 100, 96, 1); }

/* ─── Settings pair block — minimal: just QR + code ───
   Same aesthetic as the boot QR screen, sized down for the
   in-page settings view. No eyebrow, no status pill, no labels,
   no hint text — the page header above already says "Settings"
   and the QR is self-explanatory. */
.set-pair-block {
  width: 100%;
  max-width: 360px;
  margin: 12px auto 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 22px;
}
.set-pair-frame {
  position: relative;
  background: #fff;
  padding: 14px;
  border-radius: 22px;
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.08),
    0 0 50px 6px rgba(170, 213, 235, 0.16),
    0 16px 32px rgba(0, 0, 0, 0.40);
}
.set-pair-qr {
  width: 240px;
  height: 240px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 12px;
  overflow: hidden;
}
.set-pair-qr canvas,
.set-pair-qr svg {
  width: 100%;
  height: 100%;
  display: block;
}
.set-pair-qr-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
}
.set-pair-qr-spinner {
  width: 28px; height: 28px;
  border-radius: 50%;
  border: 3px solid rgba(10, 11, 15, 0.10);
  border-top-color: rgba(10, 11, 15, 0.55);
  animation: set-pair-spin 0.9s linear infinite;
}
@keyframes set-pair-spin { to { transform: rotate(360deg); } }

.set-pair-code {
  font: 600 22px/1 -apple-system, system-ui, sans-serif;
  letter-spacing: 0;
  font-feature-settings: "tnum" 1;
  color: rgba(245, 245, 247, 0.78);
}

/* ════════════════════════════════════════════════════════════════
   Empty state — unified centered composition used by all three
   pages (Use Tonight / In Fridge / Restock) when there's no data.
   Same visual register as the auth screen and QR pair screen so
   the empty pages feel intentional, not under-built.
   ════════════════════════════════════════════════════════════════ */
.empty-state {
  flex: 1;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 18px;
  padding: 32px;
  animation: empty-state-in 0.42s cubic-bezier(0.32, 0.72, 0, 1) both;
}
@keyframes empty-state-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Glass orb glyph — same recipe as the auth-mark on mobile. */
.empty-state-glyph {
  width: 76px;
  height: 76px;
  border-radius: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(170, 213, 235, 0.95);
  background:
    radial-gradient(ellipse at 50% 0%, rgba(170, 213, 235, 0.22) 0%, transparent 70%),
    rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.10);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.10),
    0 12px 32px rgba(0, 0, 0, 0.50),
    0 0 60px rgba(170, 213, 235, 0.10);
  margin-bottom: 4px;
}
.empty-state-glyph svg { display: block; }

.empty-state-title {
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Inter', system-ui, sans-serif;
  font-size: 30px;
  font-weight: 600;
  letter-spacing: -0.022em;
  line-height: 1.08;
  color: var(--text, #f1f3f6);
}
.empty-state-sub {
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', system-ui, sans-serif;
  font-size: 16px;
  font-weight: 500;
  letter-spacing: -0.005em;
  line-height: 1.5;
  color: rgba(245, 245, 247, 0.55);
  max-width: 380px;
}
.empty-state-action { margin-top: 8px; }

/* ═════════════════════════════════════════════════════════════════
   Accessibility + dead-code cleanup
   ═════════════════════════════════════════════════════════════════ */

/* Keyboard focus — visible ring for users navigating without a
   pointer. Pointer-only :focus is suppressed via :focus-visible. */
button:focus-visible,
input:focus-visible,
[role="button"]:focus-visible {
  outline: 2px solid rgba(157, 203, 226, 0.55);
  outline-offset: 2px;
}

/* Settings back button — bump from 40px to 44px so it meets the
   HIG minimum touch target on the touchscreen fridge. */
.set-back {
  min-width: 44px;
  min-height: 44px;
}

/* Cart-row hover transition — was instantaneous, now eases. */
.cart-row + .cart-row::before {
  transition: opacity 0.15s ease;
}
