Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d012699
fix: improve mobile navigation and layouts on docs site
michaelmcnees Mar 23, 2026
8100636
feat: replace mobile top nav with bottom navigation bar for better re…
michaelmcnees Mar 23, 2026
dc08a6c
fix: update QuickStart guide and SSL config for local Postgres dev
michaelmcnees Mar 23, 2026
770a9b0
fix: grpc CVE, copilot review feedback, sslmode default restored
michaelmcnees Mar 23, 2026
4703821
fix(ci): only trigger once per PR (push on main, pull_request on bran…
michaelmcnees Mar 23, 2026
6052b70
fix: address all 6 Copilot review comments on PR #4
michaelmcnees Mar 23, 2026
fc783d8
fix: address 2 new Copilot comments on PR #4
michaelmcnees Mar 23, 2026
fa47ef6
fix: address 2 Copilot comments — md-lg nav gap and sidebar bottom of…
michaelmcnees Mar 23, 2026
86874a8
fix: table :has selector and sidebar aria-expanded/aria-hidden
michaelmcnees Mar 23, 2026
a129a03
fix: aria-label on nav landmarks, reset aria state on sidebar close
michaelmcnees Mar 23, 2026
f0c8cda
fix: update site getting-started sslmode=disable → sslmode=prefer
michaelmcnees Mar 23, 2026
91044b4
chore: remove redundant root docs, update URLs to mantle.dvflw.co
michaelmcnees Mar 23, 2026
e952982
Update site/src/styles/global.css
michaelmcnees Mar 23, 2026
91ce4b1
fix: README links to mantle.dvflw.co, GitHub target="_blank", depreca…
michaelmcnees Mar 23, 2026
b82e26f
fix: add target=_blank rel=noopener to sidebar GitHub link
michaelmcnees Mar 23, 2026
152d518
fix: address 4 review comments on PR #4
michaelmcnees Mar 23, 2026
9b08941
fix: bail out of deferInitNav after 10 frames on non-docs pages
michaelmcnees Mar 23, 2026
815ec70
fix: sslmode=prefer warning for non-loopback hosts, prefix nav globals
michaelmcnees Mar 24, 2026
4b48827
Update internal/config/config.go
michaelmcnees Mar 24, 2026
00bca25
fix: add missing net and strings imports for sslmode check
michaelmcnees Mar 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ name: CI

on:
push:
branches: ['**']
branches: [main]
pull_request:
branches: ['**']

jobs:
test:
Expand Down
8 changes: 8 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ make build

The `docker compose up -d` command starts Postgres 16 on `localhost:5432` with user `mantle`, password `mantle`, and database `mantle`. The `make build` command produces a single `mantle` binary in the project root. The `mantle init` command creates all required database tables.

The default database URL uses `sslmode=prefer`, which tries TLS and falls back gracefully for local development. For production, always use `sslmode=require` or `sslmode=verify-full`:

```bash
export MANTLE_DATABASE_URL="postgres://mantle:secret@db.example.com:5432/mantle?sslmode=require"
```

See [Configuration](configuration.md) for all database options.

You should see:

```
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,6 @@ require (
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d // indirect
google.golang.org/grpc v1.79.1 // indirect
google.golang.org/grpc v1.79.3 // indirect
Comment thread
michaelmcnees marked this conversation as resolved.
google.golang.org/protobuf v1.36.11 // indirect
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -350,8 +350,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d h1:t/LOSXPJ9R0B6fnZNyALBRfZBH0Uy0gT+uR+SJ6syqQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
2 changes: 1 addition & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func Load(cmd *cobra.Command) (*Config, error) {
v := viper.New()

// Defaults
v.SetDefault("database.url", "postgres://mantle:mantle@localhost:5432/mantle?sslmode=require")
v.SetDefault("database.url", "postgres://mantle:mantle@localhost:5432/mantle?sslmode=prefer")
v.SetDefault("database.max_open_conns", 25)
v.SetDefault("database.max_idle_conns", 25)
v.SetDefault("database.conn_max_lifetime", 5*time.Minute)
Expand Down
4 changes: 2 additions & 2 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func TestLoad_Defaults(t *testing.T) {
t.Fatalf("Load() error = %v", err)
}

if cfg.Database.URL != "postgres://mantle:mantle@localhost:5432/mantle?sslmode=require" {
if cfg.Database.URL != "postgres://mantle:mantle@localhost:5432/mantle?sslmode=prefer" {
t.Errorf("Database.URL = %q, want default", cfg.Database.URL)
}
if cfg.API.Address != ":8080" {
Expand Down Expand Up @@ -97,7 +97,7 @@ func TestLoad_ImplicitConfigMissing_UsesDefaults(t *testing.T) {
t.Fatalf("Load() error = %v, want nil (silent fallback)", err)
}

if cfg.Database.URL != "postgres://mantle:mantle@localhost:5432/mantle?sslmode=require" {
if cfg.Database.URL != "postgres://mantle:mantle@localhost:5432/mantle?sslmode=prefer" {
t.Errorf("Database.URL = %q, want default", cfg.Database.URL)
}
}
Expand Down
46 changes: 25 additions & 21 deletions site/src/components/DocsSidebar.astro
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ function isParentActive(item: NavItem): boolean {
<div id="sidebar-overlay" class="fixed inset-0 z-40 bg-black/50 hidden lg:hidden" aria-hidden="true"></div>

<!-- Mobile sidebar drawer -->
<aside id="docs-sidebar-mobile" class="fixed top-16 left-0 bottom-0 z-50 w-72 bg-surface-container-lowest border-r border-outline-variant transform -translate-x-full transition-transform duration-200 lg:hidden overflow-y-auto">
<aside id="docs-sidebar-mobile" class="fixed top-16 left-0 bottom-0 z-50 w-72 bg-surface-container-lowest border-r border-outline-variant transform -translate-x-full transition-transform duration-200 lg:hidden overflow-y-auto sidebar-mobile-bottom">
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
<nav class="py-6 px-4" aria-label="Documentation sidebar mobile">
{docsNav.map((group) => (
<div class="mb-8">
Expand All @@ -111,7 +111,7 @@ function isParentActive(item: NavItem): boolean {
<a
href={item.href}
class:list={[
'flex items-center justify-between px-3 py-1.5 text-[13px] transition-colors',
'flex items-center justify-between px-3 py-2 text-sm transition-colors',
active
? 'text-primary font-medium border-l-2 border-primary bg-primary/5'
: 'text-on-surface-variant hover:text-on-surface border-l-2 border-transparent hover:border-outline-variant',
Expand Down Expand Up @@ -140,7 +140,7 @@ function isParentActive(item: NavItem): boolean {
<a
href={child.href}
class:list={[
'block pl-6 pr-3 py-1 text-[12px] transition-colors',
'block pl-6 pr-3 py-1.5 text-[13px] transition-colors',
childActive
? 'text-primary font-medium border-l-2 border-primary bg-primary/5'
: 'text-on-surface-variant/80 hover:text-on-surface border-l-2 border-outline-variant/30 hover:border-outline-variant',
Expand All @@ -159,38 +159,42 @@ function isParentActive(item: NavItem): boolean {
</ul>
</div>
))}

<!-- Site navigation -->
<div class="mt-4 pt-4 border-t border-outline-variant">
<h4 class="text-[10px] font-headline font-bold uppercase tracking-[0.15em] text-on-surface-variant/60 mb-3 px-3">
Site
</h4>
<ul>
<li>
<a href="/" class="flex items-center gap-2 px-3 py-2 text-sm text-on-surface-variant hover:text-on-surface transition-colors">
Home
</a>
</li>
<li>
<a href="https://github.com/dvflw/mantle" class="flex items-center gap-2 px-3 py-2 text-sm text-on-surface-variant hover:text-on-surface transition-colors">
GitHub
</a>
</li>
</ul>
</div>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
</nav>
</aside>

<script is:inline>
function initSidebar() {
const toggleBtn = document.getElementById('sidebar-toggle');
const mobileSidebar = document.getElementById('docs-sidebar-mobile');
const overlay = document.getElementById('sidebar-overlay');
if (!toggleBtn || !mobileSidebar || !overlay) return;
if (!mobileSidebar || !overlay) return;

function openSidebar() {
mobileSidebar.classList.remove('-translate-x-full');
overlay.classList.remove('hidden');
}
function closeSidebar() {
mobileSidebar.classList.add('-translate-x-full');
overlay.classList.add('hidden');
}

const newBtn = toggleBtn.cloneNode(true);
toggleBtn.parentNode?.replaceChild(newBtn, toggleBtn);
newBtn.addEventListener('click', () => {
const isOpen = !mobileSidebar.classList.contains('-translate-x-full');
if (isOpen) closeSidebar(); else openSidebar();
});

const newOverlay = overlay.cloneNode(true);
overlay.parentNode?.replaceChild(newOverlay, overlay);
newOverlay.addEventListener('click', closeSidebar);

overlay.onclick = closeSidebar;
mobileSidebar.querySelectorAll('a').forEach((a) => {
a.addEventListener('click', closeSidebar);
a.onclick = closeSidebar;
});
Comment on lines 190 to 203

Copilot AI Mar 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

closeSidebar() hides the overlay and drawer but does not restore aria-hidden on the overlay or reset aria-expanded on the docs menu button. If the user closes via overlay click or by selecting a link, assistive tech state can remain stuck as “expanded/visible”. Update closeSidebar() to set overlay.setAttribute('aria-hidden','true') and reset the button’s aria-expanded as well.

Copilot uses AI. Check for mistakes.
}
initSidebar();
Expand Down
75 changes: 54 additions & 21 deletions site/src/components/Nav.astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ interface Props {
}
const { isHome = false } = Astro.props;
const prefix = isHome ? '' : '/';
const isDocs = !isHome;
const isDocs = Astro.url.pathname.startsWith('/docs');
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
---
<nav class="fixed top-0 left-0 right-0 z-50 border-b border-outline-variant bg-surface/80 backdrop-blur-md">
<div class={`nav-inner mx-auto px-6 flex items-center justify-between h-16 ${isHome ? 'max-w-[1200px]' : 'max-w-[1280px]'}`}>
Expand All @@ -31,39 +31,72 @@ const isDocs = !isHome;
</a>
</div>

<!-- Mobile hamburger -->
<button id="mobile-menu-btn" class="md:hidden text-on-surface-variant hover:text-on-surface" aria-label="Menu">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg>
</button>
</div>
</nav>

<!-- Mobile menu -->
<div id="mobile-menu" class="hidden md:hidden border-t border-outline-variant bg-surface/95 backdrop-blur-md">
<div class="px-6 py-4 flex flex-col gap-3">
<a href={`${prefix}#how-it-works`} class="text-sm text-on-surface-variant hover:text-on-surface transition-colors">How It Works</a>
<a href={`${prefix}#connectors`} class="text-sm text-on-surface-variant hover:text-on-surface transition-colors">Connectors</a>
<a href={`${prefix}#comparison`} class="text-sm text-on-surface-variant hover:text-on-surface transition-colors">Compare</a>
<a href="/docs/getting-started" class:list={["text-sm transition-colors", isDocs ? "text-primary font-medium" : "text-on-surface-variant hover:text-on-surface"]}>Docs</a>
<a href={`${prefix}#get-started`} class="inline-flex justify-center px-4 py-2 bg-primary text-on-primary text-sm font-medium mt-2">Get Started</a>
</div>
<!-- Mobile bottom navigation bar -->
<nav id="mobile-bottom-nav" class="fixed bottom-0 left-0 right-0 z-50 lg:hidden border-t border-outline-variant bg-surface/95 backdrop-blur-md">
<div class="flex items-center justify-around h-14 px-2">
Comment thread
michaelmcnees marked this conversation as resolved.
Comment thread
michaelmcnees marked this conversation as resolved.
Outdated
<a href="/" class:list={["bottom-nav-item flex flex-col items-center gap-0.5 px-3 py-1 text-[11px] transition-colors", isHome ? "text-primary" : "text-on-surface-variant hover:text-on-surface"]}>
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955a1.126 1.126 0 011.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25"/></svg>
<span>Home</span>
</a>
{isDocs && (
<button id="bottom-nav-docs-btn" class="bottom-nav-item flex flex-col items-center gap-0.5 px-3 py-1 text-[11px] text-on-surface-variant hover:text-on-surface transition-colors" aria-label="Documentation menu">
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"/></svg>
<span>Menu</span>
</button>
Comment thread
michaelmcnees marked this conversation as resolved.
)}
<a href="/docs/getting-started" class:list={["bottom-nav-item flex flex-col items-center gap-0.5 px-3 py-1 text-[11px] transition-colors", isDocs ? "text-primary" : "text-on-surface-variant hover:text-on-surface"]}>
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25"/></svg>
<span>Docs</span>
</a>
<a href="https://github.com/dvflw/mantle" class="bottom-nav-item flex flex-col items-center gap-0.5 px-3 py-1 text-[11px] text-on-surface-variant hover:text-on-surface transition-colors" aria-label="GitHub">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z"/></svg>
<span>GitHub</span>
</a>
</div>
</nav>
Comment thread
coderabbitai[bot] marked this conversation as resolved.

<style>
.nav-inner {
transition: max-width 0.3s ease;
}
#mobile-bottom-nav {
padding-bottom: env(safe-area-inset-bottom, 0px);
}
.bottom-nav-item {
-webkit-tap-highlight-color: transparent;
min-width: 3.5rem;
}
.bottom-nav-item:active {
opacity: 0.7;
}
</style>

<script is:inline>
function initNav() {
const btn = document.getElementById('mobile-menu-btn');
const menu = document.getElementById('mobile-menu');
if (!btn || !menu) return;
// Remove old listeners by cloning
const newBtn = btn.cloneNode(true);
btn.parentNode?.replaceChild(newBtn, btn);
newBtn.addEventListener('click', () => menu.classList.toggle('hidden'));
const btn = document.getElementById('bottom-nav-docs-btn');
const docsSidebar = document.getElementById('docs-sidebar-mobile');
const overlay = document.getElementById('sidebar-overlay');
if (!btn || !docsSidebar || !overlay) return;
Comment thread
coderabbitai[bot] marked this conversation as resolved.

btn.setAttribute('aria-expanded', 'false');

btn.onclick = () => {
const isOpen = !docsSidebar.classList.contains('-translate-x-full');
if (isOpen) {
docsSidebar.classList.add('-translate-x-full');
overlay.classList.add('hidden');
overlay.setAttribute('aria-hidden', 'true');
btn.setAttribute('aria-expanded', 'false');
} else {
docsSidebar.classList.remove('-translate-x-full');
overlay.classList.remove('hidden');
overlay.setAttribute('aria-hidden', 'false');
btn.setAttribute('aria-expanded', 'true');
}
};
Comment on lines 77 to +101

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider extracting shared sidebar toggle logic to avoid duplication.

Both Nav.astro (lines 86-98) and DocsSidebar.astro (lines 190-196) manage the same sidebar/overlay state with similar code. The open logic here and close logic in DocsSidebar could drift out of sync.

Consider extracting a shared utility (e.g., toggleDocsSidebar(open: boolean)) that both components invoke, or consolidating all toggle/close logic in one place.

Otherwise, the implementation is correct:

  • Early return guards against missing elements on non-docs pages
  • ARIA state is properly synchronized with visual state
  • Re-initialization on astro:after-swap handles View Transitions correctly
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@site/src/components/Nav.astro` around lines 77 - 99, The sidebar toggle logic
in initNav duplicates the code in DocsSidebar; extract a shared function (e.g.,
toggleDocsSidebar(open: boolean)) that encapsulates the DOM manipulations now in
initNav (toggling '-translate-x-full' on docsSidebar, adding/removing 'hidden'
on overlay, and setting overlay aria-hidden and btn aria-expanded) and export or
import it so both Nav.astro's initNav and DocsSidebar call the same function
(use a single source of truth for open/close), keep the early guards for missing
elements in the shared helper or have callers validate, and ensure both
components still re-initialize on astro:after-swap to preserve view transition
behavior.

}
initNav();
document.addEventListener('astro:after-swap', initNav);
Expand Down
8 changes: 8 additions & 0 deletions site/src/content/docs/getting-started/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ make build

The `docker compose up -d` command starts Postgres 16 on `localhost:5432` with user `mantle`, password `mantle`, and database `mantle`. The `make build` command produces a single `mantle` binary in the project root. The `mantle init` command creates all required database tables.

The default database URL uses `sslmode=disable`, which is correct for local development with the provided Docker Compose setup. For production, always use `sslmode=require` or `sslmode=verify-full`:
Comment thread
michaelmcnees marked this conversation as resolved.
Outdated

```bash
export MANTLE_DATABASE_URL="postgres://mantle:secret@db.example.com:5432/mantle?sslmode=require"
```

See [Configuration](/docs/configuration) for all database options.

You should see:

```
Expand Down
14 changes: 1 addition & 13 deletions site/src/layouts/Docs.astro
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,7 @@ const { prev, next } = getPrevNext(currentPath);
<Base title={`${title} - Mantle Docs`}>
<Nav />
<div class="mx-auto max-w-[1280px] px-4 sm:px-6">
<div class="flex min-h-screen pt-16">
<!-- Mobile sidebar toggle -->
<button
id="sidebar-toggle"
class="lg:hidden fixed bottom-6 left-6 z-50 flex items-center gap-2 px-4 py-2.5 bg-surface-container-high border border-outline-variant text-on-surface text-sm font-medium shadow-lg"
aria-label="Toggle sidebar"
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
Menu
</button>

<div class="flex min-h-screen pt-16 pb-20 lg:pb-0">
<DocsSidebar currentPath={currentPath} />

<!-- Main content -->
Expand Down
2 changes: 1 addition & 1 deletion site/src/pages/404.astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Nav from '../components/Nav.astro';
---
<Base title="404 — Route Not Found | Mantle">
<Nav />
<main class="min-h-screen flex items-center justify-center pt-16">
<main class="min-h-screen flex items-center justify-center pt-16 pb-20 lg:pb-0">
<div class="mx-auto max-w-[700px] px-6 py-24">
<h1 class="font-headline text-4xl sm:text-5xl font-bold tracking-tight mb-8">
404: Route Not Found
Expand Down
2 changes: 1 addition & 1 deletion site/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Footer from '../components/Footer.astro';
---
<Base title="Mantle — Headless AI Workflow Automation">
<Nav isHome={true} />
<main id="main-content">
<main id="main-content" class="pb-20 lg:pb-0">
<Hero />
<Features />
<HowItWorks />
Expand Down
44 changes: 43 additions & 1 deletion site/src/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,16 @@ body {
}
}

/* Mobile sidebar clears the bottom nav; at lg+ bottom nav is hidden so reset to 0 */
.sidebar-mobile-bottom {
bottom: calc(3.5rem + env(safe-area-inset-bottom, 0px));
}
@media (min-width: 1024px) {
.sidebar-mobile-bottom {
bottom: 0;
}
}

/* Feature card hover glow */
.feature-card {
transition: border-color 0.2s, box-shadow 0.2s;
Expand Down Expand Up @@ -317,14 +327,22 @@ body {
margin-top: 0;
}

/* Tables */
/* Tables — preserve table layout for border-collapse, scroll via parent */
.prose table {
width: 100%;
border-collapse: collapse;
margin-top: 1.5em;
font-size: 0.875rem;
}

/* Make prose scrollable when it contains a wide table */
@supports selector(:has(*)) {
.prose:has(table) {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
}

.prose thead th {
background-color: var(--color-surface-container-low);
font-weight: 600;
Expand Down Expand Up @@ -365,6 +383,30 @@ body {
border: 1px solid var(--color-outline-variant);
}

/* Mobile prose adjustments */
@media (max-width: 640px) {
.prose h1 {
font-size: 1.5rem;
}

.prose h2 {
font-size: 1.25rem;
}

.prose h3 {
font-size: 1.1rem;
}

.prose pre {
font-size: 0.8125rem;
padding: 0.75em 1em;
}

.prose code {
word-break: break-word;
}
Comment thread
michaelmcnees marked this conversation as resolved.
}

/* Definition lists (used in some docs) */
.prose dt {
font-weight: 600;
Expand Down
Loading