/* site-header.css — ONE header for every public page.
   ═══════════════════════════════════════════════════════════════════
   Used by three codebases that share nothing else:

     capraseo-hello/legal/*        the policy documents
     capraseo-demo-src/index.html  the marketing landing (capraseo.com/)
     capraseo-www _includes/*      the Eleventy blog (capraseo.com/blog/)

   They previously had three headers: three brand marks, three CTA
   styles, two spellings of the company name, and a nav that vanished
   entirely below 720px on two of them.

   ONE OWNER, NO COPIES. nginx serves /css/* and /js/* from
   capraseo-demo-live for every path on capraseo.com — including
   /blog/, which is rooted elsewhere. So all three link this same file
   over HTTP rather than each keeping a copy. Change it here and every
   page changes; there is nothing to keep in sync.

   SELF-CONTAINED ON PURPOSE. The landing and the blog do not load
   css/main.css — each defines its own variables — so this file cannot
   assume the app's tokens exist, and must not borrow the ones that do.
   Every colour below is a literal; see the note on .sh-top for the two
   distinct ways that borrowing them broke. */

.sh-top {
  /* EVERY VALUE IS A LITERAL. Nothing here inherits from the host page,
     and that is the whole point.

     The first version read `var(--panel, …)`, `var(--accent, …)` and so
     on, reasoning that these tokens mean the same thing in all three
     stylesheets so the header could adopt each page's palette. It does
     not survive contact with reality, in two separate ways:

       · Same name, different ROLE. The blog's site.css sets
         `--accent: #59cf8b` — that is the app's --accent-text, an INK
         colour for type, not a fill. "Start free" came out a different
         green on the blog.
       · Same name, same role, different VALUE. The blog's `--panel` is
         #121214; the landing defines none and fell back to #0b0b12. The
         header was rgb(18,18,20) on /blog/ and rgb(11,11,18) on /. Same
         intent, visibly different bar.

     A shared header that quietly repaints itself per host is not a
     shared header. Pinning the palette is what makes "the same header on
     every page" true rather than aspirational.

     Cost, accepted deliberately: the legal pages no longer tint their
     header with the light theme — dark chrome above a light document. */
  --sh-bg:      #0b0b12;
  --sh-panel:   #0f0f18;
  --sh-raised:  #14141f;
  --sh-line:    rgba(255, 255, 255, 0.07);
  --sh-line-hi: rgba(255, 255, 255, 0.10);
  --sh-edge:    rgba(255, 255, 255, 0.05);
  --sh-text:    #eeeef5;
  --sh-muted:   #a3a4b6;
  --sh-accent:    #158444;
  --sh-accent-hi: #1daf5b;
  --sh-accent-rgb: 31, 191, 99;

  background: var(--sh-bg);
  border-bottom: 1px solid var(--sh-line);

  /* Sticky, and this RESTORES behaviour rather than adding it: the blog's
     site.css and the landing's landing.css both had `position: sticky;
     top: 0; z-index: 50` on the .nav they used before this file existed.
     Swapping in .sh-top silently dropped it on both.

     z-index 50 matches what they used. It clears the landing's fixed
     .grain layer (z-index 1) and its content (z-index 2), and sits below
     the demo chrome bar (z-index 2000) so that can never be covered.

     top:0, NOT top:var(--demo-bar-h). demo-chrome.css defines that as
     74px on any page it is injected into — including the legal
     documents — but the bar does not actually render there, so offsetting
     by it would float the header 74px down the page chasing a bar that
     isn't drawn. If the bar is ever shown on those pages, this is the
     line to revisit.

     On the legal pages the scroller is .lg-page (position:fixed;
     overflow-y:auto), not the document. Sticky resolves against the
     nearest scrolling ancestor, so it works there unchanged. */
  position: sticky;
  top: 0;
  z-index: 50;
}

/* ── Glass ────────────────────────────────────────────────────────────
   Translucent with a top-to-bottom fade, so content dissolves under the
   bar as it scrolls rather than hitting a hard opaque edge.

   THE ALPHA MUST WORK WITHOUT THE BLUR. `backdrop-filter` is declared
   below and Chromium reports it as applied, but measured on the real
   pages it does NOT actually blur the backdrop — an isolated test page
   blurs fine, these do not, so something in these layouts (the landing's
   fixed .grain root, .lg-page's fixed scroller) is defeating it. Rather
   than tune a value against an effect that may or may not exist on a
   given device, the opacity alone has to carry legibility; where the
   blur does land, it is a bonus.

   That is why the fade is shallow — 0.96 to 0.88, not the 0.90-to-0.66
   this started as. Two earlier attempts were lighter and both were wrong
   in a way only a screenshot caught: the most transparent band is the
   one nearest the content, so a line of body copy sat sharp and readable
   directly under the brand mark. Ghost text reads as a rendering fault,
   not as glass. At 0.88 content passes under as a faint wash — still
   visibly moving, which is the effect asked for, without ever resolving
   into words.

   61px is too short a run to band. */
@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .sh-top {
    background: linear-gradient(
      180deg,
      rgba(11, 11, 18, 0.96) 0%,
      rgba(11, 11, 18, 0.88) 100%
    );
    -webkit-backdrop-filter: blur(18px) saturate(150%);
    backdrop-filter: blur(18px) saturate(150%);
  }
}

.sh-inner {
  max-width: 1100px;
  margin: 0 auto;
  padding: 14px 24px;
  display: flex;
  align-items: center;
  gap: 14px;
}
/* The legal documents read at 900px; the landing and blog are wider.
   One class, set on the header, rather than three stylesheets. */
.sh-top.sh-narrow .sh-inner { max-width: 900px; }

.sh-brand {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  color: var(--sh-text);
  text-decoration: none;
  font-weight: 600;
  font-size: 15px;
  letter-spacing: -0.01em;
}
.sh-brand img {
  border-radius: 6px;
  display: block;
  width: 24px;
  height: 24px;
}

.sh-links {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 18px;
}
.sh-links a {
  color: var(--sh-muted);
  text-decoration: none;
  font-size: 13px;
  line-height: 1.4;
  transition: color 0.15s ease;
}
.sh-links a:hover,
.sh-links a[aria-current="page"] { color: var(--sh-text); }

/* "Home" is menu-only. On a wide header the brand mark sits at the far
   left, obviously clickable, so a second link to the same place would be
   clutter. Once the links collapse behind a button that reasoning stops
   holding: the brand is still up in the bar, but people open a menu
   expecting every destination to be listed in it, and a logo is not
   where most look for "take me back". Shown in the small-screen block. */
.sh-links a.sh-home { display: none; }

/* Divider between "where you can go" and "who you are". */
.sh-sep {
  width: 1px;
  align-self: stretch;
  margin: 0 4px;
  background: var(--sh-line);
}

.sh-links a.sh-signup {
  padding: 6px 12px;
  border-radius: 6px;   /* the house control radius, literal — see the accent note */
  background: var(--sh-accent);
  border: 1px solid rgba(var(--sh-accent-rgb), 0.90);
  color: #fff;
  font-weight: 550;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
.sh-links a.sh-signup:hover {
  background: var(--sh-accent-hi);
  border-color: var(--sh-accent-hi);
  color: #fff;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.10),
    0 4px 14px -8px rgba(31, 191, 99, 0.42);
}

/* ── The menu button ──────────────────────────────────────────────
   Hidden by default and shown only in the small-screen block, so a
   wide header never carries both the inline links and a button. The
   base `display:none` also means a page whose JS failed never reveals
   a control that cannot open. Visual language is the app's floating
   hamburger (css/main.css .mobile-nav-toggle). */
.sh-menu-btn {
  display: none;
  margin-left: auto;
  place-items: center;
  width: 36px;
  height: 36px;
  padding: 0;
  color: var(--sh-text);
  background: linear-gradient(180deg, var(--sh-raised), var(--sh-panel));
  border: 1px solid var(--sh-line);
  border-radius: 8px;
  box-shadow: inset 0 1px 0 0 var(--sh-edge);
  cursor: pointer;
}
.sh-menu-btn:hover { border-color: var(--sh-line-hi); }
.sh-menu-btn:active { transform: scale(0.96); }
.sh-menu-btn:focus-visible {
  outline: none;
  box-shadow: inset 0 1px 0 0 var(--sh-edge),
              0 0 0 3px rgba(31, 191, 99, 0.22);
}

/* Three bars from one element; they fold into an X when open. */
.sh-burger,
.sh-burger::before,
.sh-burger::after {
  content: "";
  display: block;
  width: 16px;
  height: 1.5px;
  border-radius: 2px;
  background: currentColor;
  transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.15s ease;
}
.sh-burger { position: relative; }
.sh-burger::before { position: absolute; top: -5px; }
.sh-burger::after  { position: absolute; top: 5px; }
[aria-expanded="true"] .sh-burger { background: transparent; }
[aria-expanded="true"] .sh-burger::before { transform: translateY(5px) rotate(45deg); }
[aria-expanded="true"] .sh-burger::after  { transform: translateY(-5px) rotate(-45deg); }
@media (prefers-reduced-motion: reduce) {
  .sh-burger, .sh-burger::before, .sh-burger::after { transition: none; }
}

/* ── Small screens ────────────────────────────────────────────────
   760px, not the 620px the legal pages used alone: the landing and
   blog carry more links than four policy documents, so they need the
   menu sooner. One breakpoint for all three — a header that collapsed
   at different widths per page would not be the same header. */
@media (max-width: 760px) {
  .sh-inner { padding: 12px 18px; }

  /* Everything below is scoped to .js-menu, a class only
     js/site-header.js adds. Without JS the links stay inline — tight,
     but reachable. The alternative failure mode is a site whose whole
     navigation is behind a button that cannot open. */
  /* NO `position: relative` here. It used to sit on this rule, to give the
     absolutely-positioned menu panel a containing block — but it would
     override the sticky above at exactly the widths where sticky matters
     most. `position: sticky` is itself a positioned value, so it is
     already the containing block the panel needs. */
  .sh-top.js-menu .sh-menu-btn { display: grid; }
  .sh-top.js-menu .sh-links {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 60;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    margin: 0;
    padding: 4px 18px 10px;
    background: var(--sh-panel);
    border-bottom: 1px solid var(--sh-line);
    box-shadow: 0 14px 28px -18px rgba(0, 0, 0, 0.8);
  }
  .sh-top.js-menu.is-open .sh-links { display: flex; }

  /* The panel gets the same glass, but far more opaque than the bar. It
     sits over body copy rather than over a hero, and a menu you have to
     squint through is a worse trade than a slightly heavier surface.
     Opaque fallback stays above, same reasoning as the bar. */
  @supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
    .sh-top.js-menu .sh-links {
      background: rgba(15, 15, 24, 0.94);
      -webkit-backdrop-filter: blur(18px) saturate(150%);
      backdrop-filter: blur(18px) saturate(150%);
    }
  }

  /* Rows, not text links: 44px including the divider. */
  .sh-top.js-menu .sh-links a {
    padding: 12px 0;
    font-size: 14px;
    border-top: 1px solid var(--sh-line);
  }
  .sh-top.js-menu .sh-links a:first-child { border-top: 0; }
  .sh-top.js-menu .sh-links a.sh-home { display: block; }

  /* The rows already divide themselves, so the vertical rule has
     nothing left to separate. */
  .sh-top.js-menu .sh-sep { display: none; }
  .sh-top.js-menu .sh-links a.sh-signup {
    margin: 10px 0 2px;
    padding: 12px;
    text-align: center;
    font-weight: 600;
    border: 1px solid rgba(var(--sh-accent-rgb), 0.90);
    border-radius: 6px;
  }
}
