/* ================================================================
   COMPONENT: NAVBAR
   Default: transparent, full-width, dark text on white background.
   Scrolled (.is-scrolled): floating dark pill, frosted glass.

   Centering trick: left: 50% + translateX(-50%) are constant.
   Only `width` and `top` animate → smooth symmetric shrink.
   ================================================================ */

.navbar-wrap {
  position: fixed;
  top: 16px;
  left: 50%;
  width: min(95vw, 1440px);
  max-width: 1440px;
  transform: translateX(-50%);
  z-index: var(--z-navbar);
  /* Reserved at rest so `.is-scrolled`'s hairline has something to fade FROM.
     With no border here the width went 0 → 1px, which cannot be transitioned
     smoothly and also nudged the inner content by 1px on every scroll. Under
     the global `border-box` the outer geometry is unchanged, so gutter
     alignment is unaffected. */
  border: 1px solid transparent;
  pointer-events: none; /* transparent areas don't block scroll/clicks */
  /* The navbar has NO page-transition special-casing at all. The page swap
     happens behind an opaque curtain (curtain.css), so nothing about the
     navbar is ever captured, snapshotted, or mid-animation on screen during
     a navigation. Everything that used to live here — a `view-transition-name`,
     a pre-navigation lift, a staged arrival, a frost flatten — existed only to
     work around the old View Transition and is gone with it. */
  /* Geometry (0.55s) is deliberately slower than surface (0.45s) — the pill
     finishes forming after its material has settled. Do not flatten them to
     one duration; the offset is what stops the morph reading as a single
     mechanical step.
     `border-color` is in the list because `.is-scrolled` adds a 1px border:
     without it the hairline popped in at full opacity on frame 1 while the
     shape was still travelling. */
  transition:
    width            0.55s var(--ease-out),
    border-radius    0.55s var(--ease-out),
    background-color 0.45s var(--ease-out),
    border-color     0.45s var(--ease-out),
    backdrop-filter  0.45s var(--ease-out),
    -webkit-backdrop-filter 0.45s var(--ease-out);
}

/* ── Scrolled: pill at 66% of navbar width, no shadow ──────────── */
/* `html.nav-prescrolled` is the pre-paint variant: when the page loads
   already below the fold (e.g. `#work` on the way back from a case page),
   the head script sets it so the pill is already the resting shape when the
   curtain lifts — no snap from top-bar to pill afterwards. `navbar.js` drops
   the class once it takes over scroll tracking. */
.navbar-wrap.is-scrolled,
html.nav-prescrolled .navbar-wrap {
  --mouse-x: 50%;
  width: min(62.7vw, 952px); /* 66% of 1440 ≈ 950, use 952 (119×8) */
  border-radius: var(--radius-pill);
  /* Warm glass, not a dark slab — see `--nav-glass` in tokens.css for why
     this is the one translucent surface on the site. */
  background: var(--nav-glass);
  border: 1px solid var(--nav-glass-edge);
  /* Blur radius kept modest: past ~20px the frosted look on a thin bar is
     indistinguishable, but the GPU resampling cost keeps climbing — this
     filter re-renders on every scrolled frame.
     `saturate(3.8) brightness(1.12)` went with the dark fill: it was pushing
     colour back INTO a slab that was eating it. Behind a light fill the same
     values turn whatever scrolls under the pill garish, and the brightness
     lift blows the paper out. 1.6 keeps the glass from going flat grey. */
  backdrop-filter: blur(20px) saturate(1.6);
  -webkit-backdrop-filter: blur(20px) saturate(1.6);
}

/* ── Cursor-tracking border spotlight — masked to border only ──── */
/* The mask XOR cuts out the interior so the gradient shows        */
/* only in the 1px border strip, never inside the pill.            */
.navbar-wrap.is-scrolled::before {
  content: '';
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  padding: 1px;
  background: radial-gradient(
    ellipse 160px 100% at var(--mouse-x) 50%,
    var(--nav-edge-mark) 0%,
    var(--nav-edge-mark-soft) 60%,
    transparent 80%
  );
  -webkit-mask:
    linear-gradient(#fff 0 0) content-box,
    linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask-composite: exclude;
  opacity: 0;
  transition: opacity var(--dur-normal) var(--ease-out);
  pointer-events: none;
}

.navbar-wrap.is-scrolled.is-hovered::before {
  opacity: 1;
}

/* ── Inner nav — re-enables pointer events, fixed height (no vertical movement) ── */
.navbar {
  position: relative;
  height: 56px;   /* slimmer */
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--gutter-inner);
  max-width: var(--page-max);
  margin: 0 auto;
  pointer-events: auto;
  transition: padding 0.55s var(--ease-out);
}

.navbar-wrap.is-scrolled .navbar {
  padding: 0 16px;
}

/* ── Logo (Webflow-style size) ─────────────────────────────────── */
.navbar__logo {
  display: flex;
  align-items: center;
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  transition: opacity var(--dur-fast) var(--ease-out);
}

.navbar__logo svg {
  width: 100%;
  height: 100%;
}

/* ONE INK IN BOTH STATES. There used to be a `fill: #ffffff` override for the
   scrolled pill, because the pill was a dark slab. On warm glass a white mark
   is a hole, so the override is gone rather than re-valued — the logo is the
   same ink at rest and scrolled, which is one less thing to keep in step. The
   `transition` stays: it is what carries the fill if the token ever changes
   per state again. */
.navbar__logo-path {
  fill: var(--gray-900);
  transition: fill var(--dur-slow) var(--ease-out);
}

/* ── Links: a content switcher ────────────────────────────────── */
/* THE LINKS ARE SLOTS IN A TRACK. The pill is the track; each link is a
   rounded slot that is empty at rest and fills with the lighter paper stock
   under the pointer. Reference: the floating nav on gustaffurusten.se — a
   translucent light track, `border-radius: 100px` on both track and slot,
   6px of track padding, 4px between slots.

   Two things there were deliberately NOT copied:

   1. **Equal-width slots.** The reference locks every slot to 80px, which is
      what makes a segmented control read as one. Ours are three unequal words
      in a right-aligned cluster, and two of them are anchors while Resume
      opens a PDF in a new tab — they are not mutually exclusive states, so a
      segmented control would be claiming a behaviour the nav does not have.
      The slots are padded to their content instead.
   2. **`opacity: 0.4` at rest.** Dimming navigation to 40% to make the hover
      read louder trades legibility for contrast between states. The ink ramp
      already separates levels without touching legibility, and here it does
      not need to: the FILL is the whole state change, and the label stays at
      `--text-primary` throughout.

   The gap went 40px → 4px. A slot needs to look like it belongs to a track;
   40px of air between three pills reads as three unrelated buttons. The width
   the gap gave up comes back as slot padding, so the cluster's total width
   barely moves (measured: +12px). */
.navbar__links {
  display: flex;
  align-items: center;
  gap: 4px;
}

/* ONE DEVICE, replacing two. This hover used to stack an underline that grew
   left-to-right AND a text carousel (the label slid out right while a clone
   slid in from the left, `.nav-animation` / `.nav-original` / `.nav-hover`,
   built by `initNavCarousel`). Both are gone. A slot filling, a rule growing,
   and the label itself travelling are three answers to one question, and the
   two that went are the borrowed ones: the pill is the switcher's own
   gesture, and it is the one that says *this slot is live*. See the project
   card, which lost six stacked treatments on the same argument. */
.navbar__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  isolation: isolate;   /* own stacking context — lets ::before sit at z-index:-1 */
  height: 36px;
  padding: 0 14px;
  border-radius: var(--radius-pill);
  font-family: var(--type-body2-font);
  font-size: var(--type-body2-size);
  line-height: var(--type-body2-line);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-wide);
  color: var(--text-primary);
  transition: color var(--dur-slow) var(--ease-out);
}

/* THE SLOT FILLS THE WAY THE FOOTER PILL FILLS — one hover gesture, one ink,
   on the whole site. `clip-path: circle(0% at 100% 100%)` → `circle(200%)`,
   bottom-right origin, `--ease-out`, and because it is a plain CSS transition
   rather than a keyframe animation, mouse-OUT is the same sweep played
   backwards. It replaced a `background-color` cross-fade, which reached the
   same colour by a completely different motion.

   IT FILLS WITH INK, not with paper. The slot was `--paper` at 94% against a
   `--paper-alt` track — a lighter chip on a light track, the reference's own
   pairing. That works only while the track exists: **at rest the bar is
   transparent over the page**, so a paper chip was paper on paper and the
   hover was invisible until you scrolled past the threshold. `--text-primary`
   with the label flipping to `--fill-contrast` is `.btn-pill`'s exact
   pairing, it reads in both states, and it means the site has one fill rather
   than a dark one downstairs and a light one up here.

   `--ease-out`, never `--ease-spring`: a spring overshoots past a radius that
   swallowed the element ~30px ago, so the overshoot is invisible and only adds
   dead time (the note on `.social-link::before` makes the same point).

   `--dur-slow`, not `--dur-fill`. The fill's own length (900ms) is tuned to
   the big surfaces — the footer pill and the Record row. This slot's diagonal
   is 73px against the pill's 222px, and at 900ms the label would still be
   turning white long after the pointer arrived. Speed cannot be held constant
   across a 10× size range; what carries the consistency is the ORIGIN, the
   SHAPE and the INK. The colour transition matches the fill's length so the
   label inverts as the sweep passes it, which is what `.btn-pill` does. */
.navbar__link::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background-color: var(--text-primary);
  clip-path: circle(0% at 100% 100%);
  transition: clip-path var(--dur-slow) var(--ease-out);
  z-index: -1;
}

/* Keyboard gets the fill too. The `:focus-visible` ring (focus.css) is an
   outline on the same element, so the two compose rather than collide. */
.navbar__link:hover::before,
.navbar__link:focus-visible::before {
  clip-path: circle(200% at 100% 100%);
}

.navbar__link:hover,
.navbar__link:focus-visible {
  color: var(--fill-contrast);
}

/* -- Responsive ------------------------------------------------- */
@media (max-width: 768px) {
  .navbar-wrap {
    width: 100%;
    top: 6px;
  }

  .navbar {
    height: 44px;
    padding: 0 var(--gutter);
  }

  .navbar__links {
    gap: 2px;
  }

  /* Slot shrinks with the bar (44px tall here, not 56). Padding drops with it
     so three slots plus the logo still clear the 90vw pill. */
  .navbar__link {
    height: 30px;
    padding: 0 10px;
    letter-spacing: var(--tracking-normal);
  }

  /* TOUCH TARGET ≥44px WITHOUT GROWING THE PILL. The bar is only 44px tall
     here, so a slot that actually measured 44 would fill it edge to edge and
     stop reading as a chip inside a track. The visual stays 30px and an
     invisible expander carries the hit area to 44 — the target grows, the
     drawn shape does not. (The old text-only links were ~20px tall, so this
     is the first time the nav clears the threshold at all.) */
  .navbar__link::after {
    content: '';
    position: absolute;
    inset: -7px 0;
  }

  .navbar__logo {
    width: 26px;
    height: 26px;
  }

  .navbar-wrap.is-scrolled {
    width: 90vw;
  }

  .navbar-wrap.is-scrolled .navbar {
    padding: 0 12px;
  }
}
