` carries `SiteNavigationElement` schema.org markup.
-- Structured data: emit JSON-LD (`Organization`, `WebSite`, `WebApplication` with `featureList`, `FAQPage` if an FAQ exists) from a server component rendered before visible content. Keep `featureList` in sync with the features the page shows (`apps/sim/app/(landing)/components/home-structured-data/`).
-- `/library` and `/blog` pagination stays server-rendered with real next/previous links. Each unfiltered page is indexable and uses its own canonical URL (`?page=2`, etc.); page 1 canonicalizes to the bare index. Tag-filtered variants remain `noindex,follow`. See [Google pagination guidance](https://developers.google.com/search/docs/specialty/ecommerce/pagination-and-incremental-page-loading).
-- After adding routes or anchors, verify `app/sitemap.ts` and `app/robots.ts` still reflect reality.
-
-## GEO (Generative Engine Optimisation)
-
-- **Answer-first pattern**: each section's H2 + first paragraph directly answers a question a user would ask an AI ("What is Sim?", "What integrations does Sim support?", "How much does Sim cost?").
-- **Atomic answer blocks**: every feature card, template, and pricing tier is independently quotable — self-contained, with "Sim" named explicitly. Never "the platform", "our tool", or a bare pronoun as the subject.
-- **Keyword density**: the hero's `sr-only` summary is the first text in the hero's DOM, so its first 150 characters name "Sim", "AI workspace", and "AI agents" for crawlers. The visible headline and description name "AI agents" (not "AI workspace" or "Sim"); "Sim" is otherwise carried by the title tag and the meta description.
-- **sr-only summaries**: the hero (and Templates) each carry a `` (~50 words) stating what Sim is, who it's for, and what it does — a clean citation target for AI summarizers. The hero's summary opens by naming Sim.
-- **Specific numbers**: concrete figures ("1,000+ integrations", "every major LLM", "100,000+ builders") over vague claims — and only numbers that are true and shipped.
-
-## Citations and linking (`/library`, `/blog`, `/comparisons`)
-
-The Princeton GEO study (Aggarwal et al., KDD 2024, [arXiv:2311.09735](https://arxiv.org/abs/2311.09735)) found that adding citations, quotations, and statistics were the three strongest of nine tested tactics, worth 30–40% relative lifts in AI-answer visibility. Sourcing is also what makes a claim checkable by a human reader.
-
-- **Every third-party factual claim carries an outbound source link.** Pricing, rate limits, feature availability, licensing, compliance certifications — link the primary source (the vendor's own pricing page, docs, changelog, or license file), not a secondary blog. External links get `rel="noopener noreferrer"`.
-- **Prefer the primary source over a roundup.** Citing another vendor's comparison post to substantiate a fact about them is second-hand and ages badly.
-- **Internal links: 3–5 per library post**, pointing at genuinely related library entries, using real `href`s (Next ` ` in TSX; a plain markdown link in MDX). A post with zero internal links is a dead end for crawlers and readers alike.
-- **Never fabricate a citation.** An unlinked claim is better than a link that does not substantiate it. If a number cannot be sourced, cut the number.
-
-## Freshness
-
-Answer engines weight recency to avoid repeating stale facts, and a reader deciding whether to trust a pricing comparison wants to know when it was last checked. The vendor-published "fresh content earns Nx more citations" figures are directional, not measured — the reason to do this is that both signals must agree and both must be real.
-
-- **Emit `dateModified`** in the page's structured data (JSON-LD or microdata), and emit it exactly once per document.
-- **Show the same date to the reader.** `/comparisons/[provider]` renders "Last verified …" from `getLatestVerifiedDate()`; `/library` and `/blog` posts render "Updated …" next to the publish date. A date that exists only in metadata is invisible to a reader deciding whether to trust the page.
-- **Only surface a modified date when it differs from the publish date** — an "Updated" label on the publish day is noise.
-- **Bump the date only on a substantive edit.** Touching frontmatter without changing the content is date-washing; it degrades the signal for every other page on the domain.
-- **Comparison facts are dated at the fact level.** Every `Fact` in `apps/sim/lib/compare/data` carries `sources: [{ url, label, asOf }]`. Re-checking a fact means updating its `asOf`, which flows through `getLatestVerifiedDate()` to the visible date, the JSON-LD, and the sitemap.
diff --git a/.cursor/rules/sim-ui-copy.mdc b/.cursor/rules/sim-ui-copy.mdc
index 99b5bb09a28..2e50a0c9142 100644
--- a/.cursor/rules/sim-ui-copy.mdc
+++ b/.cursor/rules/sim-ui-copy.mdc
@@ -9,7 +9,7 @@ globs: ["apps/sim/**/*.tsx","packages/emcn/**/*.tsx"]
**Do not add subtitles, helper text, or descriptive copy beneath headings, labels, cards, or settings by default.** Prefer one concise, self-explanatory heading or label. Only add supporting copy when the user explicitly asks for it, or when it is necessary to prevent misunderstanding or error — and never use it to restate the heading.
-This applies to product surfaces: settings rows, modals, panels, cards, list rows, empty states, form fields, and section headers. Marketing surfaces (`app/(landing)`, docs) are governed by `constitution.md` instead.
+This applies to product surfaces: settings rows, modals, panels, cards, list rows, empty states, form fields, and section headers. Documentation surfaces are governed by `constitution.md` instead.
**Carve-out — settings section metadata.** `SettingsNavigationItem.description` in `components/settings/navigation.ts` stays required, and `SettingsPanel` keeps rendering it as the page subtitle. Settings sections are reached through a nav list where the description is the only thing distinguishing adjacent sections, so it earns its place by the "prevents misunderstanding" test. Keep those descriptions verb-first and one line, per `sim-settings-pages.md`. Everything else on a settings page — inline `
` blurbs under section headings, field hints, modal bodies, row subtitles — follows the default rule above.
diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml
index 1909f3676d4..a5251408fd4 100644
--- a/.github/workflows/test-build.yml
+++ b/.github/workflows/test-build.yml
@@ -163,6 +163,17 @@ jobs:
with:
provider: ${{ vars.CI_PROVIDER }}
+ - name: Verify landing homepage boundary
+ working-directory: apps/sim
+ run: bun --no-env-file scripts/test-landing-e2e.ts
+ env:
+ LANDING_BRIDGE_REPORT_PATH: test-results/landing-bridge.json
+ - uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: landing-bridge
+ path: apps/sim/test-results/landing-bridge*.json
+
# Migrations create their own extensions, as on a fresh self-hosted install.
- name: Provision the database through migrations
working-directory: packages/db
diff --git a/README.md b/README.md
index 5a186532d0c..24b402ca5b4 100644
--- a/README.md
+++ b/README.md
@@ -127,6 +127,8 @@ Chat is a Sim-managed service. `npx sim-setup` connects a Chat API key for you
## Environment Variables
+Authentication, the application, and docs live in this repository. The public marketing website builds and deploys independently. Self-hosted installations open authentication at `/` and require no website service. Hosted Sim uses `LANDING_INTERNAL_ORIGIN` for a bounded, credential-free server request for the anonymous homepage; auth and API requests always remain with this application. Integrations and models remain canonical here, with model docs and historical catalog redirects generated by `bun run reference-docs:generate`.
+
See the [environment variables reference](https://docs.sim.ai/self-hosting/environment-variables) for the full list, or [`apps/sim/.env.example`](apps/sim/.env.example) for defaults.
## Tech Stack
diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx
index 28d02baf1c7..22f95e019f7 100644
--- a/apps/docs/components/icons.tsx
+++ b/apps/docs/components/icons.tsx
@@ -443,27 +443,6 @@ export function NotificationsIcon(props: SVGProps) {
)
}
-export function MailIcon(props: SVGProps) {
- return (
-
-
-
- )
-}
-
export function InstantlyIcon(props: SVGProps) {
return (
@@ -761,34 +740,6 @@ export function GithubIcon(props: SVGProps) {
)
}
-export function GithubOutlineIcon(props: SVGProps) {
- return (
-
-
-
-
- )
-}
-
export function BitbucketIcon(props: SVGProps) {
const id = useId()
const gradientId = `bitbucket_original_a_${id}`
@@ -1671,110 +1622,6 @@ export function PineconeIcon(props: SVGProps) {
)
}
-export function LangChainIcon(props: SVGProps) {
- return (
-
-
-
- )
-}
-
-export function CrewAIIcon(props: SVGProps) {
- return (
-
-
-
- )
-}
-
-export function DustIcon(props: SVGProps) {
- return (
-
-
-
- )
-}
-
-export function OpenClawIcon(props: SVGProps) {
- const id = useId()
- const gid = (name: string) => `openclaw_${name}_${id}`
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )
-}
-
export function OpenAIIcon(props: SVGProps) {
return (
) {
)
}
-export function MicrosoftCopilotIcon(props: SVGProps) {
- const id = useId()
- const gid = (name: string) => `mscopilot_${name}_${id}`
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )
-}
-
export function MicrosoftTeamsIcon(props: SVGProps) {
const id = useId()
const gradientId = `msteams_gradient_${id}`
@@ -4603,28 +4342,6 @@ export function WebhookIcon(props: SVGProps) {
)
}
-export function ScheduleIcon(props: SVGProps) {
- return (
-
-
-
-
-
-
- )
-}
-
export function QdrantIcon(props: SVGProps) {
const id = useId()
const gradientId = `qdrant_gradient_${id}`
@@ -9668,151 +9385,6 @@ export function EnrowIcon(props: SVGProps) {
)
}
-// Competitor brand logos (sourced via Context.dev brand-intelligence API, 2026-07-02)
-
-export function N8nIcon(props: SVGProps) {
- return (
-
-
-
- )
-}
-
-export function ZapierIcon(props: SVGProps) {
- return (
-
-
-
- )
-}
-
-export function MakeIcon(props: SVGProps) {
- return (
-
-
-
- )
-}
-
-export function GumloopIcon(props: SVGProps) {
- return (
-
-
-
- )
-}
-
-export function WorkatoIcon(props: SVGProps) {
- return (
-
-
-
- )
-}
-
-export function PipedreamIcon(props: SVGProps) {
- return (
-
-
-
- )
-}
-
-export function TinesIcon(props: SVGProps) {
- return (
-
-
-
- )
-}
-
-export function StackAIIcon(props: SVGProps) {
- return (
-
-
-
- )
-}
-
-export function VellumIcon(props: SVGProps) {
- return (
-
-
-
- )
-}
-
-export function RetoolIcon(props: SVGProps) {
- return (
-
-
-
- )
-}
-
-export function LangflowIcon(props: SVGProps) {
- return (
-
-
-
- )
-}
-
-export function FlowiseIcon(props: SVGProps) {
- return (
-
-
-
- )
-}
-
export function JotformIcon(props: SVGProps) {
return (
/SKILL.md`):
diff --git a/apps/sim/app/(auth)/components/auth-legal-footer.tsx b/apps/sim/app/(auth)/components/auth-legal-footer.tsx
index a5a80e698f1..b4d1319a1f2 100644
--- a/apps/sim/app/(auth)/components/auth-legal-footer.tsx
+++ b/apps/sim/app/(auth)/components/auth-legal-footer.tsx
@@ -1,4 +1,6 @@
+import { SIM_SITE_URL } from '@sim/utils/site'
import { AuthTextLink } from '@/app/(auth)/components/auth-text-link'
+import { getBrandConfig } from '@/ee/whitelabeling/branding'
interface AuthLegalFooterProps {
/** The gerund describing the consent action, e.g. "signing in". */
@@ -11,14 +13,15 @@ interface AuthLegalFooterProps {
* routed through {@link AuthTextLink}, so the consent copy has one source.
*/
export function AuthLegalFooter({ action }: AuthLegalFooterProps) {
+ const brand = getBrandConfig()
return (
By {action}, you agree to our{' '}
-
+
Terms of Service
{' '}
and{' '}
-
+
Privacy Policy
diff --git a/apps/sim/app/(auth)/components/auth-shell.tsx b/apps/sim/app/(auth)/components/auth-shell.tsx
index 1b8cb741c0b..caabeeb17c4 100644
--- a/apps/sim/app/(auth)/components/auth-shell.tsx
+++ b/apps/sim/app/(auth)/components/auth-shell.tsx
@@ -1,7 +1,7 @@
import type { ReactNode } from 'react'
import { SimWordmark } from '@sim/emcn'
+import { LogoMark } from '@/components/logo-mark'
import { DesktopTitleBarLane } from '@/app/_shell/desktop-title-bar'
-import { LogoMark } from '@/app/(landing)/components/navbar/components'
interface AuthShellProps {
/** Centered content column (the form, status copy, etc.). */
diff --git a/apps/sim/app/(interfaces)/components/interfaces-shell/interfaces-shell.tsx b/apps/sim/app/(interfaces)/components/interfaces-shell/interfaces-shell.tsx
index e7488618390..e9d196d36e6 100644
--- a/apps/sim/app/(interfaces)/components/interfaces-shell/interfaces-shell.tsx
+++ b/apps/sim/app/(interfaces)/components/interfaces-shell/interfaces-shell.tsx
@@ -1,6 +1,6 @@
import type { ReactNode } from 'react'
+import { LogoShell } from '@/components/logo-shell'
import { SupportFooter } from '@/app/(auth)/components'
-import { LogoShell } from '@/app/(landing)/components/logo-shell'
/**
* Chrome for the `(interfaces)` route group (chat + resume) — the lightweight,
diff --git a/apps/sim/app/(landing)/CLAUDE.md b/apps/sim/app/(landing)/CLAUDE.md
deleted file mode 100644
index 88655f18ce1..00000000000
--- a/apps/sim/app/(landing)/CLAUDE.md
+++ /dev/null
@@ -1,90 +0,0 @@
-# Landing Page - Build & Optimization Instructions
-
-This route group owns `/` and the entire public marketing surface - the home page, platform/solutions pages, pricing, legal, and the marketing subroutes (`/blog`, `/library`, `/models`, `/integrations`, `/demo`, `/partners`, `/changelog`). Read this file in full before adding or changing anything here. Positioning and language rules live in `.claude/rules/constitution.md`; SEO/GEO rules in `.claude/rules/landing-seo-geo.md`. Both apply to every file in this directory.
-
-`/blog` (editorial/company-voice posts) and `/library` (AEO/GEO content - listicles, comparisons, how-tos) are two separate route trees over one shared engine: `apps/sim/lib/content/` (generic registry factory, MDX components, SEO builders) instantiated per-section by the thin `apps/sim/lib/blog/` and `apps/sim/lib/library/` modules, rendered through the shared `Content*Page` components in `components/`. Content lives in `apps/sim/content/blog/` and `apps/sim/content/library/`; both share `apps/sim/content/authors/`. Adding a post to either section, or adding a new content section entirely, must reuse this engine - never hand-roll a divergent registry or page layout. Every new marketing subroute (including any future content section) needs `app/sitemap.ts` and `app/robots.ts` updated, same as `/blog` and `/library` were.
-
-## What this is
-
-- `app/(landing)/` - the marketing site. A shared `layout.tsx` renders the chrome once (the `LandingShell`: light tokens, navbar with server-side GitHub stars, painted pre-footer CTA, footer, site-wide JSON-LD); each page supplies only its `` content. The painted light/dark CTA and footer are owned by `LandingShell`; never add page-specific closing CTA bands or footer instances.
-- There is one marketing theme: the platform's. Never add `--landing-*` tokens, Martian Mono accents, or a separate marketing palette (the retired dark landing used them).
-
-## Styling - draw from the platform's tokens
-
-The landing page looks like the product. Its visual language is the workspace UI - light by default, dark on request - not a separate marketing theme.
-
-- **Light by default, dark on request.** The landing family follows the theme class on `` (next-themes, storage key `sim-landing-theme` - its own store, separate from the workspace's account-synced `sim-theme`, which holds `system` for every signed-in user): a visitor who has not chosen otherwise here gets light, the design baseline, and the footer's `ThemeToggle` switches to dark - the platform's own `.dark` token values from `app/_styles/globals.css`, no separate palette. Tokens flip on their own, so `dark:` variants exist here only to pair the handful of deliberate literals (the `#F8F8F8` paper band, the composer send button, the pale CTA drawing) with their dark value in the same class string - never leave a literal unpaired. Never read the theme in a Server Component; the toggle is the one client reader.
-- **Use platform tokens, never hex.** Canvas `--bg`, surfaces `--surface-1`…`--surface-7`, cards/modals `--surface-2`, hover `--surface-hover`, active `--surface-active`; text `--text-primary` / `--text-secondary` / `--text-muted` / `--text-body`, icons `--text-icon`; borders `--border` (its legacy alias `--border-1` is not for new work); brand `--brand-agent` / `--brand-secondary` / `--brand-accent`. Do **not** use the legacy `--landing-*` tokens - they belong to the old dark landing.
-- **Use emcn components where they fit.** The chip family (`Chip`, `ChipLink`, `ChipTag`, `ChipInput`, `ChipModal*`, …) from `@sim/emcn` is the canonical chrome - a demo-request form is a `ChipModal` with `ChipModalField`s, a pill CTA is a `Chip`/`ChipLink`. Components own their chrome; pass props, not className overrides. Full consumer rules: `.claude/rules/sim-styling.md`.
-- **Typography is the platform's.** Season is the global body font (`font-season` is applied on `` in the root layout). Use the platform text scale (`text-small` = 13px, `text-base` = 15px, etc. - see the `@theme` block in `app/_styles/globals.css`). Don't add new fonts or font CSS variables without explicit direction.
-- **Never touch global styles.** No additions to `app/_styles/globals.css`. All styling is local Tailwind classes; `cn()` from `@sim/emcn` for conditionals; inline `style` only for a genuinely dynamic value or a CSS variable (see `.claude/rules/sim-styling.md`).
-- **Responsive - desktop is the source of truth, scaled down via `max-*` overrides.** The page is fully responsive (iPad + phone). The desktop layout stays the unprefixed baseline; smaller screens are handled by *layering* `max-*` overrides on top, so desktop renders byte-identically. Tiers:
- - `max-xl:` (≤1279) - the hero's two-panel split (absolute visual + logos) collapses to a stacked, in-flow column. The split needs ≥1280 to avoid the headline colliding with the visual panel; iPad-landscape (1024) therefore gets the stacked hero with the desktop nav.
- - `max-lg:` (≤1023) - the desktop nav clusters hide (`hidden lg:flex`) and `MobileNav` (hamburger sheet) takes over; multi-column grids step down (footer 7→3); the shared gutter narrows; section gaps tighten.
- - `max-md:` (≤767) - two-up feature and card rows stack to one column.
- - `max-sm:` (≤639) - single-column grids, smallest type scale, `px-5` gutter, hero CTA row stacks.
-
- When adding a new section, give it the same `px-20 max-lg:px-8 max-sm:px-5` gutter so the navbar wordmark stays aligned with section content at every width. Verify desktop is unchanged and there is zero horizontal overflow at 1280 / 1024 / 768 / 390 before shipping.
-
-## Performance - page speed is a feature
-
-Target: Lighthouse 95+ on mobile, LCP < 2.0s, CLS < 0.05, minimal hydration cost.
-
-- **Server Components by default.** `'use client'` only on the smallest leaf that genuinely needs interactivity (a button with state, not the section containing it). The navbar, hero copy, footer, and every static section must be server-rendered HTML.
-- **No heavy client libraries above the fold.** No animation frameworks (framer-motion etc.), no ReactFlow, no chart libs in the initial bundle. If a below-fold section truly needs one, load it with `next/dynamic` and a dimension-stable placeholder.
-- **Images via `next/image` always.** The LCP element (logo or hero visual) gets `priority`; everything below the fold lazy-loads (the default). Every image has explicit `width`/`height` - zero layout shift.
-- **Prefer CSS over JS.** Hover states, transitions, marquees, and reveal effects in CSS (`transition-*`, `animation`) rather than scroll listeners or animation libraries. Decorative motion respects `prefers-reduced-motion`.
-- **Static rendering.** The page is statically generated with `revalidate` (set in `page.tsx`). Never fetch per-request data in the page tree; anything dynamic (e.g. GitHub stars) is fetched at build/revalidate time or deferred to a client island. A `cookies()`/`headers()`/`unstable_noStore()` call anywhere in the tree - including the root `app/layout.tsx` - silently overrides every page's `revalidate` and forces the whole app dynamic. If a marketing page builds as `ƒ` instead of `○`/`●` (check `bun run build`'s route table), look upstream, not just at the page itself.
-- **Reserve space for everything.** Fixed dimensions or aspect ratios on all media, embeds, and async content. CLS budget is effectively zero.
-- **Decorative canvases and animations are non-interactive.** A hand-built product-demo animation or embedded ReactFlow canvas is presentation only - no drag handlers, no `nodesDraggable`/`panOnDrag`/`elementsSelectable` on ReactFlow. A visitor should never be able to click or drag a decorative element.
-- **Lazy-mount a heavy client island's second occurrence.** If the same animated component appears twice on a page, only the first (usually the hero) loads eagerly - the rest go through a small `'use client'` mount wrapper built on the shared `hooks/use-lazy-mount.ts` hook: `next/dynamic(..., { ssr: false })` gated by the hook's `IntersectionObserver`. See `components/product-demo/components/product-demo-visual-mount/` for the reference pattern, and `.claude/rules/sim-imports.md` for the barrel-cleanup step that must come with it.
-- **Don't prefetch authenticated-app routes from an always-visible CTA.** ` ` prefetches its target route's JS once it's in the viewport - a navbar/hero CTA to `/signup` or `/login` is always in view, so it downloads that route's bundle on every pageview. Pass `prefetch={false}` there. Leave the default on CTAs that only enter the viewport on scroll (prefetch-on-approach is the desired behavior there).
-
-## SEO / GEO
-
-`page.tsx` owns each page's metadata (title, description, OG/Twitter, canonical, robots) — the single source of truth, aligned with the constitution's claim hierarchy. Every other SEO and GEO rule — the one hero ``, heading hierarchy, landmarks, JSON-LD, crawlable links, answer-first sections, atomic "Sim"-named blocks, the hero `sr-only` summary, citations, and freshness — is in `.claude/rules/landing-seo-geo.md`. Read it before adding a section.
-
-## Copy
-
-Follow `.claude/rules/constitution.md` exactly: Sim is "the open-source AI workspace where teams build, deploy, and manage AI agents" - never a workflow tool or automation platform. Direct sentences, active voice, concrete examples, no exclamation marks, no unexplained jargon on public pages.
-
-## Structure
-
-**Directory convention - mirror `app/workspace/[workspaceId]/`.** Every component lives in its own kebab-case folder holding `.tsx` plus an `index.ts` barrel (relative re-export); a component's children nest under that folder's `components/` (each itself foldered, with its own `index.ts`). Non-component modules - `types.ts`, `constants.ts`, data files - sit at the relevant folder root. Never leave a bare `.tsx` flat inside a `components/` directory.
-
-```
-(landing)/
-├── CLAUDE.md # this file
-├── page.tsx # route entry: metadata +
-├── landing.tsx # root composition: section order
-├── workflows/ # a platform route: page.tsx (metadata) + workflows.tsx (config + shell)
-├── hooks/ # cross-page client hooks (useLazyMount, …) - bare files, no folder/barrel
-└── components/
- ├── index.ts # top barrel
- ├── navbar/{navbar.tsx, index.ts, components//…} # : wordmark, dropdowns, stars, auth chips
- ├── hero/{hero.tsx, index.ts, components/hero-platform-loop/…} # h1, description, CTA, lazy product loop
- ├── features/, footer/, platform-suite/, product-demo/, security/, … # each: .tsx + index.ts (+ components/)
- ├── shared/ # cross-page reused chrome (folder-per-component + barrel)
- │ ├── landing-shell/ # light wrapper + skip link + Navbar(stars) + Footer; wraps every page
- │ ├── hero-cta/ # the one email-capture + Sign-up CTA (hero + every platform hero)
- │ └── logos/ # the one customer-logo set; layout='grid' (hero) | 'row' (platform)
- └── solutions-page/ # the reusable solutions/platform layout (Workflows, Knowledge, Tables, Files, Logs, solutions/*)
- ├── solutions-page.tsx, index.ts, types.ts, constants.ts # SolutionsPage + the content contract + spacing
- └── components/ # solutions-hero, solutions-logos-row, solutions-card-row (→ card, pill-cta),
- # solutions-visual-frame, solutions-structured-data
-```
-
-Each section component's TSDoc carries its layout spec - read it before implementing. Section components own their landmark (Navbar → `