/* Issue #1061 — Bottom navigation styles.
*
* Activates at viewports ≤768px. Uses position:fixed so it does not
* trigger layout reflow on the rest of the page, plus
* env(safe-area-inset-bottom) padding so the iOS home-indicator does
* not overlap the tabs. The matching already
* exists in index.html (verified pre-implementation).
*
* Tokens reused (defined in BOTH :root and dark @media in style.css):
* --nav-bg, --nav-text, --nav-text-muted, --nav-active-bg, --accent,
* --border, --space-sm.
*
* Decision: media query (not container query). The rest of the codebase
* uses @media exclusively (no @container rules in style.css today), so
* a media query keeps things consistent.
*
* Decision: top-nav suppression = display:none at ≤768px. Spec
* forbids duplicate nav UX; the bottom nav covers the 5 high-priority
* routes; long-tail routes (Tools/Lab/Perf/Analytics/etc.) remain
* reachable by URL. A "More" tab or hamburger fallback is deferred per
* the issue body's explicit guidance.
*/
/* #1174 mesh-op review: --bottom-nav-reserve is the contract page-level
* full-viewport rules use to subtract the bottom-nav's height from
* 100dvh. 0px at desktop (no nav reserved); 56px + safe-area at ≤768px.
* Pages opt-in by referencing it (see public/live.css for /live, and
* #app.app-fixed in style.css for the SPA fixed-page container). */
:root {
--bottom-nav-reserve: 0px;
}
/* Default: hidden on wide viewports. The bottom-nav element exists in
* the DOM at all widths (build runs at DOMContentLoaded) but is only
* rendered to the user at ≤768px. */
.bottom-nav {
display: none;
}
@media (max-width: 768px) {
/* #1174 mesh-op review: set the reserve token at the breakpoint so
* page-level full-viewport rules (e.g. .live-page, #app.app-fixed)
* automatically subtract the bottom-nav height. */
:root {
--bottom-nav-reserve: calc(56px + env(safe-area-inset-bottom, 0px));
}
.bottom-nav {
display: flex;
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 1200; /* above nav-links dropdown (1100) */
background: var(--nav-bg);
border-top: 1px solid var(--border);
box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.25);
/* env() falls back to 0 outside iOS notch devices. We also keep
* a small minimum so the rule resolves to a non-empty value. */
padding-bottom: env(safe-area-inset-bottom, 0px);
padding-top: 0;
/* Distribute 5 tabs evenly. */
justify-content: space-around;
align-items: stretch;
/* No transform — would create a stacking context that traps any
* fixed-position descendants (we have none, but cheap insurance). */
}
/* Suppress the inline link bar and right-side cluster — but KEEP
* .nav-brand (logo identity). #1174: also hide #hamburger at narrow
* widths — the new "More" tab in the bottom-nav now surfaces the
* long-tail routes, so the hamburger is redundant on phones. */
.top-nav .nav-links,
.top-nav .nav-more-wrap,
.top-nav .nav-right,
.top-nav .nav-stats {
display: none !important;
}
/* #1174: hamburger hidden at ≤768px (replaced by the More tab). */
#hamburger {
display: none !important;
}
/* Brand on the left, hamburger on the right at narrow widths. */
.top-nav {
justify-content: space-between;
}
/* Reserve space at page bottom so fixed-positioned bottom-nav does
* not cover the last row of content. 56px tab + 8px breathing room
* + safe-area inset. */
body {
padding-bottom: calc(56px + env(safe-area-inset-bottom, 0px));
}
}
/* Tab — anchor element. Each tab is a column with icon over label, sized
* to ≥48px tall (the Apple/Google touch-target floor confirmed by
* issue #1060). */
.bottom-nav-tab {
flex: 1 1 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2px;
/* 56px is a comfortable Material/iOS bottom-bar height; it is also
* ≥48px (a11y floor) by 8px so labels render without clipping. */
min-height: 56px;
padding: 6px 4px;
color: var(--nav-text-muted);
text-decoration: none;
font-size: 11px;
line-height: 1.1;
border-top: 2px solid transparent;
/* Reset