Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
Commit41a1450unknown_key

Fix sticky-nav CSS bleeding into every content <header> (pricing hero overlap)

Fix sticky-nav CSS bleeding into every content <header> (pricing hero overlap)

The layout's nav styles targeted the bare header element — sticky,
height: var(--header-h), blur backdrop, border. Every semantic <header>
in page content (~200 across src/routes) inherited a 60px sticky bar.
On /pricing this crushed the hero to 105px and rendered the headline on
top of the plan cards.

- Scope the nav rules to a new .site-header class on the layout's own
  header element (no JS or e2e selectors referenced bare header).
- Fix undefined var(--space-7) in .pl-compare (resolved to 0 margin);
  use --s-14.
- Add a regression test asserting the nav styles stay scoped and no
  bare header selector ships in the page CSS.

Verified by rendering /pricing, /, /explore, /changelog, /vs-github,
/login headless at 1180/1440/390px in light + dark themes: hero no
longer overlaps cards, nav remains sticky after scroll.

https://claude.ai/code/session_01BCaibgckcvsuGmByyn8d8L
Claude committed on June 10, 2026Parent: 74a8784
3 files changed+25641a145007fc847e77a288d9b988afe9305007a86
3 changed files+25−6
Modifiedsrc/__tests__/pricing-page.test.ts+16−0View fileUnifiedSplit
101101 expect(body).toContain("Bundle math vs GitHub");
102102 expect(body).toContain("$89");
103103 });
104
105 it("layout nav styles are scoped to .site-header, never bare <header>", async () => {
106 // Regression — 2026-06-10: layout.tsx styled the bare `header` element
107 // (sticky, height: var(--header-h), blur). Every page that uses a
108 // semantic <header> for a hero or section head (~200 across src/routes)
109 // inherited a 60px sticky bar, which crushed the /pricing hero and
110 // overlaid its title on top of the plan cards. The nav rule must stay
111 // scoped to .site-header.
112 const res = await app.request("/pricing");
113 const body = await res.text();
114 expect(body).toContain('<header class="site-header">');
115 // No CSS rule may target the bare element: `header {` or a
116 // `... header {` descendant/`theme header` selector outside a class.
117 const bareHeaderSelector = /(^|[}\s])header(\s+nav)?\s*\{/m;
118 expect(bareHeaderSelector.test(body)).toBe(false);
119 });
104120});
Modifiedsrc/routes/pricing.tsx+1−1View fileUnifiedSplit
670670 value proposition unmissable. Same visual rhythm as .pl-host-grid; the
671671 "us" side wins via accent border + subtle gradient glow. */
672672 .pl-compare {
673 margin-top: var(--space-7);
673 margin-top: var(--s-14);
674674 }
675675 .pl-compare-grid {
676676 display: grid;
Modifiedsrc/views/layout.tsx+8−5View fileUnifiedSplit
162162 />
163163 </div>
164164 )}
165 <header>
165 <header class="site-header">
166166 <nav>
167167 <a href="/" class="logo">
168168 gluecron
14901490 }
14911491 .playground-banner-dismiss:hover { color: var(--text-strong, #e6edf3); }
14921492
1493 /* Header — sticky, blurred, hairline border, taller for breathing room */
1494 header {
1493 /* Site nav header — sticky, blurred, hairline border. Scoped to
1494 .site-header: pages use semantic <header> elements for section/hero
1495 headings, and a bare element selector here turns every one of them
1496 into a 64px sticky bar (the /pricing hero-overlap bug). */
1497 .site-header {
14951498 position: sticky;
14961499 top: 0;
14971500 z-index: 100;
15021505 backdrop-filter: saturate(180%) blur(18px);
15031506 -webkit-backdrop-filter: saturate(180%) blur(18px);
15041507 }
1505 :root[data-theme='light'] header { background: rgba(251,251,252,0.78); }
1508 :root[data-theme='light'] .site-header { background: rgba(251,251,252,0.78); }
15061509
1507 header nav {
1510 .site-header nav {
15081511 display: flex;
15091512 align-items: center;
15101513 gap: 18px;
15111514