/*
  ✨ ChefBoy's animations (web/fx.js, window.ChefFX): ✓ Ate it (a stamp, a hop, a burst of the meal's
  emoji), the rotation gliding into its new order, groceries ticked off and ⏭ postponed, bowls filling
  after a batch, cook mode's steps, timers and finish, toasts, tabs and views, Claude's shimmer, the
  empty states and the landing page.

  <html data-fx="full | subtle | off"> (fx.js, from ⚙️ Settings → Animations and reduced motion) gates
  every animation here: full = everything; subtle = short fades only (200 ms or less: no particles,
  shakes, slides, loops or confetti); off = nothing matches. The reduced-motion block at the end stops
  everything again, whatever the setting says. Only transform and opacity move. Effects on the app's
  own elements use the separate `scale`, `translate` and `rotate` properties, so they never fight an
  element's own `transform` (the toast's centring, a dragged row).
*/

:root {
  --fx-fast: 160ms;
  --fx-med: 260ms;
  --fx-slow: 420ms;
  --fx-stagger: 35ms;
  --fx-out: cubic-bezier(.22, 1, .36, 1);
  --fx-spring: cubic-bezier(.34, 1.56, .64, 1);
  --fx-gold: #f59e0b;
  --fx-purple: #8b5cf6;
}
@media (prefers-color-scheme: dark) {
  :root { --fx-gold: #fdb022; --fx-purple: #b692f6; }
}

/* ---------- shared keyframes ---------- */
@keyframes fx-fade { from { opacity: 0; } }
@keyframes fx-fade-out { to { opacity: 0; } }
@keyframes fx-blink { from { opacity: .5; } }
@keyframes fx-rise { from { opacity: 0; translate: 0 8px; } }
@keyframes fx-in { from { opacity: 0; translate: 0 8px; } }
@keyframes fx-pop { 0% { scale: .85; } 55% { scale: 1.2; } 100% { scale: 1; } }
@keyframes fx-bump { 0%, 100% { scale: 1; } 40% { scale: 1.12; } }
@keyframes fx-settle { 0% { scale: 1.035; } 55% { scale: .985; } 100% { scale: 1; } }
@keyframes fx-shake { 0%, 100% { translate: 0 0; } 20% { translate: -2px 0; } 45% { translate: 2px 0; } 70% { translate: -1px 0; } }
@keyframes fx-drop { from { opacity: 0; translate: 0 -10px; scale: .98; } }
@keyframes fx-out { to { opacity: 0; translate: 1.5rem 0; } }
@keyframes fx-later { to { opacity: 0; translate: 110% 0; } }
@keyframes fx-stamp { from { opacity: 0; scale: 1.8; rotate: -10deg; } to { opacity: 1; scale: 1; rotate: 0deg; } }
@keyframes fx-pose { 18% { scale: 1.06 .92; } 42% { translate: 0 -12px; scale: .97 1.04; } 68% { translate: 0 0; scale: 1.03 .97; } 100% { scale: 1; } }
@keyframes fx-strike { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes fx-fill { 0% { opacity: 0; scale: .4; } 60% { opacity: 1; scale: 1.15; } 100% { scale: 1; } }
@keyframes fx-empty-bowl { from { opacity: 1; scale: 1.08; } }
@keyframes fx-slide-next { from { opacity: 0; translate: 24px 0; } }
@keyframes fx-slide-prev { from { opacity: 0; translate: -24px 0; } }
@keyframes fx-underline { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes fx-toast-in { from { opacity: 0; translate: 0 12px; } }
@keyframes fx-toast-out { to { opacity: 0; translate: 0 8px; } }
@keyframes fx-halo { 0% { opacity: .9; transform: scale(1); } 100% { opacity: 0; transform: scale(1.18, 1.5); } }
@keyframes fx-ring-pulse { 0%, 100% { scale: 1; } 50% { scale: 1.06; } }
@keyframes fx-breathe { 0%, 100% { translate: 0 0; rotate: 0deg; } 50% { translate: 0 -4px; rotate: -4deg; } }
@keyframes fx-drift { from { translate: 0 0; rotate: -.6deg; } to { translate: 0 -6px; rotate: .6deg; } }
@keyframes fx-sweep { from { transform: translateX(-100%); } to { transform: translateX(100%); } }

/* ---------- the particle layer: one fixed sheet over everything that never takes a click ---------- */
.fx-layer { position: fixed; inset: 0; z-index: 600; pointer-events: none; overflow: hidden; contain: strict; }
.fx-p { position: absolute; left: 0; top: 0; pointer-events: none; opacity: 0; will-change: transform, opacity; }

/* Sparks (a ticked grocery) and emoji (✓ Ate it, 🥣 bowls, 🍽️ done cooking, 📋 copied). */
.fx-spark { width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px; border-radius: 1.5px; background: var(--fx-color, var(--accent)); }
.fx-spark.round { width: 5px; height: 5px; margin: -2.5px 0 0 -2.5px; border-radius: 50%; }
html[data-fx="full"] .fx-spark { animation: fx-spark var(--fx-dur, .55s) cubic-bezier(.2, .7, .3, 1) var(--fx-delay, 0s) both; }
@keyframes fx-spark {
  0% { opacity: 0; transform: translate(0, 0) rotate(45deg) scale(.4); }
  18% { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--fx-dx, 0), var(--fx-dy, 0)) rotate(calc(45deg + var(--fx-rot, 90deg))) scale(.25); }
}
.fx-glyph { font-size: 18px; line-height: 1; width: 1em; height: 1em; margin: -.5em 0 0 -.5em; text-align: center; }
html[data-fx="full"] .fx-glyph { animation: fx-glyph var(--fx-dur, .7s) cubic-bezier(.2, .7, .3, 1) var(--fx-delay, 0s) both; }
@keyframes fx-glyph {
  0% { opacity: 0; transform: translate(0, 0) scale(.5) rotate(0deg); }
  20% { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--fx-dx, 0), var(--fx-dy, 0)) scale(1) rotate(var(--fx-rot, 60deg)); }
}

/* 🎉 Confetti: paper bits thrown up from an element (the end of setup, cook mode's finish, a streak)… */
.fx-bit, .fx-fall { width: 8px; height: 12px; margin-left: -4px; border-radius: 2px; background: var(--fx-color, var(--accent)); }
.fx-bit.wide, .fx-fall.wide { width: 11px; height: 6px; }
html[data-fx="full"] .fx-bit { animation: fx-bit var(--fx-dur, 1.1s) cubic-bezier(.2, .6, .4, 1) var(--fx-delay, 0s) both; }
@keyframes fx-bit {
  0% { opacity: 1; transform: translate(0, 0) rotate(0deg); }
  70% { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--fx-dx, 0), calc(var(--fx-dy, 0px) + 220px)) rotate(var(--fx-rot, 360deg)); }
}
/* …or falling from the top of the screen (all three meals ✓ today). */
html[data-fx="full"] .fx-fall { animation: fx-fall var(--fx-dur, 2s) cubic-bezier(.25, .55, .45, 1) var(--fx-delay, 0s) both; }
@keyframes fx-fall {
  0% { opacity: 0; transform: translate(0, -16px) rotate3d(1, .4, 0, 0deg); }
  6%, 82% { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--fx-dx, 0), var(--fx-fall, 100vh)) rotate3d(1, .4, 0, var(--fx-rot, 720deg)); }
}

/* ---------- one-shots on the app's own elements (fx.js play() puts them on and takes them off) ---------- */
html[data-fx="full"] .fx-pop { animation: fx-pop .3s ease-out; }
html[data-fx="full"] .fx-bump { animation: fx-bump .42s var(--fx-spring); }
html[data-fx="full"] .fx-settle { animation: fx-settle .34s var(--fx-spring); }
html[data-fx="full"] .fx-shake { animation: fx-shake .4s ease-in-out; }
html[data-fx="subtle"] .fx-pop, html[data-fx="subtle"] .fx-bump, html[data-fx="subtle"] .fx-settle { animation: fx-blink .2s ease-out; }

/* Lists: rows rise in one after another when a list opens; a new one drops in; one leaving slides out. */
html[data-fx="full"] .fx-in { animation: fx-in .24s var(--fx-out) calc(var(--fx-i, 0) * 30ms) backwards; }
html[data-fx="subtle"] .fx-in { animation: fx-fade .16s ease-out backwards; }
html[data-fx="full"] .fx-drop { animation: fx-drop .3s cubic-bezier(.2, 1.2, .4, 1) backwards; }
html[data-fx="subtle"] .fx-drop { animation: fx-fade .18s ease-out backwards; }
html[data-fx="full"] .fx-out { animation: fx-out .26s ease-in forwards; pointer-events: none; }
html[data-fx="subtle"] .fx-out { animation: fx-fade-out .18s ease-in forwards; pointer-events: none; }
/* ⏭ Postponed: off to the right. */
html[data-fx="full"] .fx-later { animation: fx-later .28s ease-in forwards; pointer-events: none; }
html[data-fx="subtle"] .fx-later { animation: fx-fade-out .18s ease-in forwards; pointer-events: none; }

/* ✓ Ate it: "✓ Eaten" stamps on, the meal's emoji hops. */
.fx-stamp { transform-origin: 0 50%; }
html[data-fx="full"] .fx-stamp { animation: fx-stamp var(--fx-slow) var(--fx-spring) both; }
html[data-fx="subtle"] .fx-stamp { animation: fx-fade .2s ease-out both; }
.fx-pose { transform-origin: 50% 100%; }
html[data-fx="full"] .fx-pose { animation: fx-pose .6s var(--fx-out); }

/* 🛒 Bought: a line drawn through the name while the row slides away. */
html[data-fx="full"] .fx-strike { position: relative; }
html[data-fx="full"] .fx-strike::after { content: ''; position: absolute; left: 0; right: 0; top: 52%; height: 2px; border-radius: 1px; background: currentColor; transform-origin: 0 50%; animation: fx-strike .18s ease-out both; }

/* 🍱 A batch made: the new bowls fill in turn; the bowl just eaten greys out. */
html[data-fx="full"] .fx-fill { display: inline-block; animation: fx-fill .42s var(--fx-spring) calc(var(--fx-i, 0) * 40ms) backwards; }
html[data-fx="subtle"] .fx-fill { animation: fx-fade .2s ease-out backwards; }
html[data-fx="full"] .fx-empty-bowl { display: inline-block; animation: fx-empty-bowl .45s ease-out; }
html[data-fx="subtle"] .fx-empty-bowl { animation: fx-blink .2s ease-out; }

/* Views and tabs: a new section rises in, a tab's body fades in, the underline draws itself. */
html[data-fx="full"] .fx-view-in { animation: fx-rise .2s var(--fx-out); }
html[data-fx="subtle"] .fx-view-in { animation: fx-fade .15s ease-out; }
html[data-fx="full"] .fx-fade-in, html[data-fx="subtle"] .fx-fade-in { animation: fx-fade .18s ease-out; }
html[data-fx="full"] .fx-slide-next { animation: fx-slide-next .24s var(--fx-out); }
html[data-fx="full"] .fx-slide-prev { animation: fx-slide-prev .24s var(--fx-out); }
html[data-fx="subtle"] .fx-slide-next, html[data-fx="subtle"] .fx-slide-prev { animation: fx-fade .16s ease-out; }
html[data-fx="full"] .fx-tab-moved .tab[aria-selected="true"] { position: relative; border-bottom-color: transparent; }
html[data-fx="full"] .fx-tab-moved .tab[aria-selected="true"]::after { content: ''; position: absolute; left: 0; right: 0; bottom: -2px; height: 2px; background: var(--accent); animation: fx-underline .22s var(--fx-out) both; }

/* ▶️ Cook mode: the next step slides in from the side you're going. */
/* (While it slides, its scroller clips sideways: no horizontal scrollbar flashing, no sideways pan.) */
.cook-body:has(> .fx-step-fwd, > .fx-step-back) { overflow-x: hidden; }
html[data-fx="full"] .fx-step-fwd { animation: fx-slide-next .26s var(--fx-out); }
html[data-fx="full"] .fx-step-back { animation: fx-slide-prev .26s var(--fx-out); }
html[data-fx="subtle"] .fx-step-fwd, html[data-fx="subtle"] .fx-step-back { animation: fx-fade .18s ease-out; }

/* ⏰ A timer that's done: it throbs with a halo (full); one flash (subtle); still (off). Its chime and
   buzz (cook.js) don't depend on any of this. */
.timer { position: relative; }
html[data-fx="full"] .timer.ring { animation: fx-ring-pulse 1s ease-in-out infinite; }
html[data-fx="full"] .timer.ring::after { content: ''; position: absolute; inset: -1px; border-radius: inherit; pointer-events: none; box-shadow: 0 0 0 2px var(--accent), 0 0 0 7px color-mix(in srgb, var(--accent) 22%, transparent); animation: fx-halo 1.2s ease-out infinite; }
html[data-fx="subtle"] .timer.ring { animation: fx-blink .2s ease-out; }

/* Toasts: in from below, a bump when one replaces another, out with a fade. */
html[data-fx="full"] .fx-toast-in { animation: fx-toast-in .18s var(--fx-out); }
html[data-fx="subtle"] .fx-toast-in { animation: fx-fade .16s ease-out; }
html[data-fx="full"] .fx-toast-out { animation: fx-toast-out .16s ease-in forwards; }
html[data-fx="subtle"] .fx-toast-out { animation: fx-fade-out .16s ease-in forwards; }

/* ✨ Claude is working: a soft light sweeps across the button (subtle: it's a little dimmer, still). */
.fx-shimmer { position: relative; overflow: hidden; isolation: isolate; }
.fx-shimmer::after { content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none; opacity: 0; background: linear-gradient(100deg, transparent 25%, color-mix(in srgb, currentColor 18%, transparent) 50%, transparent 75%); transform: translateX(-100%); }
html[data-fx="full"] .fx-shimmer::after { opacity: 1; animation: fx-sweep 1.3s ease-in-out infinite; }
html[data-fx="subtle"] .fx-shimmer { opacity: .8; }

/* A rotation row being dragged: tilted a little, then it settles where it's dropped. */
html[data-fx="full"] .fx-lifted { rotate: -1.2deg; scale: 1.02; transition: rotate .12s ease, scale .12s ease; }

/* Empty states: the big emoji bobs gently (full only; still otherwise). */
html[data-fx="full"] .empty .big { animation: fx-breathe 2.6s ease-in-out 1s infinite; }

/* The landing page: the logo drifts. */
html[data-fx="full"] .auth-hero .auth-logo { animation: fx-drift 4.8s ease-in-out infinite alternate; }

/* Buttons: a gentle press. */
html[data-fx="full"] .btn { transition: background .12s, border-color .12s, transform .06s, scale .12s ease; } /* (chefboy.css's .btn fades kept) */
html[data-fx="full"] .btn:active:not(:disabled) { scale: .97; }

/* 💬 Help: its typing dots and caret stand still at Off too. */
html[data-fx="off"] .help-dots i, html[data-fx="off"] .help-caret { animation: none; }

/* ---------- reduced motion: nothing moves, whatever the setting says ---------- */
@media (prefers-reduced-motion: reduce) {
  .fx-layer { display: none !important; }
  [class*="fx-"], [class*="fx-"]::before, [class*="fx-"]::after,
  .timer.ring, .timer.ring::after, .empty .big, .auth-hero .auth-logo, .btn { animation: none !important; transition: none !important; }
  .btn:active, .fx-lifted { scale: none !important; rotate: none !important; }
}
