/* ============================================================================
   KOREVRA SERVE — ART & CHOREOGRAPHY
   Styling for the procedural dish scenes (js/art.js) and the shared motion
   vocabulary every portal draws on. Nothing here animates anything but
   transform, opacity and filter, so a kiosk running twenty tiles at 12:45pm
   still composites on the GPU.
   ==========================================================================*/

/* ---- the dish scene ------------------------------------------------------ */
.art { display: block; }
.art-dish { overflow: visible; }

/* Steam drifts and fades. Three wisps, offset, so it reads as convection
   rather than a loop. Only ever on food the kitchen sends out hot. */
.art-steam-w {
  opacity: 0;
  transform-origin: 50% 100%;
  animation: steam-rise 4.2s ease-in-out infinite;
  animation-delay: calc(var(--i, 0) * 1.15s);
}
@keyframes steam-rise {
  0%   { opacity: 0;   transform: translateY(4%)  scaleY(.7) translateX(0); }
  22%  { opacity: .75; }
  60%  { opacity: .38; transform: translateY(-14%) scaleY(1.05) translateX(3%); }
  100% { opacity: 0;   transform: translateY(-26%) scaleY(1.25) translateX(-2%); }
}

/* ---- the dish tile ------------------------------------------------------- */
/* A plate you can almost pick up: it lifts toward the light, its shadow
   tightens, and the food parallaxes a little ahead of the plate. */
.dish-tile {
  position: relative;
  display: block;
  border-radius: var(--r-3);
  overflow: hidden;
  isolation: isolate;
  transition: transform var(--t-base) var(--ease-out), box-shadow var(--t-base) var(--ease-out);
  will-change: transform;
}
.dish-tile::before {
  content: '';
  position: absolute; inset: 0;
  background: radial-gradient(120% 90% at 30% 12%, hsl(var(--h, 30) 90% 62% / .22), transparent 62%);
  opacity: .9;
  transition: opacity var(--t-base);
  z-index: 0;
}
.dish-tile > * { position: relative; z-index: 1; }
.dish-tile .art { transition: transform var(--t-slow) var(--ease-spring); }
@media (hover: hover) {
  .dish-tile:hover { transform: translateY(-4px); box-shadow: var(--shadow-3); }
  .dish-tile:hover .art { transform: translateY(-4px) scale(1.06) rotate(-1.2deg); }
  .dish-tile:hover::before { opacity: 1; }
}
.dish-tile:active { transform: translateY(-1px) scale(.985); }
.dish-tile.is-out { filter: grayscale(.85) opacity(.55); }
.dish-tile.is-out .art-steam { display: none; }

/* the gloss sweep that runs across a tile when it is added to a basket */
.dish-tile.is-added::after {
  content: '';
  position: absolute; inset: 0; z-index: 2;
  background: linear-gradient(100deg, transparent 34%, rgba(255,255,255,.62) 50%, transparent 66%);
  background-size: 220% 100%;
  animation: gloss-sweep .72s var(--ease-out) 1;
  pointer-events: none;
}
@keyframes gloss-sweep { from { background-position: 180% 0; } to { background-position: -60% 0; } }

/* ---- ambience ------------------------------------------------------------ */
.art-ambience { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; pointer-events: none; }
.art-ambience-drift { animation: ambience-drift 26s ease-in-out infinite alternate; }
@keyframes ambience-drift {
  from { transform: scale(1.06) translate3d(-1.5%, -1%, 0); }
  to   { transform: scale(1.14) translate3d(2%, 1.5%, 0); }
}

/* ---- choreography: the shared vocabulary -------------------------------- */
/* `.cq` marks a choreographed group; children enter in sequence. The delay is
   an index, not a hand-written duration, so a list of four and a list of forty
   read at the same tempo. */
.cq > * { opacity: 0; animation: cq-in .62s var(--ease-out) both; animation-delay: calc(var(--cq-base, 0ms) + var(--i, 0) * var(--cq-step, 55ms)); }
.cq-fast { --cq-step: 34ms; }
.cq-slow { --cq-step: 90ms; }
@keyframes cq-in { from { opacity: 0; transform: translateY(14px) scale(.985); } to { opacity: 1; transform: none; } }

.cq-rise > * { animation-name: cq-rise; }
@keyframes cq-rise { from { opacity: 0; transform: translateY(26px); } to { opacity: 1; transform: none; } }
.cq-pop > * { animation-name: cq-pop; animation-timing-function: var(--ease-spring); }
@keyframes cq-pop { from { opacity: 0; transform: scale(.82); } to { opacity: 1; transform: none; } }
.cq-sweep > * { animation-name: cq-sweep; }
@keyframes cq-sweep { from { opacity: 0; transform: translateX(-18px); } to { opacity: 1; transform: none; } }

/* a card that arrives on the rail — used by KDS tickets and the live feed */
.arrive { animation: arrive .5s var(--ease-spring) both; }
@keyframes arrive { from { opacity: 0; transform: translateY(-10px) scale(.94); } to { opacity: 1; transform: none; } }
.depart { animation: depart .34s var(--ease-in-out) both; }
@keyframes depart { to { opacity: 0; transform: translateX(26px) scale(.95); } }

/* the number that changes should be felt, not just read */
.tick-up { animation: tick-up .5s var(--ease-spring); }
@keyframes tick-up { 0% { transform: translateY(35%); opacity: 0; } 100% { transform: none; opacity: 1; } }

/* a value that just moved gets a brief warm halo */
.value-hot { animation: value-hot 1.1s var(--ease-out); }
@keyframes value-hot {
  0%   { box-shadow: 0 0 0 0 var(--saffron-wash); background-color: var(--saffron-wash); }
  100% { box-shadow: 0 0 0 10px transparent; background-color: transparent; }
}

/* ---- the flight arc: basket, hand-over, collection ----------------------- */
/* An item that moves between two places on screen should travel, not teleport.
   js sets --fx/--fy (the delta) and the element flies along it, shrinking. */
.fly {
  position: fixed; z-index: 940; pointer-events: none;
  animation: fly-arc .68s cubic-bezier(.35, .02, .28, 1) forwards;
}
@keyframes fly-arc {
  0%   { transform: translate3d(0,0,0) scale(1); opacity: 1; }
  62%  { opacity: .95; }
  100% { transform: translate3d(var(--fx, 0), var(--fy, 0), 0) scale(.22) rotate(12deg); opacity: 0; }
}

/* ---- surfaces ------------------------------------------------------------ */
/* Depth without heaviness: a hairline of light along the top edge, the way a
   real card catches a ceiling light. */
.lit { position: relative; }
.lit::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background: linear-gradient(180deg, rgba(255,255,255,.55), transparent 34%);
  mix-blend-mode: overlay;
}
[data-theme="dark"] .lit::before { background: linear-gradient(180deg, rgba(255,255,255,.14), transparent 40%); }

/* ---- reduced motion: keep the food, lose the movement ------------------- */
@media (prefers-reduced-motion: reduce) {
  .art-steam-w, .art-ambience-drift, .dish-tile.is-added::after { animation: none !important; }
  .art-steam { opacity: .35; }
  .cq > *, .arrive, .depart, .tick-up, .value-hot, .fly { animation: none !important; opacity: 1 !important; transform: none !important; }
  .dish-tile:hover, .dish-tile:hover .art { transform: none !important; }
}

/* ---- photographs --------------------------------------------------------
   Real plated photography (tools/fetch-dish-photos.py). Square-cropped at
   build time, so `cover` only ever trims a hair; the radius is inherited from
   the tile so a photo never fights the card it sits in. */
.art-photo {
  width: 100%; height: 100%;
  object-fit: cover;
  border-radius: inherit;
  background: var(--bg-3);            /* holds the space while it decodes */
  transition: transform var(--t-slow) var(--ease-spring), filter var(--t-base);
}
.dish-photo {
  position: relative; overflow: hidden; border-radius: var(--r-3);
  background: var(--bg-3); isolation: isolate;
}
.dish-photo::after {                   /* a floor so white type stays legible */
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, transparent 46%, rgba(12,8,16,.52) 100%);
  opacity: 0; transition: opacity var(--t-base);
}
.dish-photo.has-caption::after { opacity: 1; }
@media (hover: hover) {
  .dish-tile:hover .art-photo { transform: scale(1.07); }
}
.dish-tile.is-out .art-photo { filter: grayscale(.9) brightness(.82); }

/* a photo arriving after decode should fade, not pop */
.art-photo { animation: photo-in .5s var(--ease-out) both; }
@keyframes photo-in { from { opacity: 0; transform: scale(1.03); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .art-photo { animation: none; } }

/* ---- photo slots the portals ask for ------------------------------------ */
/* Each is a square well the photograph fills; sizes are set by the portal's
   own stylesheet where it needs something specific, these are the defaults. */
.kk-tile-photo { display: block; width: 100%; aspect-ratio: 1; border-radius: var(--r-3); overflow: hidden; }
.kk-rline-photo,
.kk-top3-photo { display: block; width: 46px; height: 46px; border-radius: 12px; overflow: hidden; flex: none; }
.kd-photo { display: block; width: 34px; height: 34px; border-radius: 9px; overflow: hidden; flex: none; }
.kd-pd-photo { display: block; width: 44px; height: 44px; border-radius: 11px; overflow: hidden; flex: none; }
.bd-mi-photo { display: inline-block; width: 40px; height: 40px; border-radius: 10px; overflow: hidden; vertical-align: middle; margin-right: 8px; }
.ad-dish-photo { display: block; width: 56px; height: 56px; border-radius: 14px; overflow: hidden; flex: none; }
.ad-pub-e { display: block; width: 34px; height: 34px; border-radius: 9px; overflow: hidden; flex: none; }

/* Staged grids come from UI.stage(), which stamps --i on the children. The
   container keeps its own display (grid/flex) — .cq only drives the entry. */
.cq[data-staged] { }
