/* menu-media.css — the mega-menu image stage: crossfade on swap and a slow
   zoom on the image that is up. Board item 97 (owner scope Q6, Q7, Q29).

   Markup (server/menu_media.go menuStage):

     <figure class="mm-stage" data-mm-stage>
       <div class="mm-layer is-on" data-mm-key="products"><picture>…<img class="mm-img" loading="lazy" …></picture></div>
       <div class="mm-layer" data-mm-key="material:quartz" hidden><picture>…</picture></div>
     </figure>

   Layers are stacked in one grid cell, so the stage keeps the size of its
   first image and a swap never moves layout. A `hidden` layer is
   display:none, so its lazy image is never fetched until assets/js/menu-media.js
   un-hides it on hover intent, decodes it, then adds .is-on.

   Hooks: --mm-ratio (stage aspect, default 4 / 3), --mm-radius,
   --mm-fade (crossfade, default 420ms), --mm-base (resting overscan, default 1.05), --mm-zoom (end scale, default 1.1),
   --mm-zoom-dur (default 9s). */

.mm-stage {
  position: relative;
  display: grid;
  grid-template: minmax(0, 1fr) / minmax(0, 1fr);
  margin: 0;
  overflow: hidden;
  aspect-ratio: var(--mm-ratio, 4 / 3);
  border-radius: var(--mm-radius, 6px);
  background: var(--surface-raised, #2F2C26);
  isolation: isolate;
  contain: paint;
}
.mm-layer {
  grid-area: 1 / 1;
  min-width: 0;
  opacity: 0;
  z-index: 0;
}
.mm-layer[hidden] { display: none; }
.mm-layer.is-on { opacity: 1; z-index: 1; }
.mm-layer.is-leaving { opacity: 0; z-index: 2; }
.mm-layer picture { display: contents; }
.mm-img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  object-position: center;
  /* A 5% overscan, always: the catalog slab shots carry a 1-3% white studio
     border (measured on the ~800px sources), and cover alone keeps it. */
  scale: var(--mm-base, 1.05);
}
/* A soft keyline over the photo, in the site's gold-on-dark vocabulary. */
.mm-stage::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px var(--hairline-soft, rgba(255, 255, 255, 0.08));
  pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
  .mm-layer {
    transition: opacity var(--mm-fade, 420ms) cubic-bezier(0.4, 0, 0.2, 1);
  }
  /* The leaving layer fades OVER the new one (z-index 2), so the swap reads
     as one photograph dissolving into the next rather than a flash of the
     stage background between them. */
  .mm-layer.is-leaving {
    transition-duration: calc(var(--mm-fade, 420ms) * 1.15);
  }
  /* Slow zoom: starts each time a layer comes up (the class is re-added on
     every swap), runs once, and holds its end frame. Only the image that is
     up animates; scale is compositor-only, so it costs no layout or paint. */
  .mm-layer.is-on .mm-img {
    animation: mm-zoom var(--mm-zoom-dur, 9s) cubic-bezier(0.2, 0, 0.2, 1) both;
  }
  .mm-layer.is-leaving .mm-img {
    scale: var(--mm-zoom, 1.1);
  }
}
@keyframes mm-zoom {
  from { scale: var(--mm-base, 1.05); }
  to   { scale: var(--mm-zoom, 1.1); }
}

/* Reduced motion: an instant swap, no zoom (all motion above is opt-in). */
@media (prefers-reduced-motion: reduce) {
  .mm-layer, .mm-layer .mm-img { transition: none; animation: none; }
}

@media (forced-colors: active) {
  .mm-stage { border: 1px solid CanvasText; }
}
