/* site-nav.css — the "More ▾" menu, and nothing else.

   ONE definition for TWO audiences. The pages rendered from the design drop
   (`render-site-pages.py`) lay their nav out with the design's own inline
   styles and load `site-pages.css`; the hand-authored kit pages lay theirs out
   with `site.css`'s `.nav` classes. Both carry the same menu, so the menu lives
   in a sheet both of them link rather than in a copy in each — a nav that
   drifts between halves of one site is worse than a nav that is plain.

   ⛔ CSS-ONLY, on :hover AND :focus-within, deliberately. The delivered
   homepage drove this menu from component state with `onMouseEnter` and a
   `display:` binding; the delivered SUB-pages drove the identical menu from
   CSS. The CSS half is the one that ships: the apex serves `script-src 'self'`
   and refuses inline handlers outright (check.sh guard 11), and — the reason
   that matters rather than merely constrains — a menu that opens only once a
   script has run is a menu a keyboard user can lose entirely. `:focus-within`
   is what makes tabbing into it work; do not replace it with `:hover` alone.

   Loaded AFTER site.css on the kit pages, so `.navmore-menu a` wins the tie
   with `.nav__links a` (equal specificity — order decides). If you reorder the
   links in a page's <head>, the menu's colours go with them. */

.navmore { position: relative; display: flex; align-items: center; }

.navmore > button {
  cursor: pointer;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 13.5px;
  color: var(--hearth-text-secondary);
  display: flex;
  align-items: center;
  gap: 5px;
}
.navmore > button:hover { color: var(--hearth-text); }
.navmore > button > span { font-size: 9px; position: relative; top: 1px; }

.navmore-menu { display: none; position: absolute; top: 100%; right: 0; padding-top: 14px; z-index: 30; }
.navmore:hover .navmore-menu,
.navmore:focus-within .navmore-menu { display: block; }

.navmore-menu > span {
  display: flex;
  flex-direction: column;
  min-width: 214px;
  background: var(--hearth-surface);
  border: 1px solid var(--hearth-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 8px;
}
.navmore-menu a {
  display: block;
  color: var(--hearth-text-secondary);
  padding: 9px 12px;
  border-radius: var(--radius-sm);
  white-space: nowrap;
}
.navmore-menu a:hover { background: var(--hearth-sunken); color: var(--hearth-text); }
/* A menu item for the page you are already on. Both halves of the site mark the
   TOP-LEVEL current item, and four of the generated pages already marked their
   item inside this menu; `resources` and `security` are the hand-authored two
   that reach the site only through here, and they said nothing at all. Weight,
   not colour alone — the rule the design system states for every signal. */
.navmore-menu a[aria-current="page"] { color: var(--hearth-text); font-weight: 600; }

/* ---------------------------------------------------------------------------
   The masthead and footer geometry — ONE definition, both halves of the site.

   The menu above was unified here in 2026-08; the CHROME AROUND IT was not, and
   the drop drew it per page. Measured across the 33 served pages that draw a
   masthead (of 34 files — `platform.html` is a redirect stub) before this block
   existed: four nav container widths (1080 · 1140 · 1180 · 1240), three
   vertical paddings (20 · 22 · 24), `position:sticky` on the homepage and on
   the fifteen hand-authored pages but on none of the other seventeen, and a
   footer pinned at 1180 on fifteen pages whose own nav was not 1180. The
   visible result is the defect this block removes: the wordmark slides
   sideways, the nav changes height and stops sticking as you move through the
   site, and on most pages the wordmark at the top of a page does not sit above
   the wordmark at the bottom of it.

   1140/40 is the grid the SUB-PAGE CONTENT already uses (`max-width:1140px`
   with 40px gutters, so a 1060px measure). The nav is sized to it rather than
   the other way round, because content width is a design decision per page
   family and chrome alignment is not: picking the content's own number is what
   makes the logo land on the same vertical as the first heading under it.
   `site.css`'s `.nav__in`/`.wrap` are set to the same two numbers for the
   hand-authored half — change these four together or the halves drift again.

   `.sitehead` is the generated half's class; `.nav` is the hand-authored one.
   They are listed together on every rule so neither can be corrected alone. */

/* The masthead's own height, named once because three other things are measured
   from it: `scroll-padding-top` below, the in-page sticky rails the generated
   pages draw (`render-site-pages.py`'s `clear_masthead` rewrites their `top` to
   `calc(var(--sitehead-h) + …)`), and `.platform-claim` on the hand-authored
   half. 22px padding twice, plus a 172px wordmark whose 819.62x150 viewBox makes
   it 31.5px tall, plus the 1px border = 76.5 — rounded up so a fractional layout
   never leaves a hairline of content showing above it. */
:root { --sitehead-h: 78px; }

/* An anchor jump lands under a sticky bar unless the scroll port is padded for
   it. `site.css` has carried this for the hand-authored half since the bar first
   stuck there; the generated half had no media query and no scroll padding of
   any kind, and did not need either while its masthead still scrolled away. It
   sticks now, so the rule moves here where both halves read it. The extra 18px
   is clearance, not decoration: it is what stops a heading from landing flush
   against the underside of the bar. */
html { scroll-padding-top: calc(var(--sitehead-h) + 18px); }

.sitehead,
.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--hearth-canvas) 85%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--hearth-border-subtle);
}

/* One rule per part, both class names on it. `.sitehead__*` is the generated
   half, `.nav__*` the hand-authored one; listing them together is what makes a
   correction impossible to apply to only one, which is how they came apart.
   `site.css` keeps only `.nav__brand`, which has no counterpart here. */
.sitehead__in,
.nav__in {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  max-width: 1140px;
  margin: 0 auto;
  padding: 22px 40px;
}

.sitehead__links,
.nav__links {
  display: flex;
  gap: 26px;
  align-items: center;
  margin-left: auto;      /* both groups travel as one right-hand cluster */
  font-size: var(--text-sm);
  color: var(--hearth-text-secondary);
}
.sitehead__links > a,
.nav__links a { color: var(--hearth-text-secondary); font-weight: 500; transition: color .15s; }
.sitehead__links > a:hover,
.nav__links a:hover { color: var(--hearth-text); }
.sitehead__links > a[aria-current="page"],
.nav__links a[aria-current="page"] { color: var(--hearth-text); font-weight: 650; }

.sitehead__cta,
.nav__cta { display: flex; gap: 10px; align-items: center; margin-left: 16px; }

/* The footer, on the same measure as the nav above it. */
.sitefoot { border-top: 1px solid var(--hearth-border); background: var(--hearth-sunken); }
.sitefoot--rule { background: none; border-top: 0; }
.sitefoot__in {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 24px;
  flex-wrap: wrap;
  max-width: 1140px;
  margin: 0 auto;
  padding: 40px;
}
.sitefoot--rule .sitefoot__in { border-top: 1px solid var(--hearth-border); padding: 20px 40px 44px; }

/* ⚠ `flex-wrap` is deliberately NOT set on the link row. The drop shipped
   `flex-wrap:wrap` there with no media query anywhere in `site-pages.css`, so
   on a phone the six items wrapped onto two and three rows and the nav's
   height changed with the viewport — the same displacement as above, just
   driven by width instead of by page. Below 900px the links collapse and the
   wordmark keeps the CTA company, which is what the hand-authored half has
   always done (`site.css`'s `.nav__links{display:none}`); both halves are
   stated here so they cannot diverge again.
   KNOWN GAP, design-owned: that leaves a phone with no section navigation at
   all beyond the logo. Fixing it properly means folding the five top-level
   items into the existing CSS-only "More" disclosure at this breakpoint, which
   changes what the menu IS and is a design call, not a bug fix. */
@media (max-width: 900px) {
  .sitehead__links,
  .nav__links { display: none; }
  /* Links gone, so the CTA reclaims the auto margin that kept it right-aligned.
     ⚠ This is why the generated half's CTA had to be lifted out of its links
     row first (`render-site-pages.py`): nested, `display:none` above would have
     taken "Sign in" and "Start trial" down with it on all eighteen pages. */
  .sitehead__cta,
  .nav__cta { margin-left: auto; }
}
@media (max-width: 620px) {
  .sitehead__in,
  .nav__in { padding: 12px 18px; gap: 10px; }
  .sitehead__in > a > img { width: 140px; }
  .sitehead__cta,
  .nav__cta { gap: 8px; }
  .sitehead__cta .btn-sm,
  .nav__cta .btn-sm { padding: 8px 13px; }
  .sitefoot__in { padding: 28px 18px; }
  .sitefoot--rule .sitefoot__in { padding: 18px 18px 32px; }
}
/* Small phones (320-400: iPhone SE, Android compact). */
@media (max-width: 400px) {
  .sitehead__cta .btn-sm,
  .nav__cta .btn-sm { padding: 8px 11px; font-size: var(--text-xs); }
}

/* ---------------------------------------------------------------------------
   The skip link — moved here from `site.css` so the generated half stops
   carrying its own inline copy. See that file for what was wrong with two. */
.skip-link {
  position: absolute;
  inset-inline-start: var(--space-4);
  inset-block-start: var(--space-4);
  z-index: 100;
  padding: var(--space-3) var(--space-5);
  background: var(--accent-solid-bg);
  color: var(--hearth-text-on-accent);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  box-shadow: var(--shadow-lg);
  /* Visually hidden, still focusable and still announced. */
  clip-path: inset(50%);
  white-space: nowrap;
}
.skip-link:focus-visible,
.skip-link:focus {
  clip-path: none;
  outline: none;
  box-shadow: var(--shadow-lg), var(--focus-ring);
}
/* The target takes programmatic focus, so suppress the ring it would otherwise draw around the
   whole page region — the skip itself is the feedback, and a viewport-wide outline reads as a bug. */
main[tabindex="-1"]:focus { outline: none; }

/* ---- Cross-document view transitions (2026-08-23) ---------------------------------------
   955d804d made this a pure MPA: six nav items, every one a real page navigation, no in-page
   anchors left. A full document load between pages that share an identical masthead and footer
   is the exact case this feature exists for — the shared chrome persists visually across the
   swap instead of blinking, so the four destinations feel as deliberate as the old scroll did.

   Progressive enhancement in the strictest sense: a browser without support performs its normal
   instant navigation, so there is nothing to fall back to and nothing to break. Both the source
   AND the destination must opt in, which is why this lives in site-nav.css — the one stylesheet
   BOTH halves of the site link — rather than per page or in site.css, which only the
   fifteen hand-authored pages load.

   Gated on no-preference, never `reduce`: this is decorative motion across a whole document, and
   the repo's never-blank rule applies with extra force here, because a transition that stalls
   would hold the OLD page's pixels over the new one. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }

  /* Pull the masthead OUT of the root snapshot (2026-08-23).
     `navigation: auto` alone does NOT buy the persistence this feature is famous for.
     Un-named, the entire document is one `::view-transition-group(root)` that cross-
     fades — so an identical nav dissolves into an identical nav, and the reader watches
     the bar dim for no reason on every single navigation. Given a name it becomes its
     own group whose old and new boxes are the same rectangle, so it simply HOLDS while
     the content beneath it swaps. That is the whole effect.

     `.nav`/`.sitehead` is MEASURED to occur exactly once on each of the 33 pages
     that draw a masthead — the 34th file, `platform.html`, is a redirect stub with
     none, which is not a duplicate and so cannot abort anything. That count is a
     precondition rather than a nicety: a duplicated `view-transition-name` anywhere in
     one document aborts the WHOLE transition, and it fails SILENTLY — the navigation
     merely looks un-transitioned, so nothing tells you the feature stopped working. A
     new page must keep it to one, and a new name must not be reused. */
  /* BOTH masthead classes. `.sitehead` is the generated half's; a page carries one
     or the other and never both, so the name still occurs exactly once per
     document — which the note above explains is a precondition, not a nicety. */
  .nav,
  .sitehead { view-transition-name: site-nav; }

  /* The root group is now everything EXCEPT the nav, so it can carry a direction
     instead of a plain dissolve. Deliberately small and quick: this fires on every
     navigation, and motion a reader notices twice is motion they resent by the fifth.

     NEVER-BLANK — and the two halves have to be read together to see why this is safe.
     At frame 0 the backwards fill leaves the OUTGOING snapshot at full opacity while
     the incoming one is still at 0, and the incoming is painted above it. So a stalled
     transition shows the OLD page: exactly the failure the block above already weighed
     and accepted, not a blank viewport. The rule to keep when editing these: the old
     snapshot must never be zeroed faster than the new one arrives, or a stall becomes
     the blank screen this repo keeps re-learning about. */
  ::view-transition-old(root) { animation: vt-fade .18s linear both; }
  ::view-transition-new(root) { animation: vt-rise .24s var(--ease-standard) both; }
}
@keyframes vt-fade { to   { opacity: 0; } }
@keyframes vt-rise { from { opacity: 0; transform: translateY(10px); } }
