/* COET — the menu, and the flight into it.

   Ported from coet-site (src/lib/components/nav/MenuOverlay.svelte + MenuCanvas.svelte): press Menu and the page
   you are on flies out of the middle of the screen, into a ring of page previews wrapped onto a cylinder. The warp
   itself lives in assets/menu.js; this is the two-layer camera move and the chrome around it.

   THE FLIGHT IS TWO TRANSITIONS, NOT A REDRAW. The ring is drawn once, at rest, and the camera is a plain
   transform on that one frame:

     .shell   the live page — header excepted, so the bar stays put — shrinking from full bleed onto the card
     .ring    the canvas, starting blown up so its front panel sits exactly under the page, shrinking to rest

   Both transform about their own top-left and interpolate translate-then-scale, so the registration is exact at
   every frame rather than only at the ends. Writing F for the card's top-left and s for its scale, the page runs
   identity → translate(F) scale(s) and the ring runs translate(−F/s) scale(1/s) → identity; at progress e both put
   F at eF. The −F/s is algebra, not a tuned number. See menu.js's `measure()` for where F and s come from.

   What is NOT ported: coet-site flew a captured <img> of the page, because a Svelte route cannot be transformed out
   from under its own router. Here the flat layer is the REAL page, so the first frame registers by construction and
   there is no screenshot of the live page to keep in step with it. The ring's panels are still captures — of the
   destinations, not of the page you are on — and tools/capture-panels.sh shoots them.

   The base state of both layers is the flown-OUT one, which is both where an open starts and where a close ends, so
   one pair of rules runs the move in both directions: the base carries the close timing, `.is-in` the longer open. */

/* ── the bar's menu button ── */
.mnav__burger { position: relative; display: block; width: 12px; height: 9px; }
.mnav__burger i { position: absolute; left: 0; width: 100%; height: 1px; background: currentColor; transition: transform .45s var(--ml-ease), opacity .2s; }
.mnav__burger i:first-child { top: 1px; }
.mnav__burger i:last-child { bottom: 1px; }
html.menu-open .mnav__burger i:first-child { transform: translateY(3px) rotate(45deg); }
html.menu-open .mnav__burger i:last-child { transform: translateY(-3px) rotate(-45deg); }

/* open: the bar drops its glass and the button stays lit, because it is now the way out */
html.menu-open .mnav__bar { max-width: 100%; background: rgb(0 0 0 / 0) !important; box-shadow: none !important; backdrop-filter: none !important; -webkit-backdrop-filter: none !important; }
html.menu-open .mnav__menu { background: rgb(255 255 255 / .26); }

/* ── the page, flying out ── */
/* z-index, because the page is the layer ON TOP of the ring for the whole move: a ring fading up through a page
   fading out dips to the scrim in the middle and reads as a dissolve. Nothing fades up — the neighbours sweeping in
   from the gutters as the camera pulls back is the reveal. */
html.menu-open { overflow: hidden; }
html.menu-open .shell {
  position: relative; z-index: 75; pointer-events: none;
  /* The origin is the viewport's top-left in the page's own coordinates, and the clip is the viewport rect in the
     same: scaling a document that is ten screens tall would otherwise pull all ten into the card. Both are in px
     from menu.js, which is the only thing that knows the scroll offset. */
  transform-origin: 0 var(--fly-origin, 0px);
  clip-path: inset(var(--fly-top, 0px) 0 var(--fly-bottom, 0px) 0);
  will-change: transform, opacity;
  transition: transform 480ms var(--ml-ease), opacity 300ms var(--ml-ease);
}
html.menu-in .shell {
  transform: translate(var(--card-x, 0px), var(--card-y, 0px)) scale(var(--card-s, .4));
  opacity: 0;
  /* Held opaque nearly to the end: the card is a scale model of the viewport, so the page and the panel under it
     stay locked for the whole move, and fading early just shows the panel through a page that is still travelling. */
  transition: transform 620ms var(--ml-ease), opacity 220ms var(--ml-ease) 400ms;
}

/* ── the overlay ── */
.menu { position: fixed; inset: 0; z-index: 70; color: #fff; }
.menu[hidden] { display: none; }
.menu__scrim { position: absolute; inset: 0; background: #000; opacity: 0; transition: opacity 620ms var(--ml-ease); }
.menu.is-in .menu__scrim { opacity: .92; }

.menu__ring {
  position: absolute; inset: 0; display: block; width: 100%; height: 100%;
  touch-action: none; cursor: grab;
  transform-origin: 0 0;
  transform: translate(var(--ring-x, 0px), var(--ring-y, 0px)) scale(var(--ring-s, 1));
  will-change: transform;
}
.menu__ring:active { cursor: grabbing; }
/* Armed only once the flight is about to start: before that the canvas is still measuring itself, and a card scale
   that lands after the first style recalc would transition the ring out of its start pose. */
.menu.is-flying .menu__ring { transition: transform 480ms var(--ml-ease); }
.menu.is-in .menu__ring { transform: translate(0px, 0px) scale(1); transition: transform 620ms var(--ml-ease); }
/* Light mode flips the whole page (assets/theme.css); the ring is full of screenshots, so it flips back the way a
   photograph does and keeps their true colours. */
html[data-theme="light"] .menu__ring { filter: invert(1) hue-rotate(180deg); }

/* ── the chrome: the real navigation, since the ring is decoration for pointer users ── */
/* It arrives after the camera does — showing it mid-flight anchors the eye and kills the sense of moving. */
.menu__chrome { position: absolute; left: 0; right: 0; bottom: 0; display: flex; align-items: flex-end; justify-content: space-between; gap: 40px;
  padding: 0 clamp(16px, 4vw, 40px) clamp(20px, 3vh, 36px); opacity: 0; transform: translateY(14px); pointer-events: none;
  transition: opacity .36s var(--ml-ease), transform .36s var(--ml-ease); }
.menu.is-settled .menu__chrome { opacity: 1; transform: none; pointer-events: auto; }

.menu__links { display: grid; gap: 0; justify-items: start; }
.menu__links a { font: 600 clamp(19px, 1.9vw, 26px)/1.35 var(--apple); letter-spacing: -.02em; color: #fff; opacity: .55; transition: opacity .25s var(--ml-ease); }
.menu__links a:hover, .menu__links a:focus-visible, .menu__links a.is-aimed { opacity: 1; }

.menu__rail { max-width: min(52ch, 56vw); }
.menu__label { margin-bottom: 10px; color: var(--muted); text-align: right; }
.menu__pills { display: flex; flex-wrap: wrap; gap: 4px; justify-content: flex-end; }
/* The same button MetaLab uses, and the same one the home pills are now cut from (metalab.css): 32px tall, 16px of
   padding, 16px type on a 50px radius over rgba(185,185,185,.2). The chip whose panel is squared up to the
   viewfinder fills in solid white — one treatment for "this is the one", in the menu and on the home stage both. */
.m-pill { position: relative; display: inline-flex; align-items: center; height: 32px; padding: 0 16px; border-radius: 50px;
  background: rgb(185 185 185 / .2); border: .1rem solid rgb(0 0 0 / 0); color: #fff; font: 400 16px/2 var(--apple); letter-spacing: -.01em;
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); transform: none; opacity: 1;
  transition: background-color .25s var(--ml-ease), color .25s var(--ml-ease), border-color .25s var(--ml-ease); }
.m-pill:hover { background: rgb(185 185 185 / .3); }
.m-pill.is-aimed { background: #fff; border-color: #fff; color: #000; }

/* Between the two blocks rather than floating by the ring: it is an instruction about the control, so it belongs
   on the same line as the controls. */
.menu__hint { align-self: flex-end; margin-bottom: 6px; color: var(--muted); white-space: nowrap; pointer-events: none; }

.menu__links a:focus-visible, .m-pill:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }

/* ── phones: the links and the chips are the menu, and the ring is the picture above them ── */
@media (max-width: 900px) {
  .menu__chrome { flex-direction: column; align-items: stretch; gap: 18px; }
  .menu__rail { max-width: none; overflow-x: auto; scrollbar-width: none; }
  .menu__rail::-webkit-scrollbar { display: none; }
  .menu__label, .menu__pills { text-align: left; justify-content: flex-start; }
  .menu__pills { flex-wrap: nowrap; width: max-content; padding-bottom: 2px; }
  .menu__hint { display: none; }
}

/* ── capture mode (tools/capture-panels.sh) ── */
/* The bar is hidden with `visibility`, never `display`: the panel has to be a picture of the page as it is laid
   out, and dropping the bar out of the flow would reflow anything that sits under it. The orb is parked on one
   still frame for the same reason a capture cannot hold motion. */
html.is-panel .mnav, html.is-panel .menu, html.is-panel .ws-cursor { visibility: hidden; }
html.is-panel .orb__glow { display: none; }

@media (prefers-reduced-motion: reduce) {
  html.menu-open .shell, html.menu-in .shell, .menu__ring, .menu.is-in .menu__ring, .menu.is-flying .menu__ring,
  .menu__scrim, .menu__chrome { transition-duration: .01ms !important; }
  html.menu-in .shell { transform: none; }
  .menu__ring, .menu.is-in .menu__ring { transform: none; }
}
