/* ============================================================================
   Plate&Plan stylesheet
   ----------------------------------------------------------------------------
   Three layers, in cascade order. Keep them in this order and in this file:

     1. TOKENS      — the only place raw values are allowed to appear.
     2. BASE        — element defaults + accessibility guarantees.
     3. COMPONENTS  — one class per repeated UI pattern.

   Conventions
   -----------
   * Loaded via <helmet><link> in index.html, so the dc runtime hoists it into
     <head> once. Plain CSS — no build step, no preprocessor, no dependency.
   * Naming is block__element--variant (BEM-ish). Flat, one level of nesting max.
   * Inline `style=` is reserved for values that are *data*: a person's chosen
     colour, a meal's tint, a computed offset. Pass those in as custom
     properties (`style="--chip-tint:{{ g.tint }}"`) and let the class own
     every structural declaration. Anything static belongs in here.
   * Adding a raw hex, px radius, or font stack below the TOKENS section is a
     bug. test_a11y.py enforces the parts of this that are machine-checkable.
   ========================================================================== */


/* ============================================================================
   1. TOKENS
   ========================================================================== */

:root {
  /* --- Type ------------------------------------------------------------ */
  --font-display: 'Ribeye', Georgia, serif;
  --font-body: 'Poppins', system-ui, -apple-system, sans-serif;

  /* Ramp floor is 11px deliberately: below that, small-print UI copy stops
     being readable at arm's length on a phone. test_a11y.py holds the floor. */
  --fs-2xs: 11px;
  --fs-xs: 11.5px;
  --fs-sm: 12.5px;
  --fs-md: 13.5px;
  --fs-lg: 14.5px;
  --fs-xl: 17px;
  --fs-2xl: 19px;
  --fs-3xl: 24px;

  --fw-regular: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --fw-bold: 700;
  --fw-heavy: 800;

  --lh-tight: 1.2;
  --lh-snug: 1.35;
  --lh-normal: 1.45;
  --lh-relaxed: 1.6;

  --tracking-caps: 0.05em;

  /* --- Colour: surfaces ------------------------------------------------ */
  --surface: #fff;              /* cards, menus, modals                    */
  --surface-warm: #FFFDF7;      /* note cards — "paper"                    */
  --surface-sunken: #faebe6;    /* page background, inputs, quiet pills    */
  --surface-muted: #F1EEE6;     /* side rows, filter panel, small chips    */
  --surface-track: #EFEAE0;     /* segmented-control troughs               */

  /* --- Colour: borders (light → heavy) --------------------------------- */
  --border-faint: #EFEAE0;      /* hairline dividers, card edges           */
  --border-soft: #E8E2D6;       /* inputs at rest, date nav                */
  --border: #E2DCD0;            /* menus, primary input borders            */
  --border-strong: #D3CCBE;     /* dashed affordances, drag handles        */

  /* --- Colour: text ----------------------------------------------------
     Every value here clears WCAG 2.1 AA (4.5:1) on --surface AND
     --surface-sunken, except --text-faint, which is documented below and is
     verified against the 3:1 non-text threshold instead. The old palette had
     six greys between #9a948a and #cfc8ba used for real copy at 1.4:1–3.0:1;
     they all collapse onto --text-muted. See CSS-ARCHITECTURE.md. */
  --text: #0d2504;              /* headings, primary copy      14.1:1      */
  --text-body: #4a463f;         /* long-form body copy          8.1:1      */
  --text-muted: #6b655c;        /* meta, labels, secondary      5.0:1      */

  /* NON-TEXT ONLY. 3.2:1 on cream — clears 1.4.11 for icon glyphs and
     control boundaries, does NOT clear 1.4.3 for text. If you are about to
     put a word in this colour, use --text-muted. */
  --text-faint: #8a847a;

  --text-inverse: #fff;

  /* --- Colour: semantic ----------------------------------------------- */
  --success: #2FA862;           /* switch-on fill (non-text)               */
  --success-text: #1E7A46;      /* "✓ saved" copy               5.6:1      */
  --danger: #C0392B;            /* destructive label            4.7:1      */
  --danger-border: #E6B4AD;
  --warning: #F2960C;
  --warning-surface: #FFF3E0;
  --warning-text: #9A5800;      /* on --warning-surface         5.0:1      */

  /* Accent is user-configurable (props + per-person colour pickers), so it
     arrives from the logic script; this is the fallback only.

     Every accent option is dark enough to clear 4.5:1 against white, which is
     one condition doing two jobs: the accent is used as a button/badge *fill*
     with white text on it, and as the active tab's *text* colour on a white
     card. Both directions measure the same accent-vs-white contrast, so an
     accent light enough to look bright fails both at once. The whole swatch
     set sits at ~5.05:1. test_a11y.py holds the line. */
  --accent: #e00000;

  /* --- Radius --------------------------------------------------------- */
  --r-xs: 6px;
  --r-sm: 8px;
  --r-md: 10px;
  --r-lg: 12px;
  --r-xl: 16px;
  --r-2xl: 18px;
  --r-pill: 20px;
  --r-round: 50%;

  /* --- Space ---------------------------------------------------------- */
  --sp-1: 4px;
  --sp-2: 6px;
  --sp-3: 8px;
  --sp-4: 11px;
  --sp-5: 14px;
  --sp-xl: 16px;    /* out of numeric sequence: the form/modal gutter, which the
                       design uses at 16px and nowhere else on the 4/6/8 ramp */
  --sp-6: 18px;
  --sp-7: 22px;

  /* --- Elevation ------------------------------------------------------ */
  --shadow-card: 0 1px 2px rgba(0, 0, 0, .03);
  --shadow-raised: 0 1px 2px rgba(0, 0, 0, .08);
  --shadow-menu: 0 4px 16px rgba(0, 0, 0, .10);
  --shadow-modal: 0 24px 60px rgba(0, 0, 0, .25);
  --shadow-knob: 0 1px 2px rgba(0, 0, 0, .25);
  --shadow-fab: 0 6px 16px rgba(0, 0, 0, .24);
  /* Cast sideways by a sticky column onto the content sliding under it. */
  --shadow-pinned: 6px 0 8px -6px rgba(0, 0, 0, .18);

  --scrim: rgba(40, 38, 34, .45);

  /* --- Motion --------------------------------------------------------- */
  --ease: cubic-bezier(.4, 0, .2, 1);
  --dur-fast: .15s;
  --dur-base: .25s;

  /* --- Accessibility -------------------------------------------------- */
  --focus-ring: #0d2504;
  --focus-ring-offset: 2px;
  --tap-min: 44px;              /* WCAG 2.5.8 target size (minimum) is 24px;
                                   44px is the Apple/Material guidance we aim
                                   for on primary controls. */
}


/* ============================================================================
   2. BASE
   ========================================================================== */

* { box-sizing: border-box; }

/* The one horizontal scroll this app wants is `.planner__grid`, which owns a
   week that genuinely does not fit. Anything else reaching past the viewport is
   a bug, and on a phone it is an invisible one — you find it by the page
   drifting sideways under your thumb, not by seeing what caused it. Clipping at
   the root turns those into a layout mistake you can see instead of a scroll
   you cannot explain. `overflow-x` alone keeps the viewport as the scroll
   container, so `position: sticky` still resolves against it. */
html { overflow-x: hidden; }
html, body { margin: 0; padding: 0; }

body {
  font-family: var(--font-body);
  background: var(--surface-sunken);
  color: var(--text);
}

textarea, input, select, button { font-family: inherit; }

/* --- Focus -----------------------------------------------------------------
   The `!important` is a guard rail, not a fix for anything currently broken.
   The template used to carry inline `outline:none` on ~15 inputs; those are all
   gone now, but an inline declaration outranks any selector, so a single
   reintroduced `outline:none` would silently make the app unusable by keyboard.
   `!important` is the only thing that beats inline, so it stays. test_a11y.py
   asserts both halves: that this rule keeps its !important, and that no inline
   `outline:none` comes back. */
:focus-visible {
  outline: 2px solid var(--focus-ring) !important;
  outline-offset: var(--focus-ring-offset) !important;
}

/* Elements the template makes clickable with onClick but which are not
   natively focusable get the same ring once they are given tabindex. */
[role="button"]:focus-visible,
[role="switch"]:focus-visible,
[role="checkbox"]:focus-visible,
[role="tab"]:focus-visible { border-radius: var(--r-xs); }

/* --- Motion ----------------------------------------------------------------
   Respect the OS "reduce motion" setting. The drawer slide, the schedule
   day-in animation, and the switch knob all become instant rather than
   disappearing, so nothing loses its state cue. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* --- Screen-reader-only text ---------------------------------------------
   For accessible names on icon-only controls: <span class="sr-only">Close</span>
   Stays in the accessibility tree; takes no visual space. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --- Scrollbars ----------------------------------------------------------- */
::-webkit-scrollbar { width: 9px; height: 9px; }
::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: var(--r-sm); }
::-webkit-scrollbar-track { background: transparent; }

/* --- Print ---------------------------------------------------------------- */
@media print {
  @page { margin: 12mm; size: landscape; }
  body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  .drawer, .drawer-backdrop { display: none; }
  .app-footer { display: none; }
}


/* ============================================================================
   3. COMPONENTS
   ========================================================================== */

/* --- Surfaces ------------------------------------------------------------- */

.card {
  background: var(--surface);
  border: 1px solid var(--border-faint);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-card);
}

.card--note {
  background: var(--surface-warm);
  border-color: var(--border-faint);
  border-radius: var(--r-lg);
  box-shadow: none;
}

.panel {
  background: var(--surface-muted);
  border: 1px solid var(--border-faint);
  border-radius: var(--r-lg);
  padding: var(--sp-4);
}

/* Scroll regions. Height comes from --scroll-max at the call site because it
   differs per pane; the gutter compensation does not. */
.scroll-y {
  max-height: var(--scroll-max, 560px);
  overflow: auto;
  padding-right: 3px;
  margin-right: -3px;
}


/* --- Labels and rules ----------------------------------------------------- */

/* The small uppercase section label. Was 17 near-identical inline copies. */
.eyebrow {
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--text-muted);
  white-space: nowrap;
}

.eyebrow--block {
  display: block;
  margin: 0 0 var(--sp-2);
}

/* Horizontal hairline that fills the gap in a section header row. */
.rule {
  flex: 1;
  height: 1px;
  background: var(--border-faint);
  border: 0;
}

.divider {
  height: 1px;
  background: var(--border-faint);
  margin: var(--sp-1) 0;
}

.section-head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-2);
}


/* --- Buttons -------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  font-family: var(--font-display);
  font-weight: var(--fw-medium);
  font-size: var(--fs-md);
  line-height: 1;
  padding: var(--sp-3) var(--sp-5);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  text-decoration: none;
}

.btn--primary {
  background: var(--btn-bg, var(--text));
  color: var(--text-inverse);
  border-color: transparent;
}

.btn--secondary {
  background: var(--surface);
  color: var(--text-muted);
  border-color: var(--border);
}

.btn--danger {
  background: var(--surface);
  color: var(--danger);
  border-color: var(--danger-border);
}

.btn--dashed {
  background: var(--surface-sunken);
  color: var(--text-muted);
  border: 1px dashed var(--border-strong);
  font-size: var(--fs-sm);
  padding: var(--sp-2) var(--sp-4);
}

.btn[disabled], .btn[aria-disabled="true"] { cursor: not-allowed; opacity: .6; }

/* Square icon-only control. Always needs an accessible name — either
   aria-label or a .sr-only child. */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 0;
  border-radius: var(--r-xs);
  background: transparent;
  color: var(--text-faint);
  font-size: var(--fs-xl);
  line-height: 1;
  cursor: pointer;
}

.icon-btn--boxed {
  border: 1px solid var(--border);
  background: var(--surface-sunken);
  border-radius: 7px;
}

.icon-btn--tinted {
  background: var(--surface-muted);
  width: 22px;
  height: 22px;
  border-radius: var(--r-xs);
}

.icon-btn--sm { width: 22px; height: 22px; font-size: var(--fs-lg); }


/* --- Pills / tags --------------------------------------------------------- */

.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  font-size: var(--fs-sm);
  border-radius: var(--r-pill);
  padding: var(--sp-1) var(--sp-4);
  background: var(--pill-bg, var(--surface-sunken));
  color: var(--pill-fg, var(--text-muted));
  border: 1px solid var(--pill-bd, var(--border-soft));
  white-space: nowrap;
}

/* Selectable filter pill — same shell, but it is a control. */
.pill--action {
  cursor: pointer;
  font-family: var(--font-display);
  font-weight: var(--fw-medium);
}

.pill--tag {
  font-size: var(--fs-2xs);
  padding: 2px var(--sp-3);
  border-color: var(--border-faint);
}

.pill--plain {
  background: var(--surface-muted);
  border-color: transparent;
}

/* Small colour swatch inside a pill or list row. */
.dot {
  width: var(--dot-size, 8px);
  height: var(--dot-size, 8px);
  border-radius: var(--r-round);
  background: var(--dot-color, var(--text-faint));
  flex: none;
}

.dot--sm { --dot-size: 6px; }
.dot--lg { --dot-size: 7px; }

/* Count badge (e.g. active filter count). */
.badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 17px;
  height: 17px;
  padding: 0 var(--sp-1);
  border-radius: 9px;
  background: var(--badge-bg, var(--accent));
  color: var(--badge-fg, var(--text-inverse));
  font-size: var(--fs-2xs);
}

.avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--avatar-size, 26px);
  height: var(--avatar-size, 26px);
  border-radius: var(--r-round);
  background: var(--avatar-bg, var(--border-soft));
  color: var(--avatar-fg, var(--text-inverse));
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-2xs);
  line-height: 1;
}

.avatar--sm { --avatar-size: 18px; }
.avatar--md { --avatar-size: 20px; }
.avatar--lg { --avatar-size: 34px; font-size: var(--fs-lg); }


/* --- Form fields ---------------------------------------------------------- */

/* Every text input, number input, select and textarea. Note: no
   `outline:none` — the base focus rule needs somewhere to land. */
.field {
  width: 100%;
  box-sizing: border-box;
  border: 1px solid var(--border);
  background: var(--surface);
  border-radius: 9px;
  padding: 9px var(--sp-4);
  font-size: var(--fs-md);
  color: var(--text);
}

.field--sunken {
  background: var(--surface-sunken);
  border-color: var(--border-soft);
}

.field--sm { padding: var(--sp-3) 9px; font-size: var(--fs-sm); border-radius: var(--r-sm); }

.field--bare {
  border: 0;
  background: transparent;
  padding: 0;
}

.field--numeric { text-align: right; }

.field::placeholder, .field--bare::placeholder { color: var(--text-muted); opacity: 1; }

textarea.field { resize: vertical; line-height: var(--lh-normal); }


/* --- Switch (the pill toggle in the view menus) --------------------------- */

.switch {
  position: relative;
  display: inline-block;
  flex: none;
  width: 30px;
  height: 17px;
  border-radius: 9px;
  border: 0;
  padding: 0;
  cursor: pointer;
  background: var(--border-strong);
  transition: background var(--dur-fast);
}

.switch[aria-checked="true"] { background: var(--success); }

.switch__knob {
  position: absolute;
  top: 1px;
  left: 2px;
  width: 15px;
  height: 15px;
  border-radius: var(--r-round);
  background: var(--surface);
  box-shadow: var(--shadow-knob);
  transition: left var(--dur-fast);
}

.switch[aria-checked="true"] .switch__knob { left: 17px; }

/* Row that wraps a label + switch in the dropdown menus. */
.switch-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  width: 100%;
  padding: 7px var(--sp-4);
  border: 0;
  border-radius: 7px;
  background: transparent;
  font-size: var(--fs-md);
  color: var(--text);
  text-align: left;
  cursor: pointer;
}


/* --- Segmented control (week count, card style, shopping mode) ------------ */

.segmented {
  display: flex;
  gap: 2px;
  background: var(--surface-track);
  border-radius: 9px;
  padding: 3px;
}

.segmented__opt {
  flex: 1;
  text-align: center;
  padding: var(--sp-1) var(--sp-3);
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--text-muted);
  font-family: var(--font-display);
  font-weight: var(--fw-medium);
  font-size: var(--fs-sm);
  white-space: nowrap;
  cursor: pointer;
}

.segmented__opt[aria-pressed="true"], .segmented__opt[aria-selected="true"] {
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-raised);
}

/* Dark-fill variant used by the shopping/inventory switcher. */
.segmented--solid { background: var(--surface-muted); gap: 2px; padding: 2px; }
.segmented--solid .segmented__opt {
  font-family: var(--font-body);
  font-weight: var(--fw-bold);
  font-size: var(--fs-sm);
  border-radius: var(--r-xs);
}
.segmented--solid .segmented__opt[aria-pressed="true"] {
  background: var(--text);
  color: var(--text-inverse);
}


/* --- Menus / popovers ----------------------------------------------------- */

.menu {
  position: absolute;
  right: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-menu);
  padding: var(--sp-2);
  z-index: 200;
}

.menu--wide { min-width: 230px; border-radius: var(--r-lg); padding: var(--sp-3); }

/* Each popover hangs below its own trigger, so the offset is per-menu. */
.menu--profile { top: 40px; }
.menu--view { top: 38px; min-width: 180px; }
.menu--list { top: 34px; min-width: 170px; }

.menu__item {
  display: block;
  width: 100%;
  padding: 7px var(--sp-4);
  border: 0;
  border-radius: 7px;
  background: transparent;
  font-size: var(--fs-md);
  color: var(--text);
  text-align: left;
  cursor: pointer;
}

.menu__item:hover { background: var(--surface-sunken); }

.menu__row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  padding: var(--sp-1) var(--sp-2);
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  text-align: left;
  cursor: pointer;
}

.menu__row:hover { background: var(--surface-sunken); }


/* --- Tabs (sidebar) ------------------------------------------------------- */

.tabs {
  display: flex;
  gap: var(--sp-6);
  margin-bottom: 13px;
  border-bottom: 2px solid var(--border-faint);
  padding-bottom: var(--sp-3);
}

.tab {
  border: 0;
  background: transparent;
  padding: 0;
  cursor: pointer;
  font-family: var(--font-display);
  font-weight: var(--fw-bold);
  font-size: var(--fs-xl);
  line-height: 1;
  color: var(--text-muted);
  transition: color var(--dur-fast);
}

.tab[aria-selected="true"] { color: var(--tab-active, var(--accent)); }


/* --- Date navigator ------------------------------------------------------- */

.date-nav {
  display: flex;
  align-items: center;
  gap: 2px;
  background: var(--surface-sunken);
  border: 1px solid var(--border-soft);
  border-radius: var(--r-md);
  padding: 3px var(--sp-2);
}

.date-nav__step {
  width: 24px;
  height: 24px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--text-muted);
  font-size: var(--fs-xl);
  line-height: 1;
  cursor: pointer;
}

.date-nav__input {
  border: 0;
  background: transparent;
  font-family: var(--font-display);
  font-weight: var(--fw-medium);
  font-size: var(--fs-md);
  color: var(--text);
  cursor: pointer;
}


/* --- Meal chip (schedule + planner) --------------------------------------
   Driven by three custom properties set inline from the meal's own palette:
   --chip-tint (background), --chip-accent (left rule), --chip-text. */

.chip {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  padding: 7px 9px;
  border-radius: 9px;
  background: var(--chip-tint, var(--surface-muted));
  border-left: 3px solid var(--chip-accent, var(--border));
  color: var(--chip-text, var(--text));
  cursor: grab;
  text-align: left;
  width: 100%;
  border-top: 0;
  border-right: 0;
  border-bottom: 0;
}

.chip--grid {
  flex: 1;
  min-height: 96px;
  justify-content: space-between;
  padding: var(--sp-3) 9px;
}

.chip__title {
  font-family: var(--font-body);
  font-weight: var(--fw-heavy);
  font-size: var(--fs-md);
  line-height: var(--lh-tight);
  color: inherit;
}

.chip__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-1);
  width: 100%;
}

/* The card wrapper stays clickable for mouse/drag, but the title is a real
   button so the slot is reachable and activatable by keyboard. Nesting a
   <button> inside another <button> is invalid, which is why the wrapper is a
   plain div rather than a button itself. */
.chip__title--btn,
.idea__name--btn {
  border: 0;
  background: transparent;
  padding: 0;
  cursor: pointer;
  text-align: left;
  min-width: 0;
  max-width: 100%;
}

.chip__meta { font-size: var(--fs-2xs); color: inherit; opacity: .8; }

.chip__variant {
  font-size: var(--fs-2xs);
  color: inherit;
  opacity: .75;
  margin-top: 2px;
  font-style: italic;
}

/* The small × that clears a slot. Inherits the chip's text colour. */
.chip__clear {
  flex: none;
  border: 0;
  background: transparent;
  color: inherit;
  opacity: .6;
  font-size: var(--fs-lg);
  line-height: 1;
  padding: 0 2px;
  cursor: pointer;
}

/* "+ who" — assign a person to this meal. */
.chip__assign {
  flex: none;
  border: 1px dashed currentColor;
  background: transparent;
  color: inherit;
  opacity: .75;
  border-radius: var(--r-pill);
  padding: 1px var(--sp-2);
  font-size: var(--fs-2xs);
  cursor: pointer;
}

/* Empty planner cell. */
.slot-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  min-height: 96px;
  border: 1.5px dashed var(--border-faint);
  border-radius: 9px;
  background: transparent;
  color: var(--text-faint);
  font-size: var(--fs-2xl);
  cursor: pointer;
}

.slot {
  min-height: 110px;
  border-radius: 11px;
  background: var(--surface);
  border: 1px solid var(--border-faint);
  padding: 5px;
}


/* --- Side dishes ---------------------------------------------------------- */

.side-row {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  padding: 3px var(--sp-2);
  background: var(--surface-muted);
  border-radius: var(--r-xs);
}

.side-row__name {
  flex: 1;
  min-width: 0;
  font-family: var(--font-body);
  font-weight: var(--fw-heavy);
  font-size: var(--fs-2xs);
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Dashed border uses --text-faint, not --border-strong: this is an interactive
   control, so its boundary needs 3:1 (WCAG 1.4.11), which --border-strong
   (1.7:1 on white) does not clear. */
.side-add {
  display: block;
  width: 100%;
  text-align: center;
  font-size: var(--fs-2xs);
  color: var(--text-muted);
  cursor: pointer;
  padding: 3px var(--sp-1);
  border: 1px dashed var(--text-faint);
  border-radius: 5px;
  background: transparent;
}


/* --- Idea cards (sidebar list) -------------------------------------------- */

.idea {
  display: block;
  width: 100%;
  text-align: left;
  border: 1px solid var(--idea-border, var(--border-faint));
  border-radius: var(--r-lg);
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface);
  cursor: var(--idea-cursor, pointer);
}

.idea:hover { border-color: var(--border-strong); box-shadow: 0 2px 8px rgba(0, 0, 0, .05); }

.idea--compact {
  display: flex;
  align-items: center;
  gap: 9px;
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4);
}

.idea__name {
  font-family: var(--font-body);
  font-weight: var(--fw-heavy);
  font-size: var(--fs-lg);
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.idea__preview {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  margin-top: 5px;
  line-height: var(--lh-snug);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.idea__fav {
  flex: none;
  border: 0;
  background: transparent;
  padding: 0;
  cursor: pointer;
  font-size: var(--fs-lg);
  line-height: 1;
  color: var(--fav-color, var(--text-faint));
}


/* --- Shopping list -------------------------------------------------------- */

.shop-item {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-1) 2px;
}

.shop-check {
  width: 20px;
  height: 20px;
  flex: none;
  margin-top: 1px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--r-xs);
  border: 2px solid var(--check-border, var(--border-strong));
  background: var(--check-bg, transparent);
  color: var(--text-inverse);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  cursor: pointer;
}

.inventory-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) 2px;
  border-bottom: 1px solid var(--border-faint);
}


/* --- Drawer (mobile sheet) ----------------------------------------------- */

.drawer {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  width: 360px;
  z-index: 40;
  transform: translateX(100%);
  transition: transform var(--dur-base) var(--ease);
}

.drawer.open { transform: translateX(0); }

.drawer-backdrop {
  position: fixed;
  inset: 0;
  z-index: 39;
  background: var(--scrim);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-base);
}

.drawer-backdrop.open { opacity: 1; pointer-events: auto; }

.sheet-handle {
  width: 40px;
  height: 4px;
  background: var(--border-strong);
  border-radius: 2px;
  margin: 0 auto var(--sp-5);
}


/* --- Modal --------------------------------------------------------------- */

.modal-scrim {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: var(--scrim);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-7);
}

.modal {
  background: var(--surface);
  border-radius: var(--r-2xl);
  max-width: 560px;
  width: 100%;
  max-height: 88vh;
  overflow: auto;
  box-shadow: var(--shadow-modal);
}

.modal__close {
  flex: none;
  border: 0;
  background: transparent;
  padding: 0;
  cursor: pointer;
  font-size: var(--fs-3xl);
  line-height: 1;
  color: var(--text-faint);
}


/* --- Animation ----------------------------------------------------------- */

@keyframes schedNewDay {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: translateY(0); }
}


/* ============================================================================
   4. LAYOUT
   One-off structural rules for named regions of the page. These are not
   reusable components — they exist so that page scaffolding stops living in
   inline `style` attributes where nothing can find or diff it.
   ========================================================================== */

/* Breakpoint comes from the logic script's own isMobile/isTablet state (it
   drives layout branching too, not just padding), so these are classes rather
   than media queries. */
/* A column, so the page can hand its own height down to whichever region wants
   to absorb the slack — see `.planner__grid` and the small-screen block below.
   `dvh` after `vh` because on iOS `100vh` is the height the viewport has once
   the URL bar has retracted, which is taller than what you are looking at; the
   difference is exactly enough to push the footer off screen. */
.app {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
  padding: var(--sp-7) 26px 44px;
}
.app--tablet { padding: var(--sp-5) var(--sp-5) 40px; }
.app--mobile { padding: var(--sp-3) var(--sp-3) 40px; }

.app-header {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-6);
}

.app-header__profile {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sp-3);
  position: relative;
}

.brand { display: flex; align-items: center; gap: 9px; }
.brand__mark { width: 32px; height: 32px; border-radius: 9px; display: block; }
.brand__name {
  font-family: var(--font-display);
  font-weight: var(--fw-regular);
  font-size: var(--sp-7);
  color: var(--text);
  letter-spacing: -.01em;
}

.print-title {
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: 25px;
  margin: 0 0 var(--sp-5);
  color: var(--text);
}

/* `flex: 1` takes the page's leftover height; `align-items: flex-start` then
   declines to pass it on, which is what desktop wants — a sticky sidebar next
   to a main column that ends where its content ends. The small-screen block
   flips `align-items` to hand the height through. */
.layout { display: flex; gap: 20px; align-items: flex-start; flex: 1; min-height: 0; }
.layout__main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
}

.card--pad { padding: var(--sp-5) var(--sp-6) var(--sp-6); }
.card--sidebar { padding: var(--sp-5); }

.sidebar { width: 340px; flex: none; position: sticky; top: var(--sp-5); }

.sidebar--sheet {
  position: fixed;
  bottom: 0; left: 0; right: 0;
  z-index: 50;
  width: auto;
  top: auto;
  max-height: 85vh;
  overflow-y: auto;
  background: var(--surface-sunken);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  padding: var(--sp-5) var(--sp-5) 28px;
}

.sheet-scrim { position: fixed; inset: 0; z-index: 49; background: var(--scrim); }

/* --- Floating action button ------------------------------------------------
   Material's 56px FAB, sized to clear the 44px tap-target minimum on its own.
   z-index sits below .sheet-scrim (49) on purpose: when the sheet is up, the
   scrim covers the FAB and a scrim tap is the way back out. */
.fab {
  position: fixed;
  right: 20px;
  bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  z-index: 48;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  padding: 0;
  border: 0;
  border-radius: var(--r-xl);
  background: var(--fab-bg, var(--accent));
  color: var(--fab-fg, var(--text-inverse));
  box-shadow: var(--shadow-fab);
  cursor: pointer;
}

.fab__badge {
  position: absolute;
  top: -3px;
  right: -3px;
  --badge-bg: var(--surface);
  --badge-fg: var(--text);
  border: 1px solid var(--border-faint);
}

.sentinel { height: 1px; }


/* --- Eyebrow variants --------------------------------------------------- */

.eyebrow--md { font-size: var(--fs-sm); }
.eyebrow--body { font-family: var(--font-body); font-weight: var(--fw-bold); }
/* Uses the meal's `text` shade, not its `color`. The saturated fill hues are
   built for backgrounds — dinner's orange is 2.7:1 on white and side's yellow
   is 1.6:1, so using --meal-color here made the section labels unreadable.
   Each meal in the MEALS map already carries a `text` shade for exactly this. */
.eyebrow--meal { color: var(--meal-text, var(--text-muted)); }

.section-head--roomy { margin-bottom: var(--sp-4); }
.section-head--meal {
  margin: var(--sp-4) -4px var(--sp-2);
  padding: 2px var(--sp-1);
  border-radius: var(--r-xs);
  cursor: pointer;
  gap: 7px;
}


/* --- Empty states and prose -------------------------------------------- */

.empty-note {
  margin: 0;
  text-align: center;
  color: var(--text-muted);
  font-size: var(--fs-sm);
  padding: var(--sp-5) var(--sp-3);
}

.empty-note--roomy { padding: var(--sp-6) var(--sp-3); }

.empty-state { padding: var(--sp-5) var(--sp-3); text-align: center; }
.empty-state .empty-note { padding: 0 0 var(--sp-3); }

.prose {
  font-size: var(--fs-md);
  line-height: var(--lh-relaxed);
  color: var(--text-body);
  margin: 0 0 20px;
}

.callout {
  margin: 0 0 20px;
  padding: var(--sp-5);
  background: var(--surface-sunken);
  border-radius: var(--r-md);
  text-align: center;
  font-size: var(--fs-md);
  color: var(--text-muted);
  font-style: italic;
}

.link-btn {
  border: 0;
  background: transparent;
  padding: 0;
  cursor: pointer;
  font-size: var(--fs-xs);
  color: var(--text-muted);
  text-decoration: underline;
}


/* --- Profile view ------------------------------------------------------- */

.profile-head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-6);
}

.profile-head__name {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-2xl);
  color: var(--text);
}

.profile-cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-xl); }

.profile-dropzone {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  min-height: 90px;
  border-radius: var(--r-md);
  padding: var(--sp-1);
}

.profile-item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  border: 1px solid var(--border-faint);
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface);
}

.profile-item__name {
  flex: 1;
  min-width: 0;
  font-family: var(--font-body);
  font-weight: var(--fw-heavy);
  font-size: var(--fs-md);
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.profile-item__meta { flex: none; font-size: var(--fs-2xs); color: var(--text-muted); }


/* --- Placing banner ---------------------------------------------------- */

.banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  background: var(--warning-surface);
  border: 1px solid var(--warning);
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-5);
  margin-bottom: var(--sp-4);
}

.banner__text {
  font-family: var(--font-display);
  font-weight: var(--fw-medium);
  font-size: var(--fs-lg);
  color: var(--warning-text);
}

.banner__cancel {
  border: 0;
  background: transparent;
  padding: 0;
  cursor: pointer;
  font-size: var(--fs-sm);
  color: var(--warning-text);
  white-space: nowrap;
  text-decoration: underline;
}


/* --- Toolbar / view switcher ------------------------------------------- */

.toolbar {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}

.toolbar__spacer { flex: 1; }

.viewbar { position: relative; display: flex; align-items: center; }

.viewbar__group {
  display: flex;
  background: var(--surface-track);
  border-radius: 9px 0 0 9px;
  padding: 3px;
}

.viewbar__opt {
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
}

.viewbar__opt[aria-pressed="true"] {
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-raised);
}

.viewbar__caret {
  width: 22px;
  height: 34px;
  border: 0;
  border-left: 1px solid var(--border-strong);
  border-radius: 0 9px 9px 0;
  background: var(--surface-track);
  color: var(--text-faint);
  font-size: var(--fs-2xs);
  cursor: pointer;
}

.menu__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--sp-1) var(--sp-2) var(--sp-3);
}

.menu__label { display: block; padding: var(--sp-1) var(--sp-4); }

.menu__row-label {
  flex: 1;
  min-width: 0;
  font-size: var(--fs-md);
  color: var(--text);
  padding: 3px var(--sp-1);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.menu__row-chevron { flex: none; color: var(--text-faint); font-size: var(--fs-md); }

.segmented--inset { border-radius: 7px; padding: 2px; margin: 0 var(--sp-1) var(--sp-2); }
.segmented--spaced { margin-bottom: var(--sp-4); }

.pill--micro { font-size: var(--fs-2xs); padding: 2px 7px; border-radius: var(--r-xs); }

.avatar--placeholder {
  border: 0;
  cursor: pointer;
  background: var(--border-soft);
  color: var(--text-faint);
  font-size: var(--fs-lg);
}

.color-swatch {
  flex: none;
  width: 22px;
  height: 22px;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
}

.field--inline { flex: 1; min-width: 0; width: auto; cursor: text; }


/* --- Schedule view ----------------------------------------------------- */

.sched-day { margin-bottom: 20px; }

.sched-day__label {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-3);
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-lg);
  color: var(--text);
}

.note-box { padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-3); }
.note-box__input { min-height: 40px; resize: vertical; font-size: var(--fs-md); color: var(--text); }

.chip--stacked { margin-bottom: var(--sp-1); }
.chip__row--top { align-items: flex-start; }
.chip__row--foot { margin-top: var(--sp-2); }
.chip__titles { min-width: 0; }

.side-row--spaced { margin-bottom: var(--sp-1); }
.side-row__caret { font-size: var(--fs-2xs); color: var(--text); flex: none; }
.side-add--spaced { margin-bottom: var(--sp-1); }


/* --- Planner grid ------------------------------------------------------ */

.planner {
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
}

.planner__week-label {
  margin: 0 0 var(--sp-3);
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-md);
  color: var(--text-muted);
}

/* Two things here are load-bearing.
   `grid-template-rows: min-content` pins only the day-head row to its content.
   Every row after it is an implicit `auto` track, and grid's stretch step
   divides spare height equally between `auto` tracks — so when the small-screen
   block gives this grid a height to fill, the meal rows grow into it instead of
   leaving the footer floating above a screenful of nothing. `auto` keeps its
   content minimum too, so a row with a tall chip in it still wins.

   `overflow-x` sits on the grid rather than on `.planner` around it, which is
   what makes the pinned meal column possible: a sticky grid item is clamped to
   its own grid area, so it cannot travel when the scrollport is an ancestor of
   the grid — but when the grid *is* the scrollport, the areas move with the
   content and `left: 0` finally has somewhere to hold. The cost is that a
   two-week view scrolls each week separately rather than as one sheet.
   `overscroll-behavior-x` keeps a swipe past Sunday from turning into the
   browser's back gesture. */
.planner__grid {
  display: grid;
  grid-template-columns: 30px repeat(7, minmax(110px, 1fr));
  grid-template-rows: min-content;
  gap: 7px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}

.planner__week { min-width: 0; }

.day-head { text-align: center; padding: var(--sp-2) var(--sp-1); border-radius: var(--r-md); }

.day-head__dow {
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-sm);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: .04em;
}

.day-head__date {
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-2xl);
  color: var(--text);
  line-height: 1.15;
}

.day-head__today {
  margin: 3px auto 0;
  width: 16px;
  height: 3px;
  border-radius: 2px;
  background: var(--dot-color, var(--accent));
}

/* The meal column pins to the left edge of the planner's horizontal scroll.
   Seven days do not fit on a phone, and the labels were the first thing to
   leave — which loses the only cue for which row you are scrolling through.
   The negative margin lets the cell's background bleed across the 7px grid gap
   so cells pass behind it rather than through it. */
.row-label,
.planner__corner {
  position: sticky;
  left: 0;
  z-index: 2;
  margin-right: -7px;
  padding-right: 7px;
  background: var(--surface);
}

.row-label { display: flex; align-items: stretch; justify-content: center; min-height: 110px; }

.row-label__bar {
  width: 26px;
  border-radius: var(--r-sm);
  background: var(--meal-color);
  display: flex;
  align-items: center;
  justify-content: center;
}

.row-label__text {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-xs);
  color: var(--meal-text, var(--text-inverse));
  letter-spacing: .03em;
  white-space: nowrap;
}

.slot__stack { display: flex; flex-direction: column; gap: 3px; height: 100%; }

/* Flex, not a fixed-height textarea, so the field grows with the row once the
   grid starts stretching. At the row's minimum the two agree: 72px + 8px of
   padding is the 80px floor, which is what it measured before. */
.note-cell {
  display: flex;
  min-height: 80px;
  border-radius: 11px;
  background: var(--chip-tint, var(--surface-muted));
  border: 1px solid var(--border-soft);
  padding: var(--sp-1);
}

.note-cell__input {
  flex: 1;
  min-height: 72px;
  resize: none;
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  color: var(--chip-text, var(--text));
  padding: var(--sp-1);
}


/* --- Sidebar: pick banner, search, filters ---------------------------- */

.pick-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--sp-4);
  background: var(--pick-bg);
  border-radius: var(--r-md);
  padding: 9px var(--sp-4);
}

.pick-banner__title {
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-lg);
  color: var(--pick-fg);
}

.pick-banner__hint { font-size: var(--fs-xs); color: var(--pick-sub); margin-top: 1px; }

.pick-banner__close {
  border: 0;
  background: transparent;
  cursor: pointer;
  font-size: var(--fs-2xl);
  color: var(--pick-sub);
  line-height: 1;
  padding: 2px var(--sp-1);
}

.search-row { display: flex; gap: var(--sp-3); margin-bottom: var(--sp-4); }
.search-row__input { flex: 1; min-width: 0; }

.btn--filter {
  flex: none;
  font-size: var(--fs-sm);
  padding: 0 var(--sp-4);
  background: var(--filter-bg, var(--surface));
  color: var(--filter-fg, var(--text-body));
  border-color: var(--filter-bd, var(--border-soft));
}

.btn--filter[aria-pressed="true"] {
  background: var(--text);
  color: var(--text-inverse);
  border-color: var(--text);
}

.btn--accent { background: var(--btn-bg); color: var(--btn-fg, var(--text-inverse)); }
.btn--quiet {
  background: var(--surface-sunken);
  border-color: var(--border-strong);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  padding: 5px var(--sp-4);
  border-radius: var(--r-sm);
}
.btn--link { font-size: var(--fs-md); padding: var(--sp-3) var(--sp-5); color: var(--text); }
.btn--primary { cursor: var(--btn-cursor, pointer); }

.filters { margin-bottom: var(--sp-4); border-radius: 11px; }
.filters__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--sp-3);
}
.filters__row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-bottom: var(--sp-4);
}


/* --- Sidebar: idea list ------------------------------------------------ */

.idea-list { display: flex; flex-direction: column; gap: var(--sp-3); }

.idea__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-3);
}

.idea__ident { display: flex; align-items: center; gap: 7px; min-width: 0; }
.idea__qtys { display: flex; align-items: center; gap: 5px; margin-top: var(--sp-2); flex-wrap: wrap; }
.pill--qty { --pill-bg: var(--warning-surface); --pill-fg: var(--text); --pill-bd: var(--warning); }
.idea__actions { display: flex; align-items: center; gap: var(--sp-2); flex: none; }
.idea__name--flex { flex: 1; min-width: 0; font-size: var(--fs-lg); }
.idea__meta { flex: none; font-size: var(--fs-2xs); color: var(--text-muted); }

.idea__tags {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
  flex-wrap: wrap;
}

.dots { display: flex; gap: 3px; flex: none; }

.icon-btn--xs { width: 20px; height: 20px; border-radius: 5px; font-size: var(--fs-md); }
.icon-btn--solid {
  background: var(--text);
  color: var(--text-inverse);
  border-radius: 5px;
  width: 22px;
  height: 22px;
  font-size: var(--fs-lg);
}


/* --- Sidebar: shopping / inventory ------------------------------------ */

.shop-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-bottom: var(--sp-3);
}

.shop-head__range {
  font-family: var(--font-display);
  font-size: var(--fs-sm);
  color: var(--text-muted);
}

.shop-head__menu-wrap { position: relative; }

.shop-list { display: flex; flex-direction: column; gap: var(--sp-5); }

.add-row {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  margin-bottom: var(--sp-2);
  background: var(--surface-sunken);
  border: 1px solid var(--border-soft);
  border-radius: var(--r-sm);
  padding: var(--sp-1) var(--sp-2);
}

.add-row__input { flex: 1; width: auto; font-size: var(--fs-sm); }

.shop-item__body { flex: 1; min-width: 0; }

.shop-item__name {
  font-size: var(--fs-md);
  line-height: var(--lh-tight);
  text-decoration: var(--item-deco, none);
  color: var(--item-color, var(--text));
}

.shop-item__meta {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  margin-top: 1px;
}

.shop-item__qty { font-size: var(--fs-2xs); color: var(--text-muted); }
.shop-item__stocked { font-size: var(--fs-2xs); color: var(--stocked-color, var(--text-muted)); font-style: italic; }

.inventory-row__label { width: 90px; flex: none; min-width: 0; }

.inventory-row__name {
  font-size: var(--fs-md);
  line-height: var(--lh-tight);
  text-decoration: var(--item-deco, none);
  color: var(--item-color, var(--text));
}

.inventory-row__need { font-size: var(--fs-2xs); color: var(--text-muted); margin-top: 1px; }
.inventory-row__slider { flex: 1; accent-color: var(--item-color, var(--accent)); }
.inventory-row__qty { width: 46px; flex: none; color: var(--item-color, var(--text)); }


/* --- Sidebar: notes tab ----------------------------------------------- */

.note-list { display: flex; flex-direction: column; gap: var(--sp-4); }

.note-card {
  background: var(--surface-warm);
  border: 1px solid var(--border-faint);
  border-radius: var(--r-lg);
  padding: var(--sp-3) var(--sp-4);
}

.note-card--day { background: var(--surface); }
.note-card__input { min-height: 80px; resize: vertical; font-size: var(--fs-md); color: var(--text); }
.note-card__input--short { min-height: 56px; }


/* --- Modal: context strip and header ---------------------------------- */

.modal-ctx {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  background: var(--ctx-tint);
  border-bottom: 2px solid var(--ctx-color);
  border-radius: var(--r-2xl) var(--r-2xl) 0 0;
  padding: var(--sp-3) var(--sp-7);
  color: var(--ctx-text);
}

.modal-ctx__where { display: flex; align-items: center; gap: var(--sp-3); }

.modal-ctx__meal {
  font-family: var(--font-display);
  font-weight: var(--fw-bold);
  font-size: var(--fs-md);
  color: inherit;
}

.modal-ctx__sep { font-size: var(--fs-sm); color: inherit; opacity: .75; }
.modal-ctx__day { font-size: var(--fs-sm); color: inherit; }
.modal-ctx__servings { display: flex; align-items: center; gap: var(--sp-2); }
.modal-ctx__label { font-size: var(--fs-sm); color: inherit; opacity: .85; }

.stepper {
  display: flex;
  align-items: center;
  background: rgba(255, 255, 255, .6);
  border: 1px solid var(--ctx-color);
  border-radius: var(--r-sm);
  overflow: hidden;
}

.stepper__btn {
  border: 0;
  background: transparent;
  cursor: pointer;
  padding: 3px 9px;
  font-size: var(--fs-lg);
  color: inherit;
  line-height: 1;
  font-weight: var(--fw-semibold);
  user-select: none;
}

.stepper__value {
  padding: 3px var(--sp-3);
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-lg);
  color: inherit;
  border-left: 1px solid var(--ctx-color);
  border-right: 1px solid var(--ctx-color);
}

.modal-head { padding: 20px var(--sp-7) var(--sp-6); border-bottom: 1px solid var(--border-faint); }

.modal-head__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-4);
}

.modal-head__top--edit { align-items: center; margin-bottom: var(--sp-xl); }
.modal-head__titles { min-width: 0; }

.modal-head__meals {
  display: flex;
  align-items: center;
  gap: 7px;
  flex-wrap: wrap;
  margin-bottom: 9px;
}

.modal-head__name {
  margin: 0;
  font-family: var(--font-body);
  font-weight: var(--fw-heavy);
  font-size: var(--fs-3xl);
  color: var(--text);
  line-height: 1.1;
}

.modal-head__facts {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-top: 13px;
  flex-wrap: wrap;
}

.pill--meal {
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-xs);
  padding: 3px var(--sp-3);
  border-color: transparent;
}

.pill--lg { font-size: var(--fs-sm); padding: var(--sp-2) var(--sp-4); }

.pill--dashed {
  border-style: dashed;
  border-color: var(--border-strong);
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  font-size: var(--fs-sm);
}

/* A pill that hosts its own buttons (version chip: select / edit / delete). */
.pill--group { gap: 3px; padding: var(--sp-1) var(--sp-1) var(--sp-1) var(--sp-4); }

.pill__label {
  border: 0;
  background: transparent;
  padding: 0;
  cursor: pointer;
  font-family: var(--font-display);
  font-weight: var(--fw-medium);
  font-size: var(--fs-sm);
  color: inherit;
}

.pill__act {
  border: 0;
  background: transparent;
  cursor: pointer;
  color: inherit;
  opacity: .75;
  font-size: var(--fs-sm);
  line-height: 1;
  padding: 0 var(--sp-1);
}


/* --- Modal: versions bar ---------------------------------------------- */

.versions {
  padding: var(--sp-3) var(--sp-7) var(--sp-4);
  border-bottom: 1px solid var(--border-faint);
  background: var(--surface-warm);
}

.versions__row { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.versions__label { margin-right: 2px; }

.versions__current {
  margin: var(--sp-3) 0 0;
  font-size: var(--fs-xs);
  color: var(--success-text);
  font-style: italic;
}

.versions__use { margin-top: var(--sp-3); }


/* --- Modal: body and edit form --------------------------------------- */

.modal-body { padding: var(--sp-6) var(--sp-7) var(--sp-7); }
.modal-body--edit { padding: 20px var(--sp-7) var(--sp-7); }

.modal-body__tags { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 20px; }

.modal-body__actions { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }

.ing-list { display: flex; flex-direction: column; margin-bottom: 20px; }

.ing-list__row {
  display: flex;
  align-items: baseline;
  gap: var(--sp-4);
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--border-faint);
}

.ing-list__qty { width: 74px; flex: none; font-size: var(--fs-sm); color: var(--text-muted); }
.ing-list__name { font-size: var(--fs-md); color: var(--text); }

.edit-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-2xl);
  color: var(--text);
}

.form-gap { margin-bottom: var(--sp-xl); }

.form-row {
  display: flex;
  gap: var(--sp-5);
  margin-bottom: var(--sp-xl);
  align-items: flex-start;
}

.form-row__grow { flex: 1; min-width: 0; }
.form-row__fixed { width: 78px; flex: none; }
.form-row__fixed--wide { width: 100px; }
.form-row .filters__row { margin-bottom: 0; }

.ing-edit { display: flex; gap: var(--sp-2); align-items: center; margin-bottom: var(--sp-2); }
.ing-edit__qty { width: 78px; flex: none; }
.ing-edit__name { flex: 1; min-width: 0; width: auto; }
.ing-edit__cat { width: 118px; flex: none; padding-left: 7px; padding-right: 7px; }

.edit-instructions { height: 84px; line-height: 1.5; }

.edit-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-top: 20px;
  padding-top: var(--sp-xl);
  border-top: 1px solid var(--border-faint);
}

.edit-foot__right { display: flex; gap: var(--sp-3); }

/* --- Settings -------------------------------------------------------------
   Restored from the unified-settings work (People / Tags / Ingredients /
   Default assignees). The nav's width and direction are the only runtime
   values — the logic decides narrow vs wide, this file owns the rest. */

/* Wraps so the narrow layout drops the panel below the full-width picker
   rather than crushing it to zero width beside it. */
.settings { display: flex; flex-wrap: wrap; gap: var(--sp-xl); align-items: flex-start; }

/* Breadcrumb. The trailing crumb is a native <select>, stripped back to look
   like the rest of the trail — it is the section label and the section picker
   at once, which is what buys back the row the tab grid used to cost. */
.crumbs { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }

.crumbs__sep { flex: none; color: var(--text-faint); font-size: var(--fs-2xl); line-height: 1; }

.crumbs__picker {
  /* Without this a <select> is as wide as its longest option ("Default
     assignees"), which strands the caret far from the crumb. Unsupported
     browsers just get the old wider box. */
  field-sizing: content;
  width: auto;
  max-width: 100%;
  border: 0;
  background: transparent;
  padding: 2px var(--sp-1);
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-2xl);
  color: var(--text-muted);
  cursor: pointer;
}

.settings__nav {
  width: 138px;
  flex: none;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.settings__tab {
  padding: var(--sp-3) var(--sp-4);
  border: 0;
  border-radius: 9px;
  background: var(--surface-sunken);
  color: var(--text-muted);
  font-family: var(--font-display);
  font-weight: var(--fw-medium);
  font-size: var(--fs-md);
  white-space: nowrap;
  text-align: left;
  cursor: pointer;
}

.settings__tab[aria-selected="true"] { background: var(--text); color: var(--text-inverse); }

.settings__panel { flex: 1 1 260px; min-width: 0; }

.settings-list { display: flex; flex-direction: column; gap: var(--sp-1); }

.settings-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  border: 1px solid var(--row-bd, var(--border-faint));
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4);
  background: var(--row-bg, var(--surface));
  opacity: var(--row-opacity, 1);
  cursor: pointer;
}

.settings-row__name {
  flex: 1;
  min-width: 0;
  font-weight: var(--fw-heavy);
  font-size: var(--fs-md);
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.settings-row__meta { flex: none; font-size: var(--fs-2xs); color: var(--text-muted); white-space: nowrap; }

.hint { margin: var(--sp-4) 0 0; font-size: var(--fs-xs); color: var(--text-muted); }
.hint--lead { margin: 0 0 var(--sp-4); font-size: var(--fs-sm); }

.add-row__label { flex: none; font-size: var(--fs-sm); color: var(--text-muted); white-space: nowrap; }

/* People tab */

.people-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: var(--sp-3); }

.people-card {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  border: 1px solid var(--border-faint);
  border-radius: var(--r-lg);
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface);
  text-align: left;
  cursor: pointer;
}

.people-card__name {
  flex: 1;
  min-width: 0;
  font-weight: var(--fw-heavy);
  font-size: var(--fs-md);
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.profile-head__input {
  flex: 1;
  min-width: 0;
  max-width: 260px;
  width: auto;
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-2xl);
  color: var(--text);
  padding: var(--sp-1) 7px;
  border-radius: var(--r-sm);
  cursor: text;
}

.profile-head__input:hover { background: var(--surface-sunken); }

/* Default-assignees tab */

.asg-people { display: flex; gap: 7px; flex-wrap: wrap; margin-bottom: var(--sp-xl); }

.asg-person {
  display: flex;
  align-items: center;
  gap: 7px;
  border: 1px solid var(--border-faint);
  border-radius: var(--r-lg);
  padding: 7px 11px 7px var(--sp-3);
  background: var(--surface);
  cursor: grab;
}

.asg-person__name { font-weight: var(--fw-heavy); font-size: var(--fs-md); color: var(--text); white-space: nowrap; }

.asg-scroll { overflow-x: auto; }

/* The 520px floor made this grid scroll sideways on every phone, and there was
   nothing on the far side worth the trip — seven drop targets that are already
   only as wide as a three-letter day name. `min()` keeps the comfortable width
   wherever it fits and lets the whole week collapse into view where it does
   not; `minmax(0, 1fr)` stops the day columns from being propped open by their
   own content once they get narrow. */
.asg-grid {
  min-width: min(520px, 100%);
  display: grid;
  grid-template-columns: minmax(56px, 78px) repeat(7, minmax(0, 1fr));
  gap: 5px;
}

.asg-day {
  text-align: center;
  padding: var(--sp-2) 2px;
  border-radius: var(--r-sm);
  background: var(--surface-sunken);
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-2xs);
  color: var(--text-muted);
}

.asg-meal {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--sp-2) var(--sp-1);
  border-radius: var(--r-sm);
  background: var(--asg-tint);
  color: var(--asg-fg);
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
}

.asg-cell {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-1);
  min-height: 46px;
  padding: var(--sp-1);
  border: 1px dashed var(--cell-bd);
  border-radius: 9px;
  background: var(--cell-bg);
}

/* Inherited defaults read as an outline, set-here defaults as a solid fill. */
.avatar--ghost { background: transparent; color: var(--avatar-fg); border: 1px dashed var(--avatar-fg); }

.asg-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-top: var(--sp-xl);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--surface-muted);
}

.asg-foot__label { font-size: var(--fs-md); color: var(--text); }

.stepper--plain { --ctx-color: var(--border); background: var(--surface-sunken); }

.app-footer {
  margin-top: var(--sp-7);
  text-align: center;
  font-size: var(--fs-xs);
  color: var(--text-muted);
}


/* ============================================================================
   5. SMALL SCREENS
   Overrides, not a second layout. Everything above still describes the page;
   this section only says what a phone cannot afford. Keyed off `.app--mobile`
   rather than a media query for the same reason as the padding it replaces —
   the logic script already branches on `isMobile`, so one breakpoint decides
   both what renders and how it is spaced. Below 600px, per `breakpoints()`.
   ========================================================================== */

/* --- Full bleed ------------------------------------------------------------
   The page gutter (8px), the card's inset (18px) and its border were spending
   ~27px a side on decoration, on the axis with the least to spare. The card
   goes edge to edge and keeps one 8px gutter of its own, which buys ~38px of
   content width — most of a fourth day in the planner. */
.app--mobile { padding: var(--sp-3) 0 var(--sp-5); }

/* The gutter moves from the page onto the things that still want one. */
.app--mobile .app-header,
.app--mobile .print-title,
.app--mobile .app-footer {
  padding-left: var(--sp-3);
  padding-right: var(--sp-3);
}

/* A card spanning the viewport has no outside to round against: left over, the
   radius and side borders just draw a seam a hair in from the screen edge. */
.app--mobile .layout__main > .card {
  border-left: 0;
  border-right: 0;
  border-radius: 0;
}

.app--mobile .card--pad { padding: var(--sp-4) var(--sp-3) var(--sp-5); }

/* The sheet nests a `.card` inside `.sidebar--sheet`, so its inset was charged
   twice — 14 + 14 = 28px a side before any content. One gutter is enough. */
.app--mobile .sidebar--sheet { padding: var(--sp-3) var(--sp-3) 28px; }
.app--mobile .sidebar--sheet .card--sidebar { padding: var(--sp-3); }

/* --- Filling the viewport --------------------------------------------------
   The planner's rows were a fixed 110px whatever the screen, so a week of
   mostly-empty slots stopped well short of the bottom and left the footer
   stranded over a band of dead background. Handing `.app`'s height down the
   chain — layout, main, card, planner — gives `.planner__grid` something
   definite to fill, and its `auto` rows split the slack between them.

   Deliberately not on tablet: the sidebar is a sheet there too, but a tablet
   has the height to spare and stretched rows would only thin the content out.
   `:only-child` scopes it to the one-week view for the same reason — two weeks
   stacked already fill the screen, and stretching both would push the second
   below the fold. */
.app--mobile .layout { align-items: stretch; }

.app--mobile .layout__main > .card--pad {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.app--mobile .planner { flex: 1; min-height: 0; }

.app--mobile .planner__week:only-child {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.app--mobile .planner__week:only-child .planner__grid { flex: 1; }

/* Stretching only helps when there is slack. On a short phone there is not:
   four rows at the desktop 110px floor overshoot a 667px screen and put the
   footer back below the fold, which is the same complaint from the other
   direction. Lowering the floor lets the grid compress to fit there and stretch
   past it everywhere else — the row height follows the screen either way. The
   empty cell drops its own floor because it already fills the slot. */
.app--mobile .slot,
.app--mobile .row-label { min-height: 92px; }
.app--mobile .slot-empty { min-height: 0; }

/* Edge shadow only where the column is reliably doing its job. A phone can
   never fit seven days, so the planner always scrolls and the shadow always
   means something; a desktop mostly fits the week, and there the same shadow is
   just a seam drawn down an empty gutter. */
.app--mobile .row-label { box-shadow: var(--shadow-pinned); }

/* The schedule list has the same dead-space problem seen from the other side:
   a flat 640px cap, set at the call site, which is most of a tall phone and
   more than a short one has. It cannot join the flex chain above — it is its
   own scroll container, and the infinite-scroll sentinels observe it as their
   root, so handing its height to the page would break the observers. Overriding
   `--scroll-max` in the units the screen is actually measured in is enough:
   two-thirds of the viewport, leaving room for the header, the toolbar and the
   footer whatever the device. */
.app--mobile .scroll-y { --scroll-max: 66dvh; }
