/* The floating nav.
 *
 * One white circle, top left, on every page that isn't a legacy one. Closed it
 * is just the glyph — the site's whole surface is the work, and a permanent
 * link sitting next to the glyph was competing with it. Pressing the glyph
 * grows the circle downward into the menu rather than opening a panel on top of
 * it, so the thing you pressed is the thing that becomes the menu.
 *
 * It used to be black. White is the quieter choice on a site whose content is
 * photographs and video: a black disc reads as an object placed on the work,
 * where a white one reads as the page's own chrome sitting above it. It carries
 * a shadow because #fff on the #f0f0f0 canvas is only a hair of contrast — the
 * lift is what separates it, not the fill.
 *
 * Shared by index, list, work-with-me and courses. It used to be copied into
 * world.css and pages.css and maintained twice.
 *
 * Motion is all on the scale in motion.css. Opening is a panel open
 * (--duration-slow, --ease-smooth-out); closing is a panel close and quicker
 * (--duration-medium), because leaving should get out of the way. The glyph
 * crossfade is an icon swap (--duration-fast, --ease-in-out) both directions —
 * a swap is one reversible motion, not an open/close pair, so it does not split.
 */

.nav-left {
  position: fixed;
  top: 20px;
  left: 20px;
  z-index: 100;
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

/* The two widths are the one thing here that is a number rather than content.
   The labels are short and fixed, so letting the pill find its own width — and
   then having nothing to animate from when closed — costs more than it saves. */
.nav-pill {
  --nav-w-closed: 54px;
  --nav-w-open: 200px;

  background: #fff;
  color: #111;
  width: var(--nav-w-closed);
  padding: 13px 0;
  /* Half the closed width, written as a length rather than 50%, so the corner
     interpolates cleanly on the way out to the open shape. The closed box is
     square — 54 wide, 13 + 28 + 13 tall — so this is a circle, not a pill. */
  border-radius: calc(var(--nav-w-closed) / 2);
  box-shadow: var(--shadow-md);
  /* Stated rather than inherited. The nav is the one element that is the same
     object on every page, and left to inherit it picked up a different face on
     each — three pages, three pill heights. The site is one face now, so this
     resolves the same everywhere, but it stays written down: the nav's height
     should not be something the page under it can change.
     15px rather than 16, because mono sets wider — at 16 "List view" came
     close enough to the open pill's inner edge to look unplanned. */
  font-family: var(--font-plate);
  font-size: 15px;
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: var(--track-tight);
  display: block;
  overflow: hidden;
  transition:
    width var(--duration-medium) var(--ease-smooth-out),
    border-radius var(--duration-medium) var(--ease-smooth-out),
    box-shadow var(--duration-quick) ease;
}

/* The pill is built and appended by nav.js once per page, so an animation on
   the element itself runs exactly once, at insertion — no mounted flag, no
   rAF, no class for JS to remember to add. Origin is the top left corner
   because that is where the disc is pinned: it grows out of the corner rather
   than swelling in place. No fill mode, because there is no delay to fill and
   a filled transform would sit on top of the open/close width animation. */
@keyframes nav-in {
  from {
    opacity: 0;
    transform: translateY(calc(var(--distance-base) * -1)) scale(var(--scale-large));
  }
}

.nav-pill {
  transform-origin: top left;
  animation: nav-in var(--duration-slow) var(--ease-smooth-out);
}

/* Black got lighter on hover; white has nowhere to go, so the feedback moves to
   the shadow. The disc lifts rather than changing colour. */
.nav-pill:hover { box-shadow: var(--shadow-lg); }

.nav-pill.is-open {
  width: var(--nav-w-open);
  border-radius: 22px;
  box-shadow: var(--shadow-lg);
  transition:
    width var(--duration-slow) var(--ease-smooth-out),
    border-radius var(--duration-slow) var(--ease-smooth-out),
    box-shadow var(--duration-quick) ease;
}

/* --- The glyph ---------------------------------------------------------- */

.nav-toggle {
  appearance: none;
  background: none;
  border: 0;
  color: inherit;
  cursor: pointer;
  display: grid;
  place-items: center;
  width: 54px;
  height: 28px;
  padding: 0;
}

/* Both glyphs occupy the same cell so the swap is a crossfade in place rather
   than one leaving and the next arriving. */
.nav-glyph {
  grid-area: 1 / 1;
  width: 18px;
  height: 18px;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition:
    opacity var(--duration-fast) var(--ease-in-out),
    transform var(--duration-fast) var(--ease-in-out),
    filter var(--duration-fast) var(--ease-in-out);
}

.nav-glyph-close,
.nav-pill.is-open .nav-glyph-open {
  opacity: 0;
  transform: scale(var(--scale-small));
  filter: blur(var(--blur-small));
}

.nav-pill.is-open .nav-glyph-close {
  opacity: 1;
  transform: none;
  filter: none;
}

/* --- The menu ----------------------------------------------------------- */

/* 0fr → 1fr so the panel opens to whatever it holds, with no measured height
   to keep in step with the markup. */
.nav-menu {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--duration-medium) var(--ease-smooth-out);
}

.nav-pill.is-open .nav-menu {
  grid-template-rows: 1fr;
  transition: grid-template-rows var(--duration-slow) var(--ease-smooth-out);
}

.nav-menu-inner {
  overflow: hidden;
  min-height: 0;
}

.nav-sep {
  height: 1px;
  margin: 8px 18px 6px;
  background: rgba(0, 0, 0, 0.1);
}

.nav-item {
  appearance: none;
  background: none;
  border: 0;
  width: 100%;
  display: block;
  text-align: left;
  font: inherit;
  /* Literal rather than var(--text-muted): world.css doesn't define the colour
     tokens pages.css does, and the nav is on both.
     Off-black rather than grey. Greyed-back menu labels read as unavailable,
     and these are the four places you can go — they should look it. Not #111
     either: the glyph above them is #111 and a menu that matched it exactly
     flattened into one block of text. A shade off keeps the glyph first. */
  color: #2b2b28;
  padding: 9px 18px;
  cursor: pointer;
  white-space: nowrap;
  /* Rest is the open state. The closed state below is what the item animates
     from, so nothing is parked invisible if the transition never runs. */
  opacity: 1;
  transform: none;
  /* The item has two independent motions: the open/close stagger, which is
     slow and delayed per item, and the hover/press answer, which must be
     immediate. Putting both on `transform` meant one transition line had to
     serve both, and the pointer feedback inherited the stagger's delay —
     a menu item that started moving 120ms after you touched it.
     `translate` is its own animatable property and composes with `transform`,
     so the two motions can keep their own timings and never overwrite each
     other. */
  translate: none;
  transition:
    opacity var(--duration-slow) var(--ease-smooth-out) var(--nav-delay, 0ms),
    transform var(--duration-slow) var(--ease-smooth-out) var(--nav-delay, 0ms),
    translate var(--duration-micro) var(--ease-smooth-out),
    color var(--duration-quick) ease;
}

/* Top-down, one --duration-stagger apart. Four items is 120ms of total
   stagger — inside the ~300ms past which the last item starts reading as late.
   The separator is the first child, so the items are 2 through 5. */
.nav-item:nth-child(2) { --nav-delay: 0ms; }
.nav-item:nth-child(3) { --nav-delay: var(--duration-stagger); }
.nav-item:nth-child(4) { --nav-delay: calc(var(--duration-stagger) * 2); }
.nav-item:nth-child(5) { --nav-delay: calc(var(--duration-stagger) * 3); }

/* The row used to take a grey wash on hover and a darker one on press. It
   read as a highlight bar dragged down a list — the look of a native select,
   not of a menu that is four words on white. The feedback is movement now:
   the item you are pointing at lifts a hair off the stack, and the rest of
   the panel stays exactly as it was. Colour still closes the last of the gap
   to #111, but it is no longer carrying the whole signal. */
@media (hover: hover) and (pointer: fine) {
  .nav-item:hover { translate: 0 calc(var(--distance-micro) * -0.5); }
}

.nav-item:hover,
.nav-item:focus-visible {
  color: #111;
}

/* Press, on any input: the item goes down rather than up. Touch never sees
   the hover, so on a phone this is the whole answer — and it is the one
   answer that is not a colour, which is what the wash got wrong. Same
   gesture on a mouse, so the two devices agree about what pressing means. */
.nav-item:active { translate: 0 1px; }

/* Current page: still listed, so you can see where you are, but not a link.
   This is the one item that should read as unavailable, which is the job grey
   was doing for all four. */
.nav-item[aria-current='page'] {
  color: rgba(17, 17, 17, 0.3);
  pointer-events: none;
}

/* Closing never staggers and never waits — dismissal is instant or it feels
   like the menu is reluctant. */
.nav-pill:not(.is-open) .nav-item {
  opacity: 0;
  transform: translateY(calc(var(--distance-medium) * -1));
  transition:
    opacity var(--duration-quick) var(--ease-smooth-out),
    transform var(--duration-quick) var(--ease-smooth-out);
}

.nav-pill :focus-visible {
  outline: 2px solid #111;
  outline-offset: -3px;
  border-radius: 6px;
}

@media (max-width: 640px) {
  .nav-left { top: 16px; left: 16px; }
}

@media (prefers-reduced-motion: reduce) {
  @keyframes nav-in {
    from { opacity: 0; }
  }

  .nav-pill,
  .nav-pill.is-open,
  .nav-menu,
  .nav-pill.is-open .nav-menu,
  .nav-glyph {
    transition-duration: 1ms;
  }
  /* No stagger, no travel — the items just arrive. */
  .nav-item,
  .nav-pill:not(.is-open) .nav-item {
    transform: none;
    transition: opacity var(--duration-quick) linear;
  }
  /* The lift and the press go with it. Colour is left to carry the hover and
     the press on its own — gentler, not nothing. Same specificity as the
     rules that set the travel, and later in the file, so it wins. */
  .nav-item:hover,
  .nav-item:active {
    translate: none;
  }
}
