/* Ladle design system: warm editorial. Cream paper, deep olive, terracotta, charcoal ink.
   Display: Instrument Serif (self-hosted). Body: system stack for native feel. */
@font-face { font-family: 'Instrument Serif'; font-style: normal; font-weight: 400; font-display: swap; src: url('fonts/instrument-serif-latin.woff2') format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Instrument Serif'; font-style: italic; font-weight: 400; font-display: swap; src: url('fonts/instrument-serif-italic-latin.woff2') format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }

:root {
  --paper: oklch(96.5% 0.012 85);
  --paper-2: oklch(93.5% 0.016 85);
  --surface: oklch(99% 0.006 85);
  --ink: oklch(25% 0.01 70);
  --muted: oklch(50% 0.015 70);
  --line: oklch(88% 0.014 80);
  --olive: oklch(42% 0.07 135);
  --olive-ink: oklch(98% 0.01 90);
  --olive-soft: oklch(92% 0.03 135);
  --olive-deep: oklch(32% 0.06 135);
  --terracotta: oklch(50% 0.13 40);
  --terracotta-soft: oklch(94% 0.035 45);
  --mustard: oklch(62% 0.13 80);
  --danger: oklch(52% 0.18 27);
  --danger-soft: oklch(94% 0.04 27);
  --shadow: 0 1px 2px oklch(25% 0.01 70 / .06), 0 6px 20px oklch(25% 0.01 70 / .08);
  --shadow-lift: 0 12px 40px oklch(25% 0.01 70 / .18);
  --r-sm: 10px; --r: 14px; --r-lg: 20px; --r-xl: 28px;
  --display: 'Instrument Serif', 'Iowan Old Style', 'Palatino Linotype', Georgia, serif;
  --body: -apple-system, BlinkMacSystemFont, 'SF Pro Text', system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --tab-h: 64px;
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-t: env(safe-area-inset-top, 0px);
  --header-blur: oklch(96.5% 0.012 85 / .84); --bar-bg: oklch(96.5% 0.012 85);
  color-scheme: light;
}
[data-theme="dark"] {
  --paper: oklch(19% 0.012 70);
  --paper-2: oklch(23% 0.014 70);
  --surface: oklch(24% 0.013 70);
  --ink: oklch(94% 0.01 85);
  --muted: oklch(68% 0.015 80);
  --line: oklch(32% 0.014 70);
  --olive: oklch(70% 0.09 135);
  --olive-ink: oklch(18% 0.03 135);
  --olive-soft: oklch(30% 0.04 135);
  --olive-deep: oklch(78% 0.08 135);
  --terracotta: oklch(76% 0.12 42);
  --terracotta-soft: oklch(30% 0.055 42);
  --mustard: oklch(80% 0.12 85);
  --danger: oklch(68% 0.16 27);
  --danger-soft: oklch(30% 0.07 27);
  --shadow: 0 1px 2px oklch(0% 0 0 / .4), 0 6px 20px oklch(0% 0 0 / .35);
  --shadow-lift: 0 12px 40px oklch(0% 0 0 / .55);
  --header-blur: oklch(19% 0.012 70 / .82); --bar-bg: oklch(19% 0.012 70);
  color-scheme: dark;
}
@media (prefers-color-scheme: dark) {
  [data-theme="system"] {
    --paper: oklch(19% 0.012 70); --paper-2: oklch(23% 0.014 70); --surface: oklch(24% 0.013 70); --ink: oklch(94% 0.01 85); --muted: oklch(68% 0.015 80); --line: oklch(32% 0.014 70);
    --olive: oklch(70% 0.09 135); --olive-ink: oklch(18% 0.03 135); --olive-soft: oklch(30% 0.04 135); --olive-deep: oklch(78% 0.08 135);
    --terracotta: oklch(76% 0.12 42); --terracotta-soft: oklch(30% 0.055 42); --mustard: oklch(80% 0.12 85); --danger: oklch(68% 0.16 27); --danger-soft: oklch(30% 0.07 27);
    --shadow: 0 1px 2px oklch(0% 0 0 / .4), 0 6px 20px oklch(0% 0 0 / .35); --shadow-lift: 0 12px 40px oklch(0% 0 0 / .55); --header-blur: oklch(19% 0.012 70 / .82); --bar-bg: oklch(19% 0.012 70); color-scheme: dark;
  }
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }
html { -webkit-text-size-adjust: 100%; height: 100%; overscroll-behavior: none; }
body { margin: 0; min-height: 100%; font-family: var(--body); font-size: 16px; line-height: 1.45; background: var(--paper); color: var(--ink); -webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; overscroll-behavior-y: none; }
img { max-width: 100%; display: block; }
button, input, select, textarea { font: inherit; color: inherit; }
/* 16px is the floor for anything focusable on iOS: below it Safari zooms the page on
   focus and never zooms back, which shifts every fixed element off the screen edge. */
input, select, textarea { font-size: max(16px, 1em); }
button { cursor: pointer; }
a { color: var(--olive); }
h1, h2, h3 { margin: 0; font-weight: 400; }
.display { font-family: var(--display); font-weight: 400; letter-spacing: -0.01em; line-height: 1.1; }
.muted { color: var(--muted); }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
svg.i { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; flex: none; }
svg.i.sm { width: 18px; height: 18px; }

/* App frame */
/* App-shell scrolling: the document itself never scrolls, the view does. Scrolling the
   document made Safari collapse its toolbar, and the strip that uncovered sat ABOVE the
   sticky header, exposing whatever content was behind it (the previous screen's action
   row appearing over the title). With the shell pinned to the viewport there is no strip
   to uncover, and the header stays put without needing sticky at all. */
.app { height: 100vh; height: 100dvh; overflow: hidden; display: flex; flex-direction: column; }
/* An opaque band across the status-bar strip. The page runs under the status bar
   (viewport-fit=cover) and iOS was leaving stale pixels there from the position the
   sticky header scrolled away from, so the previous screen's buttons hung over the top
   of the app. Painting the strip removes the artifact whatever Safari does underneath. */
/* The blurred bars get their own compositing layer. Without it iOS Safari leaves stale
   pixels from the scrolled content painted above the sticky header (the row of action
   buttons ghosting over the title after a scroll). */
.topbar { flex: none; z-index: 20; padding: calc(8px + var(--safe-t)) 16px 8px; background: var(--bar-bg); border-bottom: 1px solid transparent; display: flex; align-items: center; gap: 10px; min-height: calc(56px + var(--safe-t)); }
/* Six controls fit a 390px header only because the icon buttons were being squeezed
   under the 44px minimum touch target. The title gives up the room instead. */
.topbar .display { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.topbar .icon-btn, .topbar .btn { flex: 0 0 auto; }
@media (max-width: 380px) { .topbar { gap: 6px; padding-left: 12px; padding-right: 12px; } }
.topbar.scrolled { border-bottom-color: var(--line); }
.topbar h1 { font-size: 30px; flex: 1; }
.topbar .title-sm { font-size: 17px; font-weight: 600; font-family: var(--body); flex: 1; text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.view { flex: 1 1 auto; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; padding: 8px 16px calc(var(--tab-h) + 28px + var(--safe-b)); max-width: 960px; width: 100%; margin: 0 auto; }
.view.wide { max-width: 1100px; }

/* Tab bar + FAB */
.tabbar { transform: translateZ(0); position: fixed; left: 0; right: 0; bottom: 0; z-index: 30; display: grid; grid-template-columns: 1fr 1fr 76px 1fr 1fr; align-items: stretch; height: calc(var(--tab-h) + var(--safe-b)); padding-bottom: var(--safe-b); background: var(--bar-bg); border-top: 1px solid var(--line); }
.tabbar a { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; text-decoration: none; color: var(--muted); font-size: 11px; font-weight: 500; letter-spacing: .01em; min-height: 44px; }
.tabbar a[aria-current="page"] { color: var(--olive); }
.tabbar a svg.i { width: 24px; height: 24px; }
.fab-wrap { display: flex; align-items: center; justify-content: center; }
.fab { width: 56px; height: 56px; border-radius: 50%; border: 0; background: var(--olive); color: var(--olive-ink); box-shadow: 0 8px 24px oklch(42% 0.07 135 / .45); display: grid; place-items: center; transform: translateY(-14px); transition: transform .2s ease, box-shadow .2s ease; }
.fab:active { transform: translateY(-14px) scale(.94); }
.fab svg.i { width: 28px; height: 28px; stroke-width: 2.2; }

/* Buttons, inputs, chips */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 44px; padding: 10px 16px; border-radius: var(--r); border: 1px solid var(--line); background: var(--surface); color: var(--ink); font-weight: 600; font-size: 15px; text-decoration: none; transition: transform .12s ease, background .15s ease; }
.btn:active { transform: scale(.97); }
.btn.primary { background: var(--olive); border-color: var(--olive); color: var(--olive-ink); }
.btn.accent { background: var(--terracotta); border-color: var(--terracotta); color: #fff; }
.btn.ghost { background: transparent; border-color: transparent; color: var(--olive); }
.btn.danger { color: var(--danger); border-color: var(--danger-soft); background: var(--danger-soft); }
.btn.block { width: 100%; }
.btn.sm { min-height: 36px; padding: 6px 12px; font-size: 14px; border-radius: var(--r-sm); }
.btn:disabled { opacity: .55; cursor: default; }
.icon-btn { width: 44px; height: 44px; border-radius: 50%; border: 0; background: transparent; color: var(--ink); display: grid; place-items: center; }
.icon-btn:active { background: var(--paper-2); }
.icon-btn.on { color: var(--terracotta); }
.input, textarea.input, select.input { width: 100%; min-height: 46px; padding: 10px 14px; border-radius: var(--r); border: 1px solid var(--line); background: var(--surface); outline: none; }
.input:focus { border-color: var(--olive); box-shadow: 0 0 0 3px var(--olive-soft); }
textarea.input { min-height: 120px; line-height: 1.5; resize: vertical; }
label.field { display: block; margin: 14px 0 0; font-size: 13px; font-weight: 600; color: var(--muted); }
/* iOS Safari zooms the whole page when a focused form control is smaller than 16px, and
   it does not zoom back out. Everything fixed (tab bar, bulk bar) then hangs off the
   right edge and taps land in the wrong place. Inputs must never inherit a smaller size. */
.input, textarea.input, select.input, .search input { font-size: 16px; }
label.field .input { margin-top: 6px; font-weight: 400; color: var(--ink); }
.row { display: flex; gap: 10px; align-items: center; }
.row.wrap { flex-wrap: wrap; }
.grow { flex: 1; min-width: 0; }
.chips { display: flex; gap: 8px; overflow-x: auto; padding: 4px 0 10px; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.chips::-webkit-scrollbar { display: none; }
.chip { flex: none; box-sizing: border-box; font: inherit; text-decoration: none; cursor: pointer; appearance: none; display: inline-flex; align-items: center; gap: 6px; min-height: 36px; padding: 6px 14px; border-radius: 999px; border: 1px solid var(--line); background: var(--surface); font-size: 14px; font-weight: 500; color: var(--ink); white-space: nowrap; }
.chip.on { background: var(--olive); color: var(--olive-ink); border-color: var(--olive); }
.chip.soft { background: var(--olive-soft); border-color: transparent; color: var(--olive-deep); }
.list .li .tick-right { margin-left: auto; color: var(--olive); display: grid; place-items: center; }
.pill { display: inline-flex; align-items: center; gap: 5px; padding: 3px 10px; border-radius: 999px; font-size: 12.5px; font-weight: 600; background: var(--paper-2); color: var(--muted); }
.pill.terra { background: var(--terracotta-soft); color: var(--terracotta); }
.pill.olive { background: var(--olive-soft); color: var(--olive-deep); }

/* Search */
.search { display: flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 12px; border-radius: var(--r); background: var(--surface); border: 1px solid var(--line); margin: 6px 0 10px; }
.search input { flex: 1; border: 0; background: transparent; outline: none; min-height: 42px; }

/* Recipe cards */
.grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
@media (min-width: 640px) { .grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 900px) { .grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.card { position: relative; display: block; text-decoration: none; color: inherit; border-radius: var(--r-lg); background: var(--surface); box-shadow: var(--shadow); overflow: hidden; transition: transform .15s ease; }
.card:active { transform: scale(.98); }
/* A card is a link, so iOS answers a long press with its own preview and Open / Copy Link
   menu before our handler ever runs. Suppressing the callout is the only thing that stops
   it; preventDefault on contextmenu does not. */
/* In-app links must not offer the browser's own long-press preview: it competes with the
   app's own press handling and with the tap that follows. External links keep it, because
   there the preview is genuinely useful. */
.card, .card *, .tabbar a, .chip, .act, .item, .fab { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
/* Select mode: the whole card is a checkbox, so the picked state has to read at a glance
   without relying on the tick alone (it sits over a photo of unknown brightness). */
.grid.selecting .card { user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; }
.grid.selecting .card .thumb { opacity: .55; }
.card.picked { outline: 3px solid var(--olive); outline-offset: -3px; }
.card.picked .thumb { opacity: 1; }
.card .tick { position: absolute; top: 8px; right: 8px; width: 28px; height: 28px; border-radius: 50%; display: grid; place-items: center; background: var(--surface); border: 2px solid var(--line); color: var(--olive-ink); }
.card.picked .tick { background: var(--olive); border-color: var(--olive); color: var(--olive-ink); }
.card .tick .i { width: 17px; height: 17px; }
.bulkbar { position: fixed; left: 50%; transform: translateX(-50%) translateZ(0); bottom: calc(var(--tab-h) + 16px + var(--safe-b)); z-index: 40; display: flex; gap: 6px; padding: 8px; border-radius: 999px; background: var(--bar-bg); border: 1px solid var(--line); box-shadow: var(--shadow-lift); width: max-content; max-width: calc(100vw - 24px); }
/* The buttons have to be allowed to shrink. At their natural width the third one ran past
   the bar and off the screen, which is exactly where Delete was. */
.bulkbar .btn { flex: 0 1 auto; min-width: 0; white-space: nowrap; padding: 8px 12px; font-size: 14px; overflow: hidden; text-overflow: ellipsis; }
.bulkbar .btn[disabled] { opacity: .45; pointer-events: none; }
.card .thumb { aspect-ratio: 4 / 3; width: 100%; object-fit: cover; background: var(--paper-2); }
.card .body { padding: 10px 12px 12px; }
.card h3 { font-size: 15px; font-weight: 600; line-height: 1.25; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.card .meta { margin-top: 4px; font-size: 12.5px; color: var(--muted); display: flex; gap: 8px; }
.card .fav { position: absolute; top: 8px; right: 8px; width: 36px; height: 36px; border-radius: 50%; border: 0; background: var(--surface); box-shadow: var(--shadow); color: var(--muted); display: grid; place-items: center; }
.card .fav.on { color: var(--terracotta); }
.card .fav svg.i { width: 18px; height: 18px; }

/* Empty states */
.empty { text-align: center; padding: 24px 8px 16px; }
.empty img { width: min(82%, 360px); margin: 0 auto 14px; }
.empty h2 { font-size: 28px; margin-bottom: 6px; }
.empty p { color: var(--muted); margin: 0 auto 16px; max-width: 36ch; }

/* Detail */
.hero { width: calc(100% + 32px); margin: -8px -16px 0; aspect-ratio: 4 / 3; object-fit: cover; max-height: 46vh; background: var(--paper-2); }
@media (min-width: 720px) { .hero { width: 100%; margin: 8px 0 0; border-radius: var(--r-xl); aspect-ratio: 16 / 9; } }
.detail h1 { font-size: 34px; margin: 16px 0 4px; }
.detail .source { font-size: 14px; color: var(--muted); }
.meta-row { display: flex; gap: 8px; flex-wrap: wrap; margin: 12px 0; }
.actions { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin: 14px 0 6px; }
.actions .act { position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; padding: 10px 4px; border-radius: var(--r); border: 1px solid var(--line); background: var(--surface); font-size: 12px; font-weight: 600; color: var(--ink); min-height: 64px; text-decoration: none; }
.actions .act .dot { position: absolute; top: 6px; right: 8px; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px; background: var(--terracotta); color: var(--paper); font-size: 11px; font-weight: 700; display: grid; place-items: center; }
.actions .act.primary { background: var(--olive); border-color: var(--olive); color: var(--olive-ink); }
/* home */
.hello { margin: 2px 0 14px; }
.hello h2 { font-size: 30px; line-height: 1.1; }
.hello p { margin-top: 2px; font-size: 14px; }
.tonight { display: flex; align-items: center; gap: 12px; padding: 12px; border-radius: var(--r-lg); background: var(--surface); box-shadow: var(--shadow); text-decoration: none; color: inherit; transition: transform .15s ease; }
.tonight:active { transform: scale(.99); }
.tonight img { width: 64px; height: 64px; border-radius: var(--r); object-fit: cover; flex: none; background: var(--paper-2); }
.tonight .t { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.tonight .t small { font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; color: var(--terracotta); }
.tonight .t b { font-size: 17px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tonight .t span { font-size: 13px; }
.tonight > svg.i { flex: none; color: var(--muted); }
.tonight.none { border: 1px dashed var(--line); box-shadow: none; background: transparent; }
.tonight.none .t small { color: var(--muted); }
/* horizontal recipe rail: cards keep their shape, the row scrolls */
.rail { display: grid; grid-auto-flow: column; grid-auto-columns: 62%; gap: 12px; overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x proximity; scroll-padding: 0 16px; padding: 0 16px 4px; margin: 0 -16px; scrollbar-width: none; }
.rail::-webkit-scrollbar { display: none; }
.rail > .card { scroll-snap-align: start; }
@media (min-width: 560px) { .rail { grid-auto-columns: 232px; } }

.section { margin-top: 22px; }
.section h2 { font-size: 24px; margin-bottom: 8px; }
.section h3.group { font-size: 14px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin: 12px 0 4px; }
.ing { display: flex; align-items: flex-start; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--line); min-height: 44px; }
.ing .box { width: 22px; height: 22px; border-radius: 7px; border: 1.5px solid var(--line); background: var(--surface); flex: none; margin-top: 2px; display: grid; place-items: center; }
.ing.done .box { background: var(--olive); border-color: var(--olive); color: var(--olive-ink); }
.ing.done .txt { color: var(--muted); text-decoration: line-through; }
.ing .qty { font-weight: 700; color: var(--olive-deep); }
.step { display: grid; grid-template-columns: 34px 1fr; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--line); }
.step .n { width: 30px; height: 30px; border-radius: 50%; background: var(--olive-soft); color: var(--olive-deep); font-weight: 700; display: grid; place-items: center; font-size: 14px; }
.step .timer { margin-top: 6px; }
.stepper { display: inline-flex; align-items: center; border: 1px solid var(--line); border-radius: 999px; background: var(--surface); overflow: hidden; }
.stepper button { width: 44px; height: 40px; border: 0; background: transparent; font-size: 20px; }
.stepper span { min-width: 70px; text-align: center; font-weight: 700; }
.seg { display: inline-flex; border: 1px solid var(--line); border-radius: 999px; padding: 3px; background: var(--paper-2); }
.seg button { border: 0; background: transparent; padding: 6px 14px; border-radius: 999px; font-weight: 600; font-size: 13px; color: var(--muted); min-height: 32px; }
.seg button.on { background: var(--surface); color: var(--ink); box-shadow: var(--shadow); }
.stars { display: inline-flex; gap: 2px; }
.stars button { border: 0; background: transparent; padding: 4px; color: var(--muted); opacity: .55; }
.stars button.on { color: var(--mustard); opacity: 1; }
.notes { white-space: pre-wrap; }
.cooked { margin-top: 18px; }

/* Sheets & modals */
.scrim { position: fixed; inset: 0; z-index: 40; background: oklch(20% 0.01 70 / .45); opacity: 0; transition: opacity .2s ease; }
.scrim.in { opacity: 1; }
.sheet { position: fixed; left: 0; right: 0; bottom: 0; z-index: 41; background: var(--surface); border-radius: var(--r-xl) var(--r-xl) 0 0; box-shadow: var(--shadow-lift); max-height: 92vh; max-height: 92dvh; display: flex; flex-direction: column; transform: translateY(100%); transition: transform .28s cubic-bezier(.2,.8,.2,1); padding-bottom: var(--safe-b); }
.sheet.in { transform: translateY(0); }
.sheet .grab { width: 40px; height: 5px; border-radius: 3px; background: var(--line); margin: 10px auto 4px; }
.sheet .head { display: flex; align-items: center; gap: 8px; padding: 6px 16px 8px; }
.sheet .head h2 { flex: 1; font-size: 24px; }
.sheet .content { overflow: auto; padding: 4px 16px 20px; -webkit-overflow-scrolling: touch; }
@media (min-width: 720px) { .sheet { left: 50%; right: auto; width: 560px; transform: translate(-50%, 100%); bottom: 24px; border-radius: var(--r-xl); } .sheet.in { transform: translate(-50%, 0); } }
/* The share message people will actually receive, shown before they pick a target. */
.blurb-preview { margin: 0 0 12px; padding: 12px 14px; border-radius: var(--r); background: var(--olive-soft); color: var(--olive-deep); font-size: 14px; line-height: 1.4; }
.menu { display: grid; gap: 8px; }
.menu .item { display: flex; align-items: center; gap: 14px; padding: 14px; border-radius: var(--r-lg); border: 1px solid var(--line); background: var(--paper); text-align: left; min-height: 64px; }
.menu .item .ic { width: 42px; height: 42px; border-radius: 12px; background: var(--olive-soft); color: var(--olive-deep); display: grid; place-items: center; flex: none; }
.menu .item.accent .ic { background: var(--terracotta-soft); color: var(--terracotta); }
.menu .item b { display: block; font-size: 15px; }
.menu .item small { color: var(--muted); font-size: 13px; }
.toast { position: fixed; left: 50%; bottom: calc(var(--tab-h) + 24px + var(--safe-b)); transform: translate(-50%, 20px); z-index: 60; background: var(--ink); color: var(--paper); padding: 10px 16px; border-radius: 999px; font-size: 14px; font-weight: 500; box-shadow: var(--shadow-lift); opacity: 0; transition: opacity .2s, transform .2s; pointer-events: none; max-width: calc(100% - 32px); text-align: center; }
.toast.in { opacity: 1; transform: translate(-50%, 0); }
.toast.has-action { display: flex; align-items: center; gap: 12px; padding: 8px 8px 8px 16px; pointer-events: none; }
.toast-act { pointer-events: auto; background: var(--paper); color: var(--ink); border: 0; border-radius: 999px; padding: 7px 14px; font: inherit; font-weight: 600; cursor: pointer; }
#confetti { position: fixed; inset: 0; pointer-events: none; z-index: 70; }
.spinner { width: 22px; height: 22px; border-radius: 50%; border: 2.5px solid var(--olive-soft); border-top-color: var(--olive); animation: spin .8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.progress { display: flex; flex-direction: column; align-items: center; gap: 12px; padding: 28px 8px; text-align: center; color: var(--muted); }
.notice { padding: 12px 14px; border-radius: var(--r); background: var(--terracotta-soft); color: var(--ink); font-size: 14px; }
.notice code { font-size: 12.5px; background: oklch(99% 0.006 85 / .6); padding: 2px 6px; border-radius: 6px; }
.notice.ok { background: var(--olive-soft); }

/* Lists / settings */
.list { background: var(--surface); border-radius: var(--r-lg); box-shadow: var(--shadow); overflow: hidden; }
.list .li { width: 100%; box-sizing: border-box; text-align: left; background: none; border: 0; border-radius: 0; font: inherit; display: flex; align-items: center; gap: 12px; padding: 12px 14px; min-height: 52px; border-bottom: 1px solid var(--line); text-decoration: none; color: inherit; }
.list .li:last-child { border-bottom: 0; }
.list .li .t { flex: 1; min-width: 0; }
.list .li .t b { display: block; font-weight: 600; font-size: 15px; }
.list .li .t small { color: var(--muted); font-size: 13px; }
.switch { width: 46px; height: 28px; border-radius: 999px; border: 0; background: var(--line); position: relative; flex: none; }
.switch::after { content: ''; position: absolute; top: 3px; left: 3px; width: 22px; height: 22px; border-radius: 50%; background: #fff; transition: transform .18s; box-shadow: 0 1px 3px rgba(0,0,0,.3); }
.switch.on { background: var(--olive); }
.switch.on::after { transform: translateX(18px); }
h2.sec { font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); margin: 20px 2px 8px; }

/* Grocery */
.aisle h3 { font-family: var(--display); font-size: 22px; margin: 18px 0 4px; display: flex; align-items: baseline; gap: 8px; }
.aisle h3 small { font-family: var(--body); font-size: 13px; color: var(--muted); }
.gi { display: flex; align-items: center; gap: 12px; padding: 8px 0; border-bottom: 1px solid var(--line); min-height: 48px; }
.gi .box { width: 24px; height: 24px; border-radius: 8px; border: 1.5px solid var(--line); flex: none; display: grid; place-items: center; background: var(--surface); }
.gi.done .box { background: var(--olive); border-color: var(--olive); color: var(--olive-ink); }
.gi.done .name { color: var(--muted); text-decoration: line-through; }
.gi .name { flex: 1; min-width: 0; }
.gi .from { font-size: 12px; color: var(--muted); display: block; }
.gi .qty { color: var(--muted); font-size: 14px; }
.quickadd { display: flex; gap: 8px; margin: 6px 0 4px; }

/* Planner */
.week-nav { display: flex; align-items: center; gap: 8px; margin: 4px 0 10px; }
.week-nav b { flex: 1; text-align: center; font-family: var(--display); font-size: 22px; font-weight: 400; }
.day { background: var(--surface); border-radius: var(--r-lg); box-shadow: var(--shadow); margin-bottom: 12px; overflow: hidden; }
.day .dh { display: flex; align-items: baseline; gap: 8px; padding: 10px 14px 6px; }
.day .dh b { font-size: 16px; }
.day .dh.today b { color: var(--terracotta); }
.day .dh small { color: var(--muted); }
.slot { display: flex; align-items: center; gap: 10px; padding: 8px 14px; border-top: 1px solid var(--line); min-height: 48px; }
.slot .sl { width: 72px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); flex: none; }
.slot .what { flex: 1; min-width: 0; font-size: 15px; }
.slot .what.empty { color: var(--muted); font-weight: 400; }
.slot .what a { color: inherit; text-decoration: none; font-weight: 600; }
.slot img { width: 40px; height: 40px; border-radius: 10px; object-fit: cover; flex: none; }

/* Cook mode */
.cook { position: fixed; inset: 0; z-index: 50; background: var(--paper); display: flex; flex-direction: column; padding: calc(10px + var(--safe-t)) 16px calc(12px + var(--safe-b)); }
.cook .ch { display: flex; align-items: center; gap: 8px; }
.cook .ch .title { flex: 1; text-align: center; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cook .bar { height: 4px; background: var(--line); border-radius: 2px; margin: 10px 0; overflow: hidden; }
.cook .bar i { display: block; height: 100%; background: var(--olive); transition: width .25s ease; }
.cook .stage { flex: 1; display: flex; flex-direction: column; justify-content: center; overflow: auto; }
.cook .stage .n { font-size: 14px; font-weight: 700; color: var(--terracotta); letter-spacing: .08em; text-transform: uppercase; }
.cook .stage p { font-family: var(--display); font-size: clamp(26px, 5.2vw, 38px); line-height: 1.25; margin: 10px 0 0; }
.cook .timers { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px; }
.cook .tm { display: inline-flex; align-items: center; gap: 8px; padding: 8px 14px; border-radius: 999px; background: var(--surface); border: 1px solid var(--line); font-variant-numeric: tabular-nums; font-weight: 700; }
.cook .tm.ring { background: var(--terracotta); color: #fff; border-color: var(--terracotta); animation: pulse 1s ease-in-out infinite; }
@keyframes pulse { 50% { transform: scale(1.04); } }
.cook .nav { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 12px; }
.cook .nav .btn { min-height: 56px; font-size: 17px; }
.cook .ingdrawer { max-height: 36vh; overflow: auto; border-top: 1px solid var(--line); margin-top: 10px; padding-top: 6px; }

/* Share page */
body.share { padding: 24px 16px; }
.share-main { max-width: 720px; margin: 0 auto; }
.share-hero { width: 100%; aspect-ratio: 16/9; object-fit: cover; border-radius: var(--r-xl); }
.share-main h1 { font-size: 38px; margin: 16px 0 4px; }
.share-main h2 { font-size: 26px; margin: 22px 0 8px; }
.share-main li { padding: 4px 0; }

/* Onboarding */
.welcome { text-align: center; padding: 10px 0 20px; }
.welcome img { width: min(100%, 440px); height: 36vh; max-height: 340px; object-fit: cover; object-position: 50% 78%; border-radius: var(--r-xl); margin: 0 auto 10px; }
.welcome h1 { font-size: 40px; margin: 6px 0 8px; }
.welcome p { color: var(--muted); max-width: 38ch; margin: 0 auto 18px; }

@media (prefers-reduced-motion: reduce) { * { transition: none !important; animation: none !important; } }


/* ---------- print ---------- */
/* Printing renders the live screen, so the servings stepper and the US/metric toggle are
   already applied to what lands on paper. Everything you cannot use on paper is removed,
   and nothing is allowed to split an ingredient or a step across a page break. */
.print-only { display: none; }
@media print {
  @page { margin: 14mm; }
  :root { --paper: #fff; --ink: #000; --muted: #333; --line: #bbb; }
  html, body { background: #fff !important; color: #000 !important; }
  .topbar, .tabbar, .bulkbar, .toast, .scrim, .sheet, #sheet-root, #confetti, .cook,
  .no-print, .fav, .tick, .chips, .search, .empty button, .rail { display: none !important; }
  .print-only { display: block !important; }
  #view, .view, main { padding: 0 !important; margin: 0 !important; max-width: none !important; }
  .detail .hero { max-height: 45mm; width: auto; max-width: 60%; border-radius: 0; }
  .detail h1.display { font-size: 26pt; margin: 6pt 0 2pt; }
  .section h2.display { font-size: 15pt; margin: 12pt 0 4pt; }
  .ing, .step { break-inside: avoid; page-break-inside: avoid; }
  h1, h2, h3 { break-after: avoid; page-break-after: avoid; }
  .section { break-inside: auto; }
  /* An empty box is useful on paper, a filled one is not: the check state belongs to the
     screen you were cooking from, not to the copy someone else is about to follow. */
  .ing .box { border: 1pt solid #000 !important; background: none !important; color: transparent !important; }
  .ing.done { opacity: 1 !important; }
  .ing.done .txt { text-decoration: none !important; }
  .pill { border: 1pt solid #666 !important; background: none !important; color: #000 !important; }
  a { color: #000 !important; text-decoration: none !important; }
  .print-footer { margin-top: 14pt; padding-top: 6pt; border-top: 1pt solid #bbb; font-size: 9pt; color: #444; }
  .step .n { border: 1pt solid #000 !important; background: none !important; color: #000 !important; }
  /* Grocery list: a tick box per line, aisles kept with their items. */
  .gi, .aisle { break-inside: avoid; page-break-inside: avoid; }
  .gi { min-height: 0 !important; padding: 3pt 0 !important; border-bottom: 1pt solid #ddd !important; }
  .gi .box { border: 1pt solid #000 !important; background: none !important; color: transparent !important; }
  .gi.done { opacity: 1 !important; }
  .gi.done .name { color: #000 !important; text-decoration: none !important; }
  .quickadd { display: none !important; }
  /* Week plan: the whole week on one sheet, no day split across pages. */
  .week-nav { justify-content: flex-start !important; }
  .week-nav .icon-btn { display: none !important; }
  .day { break-inside: avoid; page-break-inside: avoid; margin-bottom: 6pt; }
  .slot.empty, .day .slot:empty { display: none !important; }
}

/* A meal in a shared plan carries more than a title now: who wants it, who is making it, how
   far along they are, and who is bringing what. Boxing each one keeps that legible when four
   of them sit in the same slot. */
.meal { background: var(--paper-2); border-radius: var(--r); padding: 10px 12px; margin-bottom: 8px; }
.meal:last-of-type { margin-bottom: 6px; }
.meal .li { border: 0; }

/* Magic Recipes. The one screen in the app that should feel like it is doing something for you,
   so the hero gets its own surface rather than sitting in the ordinary card rhythm. */
.magic-hero {
  background: linear-gradient(160deg, var(--olive-soft) 0%, var(--surface) 62%);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 18px 16px;
  box-shadow: var(--shadow);
}
.mg-spark { display: inline-flex; color: var(--olive-deep); }
.mg-spark .i { width: 22px; height: 22px; }
/* Chips wrap here rather than scrolling: the whole point is seeing everything you picked. */
.mg-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.mg-chips:empty { display: none; }

.mg-idea {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  padding: 14px 16px; margin-bottom: 10px;
  animation: mg-rise .32s cubic-bezier(.2,.7,.3,1) backwards;
  animation-delay: calc(var(--n, 0) * 70ms);
}
@keyframes mg-rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

.mg-lane { margin-top: 14px; }
.mg-lane-h { font-size: 13px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin: 0 0 6px; }
.mg-thumb { width: 56px; height: 42px; object-fit: cover; border-radius: 8px; flex: none; }
.mg-hit .t { min-width: 0; }
.mg-hit small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The honest-labelling banner: a recipe whose amounts were guessed says so. Caution rather than
   error, so it borrows the mustard accent as a rule down the side rather than a full alarm. */
.notice.warn { background: var(--paper-2); border-left: 3px solid var(--mustard); color: var(--ink); }

/* Magic Mixer in the hero slot. When a meal is planned that card keeps the slot and the Mixer
   sits under it as a strip; when nothing is planned the Mixer IS the hero, because "Nothing
   planned yet" was a card that stated a problem and handed you a blank grid. */
.tonight.mixer {
  background: linear-gradient(145deg, var(--olive-soft) 0%, var(--surface) 72%);
  border: 1px solid var(--line);
  align-items: center;
}
.tonight.mixer .t { gap: 3px; }
/* The hero title is a QUESTION here, not a recipe name. The base .tonight rule clamps it to one
   line with an ellipsis, which is right for "Healthy Chocolate Chip Blondies" and wrong for
   "What can I make with what I have?" -- that arrived on screen as "...with what I ha...".
   It wraps at whole words instead. No line-clamp: both headlines are strings we control and
   neither reaches three lines, and -webkit-line-clamp is inert here anyway because a flex child
   blockifies -webkit-box away. Dead CSS that looks protective is worse than none. */
.tonight.mixer .t b {
  font-size: 19px; line-height: 1.22;
  white-space: normal; text-wrap: balance;
}
/* The ingredient line underneath is secondary and stays on one line, so a long pantry name
   cannot push the card taller than the thing it is competing with for attention. */
.tonight.mixer .t > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tonight.mixer .mg-spark { flex: none; margin-left: 8px; }
.tonight.mixer .mg-spark .i { width: 26px; height: 26px; }

.mixer-strip {
  display: flex; align-items: center; gap: 10px;
  margin: -4px 0 14px; padding: 11px 14px;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface); text-decoration: none; color: var(--ink);
}
.mixer-strip .t { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.mixer-strip b { font-size: 14.5px; }
.mixer-strip small { color: var(--muted); font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
