/* ============================================================================
   SWORT mobile navigation
   ---------------------------------------------------------------------------
   THE BUG THIS REPLACES
   ---------------------
   Reported as "the X does not close the menu and the user is trapped". The
   close handler was never the problem; it was attached and it worked. The
   panel was simply painted UNDERNEATH the bar that opens it.

   Measured on projects.html at 390px, menu open:

     nav (top bar)            position: fixed   z-index: 100
     .mobile-nav (panel)                        z-index:  99
     #hamburger-btn rect      334, 28, 32 x 21
     #mobile-close-btn rect   333, 24, 17 x 37     <- overlapping rects
     elementFromPoint(centre of X)  -> BUTTON#hamburger-btn

     dispatchEvent directly on the X   -> close handler fires
     click at the X's visual centre    -> HAMBURGER handler fires

   So every tap aimed at the X landed on the hamburger, whose handler only ever
   ADDS the open class. The menu could be opened repeatedly and never closed:
   a genuine trap, not a styling glitch. The close target was also 17x37, under
   the 44x44 minimum, so it was hard to hit even where it did work.

   A SECOND DEFECT FOUND WHILE VERIFYING
   -------------------------------------
   investors, research-partnerships, government-capabilities and team render
   .p-nav-links at display:none below the breakpoint and carry no trigger at
   all. Those four routes had NO mobile navigation whatsoever, which is worse
   than the reported bug. They now use this same component.

   STACKING CONTRACT
   -----------------
   The site's fixed bar sits at z-index 100. Everything here is an order of
   magnitude above it so no future bar change can bury the close control again,
   and the close button is explicitly above its own panel:

     scrim   1000     panel   1010     close   1020

   The close button is never inside a pointer-events: none parent, and nothing
   in this file creates a stacking context that could contain it.
   ========================================================================= */

/* ── Trigger ───────────────────────────────────────────────────────────── */
.mnav-trigger {
  display: none;                      /* shown by the breakpoint below */
  position: relative;
  z-index: 101;                       /* above the bar, below the panel */
  width: 44px; height: 44px;          /* the target, not the glyph */
  align-items: center; justify-content: center;
  flex-direction: column; gap: 5px;
  padding: 0; margin: 0;
  background: none; border: 0; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.mnav-trigger .mnav-bar {
  display: block; width: 22px; height: 1.5px;
  background: var(--swort-white, #fff);
  transition: transform 220ms cubic-bezier(.2,.8,.2,1), opacity 160ms linear;
}
.mnav-trigger:focus-visible { outline: 2px solid var(--focus-ring, #fff); outline-offset: 3px; }

/* ── Root ──────────────────────────────────────────────────────────────── */
.mnav {
  position: fixed; inset: 0;
  z-index: 1000;
  display: block;
}
/* `hidden` is removed by script on first open. Until then the component is out
   of the accessibility tree AND out of the hit-test, with no reliance on
   opacity alone, which would leave invisible links tappable. */
.mnav[hidden] { display: none; }

.mnav-scrim {
  position: absolute; inset: 0;
  z-index: 1000;
  background: rgba(0, 0, 0, 0.72);
  opacity: 0;
  transition: opacity 220ms cubic-bezier(.2,.8,.2,1);
}

/* Bounded sheet, not a full-bleed cover.

   With `inset: 0` the panel sat exactly on top of the scrim, which is also
   inset:0, so the scrim had no exposed area and "tap outside to close" had no
   target at all. Verified by hit test: elementFromPoint at the scrim's centre
   returned the panel. Capping the height leaves a real dismiss region below the
   sheet and gives the component a deliberate edge rather than a full black
   wash. svh so mobile browser chrome cannot push the bottom off-screen. */
.mnav-panel {
  position: absolute;
  inset: 0 0 auto 0;
  max-height: 86vh;
  max-height: 86svh;         /* svh last so it wins where supported */
  z-index: 1010;
  display: flex; flex-direction: column;
  background: var(--swort-black, #050505);
  /* Notch and home-indicator safety. max() so a device without insets still
     gets real padding rather than zero. */
  padding-top:    max(1rem, env(safe-area-inset-top));
  padding-right:  max(1.25rem, env(safe-area-inset-right));
  padding-bottom: max(1.25rem, env(safe-area-inset-bottom));
  padding-left:   max(1.25rem, env(safe-area-inset-left));
  opacity: 0;
  transform: translateY(-18px);
  transition: opacity 240ms cubic-bezier(.2,.8,.2,1),
              transform 280ms cubic-bezier(.2,.8,.2,1);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  border-bottom: 1px solid var(--swort-border-dark, #1c1c1c);
}

/* Subtle black-on-black technical field. Static, pointer-inert, no glow. */
.mnav-panel::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image:
    linear-gradient(to right, rgba(255,255,255,0.028) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255,255,255,0.028) 1px, transparent 1px);
  background-size: 44px 44px;
  mask-image: radial-gradient(ellipse at 50% 0%, #000 30%, transparent 78%);
  -webkit-mask-image: radial-gradient(ellipse at 50% 0%, #000 30%, transparent 78%);
}

.mnav[data-open="true"] .mnav-scrim { opacity: 1; }
.mnav[data-open="true"] .mnav-panel { opacity: 1; transform: translateY(0); }

/* ── Head: wordmark + close ────────────────────────────────────────────── */
.mnav-head {
  position: relative; z-index: 1020;
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex: none;
  padding-bottom: 1.25rem;
  border-bottom: 1px solid var(--swort-border-dark, #1c1c1c);
}
.mnav-mark { display: block; height: 22px; width: auto; }

/* The control that was unreachable. Explicitly above the panel, its own
   stacking level, its own hit area, and never inside an inert subtree. */
.mnav-close {
  position: relative;
  z-index: 1020;
  pointer-events: auto;
  width: 44px; height: 44px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; margin: 0;
  background: none; border: 1px solid var(--swort-border-dark, #1c1c1c);
  color: var(--swort-white, #fff);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  transition: border-color 160ms linear, background 160ms linear;
}
.mnav-close:hover { border-color: #5a5a5a; background: #131315; }
.mnav-close:focus-visible { outline: 2px solid var(--focus-ring, #fff); outline-offset: 2px; }
.mnav-close svg { width: 16px; height: 16px; display: block; }

/* ── Links ─────────────────────────────────────────────────────────────── */
.mnav-list {
  position: relative; z-index: 1011;
  list-style: none; margin: 0; padding: 0.5rem 0 0;
  flex: 1 1 auto;
}
.mnav-item { border-bottom: 1px solid rgba(255,255,255,0.06); }
.mnav-link {
  display: flex; align-items: center; gap: 0.75rem;
  min-height: 56px;                          /* comfortable thumb target */
  font-family: 'Bebas Neue', sans-serif; font-weight: 400;
  font-size: clamp(22px, 6vw, 30px); letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--swort-white, #fff); text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  /* Reveal: transform+opacity only, so it never reflows the list. */
  opacity: 0; transform: translateY(6px);
  transition: opacity 200ms linear, transform 240ms cubic-bezier(.2,.8,.2,1), color 160ms linear;
}
.mnav-link .mnav-idx {
  font-family: 'DM Mono', monospace; font-size: 10px; letter-spacing: 0.2em;
  color: #6f6f6f; min-width: 2.4em;
}
.mnav-link:active { color: #cfcfcf; }
.mnav-link:focus-visible { outline: 2px solid var(--focus-ring, #fff); outline-offset: -2px; }
.mnav[data-open="true"] .mnav-link { opacity: 1; transform: translateY(0); }
/* Restrained stagger. Index set inline by script so the list stays data-driven. */
.mnav[data-open="true"] .mnav-item:nth-child(1) .mnav-link { transition-delay: 40ms; }
.mnav[data-open="true"] .mnav-item:nth-child(2) .mnav-link { transition-delay: 70ms; }
.mnav[data-open="true"] .mnav-item:nth-child(3) .mnav-link { transition-delay: 100ms; }
.mnav[data-open="true"] .mnav-item:nth-child(4) .mnav-link { transition-delay: 130ms; }
.mnav[data-open="true"] .mnav-item:nth-child(5) .mnav-link { transition-delay: 160ms; }
.mnav[data-open="true"] .mnav-item:nth-child(6) .mnav-link { transition-delay: 190ms; }
.mnav[data-open="true"] .mnav-item:nth-child(n+7) .mnav-link { transition-delay: 210ms; }

/* ── Foot: one primary CTA plus quiet metadata ─────────────────────────── */
.mnav-foot {
  position: relative; z-index: 1011;
  flex: none; padding-top: 1.25rem;
  border-top: 1px solid var(--swort-border-dark, #1c1c1c);
}
.mnav-cta {
  display: flex; align-items: center; justify-content: center;
  min-height: 52px; padding: 0 1.25rem;
  font-family: 'DM Mono', monospace; font-size: 12px;
  letter-spacing: 0.18em; text-transform: uppercase; text-decoration: none;
  color: #0a0a0a; background: var(--swort-white, #fff);
  border: 1px solid var(--swort-white, #fff);
  transition: background 160ms linear, color 160ms linear;
}
.mnav-cta:hover { background: #e2e2e2; border-color: #e2e2e2; color: #0a0a0a; }
.mnav-cta:focus-visible { outline: 2px solid var(--focus-ring, #fff); outline-offset: 3px; }
.mnav-meta {
  margin: 0.9rem 0 0;
  font-family: 'DM Mono', monospace; font-size: 9px;
  letter-spacing: 0.22em; text-transform: uppercase; color: #5e5e5e;
}

/* ── Scroll lock ───────────────────────────────────────────────────────── */
/* position:fixed rather than overflow:hidden. overflow:hidden on body does not
   stop scroll in iOS Safari, and the previous implementation used exactly that.
   The script stores scrollY and restores it, so the page cannot jump to top. */
body.mnav-locked {
  position: fixed;
  width: 100%;
  overflow: hidden;
  overscroll-behavior: none;
}

/* ── Breakpoint ────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
  .mnav-trigger { display: inline-flex; }
}
@media (min-width: 769px) {
  /* Belt and braces: even if script fails to close on resize, the component
     cannot cover a desktop viewport. */
  .mnav { display: none; }
}

/* ── Reduced motion ────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .mnav-scrim, .mnav-panel, .mnav-link, .mnav-trigger .mnav-bar {
    transition-duration: 1ms !important;
    transition-delay: 0ms !important;
  }
  .mnav-panel { transform: none; }
  .mnav-link { transform: none; }
}
