/*
 * Kavinė XXXL — the effects layer.
 *
 * Everything in here is decoration on top of a page that already works.
 * Delete this file and assets/effects.js, remove the two enqueue lines in
 * functions.php, and nothing is lost but the shine. Written phone-first: every
 * interaction here has a tap version, and the hover versions only exist
 * behind (hover: hover).
 */

/* ==========================================================================
   1. Page transitions

   Cross-document view transitions: the browser snapshots the old page and
   fades in the new one. The dish photo carries a name, set by effects.js on
   the card being tapped and here on the dish page's own photo, so the same
   plate slides from the menu into its page instead of blinking.
   ========================================================================== */

@view-transition { navigation: auto; }

::view-transition-old(root),
::view-transition-new(root) { animation-duration: .26s; animation-timing-function: ease; }

.site-header  { view-transition-name: site-header; }
.action-bar   { view-transition-name: action-bar; }
.dish-hero-photo img { view-transition-name: dish-photo; }

::view-transition-group(dish-photo) { animation-duration: .38s; animation-timing-function: cubic-bezier(.2,.8,.3,1); }
::view-transition-old(dish-photo),
::view-transition-new(dish-photo) { mix-blend-mode: normal; object-fit: cover; }

/* ==========================================================================
   2. Hero entrance

   The headline lines rise out of a clip, the copy and buttons follow, and the
   three plates settle from a slow Ken Burns. Pure CSS, runs once per load, and
   ends in the resting state, so a browser that ignores animation shows the
   finished page.
   ========================================================================== */

.hero h1 .line { animation: line-in .7s cubic-bezier(.2,.8,.3,1) both; }
.hero h1 .line:nth-child(2) { animation-delay: .08s; }
.hero h1 .line:nth-child(3) { animation-delay: .16s; }

.hero-kicker, .hero-lede, .hero-actions { animation: fade-up .6s ease both; }
.hero-lede    { animation-delay: .28s; }
.hero-actions { animation-delay: .38s; }

.hero-shot { animation: ken-burns 14s cubic-bezier(.2,.6,.3,1) both; transform-origin: 50% 60%; }
.hero-art figure:nth-child(2) .hero-shot { animation-delay: .1s; transform-origin: 40% 40%; }
.hero-art figure:nth-child(3) .hero-shot { animation-delay: .2s; transform-origin: 60% 50%; }

@keyframes line-in {
  from { transform: translateY(.35em); opacity: 0; filter: blur(6px); }
  to   { transform: none; opacity: 1; filter: none; }
}
@keyframes fade-up {
  from { transform: translateY(10px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
@keyframes ken-burns {
  from { transform: scale(1.12); }
  to   { transform: scale(1); }
}

/* ==========================================================================
   3. Ember drift

   The warm glow behind the page moves, very slowly. A still gradient is a
   background; one that breathes is a room with a light in it.
   ========================================================================== */

body::before {
  animation: ember 38s ease-in-out infinite alternate;
  will-change: transform;
}
@keyframes ember {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(-2.5%, 1.5%, 0) scale(1.06); }
}

/* ==========================================================================
   4. Photos arrive, they do not pop

   Every photo well shows a slow shimmer until its image has loaded, then the
   image fades in over it. Gated on `.fx`, which effects.js sets on <html>: if
   the script never runs, no image is ever held back.
   ========================================================================== */

/* The shimmer runs only while the well is still waiting: 120 wells on the
   menu page each animating a background forever is a repaint on every frame,
   and that is exactly the jank a phone does not need. Once the image has
   loaded the animation stops. */
.fx .dish-photo:has(> img:not(.is-loaded)),
.fx .strip-img:has(> img:not(.is-loaded)),
.fx .cat-card-photo:has(> img:not(.is-loaded)),
.fx .hero-art figure:has(img:not(.is-loaded)),
.fx .dish-hero-photo:has(img:not(.is-loaded)),
.fx .feature-photo:has(> img:not(.is-loaded)),
.fx .band-list .ph:has(> img:not(.is-loaded)) {
  background-image: linear-gradient(100deg,
    transparent 30%,
    color-mix(in srgb, var(--ink) 5%, transparent) 50%,
    transparent 70%);
  background-size: 220% 100%;
  animation: shimmer 1.8s ease-in-out infinite;
}
.fx .dish-photo img,
.fx .strip-img img,
.fx .cat-card-photo img,
.fx .hero-art figure img,
.fx .dish-hero-photo img,
.fx .feature-photo img,
.fx .band-list .ph img { transition: opacity .45s ease; }

/* Hidden only while unloaded. The :not() keeps this rule from outranking the
   base stylesheet's own opacity on the category photo (42% by design). */
.fx .dish-photo img:not(.is-loaded),
.fx .strip-img img:not(.is-loaded),
.fx .cat-card-photo img:not(.is-loaded),
.fx .hero-art figure img:not(.is-loaded),
.fx .dish-hero-photo img:not(.is-loaded),
.fx .feature-photo img:not(.is-loaded),
.fx .band-list .ph img:not(.is-loaded) { opacity: 0; }

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

/* ==========================================================================
   5. Touch feedback

   A card that shrinks a hair under the thumb reads as pressed. Hover states
   do nothing on a phone; this is the phone's version.
   ========================================================================== */

@media (pointer: coarse) {
  .dish, .cat-card, .strip-item, .hero-shot, .band-list a, .btn, .chips button, .rail a {
    transition-property: transform, background-color, color, border-color, box-shadow;
    transition-duration: .16s;
    -webkit-tap-highlight-color: transparent;
  }
  .dish:active, .cat-card:active, .strip-item:active, .hero-shot:active { transform: scale(.975); }
  .band-list a:active { background: color-mix(in srgb, var(--char-ink) 8%, transparent); }
}

/* ==========================================================================
   6. Spotlight

   On a desk, a soft light follows the cursor over a dish or category card.
   effects.js writes --mx / --my in percent; the gradient does the rest.
   Pointer devices only: a light that follows a finger it cannot see is noise.
   ========================================================================== */

@media (hover: hover) and (pointer: fine) {
  .dish::before,
  .cat-card::before {
    content: ""; position: absolute; inset: 0; z-index: 3;
    border-radius: inherit; pointer-events: none;
    background: radial-gradient(22rem circle at var(--mx, 50%) var(--my, 50%),
      color-mix(in srgb, var(--accent) 16%, transparent) 0%,
      color-mix(in srgb, #fff 4%, transparent) 30%,
      transparent 62%);
    opacity: 0; transition: opacity .25s ease;
  }
  .dish:hover::before,
  .cat-card:hover::before { opacity: 1; }
  .is-list .dish::before { display: none; }
}

/* ==========================================================================
   7. Numbers count up
   ========================================================================== */

[data-count] { font-variant-numeric: tabular-nums; }

/* ==========================================================================
   8. Category rail: the active pill slides rather than jumps
   ========================================================================== */

.rail-scroll { position: relative; }
.rail-pill {
  position: absolute; top: .7rem; left: 0; height: calc(100% - 1.4rem);
  width: 0; border-radius: 999px; background: var(--accent);
  transition: left .32s cubic-bezier(.2,.8,.3,1), width .32s cubic-bezier(.2,.8,.3,1);
  pointer-events: none; opacity: 0;
}
.rail-pill.is-on { opacity: 1; }
.fx .rail a { position: relative; z-index: 1; transition: color .2s ease, border-color .2s ease; }
.fx .rail a.is-active { background: transparent; border-color: transparent; color: var(--accent-ink); }
@media (max-width: 47.99rem) { .rail-pill { top: .55rem; height: calc(100% - 1.1rem); } }

/* ==========================================================================
   9. Filter chips
   ========================================================================== */

.chips {
  display: flex; gap: .4rem; padding: 0 0 1rem;
  overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch;
  scroll-snap-type: x proximity;
}
.chips::-webkit-scrollbar { display: none; }
.chips button {
  flex: none; scroll-snap-align: start;
  font-family: var(--display); font-weight: 700; font-size: .74rem; letter-spacing: -.01em;
  padding: .5rem .9rem; min-height: 40px;
  border-radius: 999px; border: 1px solid var(--line); background: var(--surface); color: var(--muted);
  cursor: pointer; white-space: nowrap;
  transition: background-color .16s ease, color .16s ease, border-color .16s ease, transform .16s ease;
}
.chips button:hover { color: var(--ink); border-color: var(--ink); }
.chips button[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.chips button:active { transform: scale(.96); }
@media (max-width: 47.99rem) {
  /* Bleed to the screen edge so the row can be flicked. */
  .chips { margin-inline: calc(-1 * var(--gutter)); padding-inline: var(--gutter); }
}

/* ==========================================================================
   10. Dish page: the photo opens, the category flips

   The photo is a button with a corner mark; the lightbox is a native dialog
   that fills the screen. Prev/next is a row under the photo, and inside the
   lightbox it is the two arrows and a swipe.
   ========================================================================== */

.photo-open {
  display: block; width: 100%; height: 100%; padding: 0; margin: 0;
  border: 0; background: none; cursor: zoom-in; position: relative;
  color: inherit; font: inherit; text-align: left;
}
.photo-open img { width: 100%; height: 100%; object-fit: cover; display: block; }
.photo-open-mark {
  position: absolute; right: .7rem; bottom: .7rem;
  width: 2.2rem; height: 2.2rem; padding: .55rem; border-radius: 50%;
  background: color-mix(in srgb, #000 55%, transparent);
  backdrop-filter: blur(6px);
  fill: none; stroke: #fff; stroke-width: 2; stroke-linecap: round;
  opacity: .85; transition: opacity .2s ease, transform .2s ease;
}
.photo-open:hover .photo-open-mark { opacity: 1; transform: scale(1.06); }
.photo-open:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }

.dish-flip {
  display: grid; grid-template-columns: 1fr 1fr; gap: 1px;
  margin-top: clamp(1.5rem, 4vw, 2.5rem);
  background: var(--line); border-block: 1px solid var(--line);
}
.dish-flip a {
  display: grid; gap: .25rem; padding: .9rem 0; background: var(--paper);
  text-decoration: none; color: inherit; min-width: 0;
  transition: background-color .2s ease;
}
.dish-flip a:hover { background: var(--sunk); }
.dish-flip span:empty { background: var(--paper); }
.dish-flip-next { text-align: right; padding-left: 1rem !important; }
.dish-flip-prev { padding-right: 1rem !important; }
.dish-flip-k { font-family: var(--display); font-size: .64rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--accent); }
.dish-flip-t {
  font-family: var(--display); font-weight: 700; font-size: .92rem; letter-spacing: -.02em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.lightbox {
  width: 100vw; height: 100dvh; max-width: none; max-height: none;
  margin: 0; padding: 0; border: 0; background: #000; color: #fff;
  inset: 0;
}
.lightbox::backdrop { background: #000; }
.lightbox-stage {
  width: 100%; height: 100%; overflow: auto;
  display: grid; place-items: center;
  touch-action: pinch-zoom pan-x pan-y;
  cursor: zoom-in;
}
.lightbox-stage img {
  max-width: 100%; max-height: 100dvh; width: auto; height: auto;
  object-fit: contain; user-select: none; -webkit-user-drag: none;
  transition: transform .3s cubic-bezier(.2,.8,.3,1);
  transform-origin: var(--zx, 50%) var(--zy, 50%);
  animation: lb-in .32s cubic-bezier(.2,.8,.3,1) both;
}
.lightbox.is-zoomed .lightbox-stage { cursor: zoom-out; }
.lightbox.is-zoomed .lightbox-stage img { transform: scale(2.2); }
@keyframes lb-in { from { opacity: 0; transform: scale(.94); } to { opacity: 1; transform: none; } }

.lightbox-close,
.lightbox-nav {
  position: absolute; z-index: 2;
  display: grid; place-items: center;
  width: 2.9rem; height: 2.9rem; border-radius: 50%;
  border: 1px solid color-mix(in srgb, #fff 20%, transparent);
  background: color-mix(in srgb, #000 45%, transparent);
  backdrop-filter: blur(8px);
  color: #fff; text-decoration: none; cursor: pointer;
  font-family: var(--display); font-size: 1.35rem; line-height: 1;
  transition: background-color .16s ease, transform .16s ease;
}
.lightbox-close { top: calc(.8rem + env(safe-area-inset-top)); right: .8rem; }
.lightbox-nav.is-prev { left: .8rem; top: 50%; transform: translateY(-50%); }
.lightbox-nav.is-next { right: .8rem; top: 50%; transform: translateY(-50%); }
.lightbox-close:hover, .lightbox-nav:hover { background: var(--accent); border-color: var(--accent); }
.lightbox-nav:active { transform: translateY(-50%) scale(.94); }

.lightbox-cap {
  position: absolute; left: 0; right: 0; bottom: 0; margin: 0;
  padding: 2.5rem 1.2rem calc(1rem + env(safe-area-inset-bottom));
  font-family: var(--display); font-weight: 700; font-size: 1rem; letter-spacing: -.02em;
  text-align: center; pointer-events: none;
  background: linear-gradient(180deg, transparent, color-mix(in srgb, #000 75%, transparent));
}

/* ==========================================================================
   11. Related dishes on a phone: a row you flick, not a stack you scroll
   ========================================================================== */

@media (max-width: 47.99rem) {
  .related .dish-grid {
    display: flex; gap: .65rem;
    overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    margin-inline: calc(-1 * var(--gutter)); padding-inline: var(--gutter); padding-bottom: .25rem;
  }
  .related .dish-grid::-webkit-scrollbar { display: none; }
  .related .dish { flex: 0 0 68%; scroll-snap-align: start; }
}

/* ==========================================================================
   12. The live badge

   The countdown rewrites the text; when it says "closing in", the badge goes
   the accent colour, which the base stylesheet already knows as .is-soon.
   ========================================================================== */

.open-state .count { font-variant-numeric: tabular-nums; }
