/* ═══════════════════════════════════════════════════════════
   site-nav.css — the site navbar, its mobile sheet, and the
   button system both of them are built from.

   Pairs with:
     templates/partials/site-nav.php   markup
     public/js/site-nav.js             sticky state, pill centring,
                                       sheet open/close, focus trap

   ── WHO USES IT ────────────────────────────────────────────
   Right now: the new landing page only. Nothing else links to
   it or loads it. That is deliberate while the navbar is still
   being worked on — see the note in site-nav.php.

   ── WHY THE var(--x, fallback) EVERYWHERE ──────────────────
   Every colour, radius and easing below reads a token if the
   page defines one and falls back to the landing page's value
   if it does not. That is what makes this file droppable onto
   a page whose tokens are named differently (content.css uses
   --color-text-primary where this uses --ink) without dragging
   a whole token set along with it.

   The two exceptions are --nav-h and --nav-shift. Those describe the navbar itself, so the navbar owns them and
   declares them on :root below. Pages read them (scroll-padding,
   sticky offsets); the navbar is the one that sets them.
   ═══════════════════════════════════════════════════════════ */

:root{
  --nav-h:72px;        /* THE height, at rest and stuck alike. Anything
                          that has to sit clear of the bar reads this,
                          never a literal 72. */
  --nav-shift:0px;     /* written by site-nav.js: how far the links
                          pill travels to sit optically centred */
}

/* ─── BUTTONS ────────────────────────────────────────────── */
/* NOTE: no translucency + backdrop-filter on anything that lives inside
   a .rv wrapper. A filling animation on .rv leaves a transform on the
   element, that transform makes a stacking context, and a stacking
   context cuts backdrop-filter off from the page behind it. The blur
   then does nothing and the hero wash shows straight through the fill.
   Surfaces in the hero are solid. Real glass only above .rv (navbar). */
.btn{
  position:relative;
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  font-family:var(--font-ui,'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif);
  font-size:.9375rem;font-weight:600;line-height:1;
  padding:15px 26px;
  border-radius:var(--r-btn,12px);
  border:1px solid transparent;
  white-space:nowrap;
  top:0;
  transition:background .2s var(--ease,cubic-bezier(.16,1,.3,1)),color .2s,border-color .2s,
             transform .2s var(--ease,cubic-bezier(.16,1,.3,1)),top .2s var(--ease,cubic-bezier(.16,1,.3,1)),box-shadow .25s var(--ease,cubic-bezier(.16,1,.3,1));
}
/* pure black, not --ink (#111827 has a blue cast that reads violet
   against the lavender wash). Hover differentiates by lift alone;
   the glow is identical at rest and on hover, see below. */
.btn--primary{
  background:#000;color:#fff;
  box-shadow:0 2px 5px rgba(17,24,39,.22),0 8px 20px -12px rgba(17,24,39,.45);
}
/* the glow: one blurred gradient plate parked mostly BEHIND the button,
   bleeding ~15px past its bottom edge. A box-shadow can only ever be one
   flat colour, and the whole point here is the magenta to violet run
   across the width, so it has to be a real gradient layer.
   z-index:-1 puts it under the black fill; every container that holds a
   primary button is isolated below so the plate can never sink behind a
   section background. */
.btn--primary::after{
  content:"";position:absolute;z-index:-1;pointer-events:none;
  left:7%;right:7%;bottom:-5px;height:64%;
  border-radius:var(--r-btn,12px);
  background:linear-gradient(90deg,var(--magenta,#F368D8) 0%,var(--violet,#B45CF0) 46%,var(--lav,#6C5CE0) 100%);
  filter:blur(11px);
  opacity:.58;           /* one number to dial the glow up or down */
  transition:opacity .25s var(--ease,cubic-bezier(.16,1,.3,1)),transform .3s var(--ease,cubic-bezier(.16,1,.3,1)),filter .3s var(--ease,cubic-bezier(.16,1,.3,1));
}
/* the lift is done with top, NOT transform. A transform would turn the
   button into a stacking context, and inside a stacking context a
   z-index:-1 child paints on top of that element's own background: the
   glow plate would flood straight over the black fill on hover. top
   moves the box without creating a context, so the plate stays behind. */
.btn--primary:hover{background:#000;top:-1px;box-shadow:0 4px 10px rgba(17,24,39,.24),0 14px 30px -16px rgba(17,24,39,.5)}
/* Hover reads as LIFT ONLY. The neutral shadow above grows and the
   box comes up 1px; the coloured plate does not move, does not
   brighten, does not spread. It is the same glow at rest and on
   hover, and the button simply rises off it.

   This was arrived at by walking it down twice. The original was
   opacity .85 / translateY(3px) / blur(13px) against a rest state of
   .58 / 0 / 11px: brighter, lower and wider all at once. Worse, the
   button itself rises 1px on the same hover, so the plate ended up
   4px further out from under the fill that was meant to be covering
   most of it. Peak saturation arrived exactly when the black box
   stopped hiding it. That read as a violet flare. Halving it to .66 /
   1px / 12px was still doing the same thing, only quieter.

   So there is deliberately no `.btn--primary:hover::after` rule here
   now. The ::after transition above stays regardless — it is what
   keeps the plate from snapping if a value is ever reintroduced.

   TO PUT A WHISPER BACK, one line, nothing else:
     .btn--primary:hover::after{opacity:.63}
   Anything past about .66 and the flare returns. Do not reintroduce
   the translateY: the travel, not the opacity, is what exposed the
   plate from under the fill. */
.btn--ghost{background:#fff;color:var(--ink,#111827);border-color:var(--line,#E5E7EB);box-shadow:0 1px 2px rgba(17,24,39,.05)}
.btn--ghost:hover{background:#fff;border-color:#D1D5DB;transform:translateY(-1px);box-shadow:0 6px 16px -8px rgba(17,24,39,.28)}
/* nav CTA: same soft square as the hero, glow off. A glow inside a 72px
   bar smears against the bottom hairline. Delete the line to switch it on. */
.btn--sm{padding:11px 20px;font-size:.875rem;border-radius:10px}
.btn--sm.btn--primary::after{display:none}


/* ═══════════════════════════════════════════════════════════
   NAVBAR
   Desktop: bare centred links over the hero, no container.
   On scroll they slide right and open up, leaving exactly the
   app navbar (white blur bar, wordmark left, CTA right).
   The slide distance is measured in JS into --nav-shift so the
   cluster is optically centred at any viewport width.
   Mobile: always the app navbar. The scene below splits on the
   same 1000px so a burger navbar never sits over a two column
   layout, and so the two column pair never gets squeezed.
   ═══════════════════════════════════════════════════════════ */
.nav{
  position:fixed;top:0;left:0;right:0;z-index:100;
  font-family:var(--font-ui,'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif);
  border-bottom:1px solid transparent;
  background:transparent;
  transition:background .45s ease,backdrop-filter .45s ease,border-color .45s ease;
}
/* Two different stuck states on purpose.

   MOBILE is SOLID white. A phone screen under the bar is dense: the
   scene's phone artwork, a headline, a card edge. Frosted glass over
   that reads as a smear rather than as a surface, and a bar you cannot
   pick out from the content has stopped separating the chrome from the
   page. It also costs a full-screen backdrop-filter repaint on every
   scroll frame, on the hardware least able to pay for it.

   DESKTOP keeps the glass. There is a wide, quiet hero behind it there,
   which is the one case where the blur has something worth showing. */
.nav.is-stuck{
  background:#fff;
  border-bottom-color:var(--line,#E5E7EB);
}
@media (min-width:1000px){
  .nav.is-stuck{
    background:rgba(255,255,255,.78);
    backdrop-filter:blur(18px) saturate(180%);
    -webkit-backdrop-filter:blur(18px) saturate(180%);
  }
}
/* ONE height, at rest and stuck alike. The bar used to rest at 88px and
   collapse to 72px on scroll, which meant the wordmark and the links
   physically walked up the screen the moment you touched the wheel. A
   navbar is a fixed reference point; it does not get to move. What still
   changes on scroll is the surface (transparent -> solid) and the links
   opening out of their pill, and neither of those shifts anything. */
.nav-inner{
  max-width:var(--maxw,1200px);margin-inline:auto;
  padding-inline:var(--pad,clamp(20px,5vw,40px));
  height:var(--nav-h);
  display:flex;align-items:center;gap:16px;
}
.nav-measuring,.nav-measuring *{transition:none !important}

/* wordmark: unchanged app chrome */
.logo{display:inline-flex;align-items:center;gap:10px;font-weight:700;font-size:1.0625rem;letter-spacing:-.02em;flex:none}
.logo-mark{
  width:32px;height:32px;flex:none;border-radius:var(--r-sm,8px);
  background:var(--ink,#111827);color:#fff;display:grid;place-items:center;
}
.logo-mark svg{width:17px;height:17px}

/* links */
/* no container chrome any more: no fill, no border, no blur. The links
   just sit centred over the hero and slide right on scroll. padding and
   gap still animate, so the cluster still opens up as it travels. */
.nav-links{
  display:none;align-items:center;gap:2px;
  margin-left:auto;
  padding:6px;
  background:transparent;
  transform:translateX(var(--nav-shift));
  transition:transform .6s var(--ease,cubic-bezier(.16,1,.3,1)),padding .45s var(--ease,cubic-bezier(.16,1,.3,1)),gap .45s var(--ease,cubic-bezier(.16,1,.3,1));
}
.nav-links a{
  padding:9px 16px;border-radius:var(--r-pill,9999px);
  font-size:.875rem;font-weight:500;color:var(--ink-2,#6B7280);
  transition:color .2s,background .2s;
}
.nav-links a:hover{color:var(--ink,#111827);background:rgba(17,24,39,.05)}
.nav.is-stuck .nav-links{
  transform:translateX(0);
  padding:0;gap:30px;
}
.nav.is-stuck .nav-links a{padding:8px 0;border-radius:0}
.nav.is-stuck .nav-links a:hover{background:none}

.nav-right{display:flex;align-items:center;gap:6px;flex:none;margin-left:auto}
.nav-login{display:none;font-size:.875rem;font-weight:500;color:var(--ink-2,#6B7280);padding:10px 14px;border-radius:var(--r-pill,9999px);transition:color .2s}
.nav-login:hover{color:var(--ink,#111827)}
.nav-cta{display:none}
@media (max-width:999px){
  .nav.is-past-cta .nav-cta{display:inline-flex;animation:ctaIn .45s var(--ease,cubic-bezier(.16,1,.3,1)) both}
  .nav.is-past-cta .burger{margin-left:2px}
}
@keyframes ctaIn{from{opacity:0;transform:translateX(10px) scale(.94)}to{opacity:1;transform:none}}

/* burger */
/* FLEX, not grid, and that swap is the whole bug that made the X render
   as a chevron.

   As a grid with auto rows, align-content resolves to stretch, so the
   three rows shared out the button's spare 26px and the bars ended up
   about 15.7px apart instead of 7px. The closed icon looked stretched,
   and translateY(7px) on the top bar was then nowhere near far enough
   to reach the middle one: the two diagonals stopped short of crossing
   and read as ">".

   A flex column with justify-content:center packs the bars to their
   natural 2+5+2+5+2 = 16px and centres that block, which puts the bar
   centres exactly 7px apart. Same maths the old /landing burger used,
   which is why that one always drew a real X. */
.burger{
  width:42px;height:42px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;
  border-radius:var(--r-sm,8px);margin-right:-8px;
  -webkit-tap-highlight-color:transparent;
}
.burger i{display:block;width:20px;height:2px;background:var(--ink,#111827);border-radius:1px;
  transform-origin:center;transition:transform .3s cubic-bezier(.77,0,.18,1),opacity .2s}
body.menu-open .burger i:nth-child(1){transform:translateY(7px) rotate(45deg)}
body.menu-open .burger i:nth-child(2){opacity:0;transform:scaleX(0)}
body.menu-open .burger i:nth-child(3){transform:translateY(-7px) rotate(-45deg)}

/* ═══════════════════════════════════════════════════════════
   MOBILE SHEET
   Deliberately the SAME menu as the old /landing navbar: a quiet
   list of hairline-separated rows with a chevron, staggered in,
   and the CTAs parked at the bottom. Not the oversized 1.5rem
   display type the prototype had. Three links do not need to be
   24px tall each, and a menu that reads like a page of headings
   competes with the page it is a menu for.

   ── ONE THING NOT COPIED FROM THE OLD ONE ──────────────────
   The scroll lock. lp-shared.js locks with overflow:hidden on
   body, and that is exactly what this page cannot do: overflow
   on body propagates to the viewport, that is what kills
   position:sticky, and the whole hero is a sticky pin. The lock
   here stays position:fixed with the offset carried in `top`
   (see setMenu in public/js/site-nav.js). Same result, and it
   also survives iOS Safari, where overflow:hidden does not.

   ── AND ONE THING THAT NEEDED NO JS ────────────────────────
   The stagger. The old menu did it by removing a class, forcing
   a reflow and adding it back on every open. Here the delays
   hang off body.menu-open, so opening re-runs them for free and
   closing drops them, which is what makes the menu fade out in
   one piece instead of unwinding row by row.
   ═══════════════════════════════════════════════════════════ */
.sheet{
  position:fixed;inset:var(--nav-h) 0 0 0;z-index:95;
  background:var(--bg,#FFFFFF);
  padding:12px var(--pad,clamp(20px,5vw,40px)) calc(32px + env(safe-area-inset-bottom));
  display:flex;flex-direction:column;
  overflow-y:auto;-webkit-overflow-scrolling:touch;
  opacity:0;visibility:hidden;transform:translateY(-12px);
  transition:opacity .3s var(--ease,cubic-bezier(.16,1,.3,1)),transform .35s var(--ease,cubic-bezier(.16,1,.3,1)),visibility .3s;
}
body.menu-open .sheet{opacity:1;visibility:visible;transform:none}

/* The bar goes solid so nothing shows through from the page underneath,
   and its bottom hairline goes AWAY. The sheet's first row already draws
   one 12px below it, and two parallel lines that close together do not
   read as a boundary and a list, they read as a rendering mistake. The
   list owns the line while the menu is open. */
body.menu-open .nav{
  background:#fff;
  backdrop-filter:none;-webkit-backdrop-filter:none;
  border-bottom-color:transparent;
}

/* ── the link list ── */
.sheet-links{display:flex;flex-direction:column;border-bottom:1px solid var(--line-soft,#F1F2F4)}
.sheet-links a{
  display:flex;align-items:center;justify-content:space-between;
  padding:18px 4px;
  font-size:1.0625rem;font-weight:400;letter-spacing:-.01em;
  color:var(--ink,#111827);
  border-top:1px solid var(--line-soft,#F1F2F4);
  -webkit-tap-highlight-color:transparent;
  opacity:0;transform:translateY(8px);
  transition:color .15s,opacity .4s var(--ease,cubic-bezier(.16,1,.3,1)),transform .4s var(--ease,cubic-bezier(.16,1,.3,1));
}
.sheet-links a:active{color:var(--ink-2,#6B7280)}

/* chevron as a rotated corner rather than an inline SVG: two borders,
   no extra element, and the markup stays one <a> per link */
.sheet-links a::after{
  content:'';width:7px;height:7px;flex:none;margin-left:12px;
  border-right:1.5px solid var(--ink-3,#9CA3AF);
  border-bottom:1.5px solid var(--ink-3,#9CA3AF);
  transform:rotate(-45deg);
  transition:transform .15s;
}
.sheet-links a:active::after{transform:rotate(-45deg) translate(1px,1px)}

/* ── the CTAs, parked at the bottom ── */
.sheet-foot{margin-top:auto;padding-top:28px;display:grid;gap:10px;isolation:isolate}
/* Glow off, same call as the bar's own CTA one screen up. The magenta to
   violet plate belongs to the hero, where it sits on a lilac wash that it
   grew out of. On a plain white menu panel it is a coloured halo with
   nothing to explain it, and it is the loudest thing on a screen whose
   whole job is a list of three links. The button keeps its neutral drop
   shadow from .btn--primary, which is what the old /landing menu had. */
.sheet-foot .btn--primary::after{display:none}
.sheet-foot .btn{
  width:100%;
  opacity:0;transform:translateY(8px);
  transition:opacity .4s var(--ease,cubic-bezier(.16,1,.3,1)),transform .4s var(--ease,cubic-bezier(.16,1,.3,1)),
             background .2s var(--ease,cubic-bezier(.16,1,.3,1)),border-color .2s,top .2s var(--ease,cubic-bezier(.16,1,.3,1)),box-shadow .25s var(--ease,cubic-bezier(.16,1,.3,1));
}

/* ── the stagger ── */
body.menu-open .sheet-links a,
body.menu-open .sheet-foot .btn{opacity:1;transform:none}
body.menu-open .sheet-links a:nth-child(1){transition-delay:.03s}
body.menu-open .sheet-links a:nth-child(2){transition-delay:.07s}
body.menu-open .sheet-links a:nth-child(3){transition-delay:.11s}
body.menu-open .sheet-links a:nth-child(4){transition-delay:.15s}
body.menu-open .sheet-foot .btn{transition-delay:.13s}
body.menu-open .sheet-foot .btn+.btn{transition-delay:.17s}

@media (prefers-reduced-motion:reduce){
  .sheet-links a,.sheet-foot .btn{opacity:1;transform:none;transition-delay:0s !important}
}

@media (min-width:1000px){
  .nav-links{display:flex}
  .nav-login,.nav-cta{display:inline-flex}
  .burger,.sheet{display:none}
  .nav-right{margin-left:0}

  /* ── GROUP SPACING, and the reason it needs two values ──────
     The stuck bar is three groups: wordmark, links, actions. The
     only thing that was separating the last two was .nav-inner's
     16px flex gap, while the links themselves opened out to 30px
     on scroll (see .nav.is-stuck .nav-links above). So the gap
     INSIDE the link group ended up nearly twice the gap BETWEEN
     the groups, "Blog" sat closer to the CTA than to "FAQ", and
     it read as a fourth item glued to the button. Proximity, not
     alignment: whatever is closest is what looks related.

     margin-right takes it to 16+12 = 28px of box gap. Measured to
     ink that is 42px before "Log in", whose own 14px padding is
     invisible, against 30px between links. Group separation wins.

     THE CTA GETS ITS OWN 8px ON TOP because a solid black pill is
     not a word. Text has ragged edges that let the eye run on; a
     filled block is a hard stop, so the same numeric gap reads
     tighter next to it. This is also what keeps both branches of
     site-nav.php balanced from one pair of values:
       guest      "Log in" 14 + 6 + 8  = 28px to the button
       logged in  "Blog"   28 + 8      = 36px to the button
     Both sit clear of the 30px link rhythm without opening a hole.

     BELOW 1000px this does nothing on purpose. .nav-links is
     display:none there and .nav-right carries margin-left:auto,
     so the group hugs the right edge and any left margin on its
     first child is swallowed by that auto. Scoped here anyway,
     so it can never be read as load-bearing on mobile.

     SAFE FOR THE CENTRING. site-nav.js measures .nav-links with
     getBoundingClientRect, which is a BORDER box and excludes
     margin, and it measures in the at-rest state with these same
     values applied. The margin pushes the links 12px left, the
     measured --nav-shift grows by exactly 12px, and the cluster
     still lands optically centred over the hero. */
  .nav-links{margin-right:12px}
  .nav-cta{margin-left:8px}
}

/* The navbar CTA slides in from the right when a page marks the bar
   .is-past-cta. Reduced motion gets the end state with no travel.
   Lives here rather than in the page's own reduced-motion block so the
   keyframe above and its override can never be separated. */
@media (prefers-reduced-motion:reduce){
  .nav.is-past-cta .nav-cta{animation:none}
}