/*
  LUXVISION — Pill navigation.
  The main bar is a fixed, never-resizing pill shape (.lv-goo-bar). When
  PROLUX/ECOLUX is hovered, a second shape (.lv-goo-blob) grows outward from
  underneath that specific pill and visually fuses with the bar's edge via
  an SVG blur+contrast "goo" filter (see the inline <filter id="lv-goo">
  in the HTML) - a drop of water landing on and merging with a puddle,
  rather than a separate floating card or the whole bar reshaping itself.

  Three stacked layers, same footprint:
    1. .lv-nav-goo    - flat color shapes only, run through the SVG filter.
                         No text ever lives here (blur would wreck it).
    2. .lv-nav-content - the real pills/links/text, crisp, unfiltered.
    3. .lv-nav-glint   - cursor-tracking sheen, above everything, also
                         outside the filter so it stays sharp.

  KEY ARCHITECTURAL RULE (this is what the transparency mismatch came down
  to): the goo filter is allowed to define the SEAM between the bar and the
  blob, and nothing else. Every OUTER edge of the menu - left, right, bottom
  - is defined by .lv-nav-goo-wrap's own overflow:hidden + border-radius,
  which is a crisp, exact, resolution-independent clip. The goo shapes are
  therefore drawn deliberately OVERSIZED (--lv-goo-overhang past every outer
  edge) so their soft, blur-thresholded boundaries always fall well outside
  the visible area. See the long note on .lv-goo-bar below for why.
*/

/* Centering is done with left:0/right:0 + margin:auto and an explicit
   width:max-content, NOT the usual left:50% + translateX(-50%).

   That shortcut is a trap for a fixed-position, auto-width element: per
   spec the used width is shrink-to-fit against an available width of
   (containing block - left), which with left:50% is only HALF the
   viewport. So the bar silently started compressing its pills the moment
   the window dropped below about twice the bar's natural width - around
   1200px - and kept compressing from there. Every downstream measurement
   (the blob width captured on open, the glint's percentage-based gradient
   origin, the goo shapes' span) is derived from that width, which is why
   resizing the window pulled the fill and the layout out of register with
   each other, and why it always looked correct on a big screen.

   With width:max-content the bar is the same size at every viewport it is
   shown at, so none of those measurements can drift. */
.lv-nav {
  position: fixed;
  top: var(--lv-nav-band-top);
  left: 0;
  right: 0;
  width: max-content;
  max-width: calc(100% - 2rem);
  margin-inline: auto;
  transform: translateY(-14px);
  z-index: 50;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s var(--lv-ease), transform 0.5s var(--lv-ease);
  /* Applied as a filter (not box-shadow) so it still renders outside the
     box even though .lv-nav-goo-wrap sets overflow:hidden - a box-shadow
     there would simply get clipped away. */
  filter: drop-shadow(0 14px 30px rgba(0, 0, 0, 0.45));
}

.lv-nav.is-visible {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

.lv-nav-goo-wrap {
  /* Seeded from the shared band token so the bar and the brand logo start
     from the same number; JS then overwrites it with the measured height
     (see syncBarMetrics). */
  --lv-nav-bar-h: var(--lv-nav-band-h);
  --lv-goo-overhang: 60px;
  position: relative;
  isolation: isolate;
  overflow: hidden;
  border-radius: var(--lv-radius-pill);
  /* Floor for the JS-driven height below. The 0.3/1.4 growth curve
     overshoots its target by ~6%, which on the way back down would carry
     the closing height a dozen pixels BELOW the bar height and visibly
     squash the pill for a few frames. Clamping here keeps the springy
     open while making that undershoot structurally impossible. */
  min-height: var(--lv-nav-bar-h);
  /* Domed-glass dimensionality lives HERE, on the outer clipped container,
     not on the individual goo shapes - a border + inset top rim + inset
     bottom shadow applied once automatically wraps whatever's currently
     visible (just the bar, or bar+blob together) with zero risk of a
     per-shape gradient-stretch seam mismatch. */
  border: 1px solid var(--lv-border-strong);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.09) inset,
    0 -12px 20px rgba(0, 0, 0, 0.3) inset;
  /* Height is driven explicitly by JS (see pill-nav.js) to the single
     shared number barH + panelHeight - the same number the blob is sized
     from. Previously the panels were in flow, so switching PROLUX->ECOLUX
     had both panels mid-transition at once and the wrap's auto height
     briefly summed them; that transient extra height is what made the
     border visibly reshape into a taller, squarer box mid-switch. The
     panels are now absolutely positioned (below) so they never stack, and
     this one explicit height is the only thing that moves.

     border-radius intentionally does NOT use the 0.3/1.4 bounce curve:
     that curve overshoots past its target, and on a shrinking radius the
     overshoot means dipping toward a much smaller radius than intended -
     a visible sharp-cornered flash. Overshoot reads as springy on a fill
     shape; it reads as a bug on a 1px outlined border. */
  transition:
    border-radius 0.55s var(--lv-ease),
    height 0.62s cubic-bezier(0.3, 1.4, 0.55, 1);
}

/* Any menu open, not a named list of them. The original rule enumerated
   "prolux" and "ecolux", which meant adding a third menu (Home) opened the
   blob but never revealed its panel - the mechanic was generic and only the
   styling was not. :not([data-active-expand=""]) matches "a menu is open"
   without caring which. */
.lv-nav-goo-wrap[data-active-expand]:not([data-active-expand=""]) {
  border-radius: var(--lv-radius-lg);
}

/* ---- Layer 1: goo shapes ---- */
.lv-nav-goo {
  position: absolute;
  inset: 0;
  z-index: 1;
  filter: url(#lv-goo);
  pointer-events: none;
}

/* Both shapes are pure flat, IDENTICAL, fully opaque color - no gradient
   anywhere inside the goo-filtered layer. A uniformly flat input has
   nothing for the blur to mix unevenly (convolving a constant with itself
   is still that constant), so the seam can't develop a tonal shift.

   The bar is drawn deliberately oversized: it hangs --lv-goo-overhang past
   the top, left and right of the wrap. That is the actual fix for the
   transparency mismatch. A blurred-then-alpha-thresholded edge does not
   land back exactly on the source edge - it lands approximately there, it
   softens over a couple of pixels, and, critically, it shifts depending on
   the raster resolution the filter is evaluated at and on how much room
   the filter region gives the blur. That is why the mismatch visibly
   improved on a larger window: the filter region is sized from the
   element's box, so a bigger box gave the blur more room and clipped it
   less. Rather than chase that, no soft edge is allowed anywhere near the
   visible boundary any more - the wrap's overflow:hidden does all the
   outer cutting, which is pixel-exact at any size or DPI.

   The bar's BOTTOM edge is the one exception, and that is on purpose: it
   is the puddle surface the droplet merges into, so it has to stay at
   exactly var(--lv-nav-bar-h) for the goo fillet to form there. */
.lv-goo-bar {
  position: absolute;
  top: calc(var(--lv-goo-overhang) * -1);
  left: calc(var(--lv-goo-overhang) * -1);
  right: calc(var(--lv-goo-overhang) * -1);
  height: calc(var(--lv-nav-bar-h) + var(--lv-goo-overhang));
  border-radius: 0;
  background: #191922;
}

/* The blob: 0x0 at rest, sitting exactly on the bar's bottom edge. JS grows
   it from the hovered trigger's x-position out past all three outer edges
   (see openBlob), so only its top-corner fillet against the bar is ever
   visible - its own left/right/bottom boundaries land outside the clip. */
.lv-goo-blob {
  position: absolute;
  top: var(--lv-nav-bar-h);
  left: 50%;
  width: 0;
  height: 0;
  border-radius: var(--lv-radius-pill);
  background: #191922;
  transition:
    left 0.62s cubic-bezier(0.3, 1.4, 0.55, 1),
    width 0.62s cubic-bezier(0.3, 1.4, 0.55, 1),
    height 0.62s cubic-bezier(0.3, 1.4, 0.55, 1),
    border-radius 0.62s cubic-bezier(0.3, 1.4, 0.55, 1);
}

/* ---- Layer 3: cursor-tracking glint, above everything, outside the SVG
   filter so it always stays crisp regardless of the bar/blob shape. ---- */
.lv-nav-glint {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  opacity: 0;
  mix-blend-mode: screen;
  /* Soft and wide rather than a distinct spotlight - a gentle ambient sheen
     that eases toward the cursor (see the rAF lerp in pill-nav.js) instead
     of a sharp blob that snaps to it. */
  background: radial-gradient(260px circle at var(--mx, 50%) var(--my, 0%), rgba(255, 255, 255, 0.1), transparent 72%);
  transition: opacity 0.5s var(--lv-ease);
}
.lv-nav-goo-wrap.has-glint .lv-nav-glint { opacity: 1; }

/* ---- Layer 2: real content ---- */
.lv-nav-content {
  position: relative;
  z-index: 2;
}

/* min-height is a LITERAL here, deliberately not var(--lv-nav-bar-h).
   That custom property is now an OUTPUT: JS measures this row and writes
   the real height back into it (see syncBarMetrics in pill-nav.js) so the
   goo shapes and the panel offset track the actual laid-out bar instead of
   a hardcoded guess. Feeding the measured value back into this row's own
   min-height would make that a self-referential loop that can only ratchet
   upward and never shrink again. */
.lv-nav-inner {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: 0.3rem;
  padding: 0.4rem;
  min-height: var(--lv-nav-band-h);
}

/* flex-shrink:0 + nowrap: the bar must never reflow onto a second line.
   If it does, the laid-out row grows but the goo fill does not follow it
   the same way, and you get exactly the mismatch between the "reactive
   layout" and the "reactive transparency". The hamburger takes over
   before the viewport ever gets narrow enough for that (see the media
   query at the bottom of this file). */
.lv-pill {
  position: relative;
  display: flex;
  align-items: center;
  flex-shrink: 0;
  white-space: nowrap;
  gap: 0.35rem;
  padding: 0.6rem 1.1rem;
  border-radius: var(--lv-radius-pill);
  font-family: var(--lv-font-body);
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--lv-ink-muted);
  cursor: pointer;
  border: none;
  background: transparent;
  transition: color 0.25s var(--lv-ease), background 0.25s var(--lv-ease), transform 0.12s var(--lv-ease);
}

/* Current page marker. This deliberately does NOT reuse the hover
   treatment - when it did, the current-page pill and a hovered pill were
   pixel-identical, so PROLUX read as permanently stuck on rather than as
   telling you where you are. It's now just a brightened label: full ink
   against every other pill's muted ink. No dot, no accent, no chrome. The
   contrast step alone is enough to read as "you are here" on a five-item
   bar, and it keeps the accent reserved for deliberate moments rather
   than burning it into a bar that's on screen the whole session (Color
   Consistency Lock). Declared BEFORE :hover so hover wins the
   equal-specificity tie and the pill still lights up when pointed at. */
.lv-pill.is-active {
  color: var(--lv-ink);
}

.lv-pill:hover,
.lv-pill.is-menu-open {
  color: var(--lv-accent);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.02) 100%);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.14) inset, 0 6px 14px rgba(0, 0, 0, 0.3);
}

/* pressed/deflected tactile push */
.lv-pill:active {
  transform: translateY(2px) scale(0.97);
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.4) inset;
}

.lv-pill-caret {
  width: 8px;
  height: 8px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translateY(-2px);
  opacity: 0.55;
  transition: transform 0.3s var(--lv-ease);
}

.lv-pill.is-menu-open .lv-pill-caret { transform: rotate(225deg) translateY(2px); }

/* Kept neutral at rest (Color Consistency Lock: accent is reserved for
   deliberate moments, not a nav bar that's on screen the entire session) -
   a thin border marks these as distinct/external. Hover/active accent color
   comes from the shared .lv-pill:hover rule above, same as every other pill. */
.lv-pill-login {
  border: 1px solid var(--lv-border-strong);
  color: var(--lv-ink-muted);
}

/* ---- Expand panels ----
   Absolutely positioned, NOT in flow. Two reasons: (1) they can never both
   contribute height at once, which is what made the border briefly reshape
   when switching straight from PROLUX to ECOLUX; (2) it lets the wrap's
   single JS-driven height be the one authoritative number that the blob is
   also sized from, so the clip boundary and the fill can't drift apart. */
.lv-nav-expand {
  position: absolute;
  top: var(--lv-nav-bar-h);
  left: 0;
  right: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s var(--lv-ease);
}

.lv-nav-expand-inner {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  padding: 0.55rem 0.6rem 0.6rem;
  transform: translateY(-8px);
  transition: transform 0.45s var(--lv-ease);
}

/* The open panel is marked by JS with .is-open rather than matched by name.
   CSS cannot express "the panel whose key equals the wrap's active key", so
   the old rule hardcoded every pair - which is why a new menu silently failed
   to appear. One class, and any future menu works with no CSS change. */
.lv-nav-expand.is-open {
  opacity: 1;
  pointer-events: auto;
  transition: opacity 0.3s var(--lv-ease) 0.12s;
}

.lv-nav-expand.is-open .lv-nav-expand-inner {
  transform: translateY(0);
  transition: transform 0.5s var(--lv-ease) 0.12s;
}

.lv-nav-expand-inner::before {
  content: "";
  display: block;
  height: 1px;
  margin: 0 0.6rem 0.4rem;
  background: var(--lv-border);
}

/* Sub-items reuse .lv-pill so the hover/press treatment is byte-identical
   to the top-level pills. Only layout differs: full width, stacked list. */
.lv-pill-sub {
  width: 100%;
  justify-content: flex-start;
  font-weight: 500;
  font-size: 0.85rem;
}

@media (prefers-reduced-motion: reduce) {
  .lv-nav-goo-wrap,
  .lv-goo-blob,
  .lv-nav-expand,
  .lv-nav-expand-inner {
    transition-duration: 0.01ms !important;
  }
}

/* Mobile: hamburger + slide-down sheet.
   .lv-nav-sheet must be hidden by default (desktop) - it only gets its
   fixed-overlay treatment inside the mobile media query below. */
.lv-nav-burger { display: none; }
.lv-nav-sheet { display: none; }

/* Breakpoint sits comfortably ABOVE the bar's natural max-content width
   (~600px plus gutters), not just above a phone size. If the two are close
   together the bar spends a band of viewport widths clamped by max-width
   and compressing its pills, which is the same drift the width:max-content
   fix above exists to prevent. The hamburger takes over well before that. */
@media (max-width: 860px) {
  .lv-nav { width: calc(100% - 2rem); max-width: none; top: 1rem; }
  .lv-nav-goo-wrap { display: none; }
  .lv-nav-burger {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 52px;
    height: 52px;
    margin-left: auto;
    border-radius: var(--lv-radius-pill);
    background: linear-gradient(180deg, rgba(30, 30, 40, 0.95), rgba(18, 18, 24, 0.95));
    border: 1px solid var(--lv-border-strong);
    box-shadow: 0 14px 30px rgba(0, 0, 0, 0.45);
  }
  .lv-nav-burger span, .lv-nav-burger span::before, .lv-nav-burger span::after {
    content: ""; display: block; width: 18px; height: 1.5px; background: var(--lv-ink); position: relative;
    transition: transform 0.25s var(--lv-ease), opacity 0.25s var(--lv-ease);
  }
  .lv-nav-burger span::before { position: absolute; top: -6px; }
  .lv-nav-burger span::after { position: absolute; top: 6px; }

  .lv-nav-sheet {
    position: fixed; inset: 0; z-index: 49;
    background: rgba(11, 11, 16, 0.97);
    backdrop-filter: blur(10px);
    opacity: 0; pointer-events: none;
    transition: opacity 0.3s var(--lv-ease);
    display: flex; flex-direction: column; justify-content: center; gap: 0.25rem;
    padding: var(--lv-space-gutter);
  }
  .lv-nav-sheet.is-open { opacity: 1; pointer-events: auto; }
  .lv-nav-sheet a, .lv-nav-sheet button {
    font-family: var(--lv-font-display);
    font-size: 1.6rem; font-weight: 600; color: var(--lv-ink);
    background: none; border: none; text-align: left; padding: 0.7rem 0;
    border-bottom: 1px solid var(--lv-border);
  }
  .lv-nav-sheet .lv-sub-list { display: none; padding-left: 1rem; }
  .lv-nav-sheet .lv-sub-list.is-open { display: block; }
  .lv-nav-sheet .lv-sub-list a { font-size: 1.05rem; font-weight: 500; color: var(--lv-ink-muted); border-bottom: none; }
}
