/* ═══════════════════════════════════════════════════════════════════
   Glass dock — ported from vengenceui.com/r/glass-dock.json.

   That component is React: framer-motion for the springs, GSAP's
   MorphSVGPlugin for the icons. Neither is in this project, so the
   mechanics are rebuilt here in CSS with js/dock.js doing only the two
   things CSS cannot: measuring where the hovered icon is, and swapping
   the label text. Everything that moves, moves on a CSS transition —
   off the main thread, and interruptible, which matters on a row of
   targets a pointer sweeps across.

   What was kept, with the source's own numbers:
     · translucent panel, heavy backdrop blur, big soft shadow
     · icon lifts 3px and scales to 1.1 on hover, 0.95 on press
     · ONE tooltip that slides between icons rather than one per icon
     · the label swaps with a directional slide from ±35px through a
       6px blur, 300ms — the detail that makes the thing feel expensive

   What could not be kept: the SVG path morphing. MorphSVGPlugin is a
   GSAP Club plugin, it is not vendored here, and the source itself
   warns it may be absent. The visible half of that effect — the glyph
   collapsing to nothing and springing back — is reproduced below with
   scale and opacity; only the shape change between the two states is
   missing.

   It sits above the wordmark rather than on it. Worth knowing: with
   nothing behind it but the footer's flat ground, the backdrop blur has
   nothing to do — the panel reads as a translucent pill, not as glass.
   The filter is kept because it costs nothing where it does nothing and
   pays immediately if anything is ever put behind it; dropping the dock
   onto the letters is the version where the effect earns its name.
   ═══════════════════════════════════════════════════════════════════ */

/* Above the word, centred, in normal flow.

   position:relative and auto margins on a max-content box rather than a
   flex parent doing the centring: --x is the hovered icon's offsetLeft
   inside .dock__row, and .dock__tip is positioned against .dock. The two
   only agree while the row sits at 0,0 in here, so this box gets centred
   and the row never moves inside it. Relative for the same reason — it
   has to stay the tooltip's offset parent. */
.dock{
  position:relative;
  width:max-content;
  max-width:100%;
  margin-inline:auto;
  margin-bottom:clamp(1.75rem,5vh,3.25rem);
}

/* ═══════════════════════════════════════════════════════════════════
   .dock--nav — the dock doing the navbar's job (contact.html)

   Dropped into the middle of the site header, between the brand and the
   burger. Absolutely positioned off the header's centre rather than
   left as a flex child, because space-between would centre it on the
   space left over by two lockups of different widths — which is not the
   middle of the page, and reads as a misalignment rather than as a
   choice.

   This is where the glass finally earns its name: the header is fixed,
   the page scrolls under it, and there is real content behind the blur.
   ═══════════════════════════════════════════════════════════════════ */
/* Scoped to .nav on purpose. style.css carries `.nav > *{position:
   relative}` to lift the header's children over its own gradient, and
   that selector has the same weight as a bare .dock--nav — this file
   loading second is the only thing that would decide it. One more class
   settles it outright. */
.nav .dock--nav{
  position:absolute;
  left:50%;
  transform:translateX(-50%);
  margin:0;
  z-index:1;
}

/* Tighter than the footer dock — it has a header's height to live
   inside, not a whole section. The 44px targets do not move; only the
   air around them does. */
.dock--nav .dock__row{
  gap:.35rem;
  padding:.5rem .8rem;
  border-radius:.85rem;
}

/* The tooltip flips below. In a top bar there is nothing above the dock
   but the edge of the window, and a label at bottom:100% would be off
   screen entirely. */
.dock--nav .dock__tip{
  bottom:auto;
  top:100%;
  margin-bottom:0;
  margin-top:12px;
  /* enters from above now, so it still travels away from the icon it
     belongs to rather than towards it */
  transform:translateX(calc(var(--x, 0px) - 50%)) translateY(-8px) scale(.92);
}
.dock--nav .dock__tip.is-open{
  transform:translateX(calc(var(--x, 0px) - 50%)) translateY(0) scale(1);
}

.dock__row{
  position:relative;
  display:flex; align-items:center;
  gap:1rem;                      /* gap-4 */
  padding:1rem 1.5rem;           /* py-4 px-6 */
  border-radius:1rem;            /* rounded-2xl */

  /* The glass. The source ships bg-white/80 or black/80 with
     backdrop-blur-xl and leans on its own .glass-border for the edge;
     that class is not in the registry file, so the edge is built here
     the usual way — a hairline inset all round and a brighter one along
     the top, which is what reads as a lit rim.

     A light wash and not the source's dark one. Theirs sits over page
     content; this sits on the footer's own #16161A, and a panel darker
     than the ground it is on is a hole, not an object. Lifting it a few
     percent toward bone is what makes it read as raised — which is the
     whole job of the rim and the shadow below. */
  background:rgba(74,59,41,.055);
  -webkit-backdrop-filter:blur(20px) saturate(1.4);
          backdrop-filter:blur(20px) saturate(1.4);
  box-shadow:
    0 18px 44px -20px rgba(74,59,41,0.145),
    inset 0 0 0 1px rgba(74,59,41,.10),
    inset 0 1px 0 rgba(255,255,255,.9);
}

/* No blur available (Firefox without the flag, older Safari). Nothing
   to compensate for while the ground behind is flat — but the moment
   the dock is moved back over the letters, an unblurred 5% wash would
   show the word straight through it. A solid tint that matches what the
   blur resolves to keeps that case honest. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .dock__row{ background:#EFEBE4 }
}

/* ── the icons ────────────────────────────────────────────────────── */
.dock__i{
  position:relative;
  display:flex; align-items:center; justify-content:center;
  /* 44px, not the source's 40 — this is a real contact link and a
     finger has to be able to hit it. The gap above still leaves 16px
     between neighbours. */
  width:44px; height:44px;
  flex:0 0 auto;
  color:var(--bone-dim);
  cursor:pointer;
  /* Not a spring, but the closest single curve to the source's
     stiffness 300 / damping 24: fast out, one small overshoot, settled.
     A real spring would need a JS tick per frame for a hover state seen
     dozens of times a session. */
  transition:color .25s var(--ease-out),
             transform .28s cubic-bezier(.34,1.4,.64,1);
}
.dock__glyph{
  width:22px; height:22px;       /* h-[22px] w-[22px] */
  fill:currentColor;
  pointer-events:none;           /* the <a> owns the whole 44px box */
}

@media (hover:hover) and (pointer:fine){
  .dock__i:hover{ color:var(--bone); transform:translateY(-3px) scale(1.1) }
}
/* Keyboard gets the same lift the pointer does — the tooltip is driven
   by focus as well, and a label appearing over an icon that has not
   moved reads as pointing at the wrong one. */
.dock__i:focus-visible{ color:var(--bone); transform:translateY(-3px) scale(1.1) }

.dock__i:active{ transform:translateY(-3px) scale(.95) }

/* ── the morph substitute ─────────────────────────────────────────── */
/* The source collapses the glyph to scale .25 / opacity 0 in 100ms,
   morphs the path while it is invisible, then springs it back out with
   elastic.out(1,.9). Without MorphSVGPlugin the middle step is gone,
   but the collapse and the elastic return are the part you feel.

   Shortened from the source's ~800ms to 620. This fires on every hover
   of a five-icon row; at 800ms a pointer crossing the dock leaves four
   glyphs still bouncing behind it.

   An animation and not a transition, because it has to run out and back
   in one gesture. Re-entering restarts it from the top, which for a pop
   is the wanted behaviour — js/dock.js retriggers it by reflow. */
@keyframes dockPop{
  0%   { transform:scale(1);   opacity:1 }
  16%  { transform:scale(.25); opacity:0 }
  100% { transform:scale(1);   opacity:1 }
}
.dock__i.is-pop .dock__glyph{
  animation:dockPop .62s cubic-bezier(.16,1.36,.3,1);
}

/* ── the tooltip ──────────────────────────────────────────────────── */
/* One element for the whole row. It slides to the hovered icon instead
   of each icon owning a label that fades in place — that slide is the
   thing being copied here, and it only works if there is exactly one. */
.dock__tip{
  position:absolute;
  left:0; bottom:100%;
  margin-bottom:14px;
  /* --x is the hovered icon's centre, written by js/dock.js. The -50%
     centres the plate on it. */
  transform:translateX(calc(var(--x, 0px) - 50%)) translateY(8px) scale(.92);
  opacity:0;
  pointer-events:none;

  /* Two timings on purpose. Coming in, the plate settles softly
     (stiffness 120 / damping 18 in the source, so a slower, looser
     spring than the icons get); once it is up, the slide from icon to
     icon has to keep pace with the pointer. js/dock.js adds .is-open,
     which swaps in the faster curve below. */
  transition:transform .34s cubic-bezier(.22,1.2,.36,1),
             opacity .18s var(--ease-out);
}
.dock__tip.is-open{
  transform:translateX(calc(var(--x, 0px) - 50%)) translateY(0) scale(1);
  opacity:1;
  transition-duration:.26s, .16s;
}

.dock__tip-in{
  position:relative;
  display:block;
  /* Fixed, because the label inside is absolutely positioned so the old
     and new can cross without one pushing the other. A width that
     animated would be a layout animation on every hover — the one thing
     worth refusing here. Wide enough for the longest label. */
  width:118px; height:34px;
  border-radius:.5rem;
  background:var(--bone);
  box-shadow:0 8px 22px -10px rgba(74,59,41,0.153);
  overflow:hidden;
}

/* Both the label leaving and the label arriving are this element; the
   direction is written into --dx by the script, sign and all. */
.dock__tip-t{
  position:absolute;
  left:50%; top:50%;
  transform:translate(-50%,-50%) translateX(var(--dx,0));
  opacity:0;
  filter:blur(6px);
  white-space:nowrap;
  font-size:var(--fs-fine);            /* text-[13px] */
  font-weight:500;
  letter-spacing:.02em;
  color:var(--black);
  transition:transform .3s var(--ease-out),
             opacity .3s var(--ease-out),
             filter .3s var(--ease-out);
}
.dock__tip-t.is-in{
  transform:translate(-50%,-50%);
  opacity:1;
  filter:blur(0);
}

/* ── entrance ─────────────────────────────────────────────────────── */
/* Two homes, two openings, and each has to be scoped to the one it
   belongs to — an unscoped `opacity:0` waiting on `.ftr.is-in` would
   leave the nav dock, which is not in a footer, invisible forever.

   In the footer: rides the section's own reveal, a beat behind the
   wordmark it sits above. */
.ftr .dock{
  opacity:0;
  transition:opacity .5s var(--ease-out) .5s;
}
.ftr.is-in .dock{ opacity:1 }

/* In the header: arrives with the page, on the same 60ms stagger step
   css/contact.css uses, one place ahead of the eyebrow — it is chrome,
   so it should already be there when the reading starts.

   It also drops in rather than only fading. The footer dock has a
   wordmark under it to arrive against; this one has an empty bar, and a
   panel that only fades in there reads as a rendering delay. */
.nav .dock--nav{
  opacity:0;
  transform:translateX(-50%) translateY(-10px);
  transition:opacity .45s var(--ease-out) .1s,
             transform .45s var(--ease-out) .1s;
}
.is-ready .nav .dock--nav{
  opacity:1;
  transform:translateX(-50%);
}

.no-js .dock{ opacity:1 }
.no-js .nav .dock--nav{ opacity:1; transform:translateX(-50%) }

/* ── narrow screens ───────────────────────────────────────────────── */
/* The wordmark is only ~95px tall on a phone and the dock is 76 — it
   would cover the word rather than sit on it. Dropped below instead,
   and tightened so five 44px targets still fit inside 320px. */
@media (max-width:620px){
  /* Five 44px targets have to fit inside 320px of usable width, so the
     gap goes and the padding tightens. The targets themselves do not
     shrink — that is the one measurement here a finger depends on. */
  .dock__row{ gap:.35rem; padding:.6rem .75rem }
  .dock{ margin-bottom:clamp(1.25rem,4vh,2rem) }
}

/* ── the nav dock, off the header ─────────────────────────────────── */
/* Below this width the header cannot hold a brand, a 300px dock and a
   burger without the three touching. So the dock leaves the bar and goes
   where a dock actually belongs: pinned to the bottom of the window,
   above the home indicator, with the page running under it. The header
   goes back to being the header every other page has.

   Nothing about it changes except where it is — same targets, same
   tooltip, same pop. The tooltip flips back up because there is now
   nothing below it. */
@media (max-width:900px){
  .nav .dock--nav{
    position:fixed;
    left:50%;
    bottom:calc(clamp(.75rem,2.5vh,1.25rem) + env(safe-area-inset-bottom));
    top:auto;
    z-index:var(--z-fab);
    /* Enters from below now that it lives at the bottom — the direction
       of arrival should be the direction it came from. */
    transform:translateX(-50%) translateY(12px);
  }
  .is-ready .nav .dock--nav{ transform:translateX(-50%) }
  .dock--nav .dock__tip{
    top:auto;
    bottom:100%;
    margin-top:0;
    margin-bottom:12px;
    transform:translateX(calc(var(--x, 0px) - 50%)) translateY(8px) scale(.92);
  }
  .dock--nav .dock__tip.is-open{
    transform:translateX(calc(var(--x, 0px) - 50%)) translateY(0) scale(1);
  }
  /* Pinned over the page rather than over the footer's flat ink, so the
     shadow has to work harder to lift it off whatever is behind. */
  .dock--nav .dock__row{
    background:rgba(74,59,41,.07);
    box-shadow:
      0 20px 46px -16px rgba(74,59,41,0.16),
      inset 0 0 0 1px rgba(74,59,41,.10),
      inset 0 1px 0 rgba(255,255,255,.9);
  }
}

/* Landscape phones: the mark is short here too. */
@media (max-height:480px) and (orientation:landscape){
  .dock__row{ padding:.65rem 1rem; gap:.6rem }
}

/* ═══ reduced motion ══════════════════════════════════════════════ */
/* The pop goes entirely — it is decoration, and it is the one thing
   here that moves for its own sake. The tooltip keeps its fade and its
   position, so it still says which icon it belongs to, and loses the
   slide and the blur. */
@media (prefers-reduced-motion:reduce){
  .dock{ opacity:1 }
  /* Weight, not preference: .nav .dock--nav above is (0,2,0) and would
     otherwise keep its parked opacity. The centring transform has to be
     restated here for the same reason — dropping it would slide the dock
     a half-width left. */
  .nav .dock--nav{ opacity:1; transform:translateX(-50%) }
  .dock__i:hover,
  .dock__i:focus-visible,
  .dock__i:active{ transform:none }
  .dock__i.is-pop .dock__glyph{ animation:none }
  .dock__tip{ transform:translateX(calc(var(--x,0px) - 50%)) }
  .dock__tip.is-open{ transform:translateX(calc(var(--x,0px) - 50%)) }
  .dock__tip-t{ transform:translate(-50%,-50%); filter:none }
}
