:root {
  --canvas: #141413; --surface: #1f1e1b; --surface-2: #262521;
  --ink: #faf9f5; --muted: #b0aea5; --hairline: #3d3d3a;
  --clay: #d97757; --clay-deep: #c6613f;
  --ok: #7d9b76; --low: #d9a557; --out: #c6613f;
  --serif: Georgia, "Source Serif Pro", Charter, serif;
  --sans: Inter, system-ui, -apple-system, Arial, sans-serif;
  /* Native controls (checkboxes) default to system blue — the one cool colour
     in an all-warm palette. One line keeps them inside the accent family. */
  accent-color: var(--clay);
}
* { box-sizing: border-box; margin: 0; padding: 0; }

/* The UA default ring renders amber on this dark warm canvas, which reads as
   the ochre "low stock" signal. Ink is neutral and unambiguous. */
:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
::selection { background: var(--clay); color: var(--canvas); }
html, body { height: 100%; }
body {
  font-family: var(--sans); background: var(--canvas); color: var(--ink);
  max-width: 480px; margin: 0 auto; display: flex; flex-direction: column;
}
h1, h3 { font-family: var(--serif); font-weight: 400; }
button { font-family: var(--sans); cursor: pointer; }

.appbar { padding: 20px 18px 8px; display: flex; align-items: baseline; gap: 10px; }
.appbar h1 { font-size: 24px; }
.appbar .sub { font-size: 12px; color: var(--muted); }
#settings-btn { margin-left: auto; background: none; border: 1px solid var(--hairline); color: var(--muted); border-radius: 8px; padding: 4px 10px; font-size: 14px; }

#add-form { padding: 8px 16px 4px; }
#add-input {
  width: 100%; background: var(--surface); border: 1px solid var(--hairline);
  border-radius: 12px; padding: 11px 14px; color: var(--ink); font-size: 15px;
}
#add-input::placeholder { color: var(--muted); }

.autocomplete-wrapper { position: relative; }
.autocomplete-items {
  position: absolute; top: 100%; left: 0; right: 0; z-index: 99;
  background: var(--surface); border: 1px solid var(--hairline);
  border-radius: 8px; margin-top: 4px; box-shadow: 0 4px 12px rgba(0,0,0,0.15);
  max-height: 250px; overflow-y: auto;
}
.autocomplete-item {
  padding: 10px 14px; cursor: pointer; border-bottom: 1px solid var(--hairline);
  font-family: var(--serif); color: var(--ink); display: flex; align-items: center; justify-content: space-between;
}
.autocomplete-item:last-child { border-bottom: none; }
.autocomplete-stock { font-size: 11px; color: var(--muted); }

#list { flex: 1; padding: 4px 16px 140px; overflow-y: auto; }
.cat { font-size: 11px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .08em; margin: 14px 2px 6px; }
.row {
  display: flex; align-items: center; gap: 11px; background: var(--surface);
  border: 1px solid var(--hairline); border-radius: 12px; padding: 9px 13px; margin-bottom: 6px;
  font-family: var(--serif); font-size: 16px;
  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
  touch-action: pan-y;  /* let browser handle vertical scroll; JS handles horizontal */
  position: relative;
}
/* Lifted: the row has physically detached from the list.
   Shadow, rotation, and position are all set inline by JS. */
.row.lifted { z-index: 50; pointer-events: auto; }
.check {
  width: 22px; height: 22px; border-radius: 50%; border: 1.5px solid var(--muted);
  background: none; flex: none; color: transparent; font-size: 13px; line-height: 1; padding: 0;
}
.row.done .check { background: var(--clay); border-color: var(--clay); color: var(--canvas); }
.row.done .name { text-decoration: line-through; color: var(--muted); }
.row .name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.unit { font-family: var(--sans); font-size: 12px; color: var(--muted); flex: none; }
.track-btn { font-size: 11px; color: var(--muted); background: none; border: 1px solid var(--hairline); border-radius: 999px; padding: 2px 8px; flex: none; }
.stepper { flex: none; display: flex; align-items: center; gap: 6px; }
.step-btn { width: 26px; height: 26px; border-radius: 50%; border: 1px solid var(--hairline); background: none; color: var(--ink); font-size: 14px; padding: 0; }
.qty { font-size: 13px; min-width: 16px; text-align: center; }

/* Detached from the layout, so per the system's own rule it casts a shadow —
   without one it read as slicing through whatever row it floated over. */
#complete-trip {
  position: fixed; left: 50%; transform: translateX(-50%); bottom: 78px; z-index: 5;
  background: var(--clay); color: var(--canvas); border: none; font-weight: 600; font-size: 15px;
  padding: 12px 28px; border-radius: 0 0 8px 8px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .35);
}
#complete-trip:active { background: var(--clay-deep); }

#sheet {
  position: fixed; left: 50%; transform: translateX(-50%); bottom: 0; width: 100%; max-width: 480px;
  background: var(--surface-2); border-top: 1px solid var(--hairline);
  border-radius: 20px 20px 0 0; z-index: 10;
  height: 64px; transition: height .25s ease; overflow: hidden;
  display: flex; flex-direction: column;
}
#sheet.open { height: 86vh; }
#sheet-bar { padding: 10px 18px 14px; flex: none; cursor: pointer; -webkit-user-select: none; user-select: none; touch-action: none; }
.grabber { width: 42px; height: 4px; background: var(--hairline); border-radius: 2px; margin: 0 auto 8px; }
.peek { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.peek strong { font-family: var(--serif); font-weight: 400; font-size: 16px; }
.pill { display: inline-flex; align-items: center; gap: 5px; background: var(--surface); border: 1px solid var(--hairline); border-radius: 999px; padding: 3px 10px; font-size: 12px; color: var(--muted); }
.dot { width: 9px; height: 9px; border-radius: 50%; flex: none; display: inline-block; }
.dot.ok { background: var(--ok); } .dot.low { background: var(--low); } .dot.out { background: var(--out); }
/* Syncing is a mode, not a stock status, so it takes the neutral hue rather
   than borrowing one of the three status colours. */
.dot.pending { background: var(--muted); }
.sync-head { font-size: 13px; color: var(--muted); text-transform: uppercase; letter-spacing: .08em; margin-bottom: 8px; }
#sync-panel { border-bottom: 1px solid var(--hairline); padding-bottom: 14px; margin-bottom: 14px; }
#sync-panel .pill { margin-bottom: 6px; }
#inv-add { margin-left: auto; font-size: 12px; color: var(--ink); background: none; border: 1px solid var(--hairline); border-radius: 999px; padding: 3px 10px; }
#sheet-content { overflow-y: auto; padding: 0 16px 24px; }
.inv-cat {
  font-size: 11px; font-weight: 600; color: var(--muted); text-transform: uppercase;
  letter-spacing: .08em; margin: 12px 2px 7px;
  display: flex; align-items: center; gap: 6px; cursor: pointer;
  user-select: none; -webkit-user-select: none;
}
.inv-cat::after {
  content: '▾'; font-size: 10px; transition: transform .2s ease;
}
.inv-cat.collapsed::after { transform: rotate(-90deg); }
.inv-cat.collapsed + .tile-grid { display: none; }
.tile-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; }
.tile {
  aspect-ratio: 1; min-width: 0; overflow: hidden; position: relative;
  background: var(--surface); border: 1px solid var(--hairline); border-radius: 14px;
  padding: 6px; display: flex; flex-direction: column; text-align: left; color: var(--ink);
  font-family: inherit;
  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
}
.tile .name {
  font-family: var(--serif); font-size: 12.5px; line-height: 1.2;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* Only on-list tiles carry the ribbon, so only they give up room to it. 6px is
   the ceiling: at 8px a two-line name like "Sourdough loaf" starts losing its
   first word. Names stay top-aligned and the clamp keeps its ellipsis — the
   ribbon still slightly covers the first line, which is the accepted look. */
.tile.on-list .name { padding-right: 6px; }
.tile .bottom { margin-top: auto; display: flex; align-items: center; gap: 4px; }
.meta { display: inline-flex; align-items: center; gap: 4px; }
.count { font-weight: 600; font-size: 11px; color: var(--ink); background: none; border: none; padding: 0 2px; }
.stock-step { display: none; width: 20px; height: 20px; font-size: 12px; }
.tile.editing .stock-step { display: inline-flex; align-items: center; justify-content: center; }
/* Last price sits below the count in the hierarchy: muted, lighter weight. */
.tile .price { font-size: 10.5px; color: var(--muted); font-weight: 400; margin-left: auto; }
.tile.editing .price { display: none; }
.ribbon {
  position: absolute; top: 0; right: 0; width: 28px; height: 28px;
  background: var(--clay); color: var(--canvas);
  clip-path: polygon(0 0, 100% 0, 100% 100%);
  display: flex; align-items: flex-start; justify-content: flex-end;
  padding: 2px 4px 0 0; font-size: 10px; font-weight: 700;
}

/* Long-press armed: the dialog opens on release, so the hold needs to show
   it registered. Clay = action state, consistent with #complete-trip. */
.row.pressing, .tile.pressing { border-color: var(--clay); }

/* A list row for something you already have plenty of. Dimmed rather than
   struck through — strikethrough already means "in my basket" (.row.done).
   De-emphasised per element rather than with a blanket opacity: a .5 alpha on
   the whole row dropped "have N" to 2.8:1, and that note is the thing that
   explains why the row is dimmed at all. */
.row.have .name { color: var(--muted); }
.row.have .check { opacity: .5; }
.row .have-note { font-family: var(--sans); font-size: 11px; color: var(--muted); }

/* ---- Meals drawer (V3) ----
   Anchored to the centred 480px column, not the viewport, so the tab hugs the
   list's left edge on desktop the way it does on a phone. */
/* The hit area stays 28×84 — V4 enlarged it from 17px precisely so a thumb
   could find it. Only the *painted* tab is 16px: below 480px the column is
   flush to the viewport edge, so a 28px painted tab sat on top of the list and
   clipped the category labels ("MEAT" rendered as "EAT"). 16px stops exactly
   at the list's 16px gutter. */
#meals-tab {
  position: fixed; top: 50%; left: max(0px, calc(50% - 240px)); transform: translateY(-50%);
  z-index: 6; width: 28px; height: 84px;
  background: none; border: none; padding: 0;
  display: flex; align-items: center; justify-content: flex-start;
}
#meals-tab::before {
  content: ''; position: absolute; top: 0; bottom: 0; left: 0; width: 16px;
  background: var(--surface-2); border: 1px solid var(--hairline); border-left: none;
  border-radius: 0 12px 12px 0;
}
#meals-tab .tab-grip {
  position: relative; margin-left: 6px;
  width: 4px; height: 32px; background: var(--hairline); border-radius: 2px;
}
#meals-tab[aria-expanded="true"] { opacity: 0; pointer-events: none; }

#meals-scrim { position: fixed; inset: 0; z-index: 14; background: rgba(0, 0, 0, .45); }
#meals-scrim[hidden] { display: none; }

/* translateX(-100%) only clears the screen when the column is flush to the
   viewport edge. Wider than 480px there's a gutter, and the "closed" drawer sat
   in it in plain sight. visibility keeps it gone until it's actually open, and
   is delayed by the slide duration so the close animation still plays. */
#meals-drawer {
  position: fixed; top: 0; bottom: 0; left: max(0px, calc(50% - 240px)); z-index: 15;
  width: min(78vw, 300px); background: var(--surface-2);
  border-right: 1px solid var(--hairline);
  transform: translateX(-100%); visibility: hidden;
  transition: transform .25s ease, visibility 0s linear .25s;
  display: flex; flex-direction: column;
}
#meals-drawer.open {
  transform: translateX(0); visibility: visible;
  transition: transform .25s ease, visibility 0s;
}
.drawer-head {
  display: flex; align-items: center; gap: 10px; flex: none;
  padding: 22px 14px 10px; border-bottom: 1px solid var(--hairline);
}
.drawer-head h3 { font-size: 20px; }
#meal-save {
  margin-left: auto; font-size: 12px; color: var(--ink); background: none;
  border: 1px solid var(--hairline); border-radius: 999px; padding: 3px 10px;
}
#meal-save:disabled { color: var(--muted); opacity: .5; cursor: default; }
#meals-list { overflow-y: auto; padding: 6px 10px 20px; }

.meal {
  display: block; width: 100%; text-align: left; background: var(--surface);
  border: 1px solid var(--hairline); border-radius: 12px;
  padding: 9px 12px; margin-top: 6px; color: var(--ink);
  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
}
.meal.pressing { border-color: var(--clay); }
.meal .meal-name { display: block; font-family: var(--serif); font-size: 16px; }
/* One line, faded at the right rather than ellipsed. The mask always applies;
   short summaries simply have nothing in the faded zone to show. */
.meal .meal-items {
  display: block; font-family: var(--sans); font-size: 11.5px; color: var(--muted);
  margin-top: 2px; white-space: nowrap; overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
}
.drawer-empty { color: var(--muted); font-size: 13px; padding: 14px 4px; line-height: 1.5; }

/* Empty states were an uppercase category label with inline styles — a label
   shape carrying sentence copy. Sans, not serif: this is the app talking, not
   an item the user named. */
.empty { text-align: center; color: var(--muted); font-size: 14px; line-height: 1.5; padding: 40px 20px; }
.empty .empty-hint { display: block; font-size: 12px; margin-top: 6px; }

#banner {
  position: sticky; top: 0; z-index: 20;
  background: var(--surface-2); border-bottom: 1px solid var(--hairline); color: var(--ink);
  padding: 10px 14px; font-size: 13px; display: flex; gap: 10px; align-items: center;
}
#banner[hidden] { display: none; }
#banner button { margin-left: auto; background: none; border: none; color: var(--muted); font-size: 15px; }

dialog {
  background: var(--surface-2); color: var(--ink); border: 1px solid var(--hairline);
  border-radius: 16px; padding: 20px; width: min(90vw, 360px); margin: auto;
  /* Settings has grown past a small phone's viewport; without a cap the
     dialog overflows and the last controls become unreachable. */
  max-height: 85vh; overflow-y: auto;
}
dialog::backdrop { background: rgba(0, 0, 0, .55); }
dialog h3 { font-size: 20px; margin-bottom: 12px; }
dialog label, dialog .field { display: block; font-size: 12px; color: var(--muted); margin-bottom: 10px; }
dialog input:not([type="checkbox"]), dialog select {
  width: 100%; margin-top: 4px; background: var(--surface); border: 1px solid var(--hairline);
  border-radius: 8px; padding: 8px 10px; color: var(--ink); font-size: 14px;
}

/* Category picker — replaces a <select> so the selection dots are ours to
   position; Android's native popup drifts them inline next to the label.
   Matches the field styling above when closed, the preflight list when open. */
.picker-btn {
  width: 100%; margin-top: 4px; background: var(--surface); border: 1px solid var(--hairline);
  border-radius: 8px; padding: 8px 10px; color: var(--ink); font-size: 14px;
  display: flex; align-items: center; gap: 8px; text-align: left;
}
.picker-btn::after {
  content: '▾'; margin-left: auto; font-size: 10px; color: var(--muted);
  transition: transform .2s ease;
}
.picker-btn[aria-expanded="true"]::after { transform: rotate(180deg); }
.picker-list {
  max-height: 38vh; overflow-y: auto; margin-top: 6px;
  border: 1px solid var(--hairline); border-radius: 8px;
  background: var(--surface); padding: 4px 0;
}
.picker-list[hidden] { display: none; }
.picker-row {
  display: flex; align-items: center; gap: 11px; padding: 9px 12px;
  border-bottom: 1px solid var(--hairline); cursor: pointer;
}
.picker-row:last-child { border-bottom: none; }
.picker-row input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; }
/* The dot is a flex sibling of the label, pinned by margin-left:auto — it can
   never track the text width the way the native popup's did. */
.picker-check {
  width: 20px; height: 20px; border-radius: 50%; border: 1.5px solid var(--muted);
  flex: none; margin-left: auto; position: relative;
  transition: border-color .15s ease;
}
.picker-check::after {
  content: ''; position: absolute; inset: 3.5px; border-radius: 50%;
  background: var(--clay); transform: scale(0); transition: transform .15s ease;
}
.picker-row input:checked ~ .picker-check { border-color: var(--clay); }
.picker-row input:checked ~ .picker-check::after { transform: scale(1); }
.picker-row input:focus-visible ~ .picker-check { outline: 2px solid var(--clay); outline-offset: 2px; }
.picker-name { font-family: var(--serif); font-size: 16px; color: var(--ink); }
.picker-new { color: var(--muted); font-family: var(--serif); font-size: 15px; }
dialog menu { display: flex; flex-direction: column; gap: 8px; margin-top: 14px; padding: 0; list-style: none; }
.dialog-note { font-size: 12px; color: var(--muted); margin: -6px 0 12px; }

/* Trip dialog: one price row per bought item */
#trip-prices { max-height: 44vh; overflow-y: auto; }

/* Pre-flight meal dialog */
.preflight-list {
  max-height: 40vh; overflow-y: auto;
  border: 1px solid var(--hairline); border-radius: 8px;
  background: var(--surface); padding: 4px 0; margin-bottom: 12px;
}
.preflight-row {
  display: flex; align-items: center; gap: 11px; padding: 8px 12px;
  border-bottom: 1px solid var(--hairline);
  cursor: pointer;
}
.preflight-row:last-child { border-bottom: none; }
.preflight-row input[type="checkbox"] { display: none; }
.preflight-check {
  width: 22px; height: 22px; border-radius: 50%; border: 1.5px solid var(--muted);
  background: none; flex: none; color: transparent; font-size: 13px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
  user-select: none; -webkit-user-select: none;
}
.preflight-check.checked {
  background: var(--clay); border-color: var(--clay); color: var(--canvas);
}
.preflight-name { flex: 1; font-family: var(--serif); font-size: 16px; }
.preflight-stock { flex: none; font-size: 12px; color: var(--muted); background: var(--canvas); padding: 2px 6px; border-radius: 999px; }
.preflight-row.dimmed .preflight-name { color: var(--muted); }

/* Fly-to-list animation */
.fly-pending { opacity: 0; }
.fly-clone {
  position: fixed; z-index: 9999; pointer-events: none;
  display: flex; align-items: center; gap: 11px; padding: 8px 12px;
  background: var(--surface); border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0,0,0,.12);
  font-family: var(--serif); font-size: 16px;
  white-space: nowrap; overflow: hidden;
  /* motion is driven by the Web Animations API in app.js, not a CSS transition */
  will-change: transform, opacity;
}

.trip-row { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.trip-row .trip-name { font-family: var(--serif); font-size: 15px; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.trip-row .trip-qty { font-size: 11px; color: var(--muted); flex: none; }
/* id-scoped so it beats the generic `dialog input { width: 100% }` rule */
#trip-prices .trip-row input { width: 84px; flex: none; margin-top: 0; }

/* Price history in the item dialog */
#price-history:not(:empty) { border-top: 1px solid var(--hairline); margin: 4px 0 10px; padding-top: 10px; }
.hist-label { font-size: 11px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .08em; margin-bottom: 6px; }
.hist-row { display: flex; gap: 8px; font-size: 13px; color: var(--muted); margin-bottom: 4px; }
.hist-row .hist-price { color: var(--ink); font-weight: 600; }
.hist-row .hist-date { margin-left: auto; font-size: 11px; }
#item-form menu { flex-direction: row; }
dialog menu button, .import-label {
  padding: 9px 12px; border-radius: 8px; border: 1px solid var(--hairline);
  background: none; color: var(--ink); font-size: 14px; text-align: center; flex: 1;
}
.import-label { display: block; font-size: 14px !important; color: var(--ink) !important; margin: 0 !important; cursor: pointer; }
.btn-clay { background: var(--clay); border: none !important; color: var(--canvas) !important; font-weight: 600; border-radius: 0 0 8px 8px !important; }
.btn-danger { color: var(--out) !important; border-color: var(--out) !important; }
.check-label { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--ink); }
