/* The Groceries pane: the same scroller shape as .meals-plan -- a column owning the
   display band's single content scroller while it shows. The mobile visual-polish round
   gave it its own sage identity (the count line, the category markers below, and the
   Kroger action/status block) in place of the plain muted/ink vocabulary an earlier round
   deliberately chose; the gold-and-blue card round removes that per-destination tint again
   (SURFACES: "cards are equal") now that each category is its own opaque card
   (.grocery-group composes .app-card) rather than a plain-vocabulary group on the page --
   the count line reads the shared on-wall subtitle unchanged, and a group's own title
   reads the shared .app-card-title ink, correct now that it sits on the
   card rather than the wallpaper. The Kroger action was a pale band of its own; it sits
   directly on the wallpaper now, beside the primary button's own gold fill. */
.groceries[hidden] {
  display: none;
}

/* Programmatic only, like .meals-plan-title: focused on every open so the new pane is
   announced, but visually hidden and never in the tab order. */
.groceries-title:focus {
  outline: none;
}

/* The page's one status line is a reserved slot, not a line that comes and goes: it sits
   above the whole list, so a box that collapsed while empty would push every row down the
   moment a pending removal line appeared and pull them back up when it went -- exactly the
   shift that made a rapid second tap land on the wrong row. Its min-height is the reserved
   touch height, whatever the text size makes it, and the pending-removal line is ONE line that
   never wraps (see .groceries-removal-line), so the empty slot and the shown line are the
   same box at every text size and width. The pane is a flex column, so the slot must also opt
   out of flex-shrink or the column squeezes an empty slot away and the list still jumps. A
   failure or offline message wraps and may grow: it stands outside the removal window -- the
   tick has already come back off and nothing is pending -- so a taller line moves no row
   mid-tap. The selector outranks the page body's empty-paragraph rule so the empty slot keeps
   its height. */
.app-page-body > p.groceries-status:not([hidden]) {
  flex: none;
  display: block;
  min-height: var(--touch-min);
}

/* The pending-removal line stays on one line inside the reserved slot. */
.groceries-removal-line {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: var(--space-r2);
}

/* General status text wraps and grows when its failure or offline message needs more room. */
.groceries-status-message {
  flex: 1 1 auto;
  min-width: 0;
  line-height: 1.35;
  overflow-wrap: anywhere;
}

/* The pending-removal message stays on one line while it is pending. */
.groceries-removal-line .groceries-status-message {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* One line per grocery: the checkbox, name and optional meal action share the row. The row has no gap of its own: the checkbox margin below sets the list-lead gap before the name, and the meal mark's 48px hit area already pads its 16px icon off the name. */
.grocery-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0;
  min-width: 0;
  padding: 0;
}

/* The pane has no 64px lead rail, so the checkbox sits at the row's own start with the
   rail's gap after it. The 44px hit area is kept inside the row so the ring centres on
   the 44px name button beside it. */
.grocery-row > .agenda-task-checkbox {
  margin: 0 calc(var(--list-lead-gap) - var(--touch-slack)) 0 calc(var(--touch-slack) * -1);
}

/* The list's one control is the shared .app-check (groceries.js composes it): the same
   24px ring that fills solid --accent with an --on-accent mark when it is chosen, and
   the same 44px hit area from .app-check-target, so it still costs no row height. This
   used to be one of five hand-written copies of that circle. */

/* Grown from a zero basis, like .today-row > .today-open-row: a long grocery name must
   shrink this column rather than wrap onto a line of its own. */
.grocery-main {
  flex: 1 1 0;
  min-width: 0;
}

/* One line per row, at the support size task rows read at, so a typical combined amount and item ('1 cup + 2 tbsp cherry tomatoes') fits beside the checkbox and meal mark at 390px. A longer name, or Large text, ends in an ellipsis rather than wrapping; the recipe dialog shows the full line. */
.grocery-title {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.grocery-group .app-card-title {
  margin-bottom: 0.15rem;
}

/* The action keeps the row at its existing touch-min floor. */
.grocery-meal-mark {
  width: var(--touch-min);
  height: var(--touch-min);
  min-width: var(--touch-min);
  min-height: var(--touch-min);
  padding: 0;
  background: transparent;
  box-shadow: none;
  color: var(--muted);
}

/* A finished removal's row folds away rather than vanishing, so only the rows below it move,
   and upward. removeGroceryRow in chat.js pins the starting height inline and then sets
   the zero targets inline too, so this class only carries the clip and the transition. */
.grocery-row-collapsing {
  overflow: hidden;
  transition: height 180ms ease, padding 180ms ease, opacity 180ms ease;
}

/* The title is itself the row's edit control now, so it has to read exactly as the plain
   text it replaced -- and the generic `button` rule above fills a button with --accent, a
   pill radius, title padding and a 44px floor, every one of which would give
   the game away and the last of which would grow every row on the list. Each default is
   reset by hand here; the row keeps its own height and the whole .grocery-main column
   stays the tap area. The weight and line height come from .app-row-title on the same element; the size steps down to --text-support, the size a task row reads at, so a combined amount fits one line. The text cursor is the one pointer change: the words became
   editable, and the pointer should say so before the tap lands. Keyboard focus keeps the
   shared button:focus-visible ring, and the .grocery-title class stays on the element so
   the pending strike above still finds it. */
.grocery-title-button {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: var(--touch-min);
  padding: 0;
  color: inherit;
  text-align: start;
  background: transparent;
  border: 0;
  border-radius: 0;
  cursor: text;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Two classes, so it outranks .app-row-title on the same element. */
.grocery-row .grocery-title-button {
  font-size: var(--text-support);
}

/* The open editor stands in for the title inside .grocery-main, which shrinks around it:
   the shared .app-field ground for the draft (composed on the element in groceries.js, not
   restated here) and the editor's own error line on a full second row of its own, which is
   why the form wraps. The field is the one thing allowed to shrink (flex-basis 0, min-width 0):
   at 320px the field has enough room to shrink, so nothing overflows. */
.grocery-edit {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-r1);
  min-width: 0;
}

.grocery-edit-input {
  flex: 1 1 0;
  min-width: 0;
}

.grocery-edit-sections {
  display: flex;
  flex-direction: column;
  gap: var(--space-r1);
  flex-basis: 100%;
  min-width: 0;
}

/* Four equal cells leave “Household” touching its dividers, so a cell grows to its longest word and keeps the control’s padding. */
.grocery-edit-sections .app-segmented {
  grid-auto-columns: minmax(min-content, 1fr);
}

.grocery-edit-error {
  flex-basis: 100%;
  margin: 0;
  overflow-wrap: anywhere;
}

.grocery-edit-error[hidden] {
  display: none;
}

/* "Saving…" is a state, not a failure: the quiet ink for the wait, while .app-failure on
   the same line above carries a save that actually failed. */
.grocery-edit-saving {
  color: var(--muted);
}

/* The Add Meals dialog: the shared .app-dialog frame, one block per planned meal. Its
   footer wraps, like the two other footers that can grow an error line. */
.grocery-meals-footer {
  align-items: center;
  flex-wrap: wrap;
}

.grocery-meals-note {
  flex: 1 1 auto;
  margin: 0;
  color: var(--muted);
  font-size: var(--text-detail-meta);
}

.grocery-meals-error[hidden] {
  display: none;
}

.grocery-meal + .grocery-meal {
  margin-top: 0.9rem;
  padding-top: 0.9rem;
}

.grocery-meal-row {
  display: flex;
  align-items: center;
  gap: var(--space-r2);
  min-width: 0;
}

.grocery-meal-row > .agenda-task-checkbox {
  margin: calc(var(--touch-slack) * -1)
    calc(var(--list-lead-gap) - var(--touch-slack))
    calc(var(--touch-slack) * -1)
    calc(var(--touch-slack) * -1);
}

.grocery-meal-title {
  flex: 1 1 0;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* The meal's ingredients, a quiet nested block on the panel surface (.app-inset), inset
   past the checkbox so they read as that meal's children rather than as a second list. */
.grocery-ingredients {
  margin-top: 0.45rem;
  margin-left: calc(24px + var(--list-lead-gap));
  padding: var(--space-r2) 0.6rem;
}

/* The whole label is the touch target, so the 24px box keeps its own size and is padded
   rather than stretched -- a checkbox is a replaced element. */
.grocery-ingredient {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  min-height: var(--touch-min);
  padding: 0.1rem 0;
  color: var(--ink);
  cursor: pointer;
  font-size: var(--text-detail-body);
}

/* The per-ingredient control is the shared .app-check too -- a copy of the circle used
   to live here as well. */

.grocery-ingredient-text {
  min-width: 0;
  overflow-wrap: anywhere;
}

/* An unticked meal contributes nothing, so its ingredient choices go inert and say so --
   while still being remembered, so re-ticking the meal restores exactly what was chosen. */
.grocery-meal-excluded .grocery-ingredients {
  opacity: 0.55;
}

.grocery-meal-excluded .grocery-ingredient,
.grocery-ingredient-check:disabled {
  cursor: default;
}
