/* ==========================================================================
   Grocery List — Storybook Theme
   palette: sage / kraft / tomato / carrot / leaf / paper / warm ink
   type: Fraunces (display) + Nunito (body)
   tactile: paper-grain noise, soft shadows, hand-drawn check
   ========================================================================== */

:root {
  /* Core palette — warm earth tones lifted from the icon */
  --sage:        #7d9b6a;
  --sage-deep:   #5d7a4d;
  --sage-soft:   #c8d4b9;
  --kraft:       #c89876;
  --kraft-deep:  #9b6f4f;
  --kraft-soft:  #e9d3bc;
  --tomato:      #c84830;
  --tomato-deep: #9b3422;
  --carrot:      #e08a3c;
  --leaf:        #3d6b3a;
  --leaf-soft:   #6f9a64;

  /* Surfaces — cream paper */
  --paper:       #f4ecdc;
  --paper-deep:  #e9dec7;
  --surface:     #fbf6e9;
  --surface-2:   #efe6cf;
  --border:      #d9c9a7;
  --border-soft: #e7dcc1;

  /* Ink */
  --ink:         #2a2419;
  --ink-2:       #5b4f3a;
  --ink-3:       #8a7f66;

  --accent:      var(--leaf);
  --accent-ink:  #fbf6e9;
  --danger:      var(--tomato);

  --shadow-soft: 0 1px 0 rgba(120, 95, 50, 0.06), 0 6px 18px rgba(120, 95, 50, 0.08);
  --shadow-lift: 0 2px 0 rgba(120, 95, 50, 0.08), 0 14px 36px rgba(120, 95, 50, 0.16);

  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-top:    env(safe-area-inset-top, 0px);

  --radius-card: 14px;
  --radius-pill: 999px;

  --ease-out:    cubic-bezier(0.22, 1, 0.36, 1);
  --ease-soft:   cubic-bezier(0.4, 0, 0.2, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);

  /* Inline paper-grain — subtle multiply noise, ~1KB SVG */
  --paper-grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.45 0 0 0 0 0.32 0 0 0 0 0.18 0 0 0 0.18 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper:       #221d12;
    --paper-deep:  #1a160c;
    --surface:     #2c2618;
    --surface-2:   #3a311e;
    --border:      #4a3e26;
    --border-soft: #382f1c;

    --ink:         #f1e7cf;
    --ink-2:       #c8b893;
    --ink-3:       #8e8064;

    --sage-soft:   #4f6443;
    --kraft-soft:  #5e4632;
    --accent-ink:  #15110a;
    --shadow-soft: 0 1px 0 rgba(0, 0, 0, 0.35), 0 6px 18px rgba(0, 0, 0, 0.45);
    --shadow-lift: 0 2px 0 rgba(0, 0, 0, 0.4), 0 14px 36px rgba(0, 0, 0, 0.55);
  }
}

/* ==========================================================================
   Base
   ========================================================================== */

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  color: var(--ink);
  font-family: 'Nunito', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: 16px;
  font-weight: 500;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
}

/* The whole backdrop lives on <html>, because the root element's background is
   the one CSS paints across the *entire canvas* rather than just its own box.

   Measured on the reporting device (iPhone 16 Pro Max, 430x932, iOS 18.7,
   installed PWA, cold launch) with a probe that samples on a timeline:

     when        t(ms)  layoutVP  inner   dvh     fixed  body
     head            1     873      873   872.98    873   n/a
     dom-ready       5     873      873   872.98    873   872.98
     vv-resize      46     873      932   932       932   932    <-- jump
     +2500ms      2513     873      932   932       932   932

   Two things are going on, and only the first was obvious from a screenshot:

     1. For the first ~46ms of a cold launch the whole page is laid out at 873
        -- body, 100dvh and position:fixed alike. A visualViewport resize then
        relayouts everything to 932. Anything painting on body is 59px short
        for that window, which is where the home-indicator strip came from.
     2. document.documentElement.clientHeight -- which for the root element is
        the LAYOUT VIEWPORT -- stays 873 for the life of the page, permanently
        59px (== safe-area-inset-top) shorter than the window.

   The canvas is immune to both. Painting <html> blue on-device and leaving
   body transparent put blue at the very bottom edge of the screen despite the
   873px layout viewport, so the canvas painting area is genuinely not bounded
   by it. That is what this rule relies on.

   The two no-repeat glow layers are grown by safe-area-inset-top so they reach
   the bottom too -- and during the transient above, where html's box is 873,
   873 + 59 lands exactly on the window. env() is 0 where there is no inset, so
   this is a no-op on desktop and Android.

   TODO-30. See also 1a4e0f2 / e26368a / 0a8763b: three earlier attempts that
   all kept the paint on a viewport-sized element (body, or a fixed inset:0
   pseudo-element) and so all inherited the same 59px shortfall. */
html {
  background-color: var(--paper);
  background-image:
    radial-gradient(ellipse at 50% -10%, rgba(125, 155, 106, 0.18), transparent 60%),
    radial-gradient(ellipse at 110% 110%, rgba(200, 152, 118, 0.22), transparent 55%),
    var(--paper-grain);
  background-repeat: no-repeat, no-repeat, repeat;
  background-size:
    100% calc(100% + var(--safe-top)),
    100% calc(100% + var(--safe-top)),
    240px 240px;
  background-blend-mode: normal, normal, multiply;
}

body {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

main, header, .addbar { position: relative; z-index: 1; }

::selection { background: var(--carrot); color: var(--ink); }

/* ==========================================================================
   Topbar
   ========================================================================== */

.topbar {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: calc(14px + var(--safe-top)) 18px 14px;
  background: linear-gradient(180deg, var(--surface) 0%, color-mix(in oklab, var(--surface), var(--paper) 35%) 100%);
  border-bottom: 1px solid var(--border);
  box-shadow: 0 1px 0 var(--border-soft);
}

.topbar::after {
  /* tomato underline accent */
  content: "";
  position: absolute;
  left: 18px;
  right: 18px;
  bottom: -1px;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--tomato) 30%, var(--tomato) 70%, transparent);
  opacity: 0.55;
  border-radius: 2px;
}

.topbar__title {
  margin: 0;
  font-family: 'Fraunces', Georgia, serif;
  font-weight: 600;
  font-size: clamp(1.15rem, 1rem + 0.6vw, 1.45rem);
  letter-spacing: -0.01em;
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--ink);
}

.topbar__logo {
  width: 36px;
  height: 36px;
  display: block;
  flex-shrink: 0;
  filter: drop-shadow(0 2px 4px rgba(120, 95, 50, 0.18));
}

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

.live-pill {
  font-size: 0.7rem;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  display: inline-block;
  background: var(--ink-3);
  color: transparent;
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--ink-3), transparent 80%);
  transition: background 0.3s var(--ease-out), box-shadow 0.3s var(--ease-out);
}
.live-pill[data-state="open"] {
  background: var(--leaf-soft);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--leaf-soft), transparent 80%);
}
.live-pill[data-state="connecting"] {
  background: var(--carrot);
  animation: pulse 1.4s var(--ease-soft) infinite;
}
.live-pill[data-state="closed"] {
  background: var(--tomato);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--tomato), transparent 80%);
}

@keyframes pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.45; transform: scale(0.9); }
}

/* ==========================================================================
   Buttons
   ========================================================================== */

.iconbtn {
  appearance: none;
  border: 1px solid transparent;
  background: transparent;
  color: var(--ink);
  font-size: 1.05rem;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 0.18s var(--ease-out), transform 0.12s var(--ease-out), color 0.18s var(--ease-out);
}
.iconbtn:hover { background: var(--surface-2); }
.iconbtn:active { transform: scale(0.94); }
.iconbtn:focus-visible {
  outline: 2px solid var(--leaf);
  outline-offset: 2px;
}

.iconbtn--lg { width: 50px; height: 50px; font-size: 1.3rem; }

.iconbtn:disabled { cursor: default; }
.iconbtn:disabled:hover { background: transparent; }
.iconbtn:disabled:active { transform: none; }

.iconbtn__img {
  width: 22px;
  height: 22px;
  display: block;
  object-fit: contain;
}

#signOutBtn .iconbtn__img { width: 26px; height: 26px; transform: translateY(1px); }
#cameraBtn .iconbtn__img { width: 28px; height: 28px; }

.iconbtn__svg {
  width: 22px;
  height: 22px;
  display: block;
  color: var(--ink-2);
}
.iconbtn.is-spinning .iconbtn__svg { animation: refresh-spin 0.6s var(--ease-out); }

@keyframes refresh-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

.iconbtn__mask {
  width: 24px;
  height: 24px;
  display: block;
  background-color: var(--ink-2);
  -webkit-mask: center / contain no-repeat;
          mask: center / contain no-repeat;
  transition: background-color 0.18s var(--ease-out);
}
.iconbtn__mask--camera {
  width: 32px;
  height: 32px;
  -webkit-mask-image: url("/icons/camera.png?v=2");
          mask-image: url("/icons/camera.png?v=2");
}
#cameraBtn:hover .iconbtn__mask { background-color: var(--ink); }

.iconbtn--primary {
  background: var(--leaf);
  color: var(--accent-ink);
  border-radius: 14px;
  box-shadow:
    var(--shadow-soft),
    inset 0 1px 0 rgba(255, 255, 255, 0.18),
    inset 0 -2px 0 rgba(0, 0, 0, 0.20);
  transition: transform 0.12s var(--ease-out), filter 0.18s var(--ease-out), box-shadow 0.18s var(--ease-out);
}
/* Re-assert the fill: .iconbtn:hover above sets background to --surface-2 and
   matches this element too, which left the primary button cream with near-white
   glyph — about 1.06:1. The brightness lift was always the intended hover. */
.iconbtn--primary:hover { background: var(--leaf); filter: brightness(1.08); }
.iconbtn--primary:active {
  transform: translateY(1px) scale(0.97);
  box-shadow: var(--shadow-soft), inset 0 2px 4px rgba(0, 0, 0, 0.25);
}

.btn {
  appearance: none;
  font-family: inherit;
  font-weight: 700;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink);
  padding: 12px 18px;
  border-radius: 12px;
  font-size: 0.98rem;
  cursor: pointer;
  transition: background 0.18s var(--ease-out), transform 0.12s var(--ease-out), border-color 0.18s var(--ease-out);
}
.btn:active { transform: scale(0.97); }
.btn:disabled { cursor: default; opacity: 0.65; }
.btn:disabled:active { transform: none; }
.btn:focus-visible {
  outline: 2px solid var(--leaf);
  outline-offset: 2px;
}
.btn--primary {
  background: var(--leaf);
  color: var(--accent-ink);
  border-color: var(--leaf);
  box-shadow: var(--shadow-soft);
}
.btn--primary:hover { filter: brightness(1.05); }
.btn--danger {
  background: transparent;
  color: var(--tomato);
  border-color: color-mix(in oklab, var(--tomato), transparent 60%);
}
.btn--danger:hover { background: color-mix(in oklab, var(--tomato), transparent 90%); }
.btn--ghost {
  background: transparent;
  border-color: var(--border-soft);
  color: var(--ink-2);
}
.btn--ghost:hover { background: var(--surface-2); color: var(--ink); }

/* ==========================================================================
   Items list
   ========================================================================== */

main {
  flex: 1;
  padding: 14px 14px calc(84px + var(--safe-bottom));
  container-type: inline-size;
}

.empty {
  text-align: center;
  color: var(--ink-2);
  margin: 40px auto 0;
  padding: 0 16px;
  max-width: 320px;
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-size: 1.05rem;
  line-height: 1.5;
}


.items {
  list-style: none;
  margin: 0 auto;
  padding: 0;
  max-width: 720px;
  display: grid;
  gap: 4px;
}

.item {
  display: grid;
  grid-template-columns: 26px 36px minmax(0, 1fr) 44px 32px;
  align-items: center;
  gap: 8px;
  padding: 6px 10px 6px 4px;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  box-shadow: var(--shadow-soft);
  position: relative;
  overflow: hidden;
  touch-action: pan-y;
  --row-min-h: 44px;
  min-height: var(--row-min-h);
  transition: opacity 0.22s var(--ease-out), transform 0.22s var(--ease-out), background 0.22s var(--ease-out);
}

.item--no-thumb {
  grid-template-columns: 26px 36px minmax(0, 1fr) 32px;
}

.item--enter {
  animation: item-enter 0.36s var(--ease-out) both;
}
@keyframes item-enter {
  0%   { opacity: 0; transform: translateY(8px) scale(0.98); }
  60%  { opacity: 1; transform: translateY(0)   scale(1.015); }
  100% { transform: scale(1); }
}

.item--leaving {
  opacity: 0;
  transform: translateX(-32px) scale(0.98);
}

.item--checked {
  background: color-mix(in oklab, var(--surface), var(--sage-soft) 25%);
}
.item--checked .item__name,
.item--checked .item__qty {
  color: var(--ink-3);
}
.item--checked .item__note {
  opacity: 0.62;
}
.item--checked .item__name::after {
  /* Hand-torn receipt strikethrough — drawn left-to-right with a slight wobble */
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 52%;
  height: 1.8px;
  background: var(--ink-3);
  border-radius: 2px;
  transform: scaleX(0) rotate(-1.2deg);
  transform-origin: left center;
  animation: strike-on 0.6s var(--ease-out) forwards;
}

@keyframes strike-on {
  0%   { transform: scaleX(0)   rotate(-1.2deg); }
  60%  { transform: scaleX(1.04) rotate(-1.6deg); }
  100% { transform: scaleX(1)   rotate(-1.2deg); }
}

/* Crumple + fade after the strike-through holds.
   The frames carry an explicit max-height on purpose: the row's computed
   max-height is `none`, which can't interpolate, so without one the row holds
   full height for the whole animation and the rows below snap up in a single
   frame when the node is finally removed. --row-h is set from the row's real
   offsetHeight just before the class lands (see checkAndAutoRemove), so the
   collapse starts from the height the row actually has; the fallback only
   matters if that ever fails to run. */
.item--removing {
  animation: item-crumple 0.5s var(--ease-out) forwards;
  pointer-events: none;
}
@keyframes item-crumple {
  0%   { opacity: 1; transform: translateY(0)   scale(1)    skewX(0deg); max-height: var(--row-h, 240px); min-height: var(--row-min-h); }
  55%  { opacity: 0.65; transform: translateY(-4px) scale(0.96) skewX(-1.5deg); max-height: var(--row-h, 240px); min-height: var(--row-min-h); }
  100% { opacity: 0; transform: translateY(-12px) scale(0.92) skewX(-2.5deg); max-height: 0; min-height: 0; padding-top: 0; padding-bottom: 0; margin: 0; border-width: 0; }
}

.item__check {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 2px solid var(--ink-3);
  background: transparent;
  margin-left: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: var(--accent-ink);
  transition: background 0.22s var(--ease-out), border-color 0.22s var(--ease-out), transform 0.18s var(--ease-out);
  flex-shrink: 0;
}
.item__check:hover { border-color: var(--leaf); }
.item__check:active { transform: scale(0.9); }
.item--checked .item__check {
  background: var(--leaf);
  border-color: var(--leaf);
}

.item__check svg {
  width: 15px;
  height: 15px;
  stroke: var(--accent-ink);
  stroke-width: 2.8;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
  stroke-dasharray: 24;
  stroke-dashoffset: 24;
  transition: stroke-dashoffset 0.28s var(--ease-out);
}
.item--checked .item__check svg {
  stroke-dashoffset: 0;
}

.item__photo {
  appearance: none;
  border: 0;
  background: color-mix(in oklab, var(--kraft-soft), transparent 36%);
  width: 42px;
  height: 42px;
  padding: 3px;
  border-radius: 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  justify-self: end;
  cursor: zoom-in;
  box-shadow: inset 0 0 0 1px rgba(120, 95, 50, 0.16);
  transition: transform 0.16s var(--ease-out), background 0.18s var(--ease-out), box-shadow 0.18s var(--ease-out);
}
.item__photo:hover {
  background: color-mix(in oklab, var(--kraft-soft), var(--surface) 24%);
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--kraft-deep), transparent 55%);
}
.item__photo:active { transform: scale(0.94); }
.item__photo:focus-visible {
  outline: 2px solid var(--leaf);
  outline-offset: 2px;
}
.item--checked .item__photo { opacity: 0.58; }

.item__thumb {
  width: 36px;
  height: 36px;
  border-radius: 9px;
  background: var(--kraft-soft);
  object-fit: cover;
  display: block;
  box-shadow: inset 0 0 0 1px rgba(120, 95, 50, 0.12);
  pointer-events: none;
}

.item__body {
  min-width: 0;
  display: grid;
  gap: 2px;
}

.item__name {
  font-family: 'Fraunces', Georgia, serif;
  font-weight: 600;
  font-size: 0.98rem;
  line-height: 1.25;
  letter-spacing: -0.005em;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  position: relative;
  display: inline-block;
  max-width: 100%;
}

.item__qty {
  color: var(--ink-2);
  font-size: 0.78rem;
  font-weight: 600;
  margin-left: 5px;
}

.item__note {
  appearance: none;
  border: 0;
  background: color-mix(in oklab, var(--kraft-soft), transparent 58%);
  color: var(--ink-2);
  width: min(100%, 36rem);
  min-width: 0;
  margin: 1px 0 0;
  padding: 3px 7px 4px 6px;
  border-radius: 8px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  gap: 5px;
  text-align: left;
  font: inherit;
  font-size: 0.76rem;
  line-height: 1.25;
  cursor: pointer;
  transition: background 0.18s var(--ease-out), color 0.18s var(--ease-out), box-shadow 0.18s var(--ease-out);
}
.item__note:hover {
  background: color-mix(in oklab, var(--kraft-soft), var(--surface) 12%);
  color: var(--ink);
}
.item__note:focus-visible {
  outline: 2px solid var(--leaf);
  outline-offset: 2px;
}
.item__note-icon {
  color: inherit;
  display: grid;
  place-items: center;
  margin-top: 2px;
  transition: transform 0.18s var(--ease-out);
}
.item__note-icon svg {
  width: 0.85rem;
  height: 0.85rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.item__note--expanded .item__note-icon {
  transform: rotate(180deg);
}
.item__note-text {
  min-width: 0;
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 1;
  white-space: normal;
}
.item__note--expanded .item__note-text {
  -webkit-line-clamp: unset;
  white-space: pre-wrap;
}

.item__by {
  color: var(--ink-3);
  font-size: 0.7rem;
  margin-top: 0;
  font-style: italic;
  line-height: 1.2;
}

.item__menu {
  color: var(--ink-3);
  width: 32px;
  height: 32px;
  font-size: 1.35rem;
}

.item__drag {
  appearance: none;
  background: transparent;
  border: none;
  width: 26px;
  height: 36px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-3);
  cursor: grab;
  touch-action: none;
  flex-shrink: 0;
  border-radius: 6px;
  transition: background 0.15s var(--ease-out), color 0.15s var(--ease-out);
}
.item__drag:hover { background: var(--surface-2); color: var(--ink-2); }
.item__drag:active { cursor: grabbing; }
.item__drag svg {
  width: 16px;
  height: 16px;
  fill: currentColor;
  pointer-events: none;
}
.item--checked .item__drag {
  opacity: 0.25;
  pointer-events: none;
}

.item--dragging {
  opacity: 0.92;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.18), 0 2px 4px rgba(0, 0, 0, 0.08);
  transform: scale(1.015);
  z-index: 10;
  transition: none;
  cursor: grabbing;
}

.item--drop-target-above {
  box-shadow: inset 0 2px 0 0 var(--leaf), var(--shadow-soft);
}
.item--drop-target-below {
  box-shadow: inset 0 -2px 0 0 var(--leaf), var(--shadow-soft);
}

.item--flash {
  animation: flash 1.1s var(--ease-out);
}

@keyframes flash {
  0%   { background: color-mix(in oklab, var(--surface), var(--leaf-soft) 40%); }
  100% { background: var(--surface); }
}

/* ==========================================================================
   Floating Add button (FAB)
   ========================================================================== */

.fab {
  position: fixed;
  right: calc(20px + env(safe-area-inset-right, 0px));
  bottom: calc(12px + var(--safe-bottom));
  z-index: 11;
  width: 60px;
  height: 60px;
  border-radius: 50%;
  background: transparent;
  border: 2px solid var(--leaf);
  color: var(--leaf);
  box-shadow: var(--shadow-soft);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: inherit;
  font-weight: 700;
  font-size: 1.75rem;
  line-height: 1;
  transition: transform 0.32s var(--ease-spring), opacity 0.18s var(--ease-out), background 0.18s var(--ease-out);
  animation: fab-float-in 0.42s var(--ease-spring) both;
}
.fab:hover { filter: brightness(1.04); }
.fab:active { transform: scale(0.92); }
.fab:focus-visible {
  outline: 2px solid var(--leaf);
  outline-offset: 3px;
}

.fab__plus {
  display: block;
  width: 28px;
  height: 28px;
  transition: transform 0.32s var(--ease-spring);
}

.fab--hidden {
  opacity: 0;
  transform: scale(0.4) rotate(45deg);
  pointer-events: none;
}

@keyframes fab-float-in {
  0%   { opacity: 0; transform: scale(0.6) translateY(20px); }
  60%  { opacity: 1; transform: scale(1.06) translateY(-2px); }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}

/* Idle gentle pulse when list is empty for a beat */
.fab--idle-pulse {
  animation: fab-pulse 2.4s var(--ease-soft) infinite;
}
@keyframes fab-pulse {
  0%, 100% { box-shadow: var(--shadow-lift), 0 0 0 0 color-mix(in oklab, var(--tomato), transparent 60%); }
  50%      { box-shadow: var(--shadow-lift), 0 0 0 14px color-mix(in oklab, var(--tomato), transparent 95%); }
}

/* Pantry FAB — mirrors the + FAB on the opposite side, kraft palette */
.fab--pantry {
  right: auto;
  left: calc(20px + env(safe-area-inset-left, 0px));
  background: transparent;
  border: 2px solid var(--kraft-deep);
  color: var(--kraft-deep);
}
.fab--pantry:hover { filter: brightness(1.04); }
.fab--pantry:focus-visible {
  outline: 2px solid var(--kraft-deep);
  outline-offset: 3px;
}

.fab__pantry-icon {
  width: 28px;
  height: 28px;
  display: block;
  background-color: var(--kraft-deep);
  -webkit-mask: url("/icons/pantry.png?v=3") center / contain no-repeat;
          mask: url("/icons/pantry.png?v=3") center / contain no-repeat;
}

/* ==========================================================================
   Add bar (expands from FAB)
   ========================================================================== */

.addbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 12;
  background: linear-gradient(180deg, color-mix(in oklab, var(--surface), var(--kraft-soft) 25%), var(--surface));
  background-image: var(--paper-grain), linear-gradient(180deg, color-mix(in oklab, var(--surface), var(--kraft-soft) 25%), var(--surface));
  background-size: 240px 240px, auto;
  background-blend-mode: multiply, normal;
  border-top: 1px solid var(--border);
  border-radius: 22px 22px 0 0;
  padding: 6px 14px calc(8px + var(--safe-bottom));
  box-shadow: 0 -10px 32px rgba(120, 95, 50, 0.18);
  transform: translateY(0);
  opacity: 1;
  transition: transform 0.34s var(--ease-spring), opacity 0.2s var(--ease-out);
}

.addbar--collapsed {
  transform: translateY(110%);
  opacity: 0;
  pointer-events: none;
}

.addbar__handle {
  appearance: none;
  display: block;
  width: 44px;
  height: 4px;
  border-radius: 4px;
  background: var(--border);
  border: 0;
  margin: 4px auto 8px;
  cursor: grab;
  padding: 0;
  touch-action: none;
}
.addbar__handle:active { cursor: grabbing; }

.addbar__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 8px;
}

.addbar__close {
  width: 36px;
  height: 36px;
  font-size: 1rem;
  color: var(--ink-2);
}

.addbar__row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.addbar input[type="text"] {
  flex: 1;
  height: 50px;
  border-radius: 14px;
  border: 1.5px solid var(--border);
  background: var(--surface);
  color: var(--ink);
  padding: 0 16px;
  font-size: 1.02rem;
  font-family: inherit;
  font-weight: 600;
  outline: none;
  transition: border-color 0.2s var(--ease-out), background 0.2s var(--ease-out), box-shadow 0.2s var(--ease-out);
}
.addbar input[type="text"]::placeholder {
  color: var(--ink-3);
  font-weight: 500;
  font-style: italic;
}
.addbar input[type="text"]:focus {
  border-color: var(--leaf);
  background: var(--paper);
  box-shadow: 0 0 0 4px color-mix(in oklab, var(--leaf), transparent 86%);
}
.addbar input[type="text"].is-flashing {
  animation: input-flash 0.7s var(--ease-out);
}
@keyframes input-flash {
  0%, 100% { box-shadow: 0 0 0 4px color-mix(in oklab, var(--leaf), transparent 86%); }
  40%      { box-shadow: 0 0 0 6px color-mix(in oklab, var(--carrot), transparent 70%); }
}

/* ==========================================================================
   Pantry pill (replaces chip strip)
   ========================================================================== */

.pantry-pill {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: inherit;
  font-weight: 700;
  font-size: 0.92rem;
  color: var(--ink);
  background: linear-gradient(180deg, var(--kraft-soft), color-mix(in oklab, var(--kraft-soft), var(--paper-deep) 40%));
  border: 1.5px solid color-mix(in oklab, var(--kraft), var(--paper) 40%);
  border-radius: var(--radius-pill);
  padding: 8px 16px 8px 12px;
  cursor: pointer;
  box-shadow: var(--shadow-soft);
  transition: transform 0.14s var(--ease-out), background 0.18s var(--ease-out);
}
.pantry-pill:hover { background: linear-gradient(180deg, var(--kraft-soft), var(--paper-deep)); }
.pantry-pill:active { transform: scale(0.97); }
.pantry-pill:focus-visible {
  outline: 2px solid var(--leaf);
  outline-offset: 2px;
}
.pantry-pill:has(.pantry-pill__count:not([hidden])) { padding-right: 6px; }
.pantry-pill__icon {
  width: 22px;
  height: 22px;
  display: block;
  background-color: var(--kraft-deep);
  -webkit-mask: url("/icons/pantry.png?v=3") center / contain no-repeat;
          mask: url("/icons/pantry.png?v=3") center / contain no-repeat;
}
.pantry-pill__count {
  background: var(--ink-2);
  color: var(--surface);
  border-radius: var(--radius-pill);
  padding: 3px 9px;
  min-width: 22px;
  text-align: center;
  font-size: 0.78rem;
  margin-left: 6px;
  font-weight: 800;
  letter-spacing: 0.02em;
}

/* ==========================================================================
   Inline suggestions dropdown
   ========================================================================== */

.suggestions {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  margin-bottom: 10px;
  max-height: 38vh;
  overflow-y: auto;
  box-shadow: var(--shadow-lift);
}

.suggestion {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  cursor: pointer;
  border-bottom: 1px solid var(--border-soft);
  transition: background 0.15s var(--ease-out);
  position: relative;
  user-select: none;
}
.suggestion:last-child { border-bottom: 0; }
@media (hover: hover) and (pointer: fine) {
  .suggestion:hover { background: var(--surface-2); }
}
.suggestion img {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  object-fit: cover;
  flex-shrink: 0;
}
.suggestion__name {
  font-family: 'Fraunces', Georgia, serif;
  font-weight: 600;
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.suggestion__qty {
  color: var(--ink-3);
  font-size: 0.82rem;
  margin-left: auto;
}
.suggestion__hide {
  opacity: 0;
  background: transparent;
  border: 0;
  color: var(--ink-3);
  font-size: 0.95rem;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  cursor: pointer;
  transition: opacity 0.15s var(--ease-out), background 0.15s var(--ease-out), color 0.15s var(--ease-out);
}
.suggestion:focus-within .suggestion__hide { opacity: 1; }
@media (hover: hover) and (pointer: fine) {
  .suggestion:hover .suggestion__hide { opacity: 1; }
  .suggestion__hide:hover { background: color-mix(in oklab, var(--tomato), transparent 88%); color: var(--tomato); }
}

/* ==========================================================================
   Sheets (edit / menu / pantry)
   ========================================================================== */

.sheet {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.sheet[hidden] { display: none; }

.sheet__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(30, 22, 10, 0.42);
  animation: backdrop-in 0.24s var(--ease-out);
}
@keyframes backdrop-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.sheet__panel {
  position: relative;
  width: 100%;
  max-width: 640px;
  max-height: 92vh;
  overflow-y: auto;
  background: var(--surface);
  background-image: var(--paper-grain);
  background-size: 240px 240px;
  background-blend-mode: multiply;
  border-radius: 22px 22px 0 0;
  padding: 18px 18px calc(18px + var(--safe-bottom));
  box-shadow: var(--shadow-lift);
  border-top: 1px solid var(--border);
  animation: sheet-up 0.34s var(--ease-out);
}
.sheet__panel--small { max-width: 460px; }
.sheet__panel--tall  { max-height: 88vh; min-height: 70vh; }

@keyframes sheet-up {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}

.sheet__handle {
  width: 40px;
  height: 4px;
  background: var(--border);
  border-radius: 4px;
  margin: -6px auto 12px;
  position: relative;
  cursor: grab;
  touch-action: none;
}
.sheet__handle::before {
  content: '';
  position: absolute;
  inset: -14px -28px;
}
.sheet__handle:active { cursor: grabbing; }
.sheet__header { touch-action: none; }

.sheet__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}

.sheet__header h2 {
  margin: 0;
  font-family: 'Fraunces', Georgia, serif;
  font-size: 1.3rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink);
  display: flex;
  align-items: center;
  gap: 10px;
}

.sheet__header__icon {
  width: 30px;
  height: 30px;
  display: block;
  background-color: var(--kraft-deep);
  -webkit-mask: url("/icons/pantry.png?v=3") center / contain no-repeat;
          mask: url("/icons/pantry.png?v=3") center / contain no-repeat;
}

.sheet__form { display: grid; gap: 14px; }

.field { display: flex; flex-direction: column; gap: 6px; }
.field > span {
  font-size: 0.78rem;
  color: var(--ink-2);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.field input, .field textarea {
  border: 1.5px solid var(--border);
  border-radius: 12px;
  background: var(--paper);
  color: var(--ink);
  padding: 12px 14px;
  font-size: 1rem;
  font-family: inherit;
  font-weight: 500;
  outline: none;
  transition: border-color 0.18s var(--ease-out), background 0.18s var(--ease-out);
}
.field input:focus, .field textarea:focus {
  border-color: var(--leaf);
  background: var(--surface);
}

.photo-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.photo-row__preview {
  appearance: none;
  position: relative;
  border: 0;
  background: color-mix(in oklab, var(--kraft-soft), transparent 30%);
  width: 78px;
  height: 78px;
  padding: 3px;
  border-radius: 15px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: zoom-in;
  box-shadow: inset 0 0 0 1px rgba(120, 95, 50, 0.16);
  transition: transform 0.16s var(--ease-out), background 0.18s var(--ease-out), box-shadow 0.18s var(--ease-out);
}
.photo-row__preview:hover {
  background: color-mix(in oklab, var(--kraft-soft), var(--surface) 24%);
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--kraft-deep), transparent 55%);
}
.photo-row__preview:active { transform: scale(0.96); }
.photo-row__preview:focus-visible {
  outline: 2px solid var(--leaf);
  outline-offset: 2px;
}
.photo-row__preview[hidden] { display: none; }
.photo-row__preview img {
  width: 72px; height: 72px;
  object-fit: cover;
  border-radius: 12px;
  box-shadow: inset 0 0 0 1px rgba(120, 95, 50, 0.12);
  pointer-events: none;
}

/* ==========================================================================
   Photo upload state
   Shared by the add bar's pending chip and the edit sheet's preview.
   The spinners are decorative — the reduced-motion block near the end of this
   file hides them, leaving the "Uploading…" wording and the disabled buttons to
   carry the state on their own.
   ========================================================================== */

.pending-photo__spinner,
.photo-row__spinner,
.iconbtn--busy::after {
  position: absolute;
  border-radius: 50%;
  border: 2px solid color-mix(in oklab, var(--ink-2), transparent 70%);
  border-top-color: var(--leaf);
  animation: refresh-spin 0.7s linear infinite;
}
.pending-photo__spinner,
.photo-row__spinner { display: none; }

/* --- Edit sheet preview --- */

.photo-row__preview--busy { cursor: default; }
.photo-row__preview--busy img { opacity: 0.3; }
.photo-row__preview--busy .photo-row__spinner {
  display: block;
  width: 28px;
  height: 28px;
  border-width: 2.5px;
}

/* --- Add bar pending chip --- */

.pending-photo {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
  padding: 6px;
  border-radius: 14px;
  background: color-mix(in oklab, var(--kraft-soft), transparent 45%);
  box-shadow: inset 0 0 0 1px rgba(120, 95, 50, 0.14);
}
.pending-photo[hidden] { display: none; }

.pending-photo__frame {
  appearance: none;
  border: 0;
  padding: 0;
  position: relative;
  flex: none;
  width: 38px;
  height: 38px;
  border-radius: 11px;
  overflow: hidden;
  background: color-mix(in oklab, var(--kraft-soft), var(--surface) 45%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: zoom-in;
  transition: transform 0.16s var(--ease-out);
}
.pending-photo__frame:active { transform: scale(0.95); }
.pending-photo__frame:disabled { cursor: default; }
.pending-photo__frame:disabled:active { transform: none; }
.pending-photo__frame:focus-visible {
  outline: 2px solid var(--leaf);
  outline-offset: 2px;
}

.pending-photo__thumb {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  pointer-events: none;
}
/* No src means nothing to show — and a src-less <img> paints a broken glyph in
   some browsers. True while uploading, and for an item that has no photo yet. */
.pending-photo__thumb:not([src]),
.photo-row__preview img:not([src]) { visibility: hidden; }

.pending-photo--busy .pending-photo__spinner {
  display: block;
  width: 20px;
  height: 20px;
}

.pending-photo__label {
  flex: 1;
  min-width: 0;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--ink-2);
}

.pending-photo__remove {
  appearance: none;
  border: 0;
  background: transparent;
  /* --ink-3 measures 3.37:1 here, under the 4.5 floor. */
  color: var(--ink-2);
  font-size: 0.95rem;
  /* 44px to match .iconbtn, which is this project's touch-target convention. */
  width: 44px;
  height: 44px;
  flex: none;
  border-radius: 12px;
  cursor: pointer;
  transition: background 0.18s var(--ease-out), color 0.18s var(--ease-out);
}
.pending-photo__remove[hidden] { display: none; }
.pending-photo__remove:hover { background: var(--surface-2); color: var(--ink); }
.pending-photo__remove:focus-visible {
  outline: 2px solid var(--leaf);
  outline-offset: 2px;
}

/* --- Camera button --- */

.iconbtn--busy { position: relative; }
.iconbtn--busy .iconbtn__mask { opacity: 0.25; }
.iconbtn--busy::after { content: ""; width: 22px; height: 22px; }

.sheet__actions {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  margin-top: 8px;
}
.sheet__actions--stack {
  flex-direction: column;
  align-items: stretch;
}
.sheet__actions--row-end {
  justify-content: flex-end;
}

/* ==========================================================================
   Pantry sheet
   ========================================================================== */

.pantry__search {
  display: block;
  width: 100%;
  border: 1.5px solid var(--border);
  border-radius: 12px;
  background: var(--paper);
  color: var(--ink);
  padding: 12px 14px;
  font-size: 1rem;
  font-family: inherit;
  margin-bottom: 14px;
  outline: none;
  transition: border-color 0.18s var(--ease-out);
}
.pantry__search:focus { border-color: var(--leaf); }

.pantry__section {
  margin-bottom: 18px;
  animation: stagger 0.32s var(--ease-out) both;
}
.pantry__section:nth-child(2) { animation-delay: 50ms; }
.pantry__section:nth-child(3) { animation-delay: 100ms; }
.pantry__section:nth-child(4) { animation-delay: 150ms; }
@keyframes stagger {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

.pantry__heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  font-family: 'Fraunces', Georgia, serif;
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink-2);
  margin: 0 0 10px;
}
.pantry__heading small {
  font-family: 'Nunito', sans-serif;
  font-weight: 600;
  letter-spacing: normal;
  text-transform: none;
  color: var(--ink-3);
  font-size: 0.78rem;
}

.pantry__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 4px;
  position: relative;
}

@property --swipe-reveal {
  syntax: '<length>';
  inherits: true;
  initial-value: 0px;
}

.pantry__row {
  display: grid;
  grid-template-columns: 1fr auto auto auto;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: 12px;
  cursor: pointer;
  transition: background 0.15s var(--ease-out), transform 0.22s var(--ease-out), opacity 0.22s var(--ease-out), border-color 0.18s var(--ease-out);
  position: relative;
  overflow: hidden;
  touch-action: pan-y;
  user-select: none;
}

@keyframes pantry-swipe-hint {
  0%   { transform: translateX(0);     --swipe-reveal: 0px; }
  35%  { transform: translateX(-60px); --swipe-reveal: 60px; }
  62%  { transform: translateX(-60px); --swipe-reveal: 60px; }
  100% { transform: translateX(0);     --swipe-reveal: 0px; }
}
.pantry__row--hint {
  /* One-time discovery cue: peek the bin to teach the swipe gesture. */
  animation: pantry-swipe-hint 1850ms var(--ease-out) 700ms 1;
}

.pantry__hint-caption {
  position: absolute;
  right: 18px;
  top: -2px;
  transform: translate(8px, -100%) rotate(-1.2deg);
  background: var(--leaf);
  color: var(--accent-ink);
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-weight: 600;
  font-size: 0.88rem;
  letter-spacing: 0.01em;
  padding: 7px 12px 8px;
  border-radius: 10px;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  z-index: 5;
  box-shadow: 0 2px 0 rgba(0, 0, 0, 0.08), 0 8px 22px rgba(61, 107, 58, 0.32);
  animation: pantry-swipe-hint-caption 2250ms var(--ease-out) 450ms 1;
}
.pantry__hint-caption::after {
  /* Downward tip pointing at the row's right edge where the bin emerges. */
  content: "";
  position: absolute;
  bottom: -4px;
  right: 18px;
  width: 10px;
  height: 10px;
  background: var(--leaf);
  transform: rotate(45deg);
  border-radius: 2px;
  box-shadow: 1px 1px 0 rgba(0, 0, 0, 0.04);
}

@keyframes pantry-swipe-hint-caption {
  0%   { opacity: 0; transform: translate(8px, calc(-100% + 6px)) rotate(-1.2deg); }
  14%  { opacity: 1; transform: translate(0,   -100%)              rotate(-1.2deg); }
  82%  { opacity: 1; transform: translate(0,   -100%)              rotate(-1.2deg); }
  100% { opacity: 0; transform: translate(0,   calc(-100% + 4px))  rotate(-1.2deg); }
}

@media (prefers-reduced-motion: reduce) {
  .pantry__row--hint { animation: none; }
  .pantry__hint-caption { display: none; }
}
.pantry__row::before {
  /* Red delete reveal underneath, becomes visible on swipe */
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: var(--swipe-reveal, 0);
  background: var(--tomato);
  pointer-events: none;
  transition: width 0.18s var(--ease-out);
}
.pantry__row::after {
  /* Wireframe bin icon, masked so it inherits the cream foreground tone.
     Centers within the swipe-reveal strip; opacity gates it until the
     reveal is wide enough to contain the glyph. */
  content: "";
  position: absolute;
  top: 50%;
  right: calc((var(--swipe-reveal, 0px) - 22px) / 2);
  width: 22px;
  height: 22px;
  transform: translateY(-50%);
  background-color: var(--accent-ink);
  -webkit-mask: url("/icons/pantrybin.png?v=1") center / contain no-repeat;
          mask: url("/icons/pantrybin.png?v=1") center / contain no-repeat;
  opacity: clamp(0, calc((var(--swipe-reveal, 0px) - 22px) / 30px), 1);
  pointer-events: none;
  transition: right 0.18s var(--ease-out), opacity 0.18s var(--ease-out);
}
.pantry__row:hover { background: var(--surface-2); }
.pantry__row--leaving { opacity: 0; transform: translateX(-110%) scale(0.98); max-height: 0; padding-top: 0; padding-bottom: 0; margin: 0; border-width: 0; }

.pantry__row--has-thumb {
  grid-template-columns: 32px 1fr auto auto auto;
}
.pantry__edit {
  appearance: none;
  background: transparent;
  border: 0;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1.35rem;
  line-height: 1;
  color: var(--ink-3);
  border-radius: 8px;
  cursor: pointer;
  transition: background 0.15s var(--ease-out), color 0.15s var(--ease-out);
  margin: -4px -6px -4px 0;
}
.pantry__edit:hover { background: var(--surface-2); color: var(--ink); }
.pantry__edit:focus-visible { outline: 2px solid var(--leaf); outline-offset: 1px; }
.pantry__row__thumb {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  object-fit: cover;
  display: block;
}
.pantry__name {
  font-family: 'Fraunces', Georgia, serif;
  font-weight: 600;
  font-size: 1rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--ink);
}
.pantry__qty {
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--ink-2);
  background: var(--surface-2);
  border-radius: var(--radius-pill);
  padding: 2px 9px;
  white-space: nowrap;
}
.pantry__when {
  color: var(--ink-3);
  font-size: 0.8rem;
  font-weight: 600;
  white-space: nowrap;
}
.pantry__count {
  font-size: 0.72rem;
  font-weight: 800;
  color: var(--ink-3);
  margin-left: 4px;
}

.pantry__empty {
  text-align: center;
  color: var(--ink-3);
  padding: 28px 8px;
  font-style: italic;
  font-family: 'Fraunces', Georgia, serif;
}


/* ==========================================================================
   Image viewer
   ========================================================================== */

.image-viewer {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: grid;
  place-items: center;
  padding: calc(8px + var(--safe-top)) 8px calc(8px + var(--safe-bottom));
}
.image-viewer[hidden] { display: none; }
.image-viewer__backdrop {
  position: absolute;
  inset: 0;
  border: 0;
  padding: 0;
  background:
    radial-gradient(ellipse at 50% 18%, rgba(200, 152, 118, 0.18), transparent 46%),
    color-mix(in oklab, var(--ink), transparent 14%);
  cursor: zoom-out;
}
.image-viewer__panel {
  position: relative;
  z-index: 1;
  margin: 0;
  width: fit-content;
  max-width: calc(100vw - 16px);
  max-height: calc(100dvh - var(--safe-top) - var(--safe-bottom) - 16px);
  display: grid;
  gap: 8px;
  justify-items: center;
  animation: image-viewer-in 0.22s var(--ease-out) both;
}
@keyframes image-viewer-in {
  from { opacity: 0; transform: translateY(10px) scale(0.96); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.image-viewer__img {
  width: auto;
  height: auto;
  max-width: calc(100vw - 16px);
  max-height: calc(100dvh - var(--safe-top) - var(--safe-bottom) - 72px);
  object-fit: contain;
  display: block;
  border-radius: 18px;
  background: var(--surface);
  border: 1px solid color-mix(in oklab, var(--border), transparent 12%);
  box-shadow: var(--shadow-lift);
}
.image-viewer__caption {
  justify-self: center;
  max-width: calc(100vw - 32px);
  padding: 7px 12px;
  border-radius: var(--radius-pill);
  background: color-mix(in oklab, var(--surface), transparent 8%);
  color: var(--ink-2);
  font-family: 'Fraunces', Georgia, serif;
  font-weight: 600;
  line-height: 1.25;
  text-align: center;
  box-shadow: var(--shadow-soft);
}
.image-viewer__close {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 2;
  background: color-mix(in oklab, var(--surface), transparent 6%);
  box-shadow: var(--shadow-soft);
}
body.image-viewer-open { overflow: hidden; }

/* ==========================================================================
   Toast
   ========================================================================== */

.toast {
  position: fixed;
  left: 50%;
  transform: translate(-50%, 0);
  bottom: calc(110px + var(--safe-bottom));
  z-index: 30;
  background: color-mix(in oklab, var(--ink), transparent 4%);
  color: var(--paper);
  padding: 12px 18px;
  border-radius: var(--radius-pill);
  font-size: 0.92rem;
  font-weight: 600;
  box-shadow: var(--shadow-lift);
  display: flex;
  align-items: center;
  gap: 14px;
  max-width: 92vw;
  animation: toast-in 0.36s var(--ease-spring);
}
.toast[hidden] { display: none; }
.toast--leaving {
  animation: toast-out 0.22s var(--ease-out) forwards;
}
@keyframes toast-in {
  0%   { opacity: 0; transform: translate(-50%, 24px) scale(0.92); }
  60%  { opacity: 1; transform: translate(-50%, -3px) scale(1.02); }
  100% { opacity: 1; transform: translate(-50%, 0) scale(1); }
}
@keyframes toast-out {
  to { opacity: 0; transform: translate(-50%, 16px) scale(0.96); }
}
.toast button {
  appearance: none;
  background: transparent;
  border: 0;
  color: var(--carrot);
  font-weight: 800;
  font-family: inherit;
  cursor: pointer;
  font-size: 0.92rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 4px 8px;
  border-radius: 8px;
  transition: background 0.15s var(--ease-out);
}
.toast button:hover { background: rgba(255, 255, 255, 0.08); }

/* ==========================================================================
   Confirm dialog (centered modal)
   ========================================================================== */

.confirm {
  margin: auto 16px;
  border-radius: 18px;
  padding: 22px 22px 18px;
  max-width: 360px;
  animation: confirm-in 0.34s var(--ease-spring);
}
@keyframes confirm-in {
  0%   { opacity: 0; transform: scale(0.88) translateY(8px); }
  60%  { opacity: 1; transform: scale(1.02) translateY(0); }
  100% { opacity: 1; transform: scale(1); }
}
.confirm__title {
  margin: 0 0 8px;
  font-family: 'Fraunces', Georgia, serif;
  font-weight: 600;
  font-size: 1.2rem;
  color: var(--ink);
}
.confirm__body {
  margin: 0 0 18px;
  color: var(--ink-2);
  font-size: 0.95rem;
  line-height: 1.4;
}
#confirmDialog { align-items: center; }
#confirmDialog .sheet__panel { animation: none; }

/* ==========================================================================
   Login page
   ========================================================================== */

.login-page main.login {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 28px;
  padding: 32px;
  text-align: center;
}
.login__brand h1 {
  margin: 8px 0 4px;
  font-family: 'Fraunces', Georgia, serif;
  font-weight: 600;
  font-size: clamp(2rem, 1.4rem + 3vw, 2.6rem);
  letter-spacing: -0.02em;
}
.login__brand p {
  color: var(--ink-2);
  margin: 0;
  font-style: italic;
}
.login__bag {
  width: 160px;
  height: 160px;
  display: block;
  margin: 0 auto 4px;
  filter: drop-shadow(0 4px 12px rgba(120, 95, 50, 0.18));
}
.login__err {
  color: var(--tomato);
  font-size: 0.92rem;
  font-weight: 600;
}

/* ==========================================================================
   Container queries — desktop / tablet adapt
   ========================================================================== */

@container (min-width: 720px) {
  main { padding: 22px 22px calc(110px + var(--safe-bottom)); }
  .item { padding: 8px 14px 8px 8px; gap: 14px; }
  .item__name { font-size: 1.05rem; }
}

/* ==========================================================================
   Reduced motion
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .item--enter { animation: none; }

  /* A spinner frozen mid-rotation reads as broken, so hide it outright. The
     dimmed thumbnail and camera icon stay — they are static, so they still read
     as "busy" alongside the "Uploading…" label and the disabled buttons.
     Specificity has to match the .--busy rules that turn these on. */
  .pending-photo--busy .pending-photo__spinner,
  .photo-row__preview--busy .photo-row__spinner,
  .iconbtn--busy::after { display: none; }
}
