diff --git a/Navigation/.gitignore b/Navigation/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/Navigation/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/Navigation/README.md b/Navigation/README.md
new file mode 100644
index 0000000..5d866fe
--- /dev/null
+++ b/Navigation/README.md
@@ -0,0 +1,116 @@
+# React PDF Viewer Sample
+
+A React + TypeScript proof-of-concept that demonstrates **programmatic navigation** in a PDF using the [Syncfusion React PDF Viewer](https://www.syncfusion.com/pdf-viewer-sdk/react-pdf-viewer) component. The app loads a sample PDF from Syncfusion's CDN and exposes a custom control panel for jumping between pages, navigating to bookmarks, and running text searches.
+
+---
+
+## ✨ Features
+
+| Capability | How it works |
+|---|---|
+| **Page navigation** | Go to previous / next page, or jump to a specific page number. |
+| **Bookmark navigation** | Retrieve all PDF bookmarks, open / close the bookmark panel, and jump to a selected destination. |
+| **Text search** | Search the document for a term (with optional match-case), then move to the next or previous result. |
+| **Zoom controls** | Fit to page, fit to width, zoom in, and zoom out. |
+
+---
+
+## 📋 Prerequisites
+
+Make sure the following are installed on your machine:
+
+- [Node.js](https://nodejs.org/) (LTS recommended)
+- [npm](https://www.npmjs.com/) (bundled with Node.js)
+
+> Verify your environment:
+> ```bash
+> node --version
+> npm --version
+> ```
+
+---
+
+## 🚀 Getting started
+
+### 1. Install dependencies
+
+```bash
+npm install
+```
+
+### 2. Run the development server
+
+```bash
+npm run dev
+```
+
+Vite will print a local URL (usually `http://localhost:5173`). Open it in your browser to see the PDF viewer.
+
+### 3. Build for production
+
+```bash
+npm run build
+```
+
+The optimized output is written to the `dist/` folder.
+
+### 4. Preview the production build locally
+
+```bash
+npm run preview
+```
+
+## 🧩 Project structure
+
+```
+pdf-viewer-app/
+├── index.html # Vite entry HTML
+├── package.json # Scripts and dependencies
+├── vite.config.ts # Vite configuration
+├── tsconfig*.json # TypeScript configuration
+├── public/ # Static assets served as-is
+└── src/
+ ├── main.tsx # React root bootstrap (StrictMode)
+ ├── App.tsx # PDF viewer + custom control panel
+ ├── App.css # Layout and styling for the control grid
+ ├── index.css # Global styles
+ └── assets/ # Local assets
+```
+
+---
+
+## 🔍 How the sample works
+
+1. **`src/main.tsx`** mounts the React app inside `#root` using `StrictMode`.
+2. **`src/App.tsx`** renders the `PdfViewerComponent` from `@syncfusion/ej2-react-pdfviewer` and wires up:
+ - A custom **control grid** with page, bookmark, search, and zoom controls.
+ - A **status banner** that reflects the latest action (e.g. *"5 bookmarks retrieved"*).
+ - **Event handlers** for `documentLoad`, `pageChange`, and `documentLoadFailed`.
+
+### Key Syncfusion APIs demonstrated
+
+| API | Purpose |
+|---|---|
+| `viewer.navigation.goToPage(n)` | Jump to a specific page |
+| `viewer.navigation.goToNextPage()` / `goToPreviousPage()` | Step through pages |
+| `viewer.bookmark.getBookmarks()` | Retrieve the bookmark tree |
+| `viewer.bookmark.goToBookmark(pageIndex, y)` | Jump to a bookmark destination |
+| `viewer.bookmark.openBookmarkPane()` / `closeBookmarkPane()` | Toggle the bookmark panel |
+| `viewer.textSearch.searchText(term, isMatchCase)` | Run a text search |
+| `viewer.textSearch.searchNext()` / `searchPrevious()` | Move between matches |
+| `viewer.magnification.fitToPage()` / `fitToWidth()` / `zoomIn()` / `zoomOut()` | Adjust the view |
+
+The sample PDF and viewer resources are loaded from Syncfusion's public CDN:
+
+```ts
+const SAMPLE_PDF = 'https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf';
+const RESOURCE_URL = 'https://cdn.syncfusion.com/ej2/34.2.3/dist/ej2-pdfviewer-lib';
+```
+
+---
+
+## 🔗 Useful links
+
+- [Syncfusion React PDF Viewer — Overview](https://help.syncfusion.com/document-processing/pdf/pdf-viewer/react/overview)
+- [Syncfusion React PDF Viewer — API reference](https://ej2.syncfusion.com/react/documentation/api/pdfviewer)
+
diff --git a/Navigation/eslint.config.js b/Navigation/eslint.config.js
new file mode 100644
index 0000000..ef614d2
--- /dev/null
+++ b/Navigation/eslint.config.js
@@ -0,0 +1,22 @@
+import js from '@eslint/js'
+import globals from 'globals'
+import reactHooks from 'eslint-plugin-react-hooks'
+import reactRefresh from 'eslint-plugin-react-refresh'
+import tseslint from 'typescript-eslint'
+import { defineConfig, globalIgnores } from 'eslint/config'
+
+export default defineConfig([
+ globalIgnores(['dist']),
+ {
+ files: ['**/*.{ts,tsx}'],
+ extends: [
+ js.configs.recommended,
+ tseslint.configs.recommended,
+ reactHooks.configs.flat.recommended,
+ reactRefresh.configs.vite,
+ ],
+ languageOptions: {
+ globals: globals.browser,
+ },
+ },
+])
diff --git a/Navigation/index.html b/Navigation/index.html
new file mode 100644
index 0000000..110f0f9
--- /dev/null
+++ b/Navigation/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ pdf-viewer-app
+
+
+
+
+
+
diff --git a/Navigation/package.json b/Navigation/package.json
new file mode 100644
index 0000000..1822fca
--- /dev/null
+++ b/Navigation/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "pdf-viewer-app",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "lint": "eslint .",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@syncfusion/ej2-react-pdfviewer": "^34.2.4",
+ "react": "^19.2.8",
+ "react-dom": "^19.2.8"
+ },
+ "devDependencies": {
+ "@eslint/js": "^10.0.1",
+ "@types/node": "^24.13.3",
+ "@types/react": "^19.2.17",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^6.0.4",
+ "eslint": "^10.8.0",
+ "eslint-plugin-react-hooks": "^7.1.1",
+ "eslint-plugin-react-refresh": "^0.5.3",
+ "globals": "^17.7.0",
+ "typescript": "~6.0.2",
+ "typescript-eslint": "^8.65.0",
+ "vite": "^8.2.0"
+ }
+}
diff --git a/Navigation/public/favicon.svg b/Navigation/public/favicon.svg
new file mode 100644
index 0000000..6893eb1
--- /dev/null
+++ b/Navigation/public/favicon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Navigation/public/icons.svg b/Navigation/public/icons.svg
new file mode 100644
index 0000000..e952219
--- /dev/null
+++ b/Navigation/public/icons.svg
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Navigation/src/App.css b/Navigation/src/App.css
new file mode 100644
index 0000000..f90339d
--- /dev/null
+++ b/Navigation/src/App.css
@@ -0,0 +1,184 @@
+.counter {
+ font-size: 16px;
+ padding: 5px 10px;
+ border-radius: 5px;
+ color: var(--accent);
+ background: var(--accent-bg);
+ border: 2px solid transparent;
+ transition: border-color 0.3s;
+ margin-bottom: 24px;
+
+ &:hover {
+ border-color: var(--accent-border);
+ }
+ &:focus-visible {
+ outline: 2px solid var(--accent);
+ outline-offset: 2px;
+ }
+}
+
+.hero {
+ position: relative;
+
+ .base,
+ .framework,
+ .vite {
+ inset-inline: 0;
+ margin: 0 auto;
+ }
+
+ .base {
+ width: 170px;
+ position: relative;
+ z-index: 0;
+ }
+
+ .framework,
+ .vite {
+ position: absolute;
+ }
+
+ .framework {
+ z-index: 1;
+ top: 34px;
+ height: 28px;
+ transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg)
+ scale(1.4);
+ }
+
+ .vite {
+ z-index: 0;
+ top: 107px;
+ height: 26px;
+ width: auto;
+ transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg)
+ scale(0.8);
+ }
+}
+
+#center {
+ display: flex;
+ flex-direction: column;
+ gap: 25px;
+ place-content: center;
+ place-items: center;
+ flex-grow: 1;
+
+ @media (max-width: 1024px) {
+ padding: 32px 20px 24px;
+ gap: 18px;
+ }
+}
+
+#next-steps {
+ display: flex;
+ border-top: 1px solid var(--border);
+ text-align: left;
+
+ & > div {
+ flex: 1 1 0;
+ padding: 32px;
+ @media (max-width: 1024px) {
+ padding: 24px 20px;
+ }
+ }
+
+ .icon {
+ margin-bottom: 16px;
+ width: 22px;
+ height: 22px;
+ }
+
+ @media (max-width: 1024px) {
+ flex-direction: column;
+ text-align: center;
+ }
+}
+
+#docs {
+ border-right: 1px solid var(--border);
+
+ @media (max-width: 1024px) {
+ border-right: none;
+ border-bottom: 1px solid var(--border);
+ }
+}
+
+#next-steps ul {
+ list-style: none;
+ padding: 0;
+ display: flex;
+ gap: 8px;
+ margin: 32px 0 0;
+
+ .logo {
+ height: 18px;
+ }
+
+ a {
+ color: var(--text-h);
+ font-size: 16px;
+ border-radius: 6px;
+ background: var(--social-bg);
+ display: flex;
+ padding: 6px 12px;
+ align-items: center;
+ gap: 8px;
+ text-decoration: none;
+ transition: box-shadow 0.3s;
+
+ &:hover {
+ box-shadow: var(--shadow);
+ }
+ .button-icon {
+ height: 18px;
+ width: 18px;
+ }
+ }
+
+ @media (max-width: 1024px) {
+ margin-top: 20px;
+ flex-wrap: wrap;
+ justify-content: center;
+
+ li {
+ flex: 1 1 calc(50% - 8px);
+ }
+
+ a {
+ width: 100%;
+ justify-content: center;
+ box-sizing: border-box;
+ }
+ }
+}
+
+#spacer {
+ height: 88px;
+ border-top: 1px solid var(--border);
+ @media (max-width: 1024px) {
+ height: 48px;
+ }
+}
+
+.ticks {
+ position: relative;
+ width: 100%;
+
+ &::before,
+ &::after {
+ content: '';
+ position: absolute;
+ top: -4.5px;
+ border: 5px solid transparent;
+ }
+
+ &::before {
+ left: 0;
+ border-left-color: var(--border);
+ }
+ &::after {
+ right: 0;
+ border-right-color: var(--border);
+ }
+}
diff --git a/Navigation/src/App.tsx b/Navigation/src/App.tsx
new file mode 100644
index 0000000..1846b60
--- /dev/null
+++ b/Navigation/src/App.tsx
@@ -0,0 +1,302 @@
+import React, { useMemo, useRef, useState } from 'react';
+import {
+ BookmarkView,
+ Inject,
+ LinkAnnotation,
+ Magnification,
+ Navigation,
+ PdfViewerComponent,
+ Print,
+ TextSearch,
+ TextSelection,
+ ThumbnailView,
+ Toolbar,
+} from '@syncfusion/ej2-react-pdfviewer';
+
+const SAMPLE_PDF = 'https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf';
+const RESOURCE_URL = 'https://cdn.syncfusion.com/ej2/34.2.3/dist/ej2-pdfviewer-lib';
+
+interface BookmarkItem {
+ id: string;
+ title: string;
+ pageIndex: number;
+ y: number;
+ depth: number;
+}
+
+interface BookmarkResult {
+ bookmarks?: { bookMark?: BookmarkItem[]; bookmark?: BookmarkItem[]; BookMark?: BookmarkItem[] };
+ Bookmarks?: { bookMark?: BookmarkItem[]; bookmark?: BookmarkItem[]; BookMark?: BookmarkItem[] };
+ bookmarksDestination?: Record;
+ BookmarksDestination?: Record;
+ [key: string]: unknown;
+}
+
+function normalizeBookmarkResult(result: unknown): BookmarkItem[] {
+ if (!result) return [];
+
+ const typedResult = result as BookmarkResult;
+ const bookmarkRoot = typedResult.bookmarks ?? typedResult.Bookmarks;
+ const roots =
+ bookmarkRoot?.bookMark ??
+ bookmarkRoot?.bookmark ??
+ bookmarkRoot?.BookMark ??
+ bookmarkRoot ??
+ (Array.isArray(result) ? result : []);
+ const destinationRoot = typedResult.bookmarksDestination ?? typedResult.BookmarksDestination ?? {};
+ const destinations =
+ destinationRoot.bookMarkDestination ??
+ destinationRoot.bookmarkDestination ??
+ destinationRoot.BookMarkDestination ??
+ destinationRoot;
+
+ const flattened: BookmarkItem[] = [];
+
+ function visit(nodes: unknown, depth: number = 0): void {
+ if (!Array.isArray(nodes)) return;
+
+ nodes.forEach((node: unknown, index: number) => {
+ const typedNode = node as Record;
+ const id = typedNode.Id ?? typedNode.id ?? typedNode.BookmarkId ?? index;
+ const destination = (destinations as Record)?.[Number(id)] ??
+ (destinations as Record)?.[id as string] ??
+ typedNode.destination ??
+ typedNode.Destination ??
+ {};
+ const destinationTyped = destination as Record;
+ const pageIndex =
+ destinationTyped.PageIndex ?? destinationTyped.pageIndex ?? typedNode.PageIndex ?? typedNode.pageIndex;
+ const y = destinationTyped.Y ?? destinationTyped.y ?? typedNode.Y ?? typedNode.y ?? 0;
+
+ flattened.push({
+ id: `${depth}-${id}-${flattened.length}`,
+ title: (typedNode.Title ?? typedNode.title ?? typedNode.Text ?? typedNode.text ?? `Bookmark ${flattened.length + 1}`) as string,
+ pageIndex: Number(pageIndex),
+ y: Number(y),
+ depth,
+ });
+
+ visit(typedNode.Child ?? typedNode.child ?? typedNode.Children ?? typedNode.children, depth + 1);
+ });
+ }
+
+ visit(roots);
+ return flattened.filter((bookmark) => Number.isFinite(bookmark.pageIndex));
+}
+
+export default function App() {
+ const viewerRef = useRef(null);
+ const [pageNumber, setPageNumber] = useState('1');
+ const [searchText, setSearchText] = useState('PDF');
+ const [bookmarks, setBookmarks] = useState([]);
+ const [selectedBookmark, setSelectedBookmark] = useState('');
+ const [status, setStatus] = useState('Loading the sample PDF…');
+ const [isBookmarkPaneOpen, setIsBookmarkPaneOpen] = useState(false);
+ const [isStatusVisible, setIsStatusVisible] = useState(true);
+ const [isMatchCase, setIsMatchCase] = useState(false);
+
+ const selectedBookmarkData = useMemo(
+ () => bookmarks.find((item) => item.id === selectedBookmark),
+ [bookmarks, selectedBookmark],
+ );
+
+ const viewer = (): PdfViewerComponent | null => viewerRef.current;
+
+ function updatePageStatus(): void {
+ const instance = viewer();
+ if (!instance) return;
+ setPageNumber(String(instance.currentPageNumber || 1));
+ setStatus(`Page ${instance.currentPageNumber || 1} of ${instance.pageCount || 0}`);
+ }
+
+ function retrieveBookmarks(): void {
+ const result = viewer()?.bookmark?.getBookmarks();
+ const items = normalizeBookmarkResult(result);
+ setBookmarks(items);
+ setSelectedBookmark(items[0]?.id ?? '');
+ setStatus(
+ items.length
+ ? `${items.length} bookmark${items.length === 1 ? '' : 's'} retrieved.`
+ : 'This PDF does not expose any navigable bookmarks.',
+ );
+ }
+
+ function goToSelectedBookmark(): void {
+ if (!selectedBookmarkData) {
+ setStatus('Retrieve and select a bookmark first.');
+ return;
+ }
+
+ viewer()?.bookmark?.goToBookmark(
+ selectedBookmarkData.pageIndex,
+ selectedBookmarkData.y,
+ );
+ setStatus(`Navigated to "${selectedBookmarkData.title}".`);
+ }
+
+ function goToPage(): void {
+ const requestedPage = Number(pageNumber);
+ if (!Number.isInteger(requestedPage) || requestedPage < 1) {
+ setStatus('Enter a valid page number starting from 1.');
+ return;
+ }
+ viewer()?.navigation?.goToPage(requestedPage);
+ }
+
+ function startSearch(): void {
+ const term = searchText.trim();
+ if (!term) {
+ setStatus('Enter text to search for.');
+ return;
+ }
+ viewer()?.textSearch?.searchText(term, isMatchCase);
+ setStatus(`Searching for "${term}"${isMatchCase ? ' (case-sensitive)' : ''}.`);
+ }
+
+ function toggleBookmarkPane(): void {
+ if (isBookmarkPaneOpen) {
+ viewer()?.bookmark?.closeBookmarkPane();
+ setIsBookmarkPaneOpen(false);
+ setStatus('Bookmark panel closed.');
+ } else {
+ viewer()?.bookmark?.openBookmarkPane();
+ setIsBookmarkPaneOpen(true);
+ setStatus('Bookmark panel opened.');
+ }
+ }
+
+ return (
+
+
+
+
+
+ {isStatusVisible && (
+
+ {status}
+ setIsStatusVisible(false)} aria-label="Close status message">
+ ✕
+
+
+ )}
+
+
+ {
+ updatePageStatus();
+ setTimeout(retrieveBookmarks, 200);
+ }}
+ pageChange={updatePageStatus}
+ documentLoadFailed={(args: unknown) => {
+ const typedArgs = args as Record | undefined;
+ setStatus(`Unable to load the PDF: ${typedArgs?.message ?? 'unknown error'}`);
+ }}
+ >
+
+
+
+
+ );
+}
diff --git a/Navigation/src/assets/hero.png b/Navigation/src/assets/hero.png
new file mode 100644
index 0000000..02251f4
Binary files /dev/null and b/Navigation/src/assets/hero.png differ
diff --git a/Navigation/src/assets/react.svg b/Navigation/src/assets/react.svg
new file mode 100644
index 0000000..6c87de9
--- /dev/null
+++ b/Navigation/src/assets/react.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Navigation/src/assets/vite.svg b/Navigation/src/assets/vite.svg
new file mode 100644
index 0000000..5101b67
--- /dev/null
+++ b/Navigation/src/assets/vite.svg
@@ -0,0 +1 @@
+Vite
diff --git a/Navigation/src/index.css b/Navigation/src/index.css
new file mode 100644
index 0000000..1d0226d
--- /dev/null
+++ b/Navigation/src/index.css
@@ -0,0 +1,55 @@
+@import '@syncfusion/ej2-base/styles/tailwind3.css';
+@import '@syncfusion/ej2-buttons/styles/tailwind3.css';
+@import '@syncfusion/ej2-popups/styles/tailwind3.css';
+@import '@syncfusion/ej2-navigations/styles/tailwind3.css';
+@import '@syncfusion/ej2-splitbuttons/styles/tailwind3.css';
+@import '@syncfusion/ej2-inputs/styles/tailwind3.css';
+@import '@syncfusion/ej2-dropdowns/styles/tailwind3.css';
+@import '@syncfusion/ej2-lists/styles/tailwind3.css';
+@import '@syncfusion/ej2-react-pdfviewer/styles/tailwind3.css';
+
+:root {
+ color: #172033;
+ background: #f2f5fa;
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ font-synthesis: none;
+}
+
+* { box-sizing: border-box; }
+body { margin: 0; min-width: 320px; }
+button, input, select { font: inherit; }
+button { cursor: pointer; }
+
+.app-shell { width: min(1500px, calc(100% - 40px)); margin: 0 auto; padding: 42px 0 60px; }
+.hero { display: flex; justify-content: space-between; align-items: end; gap: 24px; margin-bottom: 28px; }
+.hero h1 { margin: 4px 0 8px; font-size: clamp(2rem, 4vw, 3.7rem); line-height: .98; letter-spacing: -.05em; }
+.eyebrow { margin: 0; color: #5e38d3; font-weight: 800; text-transform: uppercase; letter-spacing: .14em; font-size: .75rem; }
+.intro { max-width: 720px; margin: 0; color: #596176; font-size: 1.05rem; }
+.hero a { color: #4e2cc0; font-weight: 700; white-space: nowrap; }
+
+.control-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; }
+.control-card { min-height: 210px; padding: 20px; background: #fff; border: 1px solid #dfe4ee; border-radius: 16px; box-shadow: 0 8px 28px rgba(25, 35, 60, .05); }
+.control-card h2 { margin: 8px 0 18px; font-size: 1.05rem; }
+.step { color: #7051d6; font-weight: 800; font-size: .72rem; letter-spacing: .12em; }
+.button-row, .field-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 10px; }
+.field-row label { width: 100%; color: #596176; font-size: .78rem; font-weight: 700; }
+.field-row input, .field-row select { flex: 1 1 90px; min-width: 0; height: 38px; border: 1px solid #cbd2df; border-radius: 8px; padding: 0 10px; background: #fff; }
+.bookmark-row select { flex-basis: 150px; }
+.control-card button { min-height: 38px; border: 1px solid #cbd2df; background: #fff; color: #263049; border-radius: 8px; padding: 7px 12px; font-weight: 700; }
+.control-card button:hover { border-color: #7051d6; color: #4e2cc0; }
+.control-card button.primary { border-color: #5e38d3; background: #5e38d3; color: #fff; }
+.status { margin: 16px 0 10px; padding: 10px 14px; background: #e8e3fb; color: #3c248c; border-radius: 10px; font-weight: 700; font-size: .9rem; display: flex; justify-content: space-between; align-items: center; }
+.close-btn { border: none; background: none; color: #3c248c; cursor: pointer; font-size: 1.2rem; padding: 0; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; border-radius: 4px; transition: background-color 0.2s; }
+.close-btn:hover { background-color: rgba(60, 36, 140, 0.1); }
+.checkbox-row { display: flex; align-items: center; margin-top: 10px; width: 100%; }
+.checkbox-row input[type="checkbox"] { width: 18px; height: 18px; margin: 0 8px 0 0; cursor: pointer; border: 1px solid #cbd2df; border-radius: 4px; }
+.checkbox-row label { width: auto; color: #596176; font-size: .78rem; font-weight: 700; margin: 0; display: flex; align-items: center; cursor: pointer; }
+.viewer-frame { overflow: hidden; background: #fff; border: 1px solid #d8deea; border-radius: 16px; box-shadow: 0 18px 50px rgba(25, 35, 60, .11); margin-top: 20px;}
+
+@media (max-width: 1100px) { .control-grid { grid-template-columns: repeat(2, 1fr); } }
+@media (max-width: 680px) {
+ .app-shell { width: min(100% - 24px, 1500px); padding-top: 24px; }
+ .hero { display: block; }
+ .hero a { display: inline-block; margin-top: 18px; }
+ .control-grid { grid-template-columns: 1fr; }
+}
diff --git a/Navigation/src/main.tsx b/Navigation/src/main.tsx
new file mode 100644
index 0000000..bef5202
--- /dev/null
+++ b/Navigation/src/main.tsx
@@ -0,0 +1,10 @@
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import './index.css'
+import App from './App.tsx'
+
+createRoot(document.getElementById('root')!).render(
+
+
+ ,
+)
diff --git a/Navigation/tsconfig.app.json b/Navigation/tsconfig.app.json
new file mode 100644
index 0000000..6830b6f
--- /dev/null
+++ b/Navigation/tsconfig.app.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "es2023",
+ "lib": ["ES2023", "DOM"],
+ "module": "esnext",
+ "types": ["vite/client"],
+ "allowArbitraryExtensions": true,
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["src"]
+}
diff --git a/Navigation/tsconfig.json b/Navigation/tsconfig.json
new file mode 100644
index 0000000..1ffef60
--- /dev/null
+++ b/Navigation/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "files": [],
+ "references": [
+ { "path": "./tsconfig.app.json" },
+ { "path": "./tsconfig.node.json" }
+ ]
+}
diff --git a/Navigation/tsconfig.node.json b/Navigation/tsconfig.node.json
new file mode 100644
index 0000000..8455dcb
--- /dev/null
+++ b/Navigation/tsconfig.node.json
@@ -0,0 +1,23 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "target": "es2023",
+ "lib": ["ES2023"],
+ "types": ["node"],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "module": "nodenext",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+
+ /* Linting */
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/Navigation/vite.config.ts b/Navigation/vite.config.ts
new file mode 100644
index 0000000..8b0f57b
--- /dev/null
+++ b/Navigation/vite.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react()],
+})