/* KIkal dashboard. Mobile first, light + dark via prefers-color-scheme. */

:root {
  --bg: #f2f3f4;      /* cool-neutral off-white, easy on the eyes */
  --surface: #ffffff; /* cards stay white so they lift off the bg */
  --surface-2: #e9eaec;
  --ink: #1c2321;
  --ink-2: #5b6663;
  --ink-3: #8b9490;
  --line: #e3e5e1;
  --accent: #2f9e63;
  --accent-ink: #ffffff;
  --good: #2f9e63;   /* deficit = green (display convention) */
  --bad: #c2452f;    /* surplus = red */
  --c-kcal: #2f9e63;
  --c-protein: #3b82c4;
  --c-weight: #b07620;
  --radius: 10px;
  --radius-card: 14px;
  --shadow: 0 1px 2px rgba(28, 35, 33, .05), 0 4px 16px rgba(28, 35, 33, .07);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-t: env(safe-area-inset-top, 0px);
  --kb: 0px;  /* on-screen keyboard height, kept in sync from visualViewport */
  --vvt: 0px; /* visual-viewport top offset (iOS keyboard), synced likewise */
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #15181c;
    --surface: #1c2026;
    --surface-2: #23282f;
    --ink: #e8eae7;
    --ink-2: #a2aba7;
    --ink-3: #737c78;
    --line: #2c323a;
    --accent: #2ca263;
    --good: #3fbf7c;
    --bad: #e06a52;
    --c-kcal: #2ca263;
    --c-protein: #4a92cf;
    --c-weight: #bd8330;
    --shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 6px 20px rgba(0, 0, 0, .35);
  }
}

* { box-sizing: border-box; margin: 0; padding: 0; }

/* touch-action: manipulation kills iOS double-tap zoom (fast taps on the FAB
   arrows zoomed the page) while keeping scrolling and pinch zoom intact. */
html { height: 100%; touch-action: manipulation; }

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  background: var(--bg);
  color: var(--ink);
  min-height: 100%;
  -webkit-font-smoothing: antialiased;
  font-size: 16px;
  line-height: 1.45;
}

/* Carbon-weave texture: 1px diagonal crosshatch, both directions, 7px pitch.
   Light mode draws dark hairlines, dark mode light ones (same geometry,
   inverted). Contrast stays a few percent so the white/dark cards lift off
   the background without adding noise. */
body {
  background-image:
    repeating-linear-gradient(45deg, rgba(28, 35, 33, .032) 0 1px, transparent 1px 7px),
    repeating-linear-gradient(-45deg, rgba(28, 35, 33, .032) 0 1px, transparent 1px 7px);
}
@media (prefers-color-scheme: dark) {
  body {
    background-image:
      repeating-linear-gradient(45deg, rgba(255, 255, 255, .022) 0 1px, transparent 1px 7px),
      repeating-linear-gradient(-45deg, rgba(255, 255, 255, .022) 0 1px, transparent 1px 7px),
      linear-gradient(180deg, rgba(0, 0, 0, .16), transparent 45%);
  }
}

button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
/* Disabled controls get no touch events in WebKit, so touch-action can't
   suppress double-tap zoom on them; let the tap fall through to the page,
   which has touch-action: manipulation. */
button:disabled { pointer-events: none; }
input, select, textarea {
  font: inherit;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 12px;
  width: 100%;
}
input:focus-visible, select:focus-visible, textarea:focus-visible,
button:focus-visible, a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ── Layout ── */

#app { max-width: 560px; margin: 0 auto; min-height: 100vh; }

main.view {
  padding: 16px 16px calc(76px + var(--safe-b));
  animation: fade-in .18s ease-out;
}
@keyframes fade-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  main.view, .sheet, .fab { animation: none !important; transition: none !important; }
}

/* ── Login ── */

.login-wrap {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 24px;
  gap: 8px;
}
.login-wrap h1 {
  font-size: 44px;
  letter-spacing: -1px;
  font-weight: 800;
}
.login-wrap h1 .ki { color: var(--accent); }
.login-wrap .tagline { color: var(--ink-2); margin-bottom: 24px; }
.login-wrap form { display: flex; flex-direction: column; gap: 12px; }
.login-wrap .swap { margin-top: 16px; color: var(--ink-2); font-size: 14px; }
.login-wrap .reg-closed {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-2);
  padding: 16px;
  color: var(--ink-2);
}
.login-wrap .verify-box {
  margin-top: 14px;
  border: 1px solid var(--bad);
  border-radius: var(--radius);
  padding: 12px;
  font-size: 14px;
}
.login-wrap .verify-box p { color: var(--bad); margin-bottom: 8px; }
.login-wrap .verify-box button {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 8px 14px;
  font-weight: 600;
}
.login-wrap .swap button { color: var(--accent); font-weight: 600; }
/* Subtle teaser linking back to the marketing site. */
.login-wrap .landing-promo {
  display: inline-block;
  margin-top: 28px;
  padding: 8px 14px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink-2);
  font-size: 13.5px;
  text-decoration: none;
  transition: color .15s ease, border-color .15s ease;
}
.login-wrap .landing-promo:hover { color: var(--accent); border-color: var(--accent); }

/* ── Top bar / nav ── */

.topbar {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 12px;
}
.topbar h1 { font-size: 22px; font-weight: 800; letter-spacing: -0.3px; }
.topbar .sub { color: var(--ink-3); font-size: 13px; }

.tabbar {
  position: fixed;
  bottom: 0; left: 0; right: 0;
  background: var(--surface);
  border-top: 1px solid var(--line);
  display: flex;
  max-width: 560px;
  margin: 0 auto;
  padding-bottom: var(--safe-b);
  z-index: 20;
}
.tabbar button {
  flex: 1 1 auto;
  min-width: 0;
  padding: 10px 4px 8px;
  font-size: 12px;
  color: var(--ink-3);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}
.tabbar button svg { width: 22px; height: 22px; }
.tabbar button span {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tabbar button.active { color: var(--accent); font-weight: 600; }

.more-menu {
  position: fixed;
  bottom: calc(68px + var(--safe-b));
  min-width: 190px;
  padding: 6px;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow);
  z-index: 30;
}
.more-menu button {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  font-size: 15px;
  color: var(--ink);
  text-align: left;
  border-radius: var(--radius);
}
.more-menu button svg { width: 20px; height: 20px; color: var(--ink-3); }
.more-menu button:active { background: var(--surface-2); }
.more-menu button.active { color: var(--accent); font-weight: 600; }
.more-menu button.active svg { color: var(--accent); }

/* ── Today ── */

.datebar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 16px;
}
.datebar .dnav { font-size: 22px; padding: 6px 14px; color: var(--ink-2); border-radius: var(--radius); }
.datebar .dnav:active { background: var(--surface-2); }
.datebar .dnav:disabled { opacity: .3; }
.datebar .today-jump svg { width: 20px; height: 20px; display: block; }
.datebar input[type="date"] { text-align: center; font-weight: 600; border: none; background: transparent; width: auto; }

.hero { text-align: center; margin: 20px 0 24px; }
.hero .big {
  font-size: 56px;
  font-weight: 800;
  letter-spacing: -2px;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.hero .big.good { color: var(--good); }
.hero .big.bad { color: var(--bad); }
.hero .cap { color: var(--ink-2); font-size: 14px; margin-top: 2px; }
.hero .rem { font-size: 13px; font-weight: 700; margin-top: 8px; font-variant-numeric: tabular-nums; }
.hero .rem.good { color: var(--good); }
.hero .rem.bad { color: var(--bad); }

.meters { display: flex; flex-direction: column; gap: 14px; margin-bottom: 24px; }
.meter .row { display: flex; justify-content: space-between; font-size: 14px; margin-bottom: 4px; }
.meter .row .lbl { color: var(--ink-2); }
.meter .row .val { font-variant-numeric: tabular-nums; font-weight: 600; }
.meter .bar { height: 8px; border-radius: 4px; background: var(--surface-2); overflow: hidden; }
.meter .bar i { display: block; height: 100%; border-radius: 4px; transition: width .45s cubic-bezier(.22, 1, .36, 1); }
.meter.kcal .bar i { background: var(--c-kcal); }
.meter.protein .bar i { background: var(--c-protein); }
.meter .bar i.over { background: var(--bad); }

h2.section { font-size: 14px; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3); margin: 20px 0 8px; }

/* Card sections: gentle elevation instead of flat lists. */
.meters, .entry-list, .chart-block, .prod-list, .settings-form {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow);
  padding: 16px;
}

.entry-list { list-style: none; padding-top: 4px; padding-bottom: 4px; }
.entry-list li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 2px;
  border-bottom: 1px solid var(--line);
}
.entry-list li:last-child { border-bottom: none; }
.entry-list .t { color: var(--ink-3); font-size: 12px; min-width: 40px; font-variant-numeric: tabular-nums; }
.entry-list .lbl { flex: 1; min-width: 0; }
.entry-list .lbl .sub { color: var(--ink-3); font-size: 12px; }
.entry-list .lbl .prod-link { color: var(--ink-3); margin-left: 5px; }
.entry-list .lbl .prod-link svg { width: 12px; height: 12px; vertical-align: -1px; }
.entry-list .nums { text-align: right; font-variant-numeric: tabular-nums; font-size: 14px; }
.entry-list .nums .p { color: var(--ink-3); font-size: 12px; }
.entry-list li { cursor: pointer; }
.entry-list li:active { background: var(--surface-2); }

/* Slick entry summary: a single card showing the count, opens the list dialog. */
.usage-card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-card); box-shadow: var(--shadow); padding: 6px 16px;
}
.usage-card .hint { padding: 10px 0; }
.usage-row { display: flex; justify-content: space-between; align-items: baseline;
  padding: 10px 0; border-bottom: 1px solid var(--line); }
.usage-row:last-child { border-bottom: 0; }
.usage-row span { color: var(--ink-2); font-size: 14px; }
.usage-row b { font-variant-numeric: tabular-nums; }

.entries-card {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow);
  padding: 16px;
  font-weight: 600;
}
.entries-card:active { background: var(--surface-2); }
.entries-card .chev { color: var(--ink-3); font-size: 22px; line-height: 1; }

.dayvals { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.dayvals .cell {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow);
  padding: 12px 14px;
  text-align: left;
}
.dayvals .cell:active { transform: scale(.985); }
.dayvals .cell .k { color: var(--ink-3); font-size: 12px; }
.dayvals .cell .v { font-weight: 600; font-variant-numeric: tabular-nums; }
.dayvals .cell.note-cell { grid-column: 1 / -1; }
.dayvals .cell .v.empty { color: var(--ink-3); opacity: .65; font-weight: 400; font-size: 11px; }
/* Burned kcal still missing: same quiet style, but red (replaces the old
   hero caption hint). */
.dayvals .cell .v.empty.missing { color: var(--bad); }

.empty-hint { color: var(--ink-3); padding: 16px 0; font-size: 14px; }

/* Boot curtain: the session marker is only a guess, so the shell stays
   invisible until the first API response confirms (or denies) the session.
   Prevents an app-shell flash before a 401 bounce to the login page. */
body.booting #app, body.booting .fabs, body.booting .ws-ind { visibility: hidden; }

/* ── FAB row ── */

.fabs {
  position: fixed;
  bottom: calc(64px + var(--safe-b));
  right: max(16px, calc(50vw - 264px));
  display: flex;
  align-items: center;
  gap: 10px;
  z-index: 15;
}
.fab {
  background: var(--accent);
  color: var(--accent-ink);
  border-radius: 50%;
  width: 46px;
  height: 46px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 14px rgba(0,0,0,.22);
  animation: fade-in .2s ease-out;
}
.fab.secondary { background: var(--surface); color: var(--ink); border: 1px solid var(--line); }
/* Chat activity: soft pulse while a reply generates server-side. */
.fab.chat-busy { animation: fab-pulse 1.6s ease-in-out infinite; }
@keyframes fab-pulse {
  0%, 100% { box-shadow: 0 4px 14px rgba(0, 0, 0, .22), 0 0 0 0 rgba(47, 158, 99, .5); }
  50% { box-shadow: 0 4px 14px rgba(0, 0, 0, .22), 0 0 0 12px rgba(47, 158, 99, 0); }
}
/* Unread badge: a reply finished while the chat was closed. */
.fab { position: relative; }
.fab .fab-badge {
  position: absolute; top: 1px; right: 1px;
  width: 13px; height: 13px; border-radius: 50%;
  background: var(--bad); border: 2px solid var(--bg);
}
.fab svg { width: 21px; height: 21px; }
/* Date step arrows sit in the FAB cluster, same size as the action FAB. */
.fab.dnav-fab { font-size: 24px; line-height: 1; color: var(--ink-2); }
.fab:disabled, .fab[aria-disabled="true"] { opacity: .35; box-shadow: none; cursor: default; }

/* Update banner: floats above every layer (dialogs are z-index < 100) when a
   deploy lands while a dialog is open; tapping reloads onto the new version. */
.update-banner {
  position: fixed;
  top: calc(10px + var(--safe-t));
  left: 50%;
  transform: translateX(-50%);
  z-index: 100;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 14px;
  font-weight: 600;
  padding: 10px 16px;
  border-radius: 999px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .3);
  white-space: nowrap;
  /* Own keyframes: the shared fade-in ends on transform:none, which would
     drop the centering translateX mid-animation (pill sits right, then
     snaps to center). These keep the -50% throughout and slide from the top. */
  animation: banner-in .25s ease-out;
}
@keyframes banner-in { from { opacity: 0; transform: translate(-50%, -14px); } to { opacity: 1; transform: translate(-50%, 0); } }
.update-banner.out { animation: banner-out .25s ease-in forwards; pointer-events: none; }
@keyframes banner-out { to { opacity: 0; transform: translate(-50%, -14px); } }

/* Midnight hint: playful bubble bouncing above the next-day arrow when the
   viewed "today" just rolled over into yesterday. Pointer caret aims at the
   second FAB (fab-next: 46px prev + 10px gap + half of 46px = 79px). */
.day-hint {
  position: absolute;
  bottom: calc(100% + 12px);
  left: 79px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 13px;
  font-weight: 600;
  padding: 7px 13px;
  border-radius: 999px;
  white-space: nowrap;
  box-shadow: 0 4px 14px rgba(0,0,0,.25);
  pointer-events: none;
  animation: day-hint-bounce 1.1s ease-in-out infinite;
}
.day-hint::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  margin-left: -6px;
  border: 6px solid transparent;
  border-top-color: var(--accent);
}
@keyframes day-hint-bounce {
  0%, 100% { transform: translate(-50%, 0); }
  40% { transform: translate(-50%, -9px); }
  60% { transform: translate(-50%, -7px); }
}

/* ── Sheet ── */

/* Centered modal dialog. The overlay reserves keyboard height as bottom padding
   (--kb, synced from visualViewport in JS), so the dialog centers within the
   VISIBLE area above the keyboard, keeps its natural height, and scrolls
   internally when taller than that area. It is never crammed into the leftover
   strip. Backdrop is dimmed and blurred. */
.overlay {
  position: fixed; inset: 0;
  background: rgba(0,0,0,.4);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(20px + var(--safe-t) + var(--vvt)) 20px calc(20px + var(--safe-b) + var(--kb));
}
/* A layer stacked on top of another dialog: no extra darkening (the first
   layer already dimmed the page), only blur on what is behind it. */
.overlay.stacked, .confirm-ov.stacked {
  background: transparent;
}
.sheet {
  background: var(--bg);
  border-radius: 16px;
  width: 100%;
  max-width: 480px;
  max-height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  /* Top/bottom cover the sticky title and action bars, so a focused input
     auto-scrolled into view is never tucked underneath them. */
  scroll-padding: 64px 16px 84px;
  padding: 20px 18px;
  box-shadow: 0 10px 40px rgba(0,0,0,.3);
  animation: sheet-up .18s ease-out;
}
@keyframes sheet-up { from { transform: scale(.96); opacity: 0; } to { transform: none; opacity: 1; } }
.sheet h2 { font-size: 18px; margin-bottom: 14px; }
.sheet form { display: flex; flex-direction: column; gap: 12px; }
/* align-items:end keeps the inputs aligned even when one column's label wraps to
   two lines (e.g. the "How much did you have of it?" question next to "Unit"). */
.sheet .grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; align-items: end; }
.sheet label { font-size: 13px; color: var(--ink-2); display: block; margin-bottom: 4px; }
.sheet .actions { display: flex; gap: 10px; margin-top: 6px; }
.sheet .actions .primary {
  flex: 1; background: var(--accent); color: var(--accent-ink);
  border-radius: var(--radius); padding: 12px; font-weight: 700;
}
.sheet .actions .ghost { padding: 12px 16px; color: var(--ink-2); border: 1px solid var(--line); border-radius: var(--radius); }
.sheet .actions .danger { color: var(--bad); border-color: var(--bad); }
/* Round trash button replacing the text delete button in dialog footers. */
.sheet .actions .icon-danger {
  width: 44px; height: 44px; flex: none;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--bad); border-radius: 50%; color: var(--bad);
}
.sheet .actions .icon-danger svg { width: 20px; height: 20px; }
.sheet .hint { font-size: 13px; color: var(--ink-3); }
.sheet img.preview { max-width: 100%; border-radius: var(--radius); }

/* On phones the dialog uses the full width and can grow tall; content scrolls
   inside it. The overlay's --kb padding keeps it above the keyboard. */
@media (max-width: 600px) {
  .overlay { padding: calc(12px + var(--safe-t) + var(--vvt)) 12px calc(12px + var(--safe-b) + var(--kb)); }
  .sheet { max-width: none; }
}

/* Every form dialog keeps its title and action row visible while the body
   scrolls: sticky bars carry the sheet's vertical padding themselves (the
   sheet drops it) so the scroll extent stays intact; negative side margins
   only bleed them to the sheet edges. Excluded: chat sheet (own fixed-header
   layout) and the entries list sheet (own flex layout below). Actions are
   matched only at the dialog's top level so cards nested in scrolling content
   (e.g. chat proposals) never turn sticky. */
.sheet:not(.chat-sheet):not(.list-sheet) { padding-top: 0; padding-bottom: 0; }
/* A field right under the title bar paints its focus ring beneath the bar's
   background (ring = 2px outline + 2px offset above the border box); raise
   the focused field above the sticky bars so the ring stays whole. */
.sheet input:focus-visible, .sheet select:focus-visible, .sheet textarea:focus-visible {
  position: relative; z-index: 3;
}
.sheet:not(.chat-sheet):not(.list-sheet) > div > h2 {
  position: sticky; top: 0; z-index: 2;
  background: var(--bg);
  margin: 0 -18px; padding: 20px 18px 10px;
}
.sheet:not(.chat-sheet):not(.list-sheet) > div > .actions,
.sheet:not(.chat-sheet):not(.list-sheet) > div > form > .actions {
  position: sticky; bottom: 0; z-index: 2;
  background: var(--bg);
  margin: 0 -18px; padding: 12px 18px 20px;
}

/* List sheet (entries dialog): header and footer stay pinned, only the list
   scrolls. The sheet itself stops scrolling; the inner list takes over. */
.sheet.list-sheet { overflow: hidden; display: flex; }
.sheet.list-sheet > div { display: flex; flex-direction: column; width: 100%; min-height: 0; }
.sheet.list-sheet #es-list { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
/* The scroll container carries the card chrome so its rounded corners clip the
   scrolling list; the list itself sheds border/radius to avoid sharp cut-offs. */
.sheet.list-sheet #es-list {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow);
}
.sheet.list-sheet #es-list .entry-list { border: none; border-radius: 0; box-shadow: none; background: none; }
.sheet.list-sheet .actions { margin-top: 12px; }

/* Reusable approval dialog, stacks above sheets with a blurred backdrop. */
.confirm-ov {
  position: fixed; inset: 0;
  background: rgba(0,0,0,.4);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  z-index: 45;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(20px + var(--vvt)) 20px calc(20px + var(--kb));
  animation: fade-in .15s ease-out;
}
.confirm-card {
  background: var(--bg);
  border-radius: 16px;
  width: 100%;
  max-width: 360px;
  padding: 20px 18px calc(18px + var(--safe-b));
  box-shadow: 0 10px 40px rgba(0,0,0,.3);
  animation: sheet-up .18s ease-out;
}
.confirm-title { font-size: 16px; font-weight: 600; margin-bottom: 16px; text-align: center; }
/* Form variant: the stacked product-create dialog is a small form, not a
   yes/no question. */
.confirm-card.form-card { max-width: 420px; max-height: 85vh; overflow-y: auto; }
.confirm-card.form-card label { display: block; margin: 10px 0 4px; font-size: 13px; color: var(--ink-2); }
.confirm-card.form-card input { width: 100%; }
.prod-pick .prod-new { color: var(--accent); font-weight: 600; }
/* Booking target of an entry proposal; loud when it is NOT today. */
.p-when { font-size: 12px; color: var(--ink-2); margin-top: 2px; }
.p-when.off-today { color: var(--bad); font-weight: 600; }
/* AI intake pane of the product-create dialog. */
.form-card textarea { width: 100%; resize: vertical; }
.form-card .linkish { display: block; margin: 12px auto 0; color: var(--ink-2); text-decoration: underline; font-size: 13px; background: none; }
.form-card .pf-add { flex: 1; padding: 12px; color: var(--ink-2); border: 1px solid var(--line); border-radius: var(--radius); text-align: center; cursor: pointer; }
.pf-imgs { display: flex; gap: 8px; margin: 10px 0; flex-wrap: wrap; }
.pf-thumb { position: relative; }
.pf-thumb img { width: 64px; height: 64px; object-fit: cover; border-radius: 8px; }
.pf-thumb button { position: absolute; top: -6px; right: -6px; background: var(--bg); border: 1px solid var(--line); border-radius: 50%; width: 20px; height: 20px; font-size: 11px; line-height: 1; }
.confirm-card .actions { display: flex; gap: 10px; }
.confirm-card .actions .ghost { flex: 1; padding: 12px; color: var(--ink-2); border: 1px solid var(--line); border-radius: var(--radius); text-align: center; }
.confirm-card .actions .primary { flex: 1; background: var(--accent); color: var(--accent-ink); border-radius: var(--radius); padding: 12px; font-weight: 700; }
.confirm-card .actions .primary.danger { background: var(--bad); color: #fff; }

.time-row { display: flex; gap: 10px; }
.time-row input { flex: 1; }
.time-row .ghost { padding: 0 18px; color: var(--ink-2); border: 1px solid var(--line); border-radius: var(--radius); font-weight: 600; }

/* Segmented g / ml unit toggle. */
.seg { display: flex; border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.seg button {
  flex: 1; padding: 12px 0; font-weight: 600; color: var(--ink-2);
  background: transparent; border-right: 1px solid var(--line);
}
.seg button:last-child { border-right: none; }
.seg button.sel { background: var(--accent); color: var(--accent-ink); }
.seg.locked { opacity: .6; pointer-events: none; }
.cat-seg { margin-bottom: 12px; }
.cat-seg button { flex: 1; }

.search-wrap { position: relative; }
.search-wrap input { width: 100%; padding-right: 40px; }
.search-clear {
  position: absolute; top: 50%; right: 6px; transform: translateY(-50%);
  width: 28px; height: 28px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  color: var(--ink-3); font-size: 13px; line-height: 1;
}
.search-clear:active { background: var(--surface-2); }
.prod-pick { border: 1px solid var(--line); border-radius: var(--radius); max-height: 200px; overflow-y: auto; margin-top: 6px; }
.prod-pick button {
  display: flex; justify-content: space-between; width: 100%;
  padding: 10px 12px; border-bottom: 1px solid var(--line); text-align: left; font-size: 14px;
}
.prod-pick button:last-child { border-bottom: none; }
.prod-pick button .meta { color: var(--ink-3); font-variant-numeric: tabular-nums; }
.prod-pick button.sel { background: var(--surface-2); font-weight: 600; }

/* ── Stats ── */

.rangebar { display: flex; gap: 8px; margin-bottom: 16px; }
.rangebar button {
  padding: 7px 14px; border-radius: 999px; border: 1px solid var(--line);
  font-size: 13px; color: var(--ink-2);
}
.rangebar button.active { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); font-weight: 600; }

.stat-tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 20px; }
.stat-tiles .tile {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow);
  padding: 12px 14px;
}
.stat-tiles .tile .k { font-size: 12px; color: var(--ink-3); }
.stat-tiles .tile .v { font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: -0.5px; }
.stat-tiles .tile .v.good { color: var(--good); }
.stat-tiles .tile .v.bad { color: var(--bad); }

.chart-block { margin-bottom: 24px; }
.chart-block h3 { font-size: 14px; margin-bottom: 8px; display: flex; align-items: center; gap: 6px; }
.chart-block h3 .dot { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
.chart-block svg { width: 100%; height: auto; display: block; touch-action: pan-y; }
.chart-block .axis text { font-size: 10px; fill: var(--ink-3); }
.chart-block .grid line { stroke: var(--line); stroke-width: 1; }
.chart-tip {
  position: fixed; z-index: 40; pointer-events: none;
  background: var(--surface); border: 1px solid var(--line); border-radius: 8px;
  padding: 6px 9px; font-size: 12px; box-shadow: 0 2px 10px rgba(0,0,0,.15);
  font-variant-numeric: tabular-nums;
}
.chart-tip .d { color: var(--ink-3); }

details.table-view { margin-top: -12px; margin-bottom: 24px; }
details.table-view summary { font-size: 13px; color: var(--ink-3); cursor: pointer; padding: 4px 0; }
details.table-view table { width: 100%; border-collapse: collapse; font-size: 13px; font-variant-numeric: tabular-nums; }
details.table-view th, details.table-view td { text-align: right; padding: 5px 6px; border-bottom: 1px solid var(--line); }
details.table-view th:first-child, details.table-view td:first-child { text-align: left; }
details.table-view td.good { color: var(--good); }
details.table-view td.bad { color: var(--bad); }
details.table-view th { color: var(--ink-3); font-weight: 500; }

/* ── Products ── */

.searchbar { margin-bottom: 12px; position: relative; }
.searchbar #pr-clear {
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  padding: 8px 12px;
  font-size: 14px;
  color: var(--ink-3);
}
.searchbar input { padding-right: 40px; }
.prod-list { list-style: none; }
.prod-list li {
  display: flex; justify-content: space-between; align-items: center; gap: 10px;
  padding: 11px 2px; border-bottom: 1px solid var(--line); cursor: pointer;
}
.prod-list li:last-child { border-bottom: none; }
.prod-list li:active { background: var(--surface-2); }
.prod-list .n { flex: 1; min-width: 0; }
.prod-list .n .nm, .prod-list .n .sub { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.prod-list .n .sub { color: var(--ink-3); font-size: 12px; }
.prod-list .n .sub.vendor { color: var(--ink-2); }
.prod-list .badge {
  font-size: 11px; color: var(--ink-2); border: 1px solid var(--line);
  border-radius: 999px; padding: 2px 8px;
}
.prod-list .nums { font-variant-numeric: tabular-nums; font-size: 13px; color: var(--ink-2); text-align: right; }

/* ── Settings ── */

.settings-form { display: flex; flex-direction: column; gap: 14px; }
.settings-form .field label { font-size: 13px; color: var(--ink-2); display: block; margin-bottom: 4px; }
.settings-form .hint { font-size: 12px; color: var(--ink-3); margin-top: 3px; }
.settings-form .primary {
  background: var(--accent); color: var(--accent-ink); border-radius: var(--radius);
  padding: 12px; font-weight: 700;
}
.logout-btn { margin-top: 24px; color: var(--bad); font-weight: 600; padding: 12px 0; }

/* ── Live-sync indicator ── */

.ws-ind {
  position: fixed;
  top: calc(14px + env(safe-area-inset-top, 0px));
  right: max(16px, calc(50vw - 264px));
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--bad);
  z-index: 25;
  animation: ws-blink 1s ease-in-out infinite;
}
@keyframes ws-blink { 0%, 100% { opacity: 1; } 50% { opacity: .2; } }
@media (prefers-reduced-motion: reduce) {
  .ws-ind { animation: none; }
}

/* ── Toast ── */

.toast {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(84px + var(--safe-b));
  max-width: min(92vw, 420px);
  text-align: center;
  background: var(--ink); color: var(--bg);
  border-radius: 999px; padding: 11px 20px;
  font-size: 14px; font-weight: 600; line-height: 1.3; letter-spacing: .01em;
  box-shadow: 0 6px 22px rgba(0, 0, 0, .22), 0 2px 6px rgba(0, 0, 0, .14);
  z-index: 50; animation: toast-in .24s cubic-bezier(.2, .8, .2, 1);
}
@keyframes toast-in {
  from { opacity: 0; transform: translate(-50%, 10px) scale(.96); }
  to   { opacity: 1; transform: translate(-50%, 0) scale(1); }
}

/* ── Chat (conversational logging agent) ── */

/* The chat is a full-height sheet: a fixed header, a scrolling message log,
   and a composer pinned to the bottom (above the keyboard via the overlay's
   --kb padding). */
.sheet.chat-sheet {
  padding: 0;
  height: 100%;
  max-width: 520px;
  display: flex;
  overflow: hidden;
}
.chat { display: flex; flex-direction: column; width: 100%; min-height: 0; position: relative; }

.chat-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 16px; border-bottom: 1px solid var(--line);
}
.chat-head h2 { font-size: 16px; margin: 0; display: flex; align-items: center; gap: 8px; }
.chat-head h2 svg { width: 20px; height: 20px; color: var(--accent); }
.chat-x { font-size: 18px; color: var(--ink-2); padding: 4px 8px; line-height: 1; }
.chat-head { position: relative; gap: 2px; }
/* Copyable conversation-id chip (debug aid). */
.conv-id { font: 600 11px/1 ui-monospace, monospace; color: var(--ink-2); border: 1px solid var(--line); border-radius: 999px; padding: 4px 8px; margin-right: 4px; cursor: pointer; }
/* Thread model/cost info icon: quiet, details in a popover on hover/tap. */
.conv-usage { font-size: 14px; line-height: 1; color: var(--ink-2); opacity: .6; margin-right: 6px; cursor: help; user-select: none; }
.conv-usage:hover { opacity: 1; }
.usage-pop {
  position: absolute; top: 100%; right: 8px; z-index: 5;
  background: var(--bg); border: 1px solid var(--line); border-radius: 10px;
  padding: 10px 12px; box-shadow: 0 6px 24px rgba(0,0,0,.18);
  font: 500 12px/1.7 ui-monospace, monospace; color: var(--ink-2); white-space: nowrap;
}
.chat-head h2 { margin-right: auto; }
.chat-x svg { width: 20px; height: 20px; vertical-align: -4px; }

/* Thread menu (persisted chat history, issue #8) */
.chat-menu {
  position: absolute; top: calc(100% - 6px); right: 12px; z-index: 5;
  min-width: 230px; max-width: 85%; max-height: 50vh; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, .18); padding: 6px;
}
.cm-empty { color: var(--ink-3); font-size: 13px; padding: 10px 12px; }
.cm-row {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 6px 9px 12px; border-radius: 8px; cursor: pointer;
}
.cm-row:hover { background: var(--surface-2); }
.cm-row.cur { background: var(--surface-2); }
.cm-title {
  flex: 1; min-width: 0; font-size: 14px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cm-dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--accent);
  animation: ws-blink 1.2s ease-in-out infinite; flex: none;
}
.cm-del { color: var(--ink-3); padding: 4px; line-height: 0; flex: none; }
.cm-del svg { width: 16px; height: 16px; }
.cm-del:hover { color: #c0392b; }

.chat-log {
  flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  /* We manage scroll position ourselves while a reply streams; the browser's
     scroll anchoring fights the per-delta DOM replacement and freezes/yanks
     manual scrolling. */
  overflow-anchor: none;
  padding: 16px; display: flex; flex-direction: column; gap: 12px;
}
.chat-intro { color: var(--ink-3); font-size: 14px; text-align: center; margin: auto 8px; }

.chat-msg { display: flex; flex-direction: column; align-items: flex-start; }
/* The tool row sits under its own message, so a message is a column: bubble
   first, tools below, aligned to the side the message is on. */
.chat-msg.user { align-items: flex-end; }
/* Streaming: each newly arrived chunk (.tok) focuses in from a soft blur so the
   reply materialises at its growing edge instead of snapping in. */
.chat-msg .tok { animation: tok-in .34s cubic-bezier(.22, .61, .36, 1) both; }
@keyframes tok-in {
  from { opacity: 0; filter: blur(4px); }
  to { opacity: 1; filter: blur(0); }
}
@media (prefers-reduced-motion: reduce) { .chat-msg .tok { animation: none; } }

.chat-msg .bubble {
  max-width: 84%; padding: 10px 13px; border-radius: 15px;
  font-size: 15px; line-height: 1.4; white-space: pre-wrap; word-break: break-word;
}
.chat-msg.user .bubble { background: var(--accent); color: var(--accent-ink); border-bottom-right-radius: 5px; }
.chat-msg.assistant .bubble { background: var(--surface-2); color: var(--ink); border-bottom-left-radius: 5px; }
/* Assistant turns claim their column outright. A shrink-to-fit bubble would
   resize on every streamed token, so the text reflows and the bubble drifts
   rightwards mid-reply; a fixed width keeps it anchored while it fills. */
.chat-msg.assistant > div:first-child { width: 100%; max-width: 100%; }
.chat-msg.assistant .bubble { max-width: 100%; width: 100%; }
/* The typing dots and the failed-turn box stay as small as their content. */
.chat-msg.assistant .bubble.dots,
.chat-msg.assistant .retry-box { width: auto; }

.msg-imgs { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 6px; }
.msg-imgs img { width: 96px; height: 96px; object-fit: cover; border-radius: 10px; display: block; }
.msg-img { border-radius: 10px; overflow: hidden; line-height: 0; }

/* Typing indicator (three bouncing dots). */
/* Rendered markdown inside assistant bubbles. Line breaks come from block
   elements here, not from pre-wrap (which would double the spacing). */
.bubble.md { white-space: normal; }
.bubble.md .md-h { font-weight: 700; margin: 10px 0 4px; }
.bubble.md .md-h:first-child { margin-top: 0; }
.bubble.md .md-gap { display: block; height: 8px; }
.bubble.md hr { border: none; border-top: 1px solid var(--line); margin: 10px 0; }
.bubble.md ul, .bubble.md ol { margin: 4px 0; padding-left: 20px; }
.bubble.md li { margin: 2px 0; }
.bubble.md code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px; background: var(--surface); border: 1px solid var(--line);
  border-radius: 5px; padding: 1px 5px;
}
.chat-msg.user .bubble.md code { background: rgba(255, 255, 255, .18); border-color: transparent; color: inherit; }

.bubble.md .md-tbl { overflow-x: auto; margin: 8px 0; }
.bubble.md table { border-collapse: collapse; font-size: 13px; min-width: 100%; }
.bubble.md th, .bubble.md td {
  border: 1px solid var(--line); padding: 5px 8px; text-align: left;
  vertical-align: top; word-break: normal; min-width: 72px;
}
.bubble.md th { background: var(--surface); font-weight: 700; }

/* Jump-to-bottom arrow: appears when auto-scroll pauses during streaming. */
.chat-jump {
  position: absolute; left: 50%; bottom: 96px; z-index: 6;
  width: 38px; height: 38px; border-radius: 50%;
  background: var(--surface); border: 1px solid var(--line);
  box-shadow: var(--shadow); color: var(--accent);
  display: flex; align-items: center; justify-content: center;
  animation: jump-bounce 1.5s ease-in-out infinite;
}
/* display:flex above would defeat the hidden attribute without this. */
.chat-jump[hidden] { display: none; }
.chat-jump svg { width: 20px; height: 20px; }
@keyframes jump-bounce {
  0%, 100% { transform: translate(-50%, 0); }
  50% { transform: translate(-50%, 5px); }
}

.bubble.dots { display: inline-flex; gap: 4px; align-items: center; }
.bubble.dots i { width: 7px; height: 7px; border-radius: 50%; background: var(--ink-3); animation: chat-dot 1.2s infinite ease-in-out; }
.bubble.dots i:nth-child(2) { animation-delay: .18s; }
.bubble.dots i:nth-child(3) { animation-delay: .36s; }
@keyframes chat-dot { 0%, 60%, 100% { opacity: .3; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-3px); } }

/* Proposal cards inside an assistant turn. */
.props { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.prop-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-card); padding: 12px; }
.prop-card.done { opacity: .6; }
/* Saved: green card, not dimmed. */
.prop-card.saved { opacity: 1; border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, var(--surface)); }
/* Delete proposals: a danger card, visually distinct from ordinary confirms. */
.prop-card.danger:not(.saved) { border-color: color-mix(in srgb, var(--bad) 45%, var(--line)); }
.prop-card.danger:not(.saved) .p-kind { color: var(--bad); }
.prop-card .p-actions .primary.danger { background: var(--bad); color: #fff; }
.p-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
.p-kind { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3); font-weight: 700; }
.p-label { font-weight: 600; }
.p-meta { color: var(--ink-2); font-size: 13px; margin-top: 2px; font-variant-numeric: tabular-nums; }
.p-actions { display: flex; gap: 8px; margin-top: 10px; }
.p-actions .ghost { flex: 1; padding: 9px; color: var(--ink-2); border: 1px solid var(--line); border-radius: var(--radius); text-align: center; }
.p-actions .primary { flex: 2; background: var(--accent); color: var(--accent-ink); border-radius: var(--radius); padding: 9px; font-weight: 700; }
/* Confirmed button (saved state): muted green, sits in the confirm slot, reads as Undo. */
.p-actions .confirmed { flex: 1; background: color-mix(in srgb, var(--accent) 20%, transparent); color: var(--accent); border-radius: var(--radius); padding: 9px; font-weight: 700; }
.p-actions .confirmed:disabled { opacity: .6; }
.p-state { color: var(--ink-3); font-size: 14px; padding: 6px 2px; }
.p-state.ok { color: var(--good); font-weight: 600; }

/* Confidence pill on entry proposals. */
.conf { font-size: 11px; font-weight: 700; padding: 2px 7px; border-radius: 999px; font-variant-numeric: tabular-nums; }
.conf.hi { background: color-mix(in srgb, var(--good) 18%, transparent); color: var(--good); }
.conf.mid { background: color-mix(in srgb, #d9932f 20%, transparent); color: #b9791f; }
.conf.lo { background: color-mix(in srgb, var(--bad) 18%, transparent); color: var(--bad); }
@media (prefers-color-scheme: dark) { .conf.mid { color: #e0a94a; } }

/* Composer. */
.chat-compose { border-top: 1px solid var(--line); padding: 10px 12px calc(10px + var(--safe-b)); background: var(--bg); }
.chat-thumbs { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
.chat-thumbs .thumb { position: relative; }
.chat-thumbs img { width: 56px; height: 56px; object-fit: cover; border-radius: 10px; display: block; }
.chat-thumbs .thumb button {
  position: absolute; top: -6px; right: -6px; width: 20px; height: 20px; border-radius: 50%;
  background: var(--ink); color: var(--bg); font-size: 12px; line-height: 1; display: flex; align-items: center; justify-content: center;
}
.chat-row { display: flex; align-items: flex-end; gap: 8px; }
.chat-attach, .chat-mic, .chat-send { width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; flex: none; }
/* display:flex above would otherwise defeat the hidden attribute. */
/* These carry a display of their own, which would beat the [hidden] attribute. */
.chat-attach[hidden], .chat-mic[hidden], .chat-send[hidden], .chat-wave[hidden],
.chat-row textarea[hidden], .chat-drop[hidden] { display: none !important; }
.chat-attach, .chat-mic { background: var(--surface-2); color: var(--ink-2); }
.chat-attach svg, .chat-mic svg { width: 20px; height: 20px; }
.chat-mic:disabled, .chat-attach:disabled { opacity: .4; }
.chat-send.as-check { background: var(--good, #3a9d5d); }
/* Recording / transcribing: composer shows a live waveform instead of the
   textarea, camera + mic hidden, send becomes the checkmark. */
.chat-wave { flex: 1; display: flex; align-items: center; gap: 10px; min-height: 40px;
  padding: 0 14px; border: 1px solid var(--line); border-radius: 20px; background: var(--surface); }
.chat-wave canvas { flex: 1; height: 32px; display: block; }
.chat-wave .wave-label { color: var(--ink-2); font-size: 13px; white-space: nowrap; }
.chat-wave.busy canvas { display: none; }
.chat-wave.busy { animation: wave-busy 1.2s ease-in-out infinite; }
@keyframes wave-busy { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }
.chat-send { background: var(--accent); color: var(--accent-ink); }
.chat-send svg { width: 20px; height: 20px; }
.chat-send:disabled { opacity: .4; }
.chat-row textarea {
  flex: 1; resize: none; border: 1px solid var(--line); border-radius: 20px;
  padding: 9px 14px; font: inherit; line-height: 1.35; max-height: 140px; background: var(--surface);
  overflow-y: hidden; /* JS switches to auto only once content exceeds max-height */
}

/* Chat sheet goes edge to edge on phones. */
@media (max-width: 600px) {
  .sheet.chat-sheet { max-width: none; border-radius: 14px; }
}

/* Saved state: a tick plus a Discard that undoes the write. */
.p-actions .p-state.ok { flex: 1; }
/* Keyboard-shortcut hint inside the action buttons (desktop only). */
.p-actions kbd {
  display: inline-block; margin-left: 6px; padding: 0 5px; min-width: 15px;
  font: inherit; font-size: 11px; line-height: 16px; text-align: center;
  border: 1px solid currentColor; border-radius: 4px; opacity: .55; vertical-align: middle;
}

/* ── Onboarding ── */
/* Branded backdrop behind the mandatory setup chat (no tab bar, no views). */
.onboard-bg {
  position: fixed; inset: 0; display: flex; align-items: center; justify-content: center;
  padding: 24px calc(24px + env(safe-area-inset-right, 0px)) 24px calc(24px + env(safe-area-inset-left, 0px));
  background:
    radial-gradient(120% 80% at 50% -10%, color-mix(in srgb, var(--accent) 16%, var(--bg)), var(--bg) 60%);
  text-align: center;
}
.onboard-hero { max-width: 420px; color: var(--ink); }
.onboard-logo {
  width: 56px; height: 56px; margin: 0 auto 18px; display: grid; place-items: center;
  border-radius: 16px; background: var(--surface); box-shadow: var(--shadow); color: var(--accent);
}
.onboard-logo svg { width: 30px; height: 30px; }
.onboard-hero h1 { font-size: 26px; font-weight: 700; margin-bottom: 8px; }
.onboard-hero p { color: var(--ink-2); font-size: 15px; }

/* Onboarding has no photo logging: hide the camera attach in the composer. */
.chat.onboarding .chat-attach { display: none !important; }

/* Logout affordance in the onboarding chat header (only exit while un-onboarded). */
.chat-logout {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 6px 12px; font-size: 13px; font-weight: 600; color: var(--ink-2);
  background: var(--surface);
}
.chat-logout:hover { color: var(--ink); }

/* Quick-reply chips under an assistant question (fixed-choice answers). */
.chat-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.chat-chips .chip {
  padding: 9px 16px; border-radius: 999px; font-size: 14px; font-weight: 600;
  border: 1px solid var(--accent); color: var(--accent); background: var(--surface);
}
.chat-chips .chip:hover { background: color-mix(in srgb, var(--accent) 10%, var(--surface)); }
.chat-chips .chip:disabled { opacity: .45; }

/* Settings proposal (onboarding finish card): the computed targets summary. */
.p-settings { display: flex; flex-direction: column; gap: 4px; }
.p-settings > div { font-size: 14px; color: var(--ink-2); }
.p-settings strong { color: var(--ink); font-weight: 700; }

/* Language switch on the public auth screens (settings owns the signed-in one). */
.lang-pick {
  display: flex;
  justify-content: flex-end;
  gap: 4px;
  margin-bottom: 12px;
}
.lang-pick button {
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 13px;
  color: var(--ink-2);
}
.lang-pick button.active {
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
}

/* Failed chat turn: one bubble, message and resend action on a single line. */
.retry-box {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
}
.retry-box button {
  color: var(--accent);
  font-weight: 600;
  white-space: nowrap;
}

/* Send button doubles as the stop control while a turn streams. */
.chat-send.stop { background: var(--ink-2); }

/* Waiting hint under the typing dots: fades in only once a wait gets long. */
.chat-hint {
  margin-top: 6px;
  padding-left: 4px;
  font-size: 13px;
  line-height: 1.35;
  color: var(--ink-3);
  opacity: 0;
  transform: translateY(-2px);
  transition: opacity .45s ease, transform .45s ease;
}
.chat-hint.in { opacity: 1; transform: none; }

/* ── Shopping lists ── */
.list-cards { display: flex; flex-direction: column; gap: 8px; }
.list-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  overflow: hidden;
}
.list-card.open { border-color: var(--accent); }
.list-head {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 12px; text-align: left;
}
.list-head .n { flex: 1; min-width: 0; }
.list-head .nm { font-weight: 600; display: block; }
.list-head .sub { color: var(--ink-3); font-size: 13px; }
.list-head .chev { color: var(--ink-3); }
.sub-note { text-align: left; margin: -4px 0 10px; font-size: 13px; }
/* A template has nothing to tick, so its rows sit flush with the label. */
.list-card .items.plain .item { padding-left: 12px; }

.list-card .items { border-top: 1px solid var(--line); }
.list-card .item {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px;
  border-bottom: 1px solid var(--line);
}
.list-card .item:last-child { border-bottom: 0; }
.list-card .item .tick {
  flex: none; width: 24px; height: 24px;
  border: 2px solid var(--line); border-radius: 7px;
  display: grid; place-items: center; color: var(--accent-ink);
}
.list-card .item .tick svg { width: 15px; height: 15px; }
.list-card .item.checked .tick { background: var(--accent); border-color: var(--accent); }
.list-card .item .lbl { flex: 1; min-width: 0; text-align: left; }
.list-card .item.checked .lbl { color: var(--ink-3); text-decoration: line-through; }
.list-card .item .amt { color: var(--ink-2); font-size: 13px; }
.list-card .item .note { display: block; color: var(--ink-3); font-size: 12px; }
.list-card .item .del { color: var(--ink-3); font-size: 20px; line-height: 1; padding: 0 4px; }

.list-actions {
  display: flex; flex-wrap: wrap; gap: 6px;
  padding: 10px 12px; border-top: 1px solid var(--line); background: var(--surface-2);
}
.list-actions button {
  padding: 6px 10px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface);
  font-size: 13px; color: var(--ink-2);
}
.list-actions button.danger { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 35%, var(--line)); }
.arch-toggle { margin-top: 14px; text-align: center; }
.arch-toggle button { color: var(--accent); font-size: 14px; font-weight: 600; }

/* Items inside a chat proposal card. */
/* Own bullet, drawn inside the padding: the default outside marker hangs over
   the card's border. */
.p-items { list-style: none; margin-top: 6px; display: flex; flex-direction: column; gap: 3px; }
.p-items li { position: relative; padding-left: 14px; font-size: 14px; }
.p-items li::before {
  content: '';
  position: absolute;
  left: 3px; top: .62em;
  width: 4px; height: 4px;
  border-radius: 50%;
  background: var(--ink-3);
}
.p-items .amt { color: var(--ink-2); font-size: 13px; }

/* Shopping mode: the list as it is walked in the shop, one tap per item. */
.shopping-sheet .sh-progress { color: var(--ink-3); font-size: 14px; margin-bottom: 10px; }
.shopping-sheet .items { border: 1px solid var(--line); border-radius: var(--radius-card); overflow: hidden; }
.shopping-sheet .item { display: flex; align-items: center; gap: 12px; padding: 12px; border-bottom: 1px solid var(--line); }
.shopping-sheet .item:last-child { border-bottom: 0; }
.shopping-sheet .item .tick {
  flex: none; width: 26px; height: 26px;
  border: 2px solid var(--line); border-radius: 8px;
  display: grid; place-items: center; color: var(--accent-ink);
}
.shopping-sheet .item .tick svg { width: 16px; height: 16px; }
.shopping-sheet .item.checked .tick { background: var(--accent); border-color: var(--accent); }
.shopping-sheet .item.checked .lbl { color: var(--ink-3); text-decoration: line-through; }
.shopping-sheet .item .amt { color: var(--ink-2); font-size: 13px; }
.shopping-sheet .item .note { display: block; color: var(--ink-3); font-size: 12px; }
.shopping-sheet .list-actions { border: 0; background: none; padding: 10px 0 0; }

/* Per-message tools: copy, regenerate, and how long the reply took. Outside
   the bubble so they never disturb the text or the streaming animation. */
.msg-tools {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-top: 3px;
  opacity: .55;
  transition: opacity .2s ease;
}
.chat-msg.user .msg-tools { justify-content: flex-end; }
.msg-tools:hover, .msg-tools:focus-within { opacity: 1; }
.msg-tools button {
  padding: 4px;
  border-radius: 7px;
  color: var(--ink-3);
  display: grid;
  place-items: center;
}
.msg-tools button svg { width: 15px; height: 15px; }
.msg-tools button.ok { color: var(--accent); }
.msg-tools .ms { color: var(--ink-3); font-size: 11px; margin-left: 4px; font-variant-numeric: tabular-nums; }

.shopping-sheet .item .del { color: var(--ink-3); padding: 4px; display: grid; place-items: center; }
.shopping-sheet .item .del svg { width: 16px; height: 16px; }

/* Drag and drop into the chat. The overlay only appears while a drag carrying
   files is over the dialog. */
.chat-drop {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  border: 2px dashed var(--accent);
  border-radius: inherit;
  animation: drop-in .16s ease both;
}
@keyframes drop-in { from { opacity: 0; } to { opacity: 1; } }
.chat-drop-inner {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  color: var(--accent); font-weight: 600; font-size: 15px;
}
.chat-drop-inner svg { width: 28px; height: 28px; }

/* A PDF has no preview: it travels as a named chip, in the composer and in the
   message it was sent with. */
.thumb.doc {
  display: flex; align-items: center; gap: 6px;
  width: auto; padding: 0 10px 0 8px; height: 56px;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: 10px;
  color: var(--ink-2); font-size: 13px;
}
.thumb.doc svg { width: 18px; height: 18px; flex: none; }
.thumb.doc .fname { max-width: 130px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msg-doc {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 10px; border-radius: 9px;
  background: color-mix(in srgb, var(--accent-ink) 14%, transparent);
  font-size: 13px;
}
.msg-doc svg { width: 16px; height: 16px; }

/* Full-size view of a sent attachment. Above the chat sheet, since it is opened
   from inside it. */
.att-view {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(0, 0, 0, .86);
  display: flex;
  flex-direction: column;
  animation: att-in .16s ease both;
}
@keyframes att-in { from { opacity: 0; } to { opacity: 1; } }
.att-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  padding-top: max(12px, env(safe-area-inset-top));
  color: #fff;
}
.att-name { flex: 1; min-width: 0; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.att-open { color: #fff; font-size: 13px; text-decoration: underline; opacity: .85; }
.att-close { color: #fff; font-size: 20px; line-height: 1; padding: 4px 8px; }
.att-img {
  flex: 1;
  min-height: 0;
  object-fit: contain;
  padding: 0 8px 8px;
}
.att-pdf {
  flex: 1;
  min-height: 0;
  width: 100%;
  border: 0;
  background: #fff;
}

/* Batched proposals: one list, one confirm. Each row can be dropped first. */
.prop-card.batch .p-head { display: flex; align-items: center; gap: 8px; }
.prop-card.batch .p-count {
  margin-left: auto;
  min-width: 22px; height: 22px; padding: 0 6px;
  border-radius: 999px; background: var(--surface-2);
  color: var(--ink-2); font-size: 12px; font-weight: 700;
  display: grid; place-items: center;
}
.batch-rows { display: flex; flex-direction: column; margin: 8px 0 12px; }
.batch-rows li {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 0; border-bottom: 1px solid var(--line);
}
.batch-rows li:last-child { border-bottom: 0; }
.batch-rows .n { flex: 1; min-width: 0; }
.batch-rows .t { display: block; font-weight: 600; font-size: 14px; }
.batch-rows .m { display: block; color: var(--ink-2); font-size: 13px; }
.batch-rows .drop { color: var(--ink-3); font-size: 16px; line-height: 1; padding: 2px 4px; }
.prop-card.batch.saved .batch-rows .t { font-weight: 500; color: var(--ink-2); }
.batch-rows .k {
  display: block;
  font-size: 11px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink-3);
}

/* Recipes on the products tab. */
.sec-head { display: flex; align-items: center; justify-content: space-between; }
.sec-head .section { margin-bottom: 0; }
.sec-add { width: 28px; height: 28px; border-radius: 50%; border: 1px solid var(--line); color: var(--ink-2); font-size: 18px; line-height: 1; }
.recipe-list { margin-top: 8px; }
.prod-list .mini-log { padding: 7px; border: 1px solid var(--accent); color: var(--accent); border-radius: var(--radius); flex: none; }
.prod-list .mini-log svg { width: 18px; height: 18px; display: block; }
.rc-row { align-items: center; margin-bottom: 8px; }
.rc-pick { display: flex; gap: 8px; position: relative; }
.rc-pick input { flex: 1; min-width: 0; }
.rc-results {
  position: absolute;
  top: 100%; left: 0; right: 0;
  margin-top: 4px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  max-height: 220px;
  overflow-y: auto;
  z-index: 5;
}
.rc-results:empty { display: none; }
.rc-results .rc-hit { display: block; width: 100%; text-align: left; padding: 11px 12px; }
.rc-results .rc-hit:active { background: var(--surface-2); }
.rc-results .empty-hint { padding: 11px 12px; margin: 0; }
.rc-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 12px;
}
.rc-card .n { flex: 1; min-width: 0; }
.rc-card .n .nm, .rc-card .n .sub {
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rc-card .n .nm { font-size: 14px; }
.rc-card .n .sub { color: var(--ink-3); font-size: 12px; }
.rc-card .rc-amt { flex: 0 0 auto; width: 130px; }
.rc-amt { display: flex; align-items: center; gap: 6px; }
.rc-amt input { flex: 1; min-width: 0; }
.rc-amt .unit { color: var(--ink-3); font-size: 13px; }
.rc-amt .drop { color: var(--ink-3); padding: 4px; }

