/* ============================================================================
   Aisle — design system

   Dark-first, because the app's most important context is a shop: a dim screen
   at arm's length, one-handed, in a hurry (FRD §2.2). Light mode is a full
   citizen for planning at home.

   Constraints that shaped this, not decoration:
   - Every tap target is at least 44x44 (NFR-06, deliberately above WCAG AA's
     24px, justified by gloves and trolleys).
   - Colour never carries meaning alone — bought state has a check AND a
     strikethrough AND opacity.
   - Type scales with the OS setting; nothing is pinned in px that holds text.
   - All motion is off under prefers-reduced-motion.
   ========================================================================= */

:root {
  color-scheme: dark light;

  /* Dark, the default */
  --bg: #070b12;
  --bg-glow-1: #0e7490;
  --bg-glow-2: #15803d;
  --surface: rgba(255, 255, 255, 0.045);
  --surface-solid: #121826;
  --surface-hover: rgba(255, 255, 255, 0.075);
  --border: rgba(255, 255, 255, 0.09);
  --border-strong: rgba(255, 255, 255, 0.16);

  --text: #e9eef7;
  --text-muted: #93a1b5;
  --text-faint: #64748b;

  --accent: #34d399;
  --accent-2: #22d3ee;
  --accent-contrast: #04140f;
  --accent-soft: rgba(52, 211, 153, 0.14);
  --danger: #fb7185;
  --warn: #fbbf24;

  --gradient: linear-gradient(135deg, var(--accent) 0%, var(--accent-2) 100%);

  --radius-sm: 10px;
  --radius: 16px;
  --radius-lg: 24px;
  --radius-full: 999px;

  --shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 8px 24px -12px rgba(0, 0, 0, .6);
  --shadow-lift: 0 2px 6px rgba(0, 0, 0, .35), 0 20px 48px -20px rgba(0, 0, 0, .8);

  /* A spring-ish curve. The overshoot is what makes ticking feel physical. */
  --ease-spring: cubic-bezier(.22, 1.2, .36, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);

  --tap: 44px;
  --gutter: clamp(1rem, 4vw, 2rem);
  --shell: 720px;

  /* Member avatars (FR-HH-04). Tokens like every other colour here, so the theme
     rules below cover them without a special case of their own. */
  --av0: #38bdf8;
  --av1: #fb7185;
  --av2: #a78bfa;
  --av3: #fbbf24;
  --av4: #34d399;
  --av5: #f472b6;
  --av6: #818cf8;
  --av7: #2dd4bf;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --bg: #f4f7fb;
    --bg-glow-1: #a5f3fc;
    --bg-glow-2: #bbf7d0;
    --surface: rgba(255, 255, 255, 0.85);
    --surface-solid: #ffffff;
    --surface-hover: #ffffff;
    --border: rgba(15, 23, 42, 0.09);
    --border-strong: rgba(15, 23, 42, 0.18);

    --text: #0f172a;
    --text-muted: #52627a;
    --text-faint: #94a3b8;

    --accent: #059669;
    --accent-2: #0891b2;
    --accent-contrast: #ffffff;
    --accent-soft: rgba(5, 150, 105, 0.1);
    --danger: #e11d48;
    --warn: #b45309;

    --shadow: 0 1px 2px rgba(15, 23, 42, .06), 0 8px 24px -14px rgba(15, 23, 42, .25);
    --shadow-lift: 0 2px 8px rgba(15, 23, 42, .08), 0 24px 48px -24px rgba(15, 23, 42, .35);

    /* Member avatars, deepened for light: the dark set goes washed out on white. */
    --av0: #0ea5e9;
    --av1: #f43f5e;
    --av2: #8b5cf6;
    --av3: #f59e0b;
    --av4: #10b981;
    --av5: #ec4899;
    --av6: #6366f1;
    --av7: #14b8a6;
  }
}

/* An explicit choice overrides the device. The light palette is repeated rather
   than shared because CSS cannot apply one declaration block both inside a media
   query and outside it; keep the two in step. */
:root[data-theme="light"] {
  --bg: #f4f7fb;
  --bg-glow-1: #a5f3fc;
  --bg-glow-2: #bbf7d0;
  --surface: rgba(255, 255, 255, 0.85);
  --surface-solid: #ffffff;
  --surface-hover: #ffffff;
  --border: rgba(15, 23, 42, 0.09);
  --border-strong: rgba(15, 23, 42, 0.18);

  --text: #0f172a;
  --text-muted: #52627a;
  --text-faint: #94a3b8;

  --accent: #059669;
  --accent-2: #0891b2;
  --accent-contrast: #ffffff;
  --accent-soft: rgba(5, 150, 105, 0.1);
  --danger: #e11d48;
  --warn: #b45309;

  --shadow: 0 1px 2px rgba(15, 23, 42, .06), 0 8px 24px -14px rgba(15, 23, 42, .25);
  --shadow-lift: 0 2px 8px rgba(15, 23, 42, .08), 0 24px 48px -24px rgba(15, 23, 42, .35);

  /* Member avatars, deepened for light: the dark set goes washed out on white. */
  --av0: #0ea5e9;
  --av1: #f43f5e;
  --av2: #8b5cf6;
  --av3: #f59e0b;
  --av4: #10b981;
  --av5: #ec4899;
  --av6: #6366f1;
  --av7: #14b8a6;
}

/* Native controls — scrollbars, date pickers, form widgets — follow the choice too.
   Without this a light page keeps dark scrollbars. */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }

* { box-sizing: border-box; }

/* Icons have no intrinsic size, so give them one relative to their text. */
.ico { width: 1.2em; height: 1.2em; flex: none; }

html {
  /* rem follows the OS text size (NFR-06) rather than a fixed pixel value. */
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--bg);
  color: var(--text);
  font-family: ui-rounded, "SF Pro Rounded", -apple-system, BlinkMacSystemFont,
               "Segoe UI Variable Display", "Segoe UI", system-ui, sans-serif;
  font-size: clamp(1rem, 0.97rem + 0.16vw, 1.0625rem);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Two soft light sources. Fixed, so scrolling feels like moving under them. */
body::before {
  content: "";
  position: fixed;
  inset: -30% -10% auto -10%;
  height: 70vh;
  background:
    radial-gradient(50% 60% at 20% 0%, var(--bg-glow-1) 0%, transparent 70%),
    radial-gradient(45% 55% at 85% 10%, var(--bg-glow-2) 0%, transparent 70%);
  opacity: .16;
  filter: blur(40px);
  pointer-events: none;
  z-index: -1;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) body::before { opacity: .55; }
}

:root[data-theme="light"] body::before { opacity: .55; }

/* ── Shell ───────────────────────────────────────────────────────────────── */

.shell {
  /* Deliberately NOT a stacking context: z-index here would trap the item sheet
     underneath the tab bar. The background glow sits at z-index -1 instead. */
  position: relative;
  max-width: var(--shell);
  margin-inline: auto;
  padding: 0 var(--gutter);
  /* Clears the composer and the tab bar, both fixed to the bottom. */
  padding-bottom: calc(12rem + env(safe-area-inset-bottom));
}

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: max(1rem, env(safe-area-inset-top)) 0 1rem;
  background: color-mix(in srgb, var(--bg) 86%, transparent);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
}

.topbar h1 {
  margin: 0;
  font-size: clamp(1.4rem, 1.2rem + 1vw, 1.9rem);
  font-weight: 700;
  letter-spacing: -0.025em;
}

.topbar .sub {
  margin: .1rem 0 0;
  font-size: .82rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.grow { flex: 1; min-width: 0; }

/* ── The wordmark ────────────────────────────────────────────────────────── */

.brand {
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  font-weight: 700;
  letter-spacing: -0.03em;
  text-decoration: none;
  color: var(--text);
}

.brand-mark {
  width: 34px;
  height: 34px;
  border-radius: 11px;
  background: var(--gradient);
  display: grid;
  place-items: center;
  flex: none;
  box-shadow: 0 6px 18px -8px var(--accent);
}

.brand-mark .ico { width: 19px; height: 19px; color: var(--accent-contrast); }

/* ── Surfaces ────────────────────────────────────────────────────────────── */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  box-shadow: var(--shadow);
}

.stack { display: flex; flex-direction: column; gap: .55rem; }
.row { display: flex; align-items: center; gap: .6rem; }

/* ── Buttons ─────────────────────────────────────────────────────────────── */

.btn {
  min-height: var(--tap);
  padding: .7rem 1.15rem;
  border-radius: var(--radius-full);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  transition: transform .18s var(--ease-spring), background .18s, border-color .18s, opacity .18s;
  -webkit-tap-highlight-color: transparent;
}

.btn:hover:not(:disabled) { background: var(--surface-hover); }
.btn:active:not(:disabled) { transform: scale(.96); }
.btn:disabled { opacity: .45; cursor: not-allowed; }

.btn-primary {
  background: var(--gradient);
  color: var(--accent-contrast);
  border-color: transparent;
  box-shadow: 0 8px 24px -12px var(--accent);
}

/* Restates the gradient: .btn:hover outranks .btn-primary and would swap it for
   --surface-hover — white text on white in light mode, near-black on near-black in dark. */
.btn-primary:hover:not(:disabled) { background: var(--gradient); filter: brightness(1.07); }

.btn-ghost { background: transparent; border-color: transparent; color: var(--text-muted); }
.btn-ghost:hover:not(:disabled) { background: var(--surface-hover); color: var(--text); }
.btn-danger { color: var(--text-faint); border-color: transparent; background: transparent; }
.btn-danger:hover:not(:disabled) {
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 14%, transparent);
}

.btn-icon {
  min-width: var(--tap);
  width: var(--tap);
  height: var(--tap);
  padding: 0;
  border-radius: var(--radius-full);
}

.btn-icon .ico { width: 20px; height: 20px; }

/* ── Fields ──────────────────────────────────────────────────────────────── */

.field {
  width: 100%;
  min-height: var(--tap);
  padding: .75rem 1rem;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--surface-solid);
  color: var(--text);
  font: inherit;
  transition: border-color .18s, box-shadow .18s;
}

.field::placeholder { color: var(--text-faint); }

.field:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-soft);
}

select.field {
  appearance: none;
  padding-right: 2.6rem;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 1.25rem) 55%, calc(100% - .95rem) 55%;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}

.label {
  display: block;
  font-size: .8rem;
  font-weight: 600;
  color: var(--text-muted);
  margin-bottom: .35rem;
  letter-spacing: .01em;
}

:where(a, button, input, select, textarea):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ── Composer: the fastest path from intent to a saved item (FR-LIST-01) ─── */

/* Fixed above the tab bar rather than sticky inside the list: adding an item is the
   most frequent action in the product, and it should sit in the same place whether
   the list has three rows or thirty. */
.composer {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 15;
  /* Owns the whole bottom strip, down past the tab bar. Anything less leaves a gap
     for rows to peek through between the input and the tabs. The top padding is the
     fade itself: content dissolves into the background rather than being sliced. */
  padding: 2.75rem var(--gutter) calc(4.6rem + env(safe-area-inset-bottom));
  background: linear-gradient(
    to top,
    var(--bg) 0%,
    var(--bg) 74%,
    color-mix(in srgb, var(--bg) 65%, transparent) 89%,
    transparent 100%);
  pointer-events: none;
}

.composer > * { pointer-events: auto; }

.composer form {
  display: flex;
  gap: .5rem;
  align-items: center;
  max-width: var(--shell);
  margin-inline: auto;
}
/* The composer floats over the list now, so the field carries its own surface. */
.composer .field {
  border-radius: var(--radius-full);
  border-color: var(--border-strong);
  box-shadow: var(--shadow-lift);
  backdrop-filter: blur(18px) saturate(150%);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  background: color-mix(in srgb, var(--surface-solid) 92%, transparent);
}

/* ── List items ──────────────────────────────────────────────────────────── */

.items { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .5rem; }

.item {
  display: flex;
  align-items: center;
  gap: .85rem;
  padding: .6rem .85rem .6rem .6rem;
  min-height: 60px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  transition: background .2s, border-color .2s, opacity .3s var(--ease-out),
              transform .3s var(--ease-spring);
  animation: item-in .34s var(--ease-spring) both;
}

@keyframes item-in {
  from { opacity: 0; transform: translateY(8px) scale(.985); }
  to { opacity: 1; transform: none; }
}

.item:hover { border-color: var(--border-strong); }

.item.is-bought { opacity: .45; }
.item.is-bought .item-title { text-decoration: line-through; text-decoration-thickness: 2px; }

.item-body { flex: 1; min-width: 0; }

.item-title {
  font-weight: 600;
  letter-spacing: -0.01em;
  overflow-wrap: anywhere;
}

.item-meta {
  display: flex;
  flex-wrap: wrap;
  gap: .3rem .5rem;
  margin-top: .15rem;
  font-size: .78rem;
  color: var(--text-muted);
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: .28rem;
  padding: .1rem .5rem;
  border-radius: var(--radius-full);
  background: var(--surface-hover);
  border: 1px solid var(--border);
  white-space: nowrap;
}

.chip-qty { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--text); }

/* ── The tick. The single most-repeated interaction in the product. ──────── */

.tick {
  flex: none;
  width: var(--tap);
  height: var(--tap);
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  display: grid;
  place-items: center;
  -webkit-tap-highlight-color: transparent;
}

.tick-box {
  width: 27px;
  height: 27px;
  border-radius: 9px;
  border: 2px solid var(--border-strong);
  display: grid;
  place-items: center;
  transition: background .22s var(--ease-spring), border-color .22s,
              transform .32s var(--ease-spring);
}

.tick:active .tick-box { transform: scale(.82); }
.tick:hover .tick-box { border-color: var(--accent); }

.tick.on .tick-box {
  background: var(--gradient);
  border-color: transparent;
  transform: scale(1.06);
}

.tick svg { width: 15px; height: 15px; }

.tick-check {
  stroke: var(--accent-contrast);
  stroke-width: 3.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
  stroke-dasharray: 22;
  stroke-dashoffset: 22;
  /* A round line cap still paints a dot on a fully-dashed path, which reads as a
     half-ticked item. Hide it outright when off. */
  opacity: 0;
  transition: stroke-dashoffset .26s var(--ease-out) .04s, opacity .12s;
}

.tick.on .tick-check { stroke-dashoffset: 0; opacity: 1; }

/* ── Aisle headings (FR-SHOP-04) ─────────────────────────────────────────── */

.aisle + .aisle { margin-top: 1.6rem; }

.aisle-head {
  display: flex;
  align-items: center;
  gap: .7rem;
  margin: 0 0 .6rem;
  padding-left: .25rem;
}

.aisle-rank {
  flex: none;
  width: 26px;
  height: 26px;
  border-radius: 8px;
  display: grid;
  place-items: center;
  font-size: .72rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  background: var(--accent-soft);
  color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
}

.aisle-name {
  font-size: .82rem;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.aisle-rule { flex: 1; height: 1px; background: var(--border); }

/* Shopping mode is read at arm's length (FR-SHOP-08). */
.shopping .item-title { font-size: 1.1rem; }
.shopping .item { min-height: 66px; }

/* ── Store selector ──────────────────────────────────────────────────────── */

.stores {
  display: flex;
  gap: .5rem;
  overflow-x: auto;
  padding: .25rem .25rem .75rem;
  margin: 0 calc(var(--gutter) * -1);
  padding-inline: var(--gutter);
  scrollbar-width: none;
  scroll-snap-type: x mandatory;
}

.stores::-webkit-scrollbar { display: none; }

.store-pill {
  flex: none;
  scroll-snap-align: start;
  min-height: var(--tap);
  padding: .6rem 1.1rem;
  border-radius: var(--radius-full);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-muted);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: transform .2s var(--ease-spring), background .2s, color .2s, border-color .2s;
}

.store-pill:active { transform: scale(.95); }

.store-pill.on {
  background: var(--gradient);
  color: var(--accent-contrast);
  border-color: transparent;
  box-shadow: 0 8px 22px -12px var(--accent);
}

/* ── Progress (FR-SHOP-05) ───────────────────────────────────────────────── */

.progress {
  height: 5px;
  border-radius: var(--radius-full);
  background: var(--surface-hover);
  overflow: hidden;
  margin-bottom: 1.25rem;
}

.progress span {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--gradient);
  transition: width .5s var(--ease-out);
}

/* ── Empty and error states — never a blank screen (FR-STORE-00) ─────────── */

.empty {
  text-align: center;
  padding: 3rem 1.5rem;
  color: var(--text-muted);
}

.empty-mark {
  width: 64px;
  height: 64px;
  margin: 0 auto 1.1rem;
  border-radius: 20px;
  display: grid;
  place-items: center;
  background: var(--accent-soft);
  color: var(--accent);
}

.empty-mark .ico { width: 30px; height: 30px; }
.empty h2 { margin: 0 0 .4rem; color: var(--text); font-size: 1.15rem; }
.empty p { margin: 0 auto; max-width: 34ch; font-size: .92rem; }

.notice {
  display: flex;
  gap: .65rem;
  align-items: flex-start;
  padding: .8rem 1rem;
  border-radius: var(--radius);
  font-size: .9rem;
  margin-bottom: 1rem;
  border: 1px solid transparent;
}

.notice-error {
  background: color-mix(in srgb, var(--danger) 12%, transparent);
  border-color: color-mix(in srgb, var(--danger) 35%, transparent);
  color: var(--danger);
}

.notice-info {
  background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent) 30%, transparent);
  color: var(--accent);
}

.notice-warn {
  background: color-mix(in srgb, var(--warn) 12%, transparent);
  border-color: color-mix(in srgb, var(--warn) 35%, transparent);
  color: var(--warn);
}

/* ── Undo toast (FR-LIST-11, FR-BUY-04) ──────────────────────────────────── */

.toast {
  position: fixed;
  left: 50%;
  bottom: calc(9.5rem + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  z-index: 60;
  display: flex;
  align-items: center;
  gap: .9rem;
  padding: .7rem .7rem .7rem 1.15rem;
  border-radius: var(--radius-full);
  background: var(--surface-solid);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-lift);
  font-size: .9rem;
  width: max-content;
  max-width: calc(100vw - 2rem);
  animation: toast-in .35s var(--ease-spring) both;
}

@keyframes toast-in {
  from { opacity: 0; transform: translate(-50%, 14px) scale(.95); }
  to { opacity: 1; transform: translate(-50%, 0) scale(1); }
}

.toast .btn { min-height: 36px; padding: .35rem 1rem; }

/* A visible countdown, so "10 seconds" is a fact rather than a promise. */
.toast-ring { --p: 1; flex: none; width: 22px; height: 22px; }
.toast-ring circle { fill: none; stroke-width: 3; }
.toast-ring .track { stroke: var(--border-strong); }
.toast-ring .bar {
  stroke: var(--accent);
  stroke-linecap: round;
  stroke-dasharray: 57;
  stroke-dashoffset: calc(57 * (1 - var(--p)));
  transform: rotate(-90deg);
  transform-origin: center;
  transition: stroke-dashoffset 1s linear;
}

/* ── Tab bar ─────────────────────────────────────────────────────────────── */

.tabs {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(.85rem + env(safe-area-inset-bottom));
  z-index: 31;
  display: flex;
  gap: .2rem;
  padding: .3rem;
  border-radius: var(--radius-full);
  background: color-mix(in srgb, var(--surface-solid) 92%, transparent);
  border: 1px solid var(--border);
  backdrop-filter: blur(18px) saturate(150%);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  box-shadow: var(--shadow-lift);
}

.tab {
  min-width: 76px;
  min-height: var(--tap);
  padding: .4rem .9rem;
  border: none;
  background: none;
  border-radius: var(--radius-full);
  color: var(--text-muted);
  font: inherit;
  font-size: .72rem;
  font-weight: 600;
  cursor: pointer;
  display: grid;
  justify-items: center;
  gap: .12rem;
  text-decoration: none;
  transition: color .2s, background .2s;
}

.tab .ico { width: 21px; height: 21px; }
.tab:hover { color: var(--text); }
.tab.active { color: var(--accent-contrast); background: var(--gradient); }

/* ── Auth ────────────────────────────────────────────────────────────────── */

.auth {
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: var(--gutter);
  position: relative;
  z-index: 1;
}

.auth-card {
  width: min(420px, 100%);
  padding: clamp(1.5rem, 5vw, 2.25rem);
  border-radius: var(--radius-lg);
  background: var(--surface);
  border: 1px solid var(--border);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  box-shadow: var(--shadow-lift);
}

.auth-card h1 { margin: 1.1rem 0 .3rem; font-size: 1.5rem; letter-spacing: -0.025em; }
.auth-card .lede { margin: 0 0 1.6rem; color: var(--text-muted); font-size: .93rem; }
.auth-card .stack { gap: .9rem; }

.switcher {
  margin: 1.25rem 0 0;
  text-align: center;
  font-size: .88rem;
  color: var(--text-muted);
}

.switcher button {
  background: none;
  border: none;
  padding: .25rem;
  font: inherit;
  font-weight: 600;
  color: var(--accent);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ── Skeletons ───────────────────────────────────────────────────────────── */

.skeleton {
  height: 60px;
  border-radius: var(--radius);
  background: linear-gradient(90deg, var(--surface) 25%, var(--surface-hover) 37%, var(--surface) 63%);
  background-size: 400% 100%;
  animation: shimmer 1.4s ease-in-out infinite;
}

@keyframes shimmer {
  from { background-position: 100% 0; }
  to { background-position: 0 0; }
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Respect the user's motion preference — a shopping list is not the place to
   insist on animation. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* Icon sizing where the icon stands in for a rank number or a label. */
.aisle-rank .ico { width: 15px; height: 15px; }
.notice .ico { margin-top: .15rem; }
.item .tick-box .ico { width: 15px; height: 15px; color: var(--text-muted); }

/* ── Item sheet ──────────────────────────────────────────────────────────── */

.sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: rgba(0, 0, 0, .55);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  animation: fade-in .2s var(--ease-out) both;
}

@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }

.sheet {
  position: fixed;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  z-index: 41;
  width: min(var(--shell), 100%);
  max-height: 88dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: .6rem var(--gutter) calc(1.1rem + env(safe-area-inset-bottom));
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  background: var(--surface-solid);
  border: 1px solid var(--border);
  border-bottom: none;
  box-shadow: var(--shadow-lift);
  animation: sheet-up .32s var(--ease-spring) both;
}

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

/* Reads as draggable even though it is not, which sets the right expectation for
   how to dismiss it — tapping the backdrop. */
.sheet-grip {
  width: 40px;
  height: 4px;
  margin: 0 auto 1rem;
  border-radius: var(--radius-full);
  background: var(--border-strong);
}

.sheet-body { display: flex; flex-direction: column; gap: .95rem; }
.sheet-pair { display: grid; grid-template-columns: 1fr 1fr; gap: .7rem; }

.sheet-actions {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin-top: 1.4rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}

/* ── Switch ──────────────────────────────────────────────────────────────── */

.switch {
  display: flex;
  align-items: flex-start;
  gap: .8rem;
  min-height: var(--tap);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.switch input { position: absolute; opacity: 0; width: 0; height: 0; }

.switch-track {
  flex: none;
  width: 46px;
  height: 28px;
  margin-top: .1rem;
  border-radius: var(--radius-full);
  background: var(--surface-hover);
  border: 1px solid var(--border-strong);
  transition: background .22s var(--ease-spring), border-color .22s;
  display: flex;
  align-items: center;
  padding: 2px;
}

.switch-knob {
  width: 20px;
  height: 20px;
  border-radius: var(--radius-full);
  background: var(--text-muted);
  transition: transform .28s var(--ease-spring), background .22s;
}

.switch input:checked + .switch-track {
  background: var(--gradient);
  border-color: transparent;
}

.switch input:checked + .switch-track .switch-knob {
  transform: translateX(18px);
  background: var(--accent-contrast);
}

.switch input:focus-visible + .switch-track {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.switch-label { font-size: .92rem; font-weight: 600; }
.switch-label small {
  display: block;
  margin-top: .15rem;
  font-weight: 400;
  font-size: .78rem;
  color: var(--text-muted);
}

/* The item body becomes the way in to the sheet, so it needs to look tappable. */
.item-open {
  flex: 1;
  min-width: 0;
  text-align: left;
  background: none;
  border: none;
  font: inherit;
  color: inherit;
  padding: .4rem 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* ── Archive confirmation ────────────────────────────────────────────────── */

.sheet-title {
  margin: 0 0 .35rem;
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.sheet-lede {
  margin: 0 0 1.1rem;
  font-size: .88rem;
  color: var(--text-muted);
}

/* Scrolls on its own so the confirm button never leaves the screen, however long
   the trip was. */
.archive-preview {
  max-height: 42dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-right: .2rem;
}

.archive-preview .item { min-height: 52px; }
.archive-preview .tick { cursor: default; }

/* ── Aisle reordering (FR-STORE-02) ──────────────────────────────────────── */

.hint {
  margin: 0 0 1rem;
  font-size: .88rem;
  color: var(--text-muted);
}

.reorder { touch-action: pan-y; }

.reorder-row {
  /* The transition is what sells the reorder: rows slide out of the way rather than
     teleporting. Suppressed on the row under the finger, which must track it exactly. */
  transition: transform .22s var(--ease-out), box-shadow .2s, background .2s;
  will-change: transform;
}

.reorder-row.dragging {
  transition: box-shadow .2s, background .2s;
  z-index: 2;
  position: relative;
  background: var(--surface-solid);
  border-color: var(--accent);
  box-shadow: var(--shadow-lift);
}

/* While a drag is in flight the page must not scroll or select under the finger. */
body.is-dragging {
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
  cursor: grabbing;
}

.grip {
  flex: none;
  width: var(--tap);
  min-height: var(--tap);
  border: none;
  background: none;
  border-radius: var(--radius-sm);
  cursor: grab;
  display: grid;
  place-items: center;
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
}

.grip:active { cursor: grabbing; }
.grip:hover .grip-lines::before { background: var(--text-muted); }

/* Six dots, drawn with a gradient rather than six elements. */
.grip-lines {
  width: 16px;
  height: 18px;
  position: relative;
}

.grip-lines::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--text-faint);
  transition: background .18s;
  -webkit-mask-image: radial-gradient(circle at 3px 3px, #000 1.6px, transparent 1.7px);
  mask-image: radial-gradient(circle at 3px 3px, #000 1.6px, transparent 1.7px);
  -webkit-mask-size: 10px 7px;
  mask-size: 10px 7px;
}

.reorder .aisle-rank { margin-right: .1rem; }

/* A quiet affordance that a row leads somewhere. */
.ico-chevron {
  flex: none;
  display: grid;
  place-items: center;
  width: 28px;
  color: var(--text-faint);
}

.ico-chevron .ico { width: 16px; height: 16px; }

/* ── Reorder, corrected ──────────────────────────────────────────────────── */

/* THE bug that made dragging feel dead: .item carries `animation: item-in ... both`,
   and an animation's filled value overrides an inline style in the cascade. The row's
   transform was being set and then ignored, so nothing moved until the drop. Rows in
   the reorder list opt out of the entrance animation entirely. */
.reorder-row { animation: none; }

/* The container is the positioning context for the drop indicator. */
.reorder { position: relative; }

/* Where the row will land, which on a long drag is nowhere near the pointer. */
.drop-line {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 3px;
  margin-top: -2px;
  border-radius: var(--radius-full);
  background: var(--gradient);
  box-shadow: 0 0 14px -2px var(--accent);
  opacity: 0;
  pointer-events: none;
  transition: transform .18s var(--ease-out), opacity .14s;
}

.drop-line.is-moving { opacity: 1; }

/* A small lift, so the dragged row reads as picked up rather than just recoloured. */
.reorder-row.dragging {
  transform-origin: center;
  cursor: grabbing;
}

.reorder-row.dragging .item-title { color: var(--text); }

/* ── Suggestions (FR-LIST-02) ────────────────────────────────────────────── */

.composer form { position: relative; }

/* Above the input, because the input sits at the bottom of the screen and a list
   below it would be under the tab bar or off-screen entirely. */
.suggestions {
  position: absolute;
  bottom: calc(100% + .5rem);
  left: 0;
  right: 0;
  z-index: 16;
  list-style: none;
  margin: 0;
  padding: .3rem;
  max-height: 46dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--surface-solid) 96%, transparent);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-lift);
  backdrop-filter: blur(18px) saturate(150%);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  animation: suggest-in .18s var(--ease-out) both;
}

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

.suggestion {
  display: flex;
  align-items: center;
  gap: .7rem;
  width: 100%;
  min-height: var(--tap);
  padding: .5rem .75rem;
  border: none;
  border-radius: var(--radius);
  background: none;
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background .14s;
}

.suggestion:hover,
.suggestion.on { background: var(--surface-hover); }

.suggestion-title {
  flex: 1;
  min-width: 0;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.suggestion-meta {
  display: flex;
  gap: .35rem;
  flex: none;
  font-size: .75rem;
  color: var(--text-muted);
}

/* The count is what shows the list is learning, so it gets the accent. */
.suggestion-meta .chip:first-child {
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 30%, transparent);
  background: var(--accent-soft);
}

/* ── History, inline under the list (FR-ARCH-05) ─────────────────────────── */

.history-head {
  display: flex;
  align-items: center;
  margin: 1.75rem 0 .25rem;
  padding-top: 1.25rem;
  border-top: 1px dashed var(--border);
}

.history-toggle {
  padding-inline: .6rem;
  font-size: .82rem;
  letter-spacing: .04em;
  text-transform: uppercase;
}

.history-toggle .ico { width: 15px; height: 15px; }

/* History rows read as settled rather than pending: no fill, a dashed edge, and muted
   text. Distinct from a bought-but-unarchived row, which is struck through instead. */
.item.is-history {
  background: transparent;
  border-style: dashed;
  border-color: var(--border);
}

.item.is-history .item-title {
  color: var(--text-muted);
  font-weight: 500;
}

.item.is-history:hover {
  background: var(--surface);
  border-style: solid;
}

/* Ticked, but in the muted key rather than the accent gradient — this is history, not
   something achieved a moment ago. */
.tick.on.history-tick .tick-box {
  background: var(--surface-hover);
  border-color: var(--border-strong);
  transform: none;
}

.tick.on.history-tick .tick-check { stroke: var(--text-muted); }

.tick.on.history-tick:hover .tick-box {
  border-color: var(--accent);
  background: var(--accent-soft);
}

.tick.on.history-tick:hover .tick-check { stroke: var(--accent); }

/* ── Sheet choices (FR-BUY-06) ─────────────────────────────────────────────
   Two destructive-ish operations that need explaining before they are picked,
   so each is a row with its consequence written underneath rather than a
   button with a verb on it. */
.choices {
  display: flex;
  flex-direction: column;
  gap: .5rem;
  margin: 1.1rem 0 .25rem;
}

.choice {
  display: flex;
  gap: .8rem;
  align-items: flex-start;
  width: 100%;
  text-align: left;
  font: inherit;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: .85rem .9rem;
  cursor: pointer;
  transition: background .16s ease, border-color .16s ease, transform .16s ease;
}

.choice:hover:not(:disabled) {
  background: var(--surface-hover);
  border-color: var(--border-strong);
}

.choice:active:not(:disabled) { transform: scale(.99); }

.choice:disabled { opacity: .45; cursor: not-allowed; }

.choice .ico { flex: none; margin-top: .1rem; }

.choice > span { font-weight: 600; }

.choice small {
  display: block;
  margin-top: .15rem;
  font-weight: 400;
  font-size: .82rem;
  color: var(--text-muted);
}

/* Clearing throws purchases away without recording them, so it is coloured like
   the delete it is. The explanation stays muted rather than shouting twice. */
.choice.is-danger { color: var(--danger); }

.choice.is-danger:hover:not(:disabled) {
  border-color: color-mix(in srgb, var(--danger) 45%, transparent);
  background: color-mix(in srgb, var(--danger) 10%, transparent);
}

/* ── Aisle glyphs (FR-CAT-01) ──────────────────────────────────────────────
   Emoji, so they carry their own colour and must not inherit the text one.
   Sized a little above their line so they read as a mark rather than a letter. */
.aisle-glyph {
  font-size: 1.15em;
  line-height: 1;
  font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
}

/* The shop screen is where a glyph actually earns its place: a landmark you catch
   at a glance while walking, with the name still beside it for the ones no picture
   could disambiguate. */
.aisle-head .aisle-glyph {
  font-size: 1.35rem;
  margin-right: .1rem;
}

/* One emoji is all that fits, so the field is sized to say so. */
.field-glyph {
  width: 5.5rem;
  text-align: center;
  font-size: 1.35rem;
  line-height: 1.2;
}

/* ── Item photos (FR-LIST-08) ──────────────────────────────────────────────
   Always square, because the server refuses anything else — so the box can be
   reserved and nothing reflows when a photo finishes loading. */
.item-photo {
  float: left;
  width: 2.6rem;
  height: 2.6rem;
  margin: 0 .7rem .2rem 0;
  border-radius: 10px;
  object-fit: cover;
  background: var(--surface-hover);
}

.photo-row {
  display: flex;
  gap: .9rem;
  align-items: flex-start;
}

.photo-preview {
  width: 5.5rem;
  height: 5.5rem;
  flex: none;
  border-radius: var(--radius);
  object-fit: cover;
  background: var(--surface-hover);
  border: 1px solid var(--border);
}

.photo-preview.is-empty {
  display: grid;
  place-items: center;
  color: var(--text-muted);
  border-style: dashed;
}

.photo-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .45rem;
}

/* The crop square itself. Capped by width so it stays square on a phone and does not
   push the sheet's buttons off the bottom of a laptop. */
.crop-stage {
  margin: 1rem 0 .75rem;
  display: grid;
  place-items: center;
}

.crop-canvas {
  width: min(100%, 20rem);
  height: auto;
  aspect-ratio: 1;
  border-radius: var(--radius);
  background: var(--surface-hover);
  touch-action: none;          /* the drag is ours, not the browser's scroll */
  cursor: grab;
}

.crop-canvas:active { cursor: grabbing; }

.crop-zoom {
  display: flex;
  align-items: center;
  gap: .75rem;
  justify-content: center;
}

.crop-zoom .hint { margin: 0; }

/* The reserved square, before the bytes arrive. Same box as the image so nothing moves. */
.item-photo.is-loading,
.photo-preview.is-loading {
  display: inline-block;
  background: var(--surface-hover);
}

/* ── Household mark (FR-HH-05) ─────────────────────────────────────────────
   The top-corner mark, now the household's initial rather than the logo: it says
   whose list this is and opens the switcher. The caret badge is the whole
   affordance — without it a coloured square with a letter reads as a logo. */
.hh-mark {
  position: relative;
  width: 34px;
  height: 34px;
  border-radius: 11px;
  background: var(--gradient);
  color: var(--accent-contrast);
  display: grid;
  place-items: center;
  flex: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font: inherit;
  box-shadow: 0 6px 18px -8px var(--accent);
  transition: transform .16s ease, box-shadow .16s ease;
}

.hh-mark:hover { transform: translateY(-1px); box-shadow: 0 10px 22px -10px var(--accent); }
.hh-mark:active { transform: scale(.96); }
.hh-mark .ico { width: 19px; height: 19px; }

.hh-mark-initial {
  font-weight: 800;
  font-size: 1.05rem;
  letter-spacing: -.02em;
  line-height: 1;
}

.hh-mark-caret {
  position: absolute;
  right: -5px;
  bottom: -5px;
  width: 16px;
  height: 16px;
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  display: grid;
  place-items: center;
  color: var(--text-muted);
  box-shadow: var(--shadow);
}

.hh-mark-caret .ico { width: 10px; height: 10px; }

/* Rows in the switcher: the household you are in keeps the gradient, the others
   sit quiet until tapped. */
.hh-avatar {
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 12px;
  background: var(--gradient);
  color: var(--accent-contrast);
  display: grid;
  place-items: center;
  font-weight: 800;
  font-size: 1.05rem;
  flex: none;
}

.hh-avatar.is-other {
  background: var(--surface-hover);
  color: var(--text-muted);
  border: 1px solid var(--border-strong);
}

/* ── Member avatars (FR-HH-04) ─────────────────────────────────────────────
   Eight slots, assigned per household so five people are five colours. Defined
   here rather than stored, so both themes can be tuned without a migration and
   contrast is a design decision rather than luck. */
.avatar {
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 999px;
  flex: none;
  display: grid;
  place-items: center;
  font-weight: 700;
  font-size: .82rem;
  line-height: 1;
  color: #fff;
  background: var(--avatar, #64748b);
  user-select: none;
}

/* The one that rides on a list row: small enough to be a footnote, which is what
   FR-HH-04's "subtle" asks for. */
.avatar.is-small {
  width: 1.25rem;
  height: 1.25rem;
  font-size: .62rem;
  font-weight: 800;
}

.avatar.c0 { --avatar: var(--av0); }
.avatar.c1 { --avatar: var(--av1); }
.avatar.c2 { --avatar: var(--av2); }
.avatar.c3 { --avatar: var(--av3); }
.avatar.c4 { --avatar: var(--av4); }
.avatar.c5 { --avatar: var(--av5); }
.avatar.c6 { --avatar: var(--av6); }
.avatar.c7 { --avatar: var(--av7); }

/* Sits with the chips under a title, so a row reads: what it is, then who put it
   there — never the other way round. */
.item-meta .avatar.is-small { margin-right: .1rem; }

/* ── Segmented control ─────────────────────────────────────────────────────
   For a small set of mutually exclusive choices where seeing the alternatives
   matters — three states named on screen beat a dropdown you have to open. */
.seg {
  display: flex;
  gap: .25rem;
  padding: .25rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: calc(var(--radius) - 2px);
}

.seg-option {
  flex: 1;
  padding: .5rem .6rem;
  border: 0;
  border-radius: calc(var(--radius) - 6px);
  background: none;
  color: var(--text-muted);
  font: inherit;
  font-weight: 600;
  font-size: .86rem;
  cursor: pointer;
  transition: background .16s ease, color .16s ease;
}

.seg-option:hover:not(.on) { background: var(--surface-hover); color: var(--text); }

.seg-option.on {
  background: var(--gradient);
  color: var(--accent-contrast);
  box-shadow: 0 4px 12px -6px var(--accent);
}

/* ── Barcode scanner (FR-SMART-01) ─────────────────────────────────────────
   A letterbox rather than the full camera frame: a barcode is wide and short, and a
   smaller view keeps the sheet's form in reach of a thumb. */
.scan-view {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: #000;
}

.scan-view.is-off { display: none; }

.scan-video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Where to hold the barcode. Purely a guide; the decoder reads the whole frame. */
.scan-frame {
  position: absolute;
  inset: 28% 12%;
  border: 2px solid rgba(255, 255, 255, 0.85);
  border-radius: 10px;
  box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.25);
  pointer-events: none;
}

/* ── Item drawings (ADR-0014) ──────────────────────────────────────────────
   The photo's square, without the cover-crop or the grey backing: a drawing has its own
   transparent margins. A drawing that arrives while the list is open pops in, once. */
.item-illustration {
  object-fit: contain;
  background: transparent;
}

.item-illustration.is-new {
  animation: illustration-pop .45s cubic-bezier(.3, 1.4, .5, 1) both;
}

@keyframes illustration-pop {
  from { opacity: 0; transform: scale(.6); }
  to { opacity: 1; transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .item-illustration.is-new { animation: none; }
}

/* ── Drawing notes, development builds only (ADR-0014) ───────────────────── */
.dev-notes {
  position: fixed;
  top: calc(env(safe-area-inset-top) + .75rem);
  left: 50%;
  transform: translateX(-50%);
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: .4rem;
  width: min(92vw, 26rem);
  pointer-events: none;
}

.dev-note {
  padding: .55rem .85rem;
  border-radius: var(--radius-full);
  background: var(--surface-solid);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow);
  font-size: .82rem;
  color: var(--text);
  animation: illustration-pop .3s ease-out both;
}

.dev-note.is-made { border-color: var(--accent); }
