/* ==========================================================================
   Site navigation

   Two behaviours are borrowed from references:

   1. monica-ellis.framer.website — the bar is just a name on the left and the
      word "Menu" a little past centre. The links live to the right of "Menu"
      but sit at opacity 0 until the group is hovered, then fade in one after
      another. Below 767px the same markup becomes a full-screen menu.

   2. untitledfilm.framer.website — the bar never picks a colour. It is painted
      with mix-blend-mode: difference over white text, so every nav item
      inverts against whatever happens to be behind it: white over the black
      page, black over the light theme, and inverted over any image that
      scrolls underneath.

   The blend is what dictates this file's structure. An element only blends
   with the backdrop of its parent stacking context, so .site-nav has to sit
   outside #scroll-wrap (which is a stacking context of its own, via
   will-change: transform, and would also trap position: fixed). Everything
   the nav must invert against therefore paints below it at the root, and
   anything that must NOT invert — the hover avatar, the mobile backdrop —
   is a sibling rather than a child.
   ========================================================================== */

.site-nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 999;
  display: flex;
  justify-content: center;

  /* Deliberately not var(--text): difference inverts this against the
     backdrop, which already yields black text in the light theme and white
     in the dark one. A theme-aware colour here would invert twice. */
  color: #fff;
  mix-blend-mode: difference;

  /* The bar spans the full width but is mostly empty. Only the two clusters
     below opt back in, so the rest of the top strip stays clickable. */
  pointer-events: none;
}

/* Blending is a paint-time effect with no fallback: where it is unsupported
   the nav would render pure white over a white theme. Vanishingly rare, but
   the cost of guarding is one rule. */
@supports not (mix-blend-mode: difference) {
  .site-nav {
    color: var(--text);
    mix-blend-mode: normal;
  }
}

/* 1240 = the 1200px content measure plus the 20px gutters .container sits in,
   so the bar's text lands on exactly the same left edge as the page below it.
   The side padding then tracks that edge at each breakpoint: 20 / 32 / 16.
   Below 992px .container declares margin:10px + padding:32px, but it is a
   centred grid item whose width:100% already fills the column, so the margin
   is absorbed and the edge it actually renders at is 32px, not 42px. These
   numbers are measured, not derived from the declarations. */

.site-nav__row {
  display: flex;
  align-items: center;
  width: 100%;
  max-width: 1240px;
  padding: 15px 20px;
}

@media screen and (max-width: 991px) {
  .site-nav__row {
    padding-left: 32px;
    padding-right: 32px;
  }
}

@media screen and (max-width: 479px) {
  .site-nav__row {
    padding-left: 16px;
    padding-right: 16px;
  }
}

/* 0.7 / 0.5 splits the bar the way the reference does: the name on the left,
   "Menu" starting a little under 60% across. */
.site-nav__brand {
  flex: 0.7 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 14px;
}

.site-nav__menu {
  flex: 0.5 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
}

.site-nav__logo,
.site-nav__toggle,
.site-nav__link {
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -.02em;
  line-height: 1.2;
  color: inherit;
}

.site-nav__logo {
  pointer-events: auto;
  display: inline-block;
  text-decoration: none;
  white-space: nowrap;
}

/* The hover target is the whole cluster, not just the word "Menu": the links
   keep their layout width while hidden, so the invisible half of the cluster
   is what the pointer crosses first on its way over. */
.site-nav__menu-inner {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 16px;
}

.site-nav__toggle {
  cursor: pointer;
  background: none;
  border: 0;
  padding: 0;
  white-space: nowrap;

  /* Both words are stacked in a one-line-tall window and the pair slides up
     to swap them. Only the mobile menu actually swaps; on desktop the label
     stays "Menu" because hovering does not commit you to anything. */
  height: 1.2em;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.site-nav__toggle-label {
  flex: none;
  display: block;
  transition: transform .45s cubic-bezier(.16, 1, .3, 1);
}

.site-nav__links {
  display: flex;
  align-items: center;
  gap: 16px;

  /* Unreachable while hidden, so a stray click in the empty half of the
     cluster can never land on a link nobody can see. */
  pointer-events: none;
}

.site-nav__link {
  text-decoration: none;
  white-space: nowrap;
  opacity: 0;
  transform: translateX(-8px);
  transition: opacity .45s ease, transform .45s cubic-bezier(.16, 1, .3, 1);
}

.site-nav.is-open .site-nav__links {
  pointer-events: auto;
}

/* 0.4 resting / 1 on hover is the reference's own treatment: the whole set
   reads as secondary until you single one out. */
.site-nav.is-open .site-nav__link {
  opacity: .4;
  transform: none;
}

.site-nav.is-open .site-nav__link:hover,
.site-nav.is-open .site-nav__link:focus-visible,
.site-nav.is-open .site-nav__link[aria-current="page"] {
  opacity: 1;
}

/* Staggered on the way in, simultaneous on the way out — a reversed stagger
   on close leaves the last link hanging after the pointer has gone. */
.site-nav.is-open .site-nav__link:nth-child(1) { transition-delay: .02s; }
.site-nav.is-open .site-nav__link:nth-child(2) { transition-delay: .06s; }
.site-nav.is-open .site-nav__link:nth-child(3) { transition-delay: .10s; }
.site-nav.is-open .site-nav__link:nth-child(4) { transition-delay: .14s; }

/* --------------------------------------------------------------------------
   Availability status

   The one place on the bar that is allowed a colour. It rides beside the name
   because that is the only cluster with room to spare: the menu cluster keeps
   its links laid out at full width even while they are invisible, so anything
   parked to the right of "Menu" would be sitting where they fade in.

   Quiet by default — a 10px label at .5 next to 14px nav text — with the
   whole attention job handed to the dot, which pulses once every three
   seconds. Motion rather than size or weight is what makes it findable
   without letting it outrank the name.
   -------------------------------------------------------------------------- */

.site-nav__status {
  --status-dot: #4ade80;
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  text-decoration: none;
  color: inherit;
  white-space: nowrap;
}

/* Green through the difference blend.

   The nav inverts against whatever is behind it, so the literal #4ade80 above
   comes back as green over the black theme but as magenta over the light one.
   The fix is to feed the blend the colour that *resolves* to green: its
   complement #b5217f over white, which difference turns back into the same
   #4ade80. Scrolling a work cover under the bar still shifts it, exactly as
   it shifts every other item up here — that is the bar's whole conceit, not a
   bug in the dot. */

html[data-theme="light"] .site-nav__status {
  --status-dot: #b5217f;
}

/* No blend, nothing to pre-invert against. */
@supports not (mix-blend-mode: difference) {
  html[data-theme="light"] .site-nav__status {
    --status-dot: #4ade80;
  }
}

/* The dot is drawn by the two pseudo-elements rather than the span's own
   background, purely for paint order: ::before is the halo and has to sit
   *under* the solid dot, and a background on the span itself would paint
   below both. */

.site-nav__status-dot {
  position: relative;
  flex: none;
  width: 6px;
  height: 6px;
}

.site-nav__status-dot::before,
.site-nav__status-dot::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--status-dot);
}

.site-nav__status-dot::before {
  animation: nav-status-ping 3s cubic-bezier(.16, 1, .3, 1) infinite;
}

/* Held at 0 for the last third of the cycle so the ping reads as an
   occasional signal rather than a continuous throb. */
@keyframes nav-status-ping {
  0% {
    transform: scale(1);
    opacity: .55;
  }
  70%, 100% {
    transform: scale(3.4);
    opacity: 0;
  }
}

.site-nav__status-label {
  font-family: var(--font-sans);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .08em;
  line-height: 1.2;
  text-transform: uppercase;

  /* Same resting/hover treatment as the menu links, so the bar has one idea
     of what "secondary" looks like. */
  opacity: .5;
  transition: opacity .3s ease;
}

.site-nav__status:hover .site-nav__status-label,
.site-nav__status:focus-visible .site-nav__status-label {
  opacity: 1;
}

/* --------------------------------------------------------------------------
   Glass behind the bar

   A liquid-glass strip pinned under the nav: whatever scrolls past underneath
   is blurred and faintly tinted rather than cut off sharply, so the bar reads
   as a pane of glass rather than a hard edge. It sits below .site-nav in the
   same way the mobile backdrop does — as a plain layer for the blend to
   invert against, never as a background on the nav itself.

   The blur is faded out with a mask rather than left as a hard-edged rect:
   a uniformly blurred block would read as a solid bar, where the mask lets
   the glassiness taper into the page the way a real pane thins toward its
   edge. Both the mask and the tint use a multi-stop curve rather than one
   linear ramp — a straight fade still shows its own end as a faint line,
   while a curve that spends most of its length in the single-digit percents
   lets the effect run out of strength well before it runs out of room.

   The tint is kept deliberately faint. The nav text has no colour of its
   own — it is white blended with mix-blend-mode: difference, so its actual
   painted value is 255 minus whatever is behind it. A flat tint mixed in at
   any real strength drags that backdrop toward mid-grey, and mid-grey is
   the one value difference can't invert its way out of (255 - 128 ≈ 128:
   the text becomes the same value as the thing it's sitting on). Blur and
   saturation don't have this problem — they redistribute and amplify the
   backdrop's own colour rather than pulling it toward a fixed one — so the
   filter carries the glass effect and the tint only adds the faintest wash.

   The filter is weighted toward saturation rather than blur, which is what
   separates Apple's glass from a plain frosted panel: frosting hides what is
   behind it, glass bends and intensifies it. A short blur keeps shapes
   readable through the pane, the saturation boost gives the colour that
   passes through more life than it had, and the slight contrast bump pushes
   values away from the mid-grey the nav's difference blend goes blind in.
   -------------------------------------------------------------------------- */

.site-nav-glass {
  --glass-rgb: 0, 0, 0;

  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 997;
  height: 220px;
  pointer-events: none;

  backdrop-filter: blur(9px) saturate(200%) contrast(106%);
  -webkit-backdrop-filter: blur(9px) saturate(200%) contrast(106%);

  background: linear-gradient(
    to bottom,
    rgba(var(--glass-rgb), .16) 0%,
    rgba(var(--glass-rgb), .12) 18%,
    rgba(var(--glass-rgb), .07) 34%,
    rgba(var(--glass-rgb), .04) 50%,
    rgba(var(--glass-rgb), .018) 66%,
    rgba(var(--glass-rgb), .006) 82%,
    rgba(var(--glass-rgb), 0) 100%
  );

  mask-image: linear-gradient(
    to bottom,
    #000 0%,
    #000 10%,
    rgba(0, 0, 0, .7) 24%,
    rgba(0, 0, 0, .42) 40%,
    rgba(0, 0, 0, .22) 56%,
    rgba(0, 0, 0, .09) 72%,
    rgba(0, 0, 0, .025) 88%,
    transparent 100%
  );
  -webkit-mask-image: linear-gradient(
    to bottom,
    #000 0%,
    #000 10%,
    rgba(0, 0, 0, .7) 24%,
    rgba(0, 0, 0, .42) 40%,
    rgba(0, 0, 0, .22) 56%,
    rgba(0, 0, 0, .09) 72%,
    rgba(0, 0, 0, .025) 88%,
    transparent 100%
  );
}

html[data-theme="light"] .site-nav-glass {
  --glass-rgb: 255, 255, 255;
}

@media screen and (max-width: 767px) {
  .site-nav-glass {
    height: 170px;
  }
}

/* No blur support: fall back to just the tint gradient rather than nothing. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-nav-glass {
    background: linear-gradient(
      to bottom,
      rgba(var(--glass-rgb), .35) 0%,
      rgba(var(--glass-rgb), .1) 40%,
      rgba(var(--glass-rgb), .02) 70%,
      rgba(var(--glass-rgb), 0) 100%
    );
  }
}

/* --------------------------------------------------------------------------
   Backdrop for the mobile menu

   A sibling painted just under the nav rather than a background on the nav
   itself: the nav's own pixels are blended, so a background there would be
   inverted along with the text and the panel would come out black in the
   light theme. As a separate layer underneath, it is simply part of the
   backdrop the nav inverts against — which is what makes the open menu's
   text resolve to the right colour in both themes for free.
   -------------------------------------------------------------------------- */

.site-nav-backdrop {
  position: fixed;
  inset: 0;
  z-index: 998;
  background: var(--bg);
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
  transition: opacity .4s ease, visibility 0s linear .4s;
}

.site-nav-backdrop.is-active {
  opacity: 1;
  visibility: visible;
  transition: opacity .4s ease, visibility 0s;
}

/* --------------------------------------------------------------------------
   Avatar that follows the cursor while the name is hovered

   Same mechanic as the cover previews on /work (js/index-hover.js): a fixed
   element parked off-screen, moved by transform so the follow stays on the
   compositor. Kept out of .site-nav because a photo pushed through
   difference would come back as its own negative.
   -------------------------------------------------------------------------- */

.nav-avatar {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1000;
  width: 104px;
  height: 104px;
  border-radius: 50%;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transform: translate(-9999px, -9999px);
  transition: opacity .3s ease, transform .5s cubic-bezier(.16, 1, .3, 1);
  box-shadow: 0 20px 48px rgba(0, 0, 0, .4);
  background: var(--bg);
}

.nav-avatar.is-active {
  opacity: 1;
}

.nav-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* No cursor to follow. The script also bails out, but this keeps the element
   out of the way even if it never runs. */
html.has-touch .nav-avatar {
  display: none;
}

/* --------------------------------------------------------------------------
   Page header left behind on the case pages

   These pages used to open with a three-column bar: "← Home" | project title |
   Email + LinkedIn. The outer two columns are what the site nav now provides,
   so only the title block survives, re-aligned to the content measure.
   -------------------------------------------------------------------------- */

/* Margins and padding are copied from .container, breakpoint for breakpoint,
   rather than approximated: the header and the content below it have to sit on
   the same left edge, and .container reaches that edge with a different
   margin/padding mix at every width. */

.case-header {
  width: 100%;
  max-width: 1200px;
  margin: 104px 20px 0;
}

/* The case title is the page's one display line, so it takes the same size
   and weight as .page-hero__label on /about, /work and /contact — before
   this it inherited the global h1 (16px/400) and sat at the same size as the
   body copy underneath it. */

.case-header .modernbody {
  margin-top: 0;
  margin-bottom: 12px;
  font-size: 34px;
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: -.01em;
}

@media screen and (max-width: 479px) {
  .case-header .modernbody {
    font-size: 28px;
  }
}

@media screen and (max-width: 991px) {
  .case-header {
    margin-left: 10px;
    margin-right: 10px;
    padding-left: 32px;
    padding-right: 32px;
  }
}

@media screen and (max-width: 479px) {
  .case-header {
    margin-left: 0;
    margin-right: 0;
    padding-left: 16px;
    padding-right: 16px;
  }
}

/* --------------------------------------------------------------------------
   Mobile: the same markup, as a full-screen menu
   -------------------------------------------------------------------------- */

@media screen and (max-width: 767px) {
  .site-nav__row {
    padding-top: 14px;
    padding-bottom: 14px;
  }

  /* The name and "Menu" go to opposite ends; the link list is pulled out of
     the flow below, so the row is just those two. */
  .site-nav__brand {
    flex: 1 1 auto;

    /* The status drops under the name rather than sitting beside it: side by
       side, the pair is wider than a phone and shoves "Menu" off the row.
       Stacked, it reads as a signature block and still fits at 320px. */
    flex-direction: column;
    align-items: flex-start;
    gap: 5px;
  }

  .site-nav__menu {
    flex: 0 0 auto;
    justify-content: flex-end;
  }

  .site-nav.is-open .site-nav__toggle-label {
    transform: translateY(-100%);
  }

  .site-nav__links {
    position: fixed;
    inset: 0;
    flex-direction: column;
    justify-content: center;
    align-items: flex-start;
    gap: 4px;

    /* Matches .site-nav__row's own side padding at these widths, so the open
       menu's links stack under the name rather than beside it. */
    padding: 0 32px;

    /* Behind the row, so the row's "Close" stays legible over the top of the
       list rather than the list scrolling through it. */
    z-index: -1;
  }

  .site-nav__link {
    font-size: clamp(34px, 10vw, 48px);
    font-weight: 400;
    letter-spacing: -.035em;
    line-height: 1.18;
    transform: translateY(14px);
  }

  /* Full strength on a phone: there is no pointer to single one out with, so
     the desktop's dimmed resting state would just look like a bug. */
  .site-nav.is-open .site-nav__link {
    opacity: 1;
    transform: none;
  }
  /* The links are laid out even while closed, so without this they would be
     tabbable behind a shut menu. */
  .site-nav:not(.is-open) .site-nav__links {
    visibility: hidden;
  }

  .case-header {
    margin-top: 80px;
  }
}

@media screen and (max-width: 479px) {
  .site-nav__links {
    padding: 0 16px;
  }
}

/* --------------------------------------------------------------------------
   Home page: a narrower bar

   Every other page reads at 1200px, but the home page's column is 562px, so
   the shared bar starts its name some 300px to the left of the text below it.
   This modifier re-runs the same alignment against .container's 562px box:
   602 = that column plus the 20px gutters it sits in.

   The numbers are measured off the rendered page rather than read off the
   declarations. Below 992px .container declares margin:10px + padding:32px;
   the margin survives while the viewport has room for its centred box (edge
   at 42px), but is absorbed once the viewport drops below 582px, where the
   edge becomes a flat 32px.

   The bar also switches to space-between here. The 0.7/0.5 split that puts
   "Menu" 58% across a 1200px bar would push the expanded links ~150px past
   the right edge of a 562px column, which is the opposite of aligning to it;
   packing the cluster against the right edge keeps the whole bar inside the
   column at every width.
   -------------------------------------------------------------------------- */

.site-nav--narrow .site-nav__row {
  max-width: 602px;
  justify-content: space-between;
}

.site-nav--narrow .site-nav__brand,
.site-nav--narrow .site-nav__menu {
  flex: 0 0 auto;
}

@media screen and (max-width: 991px) {
  .site-nav--narrow .site-nav__row {
    max-width: 582px;
    padding-left: 42px;
    padding-right: 42px;
  }
}

@media screen and (max-width: 581px) {
  .site-nav--narrow .site-nav__row {
    padding-left: 32px;
    padding-right: 32px;
  }
}

@media screen and (max-width: 479px) {
  .site-nav--narrow .site-nav__row {
    padding-left: 16px;
    padding-right: 16px;
  }
}

/* The open menu tracks the same column, so its links stack under the name
   instead of against the screen edge. */

@media screen and (max-width: 767px) {
  .site-nav--narrow .site-nav__links {
    max-width: 582px;
    margin: 0 auto;
    padding: 0 42px;
  }
}

@media screen and (max-width: 581px) {
  .site-nav--narrow .site-nav__links {
    padding: 0 32px;
  }
}

@media screen and (max-width: 479px) {
  .site-nav--narrow .site-nav__links {
    padding: 0 16px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .site-nav__link,
  .site-nav__toggle-label,
  .nav-avatar,
  .site-nav-backdrop {
    transition-duration: .01ms;
  }

  .site-nav.is-open .site-nav__link {
    transition-delay: 0s;
  }

  /* The dot stays; only its ping goes. Colour alone still marks the status. */
  .site-nav__status-dot::before {
    animation: none;
  }
}

@media (prefers-reduced-transparency: reduce) {
  .site-nav-glass {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}
