diff --git a/apps/web/astro.config.mjs b/apps/web/astro.config.mjs
index 17f6a62..3779032 100644
--- a/apps/web/astro.config.mjs
+++ b/apps/web/astro.config.mjs
@@ -1,5 +1,14 @@
// @ts-check
import { defineConfig } from "astro/config";
+import tailwindcss from "@tailwindcss/vite";
+import sitemap from "@astrojs/sitemap";
+import { site } from "./src/lib/site.ts";
// https://astro.build/config
-export default defineConfig({});
+export default defineConfig({
+ site: site.url,
+ integrations: [sitemap()],
+ vite: {
+ plugins: [tailwindcss()],
+ },
+});
diff --git a/apps/web/package.json b/apps/web/package.json
index d7ae837..ad46398 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -10,13 +10,16 @@
"build": "astro build",
"preview": "astro preview",
"astro": "astro",
- "check:types": "astro check",
+ "check:types": "astro check --minimumFailingSeverity hint",
"check:lint": "eslint --max-warnings 0"
},
"dependencies": {
+ "@astrojs/sitemap": "^3.7.2",
"@midwifes-notebook/server": "workspace:*",
+ "@tailwindcss/vite": "^4.3.0",
"@trpc/client": "^11.12.0",
- "astro": "^6.3.5"
+ "astro": "^6.3.5",
+ "tailwindcss": "^4.2.1"
},
"devDependencies": {
"@astrojs/check": "^0.9.9",
diff --git a/apps/web/public/favicon.ico b/apps/web/public/favicon.ico
index 7f48a94..954f09b 100644
Binary files a/apps/web/public/favicon.ico and b/apps/web/public/favicon.ico differ
diff --git a/apps/web/public/holding-baby.webp b/apps/web/public/holding-baby.webp
new file mode 100644
index 0000000..60eb3f1
Binary files /dev/null and b/apps/web/public/holding-baby.webp differ
diff --git a/apps/web/public/logo.svg b/apps/web/public/logo.svg
new file mode 100644
index 0000000..bd6da0e
--- /dev/null
+++ b/apps/web/public/logo.svg
@@ -0,0 +1,292 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web/public/mockup-client-detail.png b/apps/web/public/mockup-client-detail.png
new file mode 100644
index 0000000..31e015d
Binary files /dev/null and b/apps/web/public/mockup-client-detail.png differ
diff --git a/apps/web/public/mockup-client-list.png b/apps/web/public/mockup-client-list.png
new file mode 100644
index 0000000..cc646f2
Binary files /dev/null and b/apps/web/public/mockup-client-list.png differ
diff --git a/apps/web/public/og-image.png b/apps/web/public/og-image.png
new file mode 100644
index 0000000..b982e18
Binary files /dev/null and b/apps/web/public/og-image.png differ
diff --git a/apps/web/src/assets/apple-logo.svg b/apps/web/src/assets/apple-logo.svg
new file mode 100644
index 0000000..10ebf9f
--- /dev/null
+++ b/apps/web/src/assets/apple-logo.svg
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/web/src/assets/google-play-icon.svg b/apps/web/src/assets/google-play-icon.svg
new file mode 100644
index 0000000..05def19
--- /dev/null
+++ b/apps/web/src/assets/google-play-icon.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/web/src/components/Footer.astro b/apps/web/src/components/Footer.astro
new file mode 100644
index 0000000..30aec07
--- /dev/null
+++ b/apps/web/src/components/Footer.astro
@@ -0,0 +1,37 @@
+---
+
+---
+
+
diff --git a/apps/web/src/components/Navigation.astro b/apps/web/src/components/Navigation.astro
new file mode 100644
index 0000000..cbabe3d
--- /dev/null
+++ b/apps/web/src/components/Navigation.astro
@@ -0,0 +1,22 @@
+---
+
+---
+
+
+
+
+
+
+ Midwife's Notebook
+
+
+
Coming August 2026
+
+
diff --git a/apps/web/src/components/PhoneFrame.astro b/apps/web/src/components/PhoneFrame.astro
new file mode 100644
index 0000000..5eb1b52
--- /dev/null
+++ b/apps/web/src/components/PhoneFrame.astro
@@ -0,0 +1,28 @@
+---
+interface Props {
+ src: string;
+ alt: string;
+ class?: string;
+}
+const { src, alt, class: className } = Astro.props;
+---
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web/src/components/SectionLabel.astro b/apps/web/src/components/SectionLabel.astro
new file mode 100644
index 0000000..f0a859a
--- /dev/null
+++ b/apps/web/src/components/SectionLabel.astro
@@ -0,0 +1,15 @@
+---
+interface Props {
+ class?: string;
+}
+const { class: className } = Astro.props;
+---
+
+
+
+
diff --git a/apps/web/src/components/StoreButton.astro b/apps/web/src/components/StoreButton.astro
new file mode 100644
index 0000000..4cd5637
--- /dev/null
+++ b/apps/web/src/components/StoreButton.astro
@@ -0,0 +1,46 @@
+---
+import AppleLogo from "../assets/apple-logo.svg";
+import GooglePlayIcon from "../assets/google-play-icon.svg";
+
+interface Props {
+ store: "apple" | "google";
+ href?: string;
+}
+
+const { store, href } = Astro.props;
+
+const data = {
+ apple: { label: "Download on the", storeName: "App Store" },
+ google: { label: "Get it on", storeName: "Google Play" },
+} as const;
+
+const { label, storeName } = data[store];
+const disabled = !href;
+---
+
+
+ {
+ store === "apple" && (
+
+ )
+ }
+ {
+ store === "google" && (
+
+ )
+ }
+
+ {label}
+ {storeName}
+
+
diff --git a/apps/web/src/components/WaitlistForm.astro b/apps/web/src/components/WaitlistForm.astro
new file mode 100644
index 0000000..12988e5
--- /dev/null
+++ b/apps/web/src/components/WaitlistForm.astro
@@ -0,0 +1,193 @@
+---
+interface Props {
+ buttonLabel?: string;
+ class?: string;
+}
+
+const { buttonLabel = "Stay in the loop", class: className } = Astro.props;
+const inputId = `waitlist-email-${Math.random().toString(36).slice(2, 8)}`;
+---
+
+
+
+
+
+ You're on the list β we'll be in touch before launch.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web/src/layouts/BaseLayout.astro b/apps/web/src/layouts/BaseLayout.astro
new file mode 100644
index 0000000..95f0bbc
--- /dev/null
+++ b/apps/web/src/layouts/BaseLayout.astro
@@ -0,0 +1,69 @@
+---
+import Navigation from "../components/Navigation.astro";
+import Footer from "../components/Footer.astro";
+import "../styles/global.css";
+import { site } from "../lib/site";
+import { mobileApplicationSchema } from "../lib/schema";
+
+interface Props {
+ title?: string;
+ description?: string;
+ ogImage?: string;
+}
+
+const {
+ title = site.title,
+ description = site.description,
+ ogImage = site.ogImage,
+} = Astro.props;
+
+const canonicalURL = new URL(Astro.url.pathname, Astro.site);
+const ogImageURL = new URL(ogImage, Astro.site);
+---
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Skip to main content
+
+
+
+
+
+
+
+
diff --git a/apps/web/src/lib/schema.ts b/apps/web/src/lib/schema.ts
new file mode 100644
index 0000000..e55d030
--- /dev/null
+++ b/apps/web/src/lib/schema.ts
@@ -0,0 +1,33 @@
+import { site } from "./site";
+
+export function mobileApplicationSchema() {
+ return {
+ "@context": "https://schema.org",
+ "@type": "MobileApplication",
+ name: site.name,
+ description: site.description,
+ url: site.url,
+ operatingSystem: "iOS, Android",
+ applicationCategory: "HealthApplication",
+ offers: {
+ "@type": "Offer",
+ price: "0",
+ priceCurrency: "CAD",
+ },
+ };
+}
+
+export function faqPageSchema(faqs: Array<{ q: string; a: string }>) {
+ return {
+ "@context": "https://schema.org",
+ "@type": "FAQPage",
+ mainEntity: faqs.map((faq) => ({
+ "@type": "Question",
+ name: faq.q,
+ acceptedAnswer: {
+ "@type": "Answer",
+ text: faq.a,
+ },
+ })),
+ };
+}
diff --git a/apps/web/src/lib/site.ts b/apps/web/src/lib/site.ts
new file mode 100644
index 0000000..87796f5
--- /dev/null
+++ b/apps/web/src/lib/site.ts
@@ -0,0 +1,9 @@
+export const site = {
+ url: "https://midwifesnotebook.com",
+ name: "Midwife's Notebook",
+ title:
+ "Midwife's Notebook β Private Client Records App for Midwives & Doulas",
+ description:
+ "A private practice management app for midwives and doulas. Client records, clinical tools, and delivery tracking β all stored locally on your device.",
+ ogImage: "/og-image.png",
+} as const;
diff --git a/apps/web/src/pages/index.astro b/apps/web/src/pages/index.astro
index 6a1a0cc..b67dbb7 100644
--- a/apps/web/src/pages/index.astro
+++ b/apps/web/src/pages/index.astro
@@ -1,19 +1,567 @@
---
-import { trpc } from "../lib/trpc";
+import BaseLayout from "../layouts/BaseLayout.astro";
+import PhoneFrame from "../components/PhoneFrame.astro";
+import SectionLabel from "../components/SectionLabel.astro";
+import StoreButton from "../components/StoreButton.astro";
+import WaitlistForm from "../components/WaitlistForm.astro";
+import { faqPageSchema } from "../lib/schema";
-const message = await trpc.hello.query();
+const features = [
+ {
+ title: "Client Records",
+ description:
+ "A full client register with pregnancy status, clinical details, partner info, GBS status, and care notes.",
+ icon: "users",
+ },
+ {
+ title: "Clinical Tools",
+ description:
+ "LMP, gestational age, and EDD calculators that work in both directions. Enter any one value and the rest are derived instantly.",
+ icon: "wrench",
+ },
+ {
+ title: "Due Date Calendar",
+ description:
+ "A continuous scrolling calendar showing upcoming EDDs and delivery dates at a glance. Tap any date to see the relevant clients.",
+ icon: "calendar",
+ },
+ {
+ title: "Practice Statistics",
+ description:
+ "Delivery counts, GBS rates, feeding breakdowns, and more β derived entirely from your local records. No data leaves your device.",
+ icon: "chart",
+ },
+] as const;
+
+const faqs = [
+ {
+ q: "Is my data stored in the cloud?",
+ a: "No. All client data is stored in a local SQLite database on your device. Nothing is uploaded to a server or synced anywhere. The app works entirely offline.",
+ },
+ {
+ q: "Are there ads in this app?",
+ a: "No.",
+ },
+ {
+ q: "Do I need to create an account?",
+ a: "No account, no sign-in, no subscription. Install the app and it's ready to use immediately.",
+ },
+ {
+ q: "Is Midwife's Notebook PHIPA compliant?",
+ a: "Because all data stays on your device and never touches a server, there is no data transmission or third-party storage to govern. You remain the sole custodian of your records.",
+ },
+ {
+ q: "What happens if I lose my phone?",
+ a: "The app supports biometric lock (Face ID, Touch ID, or PIN) to prevent unauthorised access. If you lose your phone, or switch to a new device you will lose your client records. We are planning to implement encrypted database backups in the future to mitigate this.",
+ },
+ {
+ q: "How do you make money?",
+ a: "Essentially, we don't. This app is a labour of love.",
+ },
+ {
+ q: "Why did you make this app?",
+ a: "My wife is a midwife in Ontario, I built this app for her. β₯οΈ",
+ },
+ {
+ q: "When will it be available?",
+ a: "We're targeting August 2026 for the initial release on iOS and Android. Sign up above and we'll email you the moment it's live.",
+ },
+];
---
-
-
-
-
-
-
-
- Astro
-
-
- {message}
-
-
+
+
+
+
+
+
+
+
+
+
+
+ Private Β· Offline-first
+
+
+
+ Your notes,
+ your clients , your device.
+
+
+
+
+ Midwife's Notebook is a private client management app for midwives
+ and doulas.
+
+
+ Track client records while on-the-go β all stored locally on your
+ phone.
+
+
No cloud. No account. PHIPA Compliant.
+
+
+
+
+ Be first to know when we launch:
+
+
+
+ No spam. One email when the app is available.
+
+
+
+
+
+
+
+
+
+
+
+
+ Available soon on
+
+
+
+
+
+
+ Links live at launch β August 2026
+
+
+
+
+
+
+
+
+
What's inside
+
+ Everything you need to reference your records on the go.
+
+
+ Built specifically for midwives and doulas β not adapted from a
+ generic clinic system.
+
+
+
+
+ {
+ features.map((f) => (
+
+
+ {f.icon === "users" && (
+
+
+
+
+
+
+ )}
+ {f.icon === "wrench" && (
+
+
+
+ )}
+ {f.icon === "calendar" && (
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
+ {f.icon === "chart" && (
+
+
+
+
+
+
+ )}
+
+
+ {f.title}
+
+
+ {f.description}
+
+
+ ))
+ }
+
+
+
+
+
+
+
+
The app
+
+ Designed for how you actually work.
+
+
+ Clean, clear, and fast. Inspired by modern consumer apps β but built
+ for the realities of a midwifery practice.
+
+
+
+
+
+
+
+
+
+
+
+
Who it's for
+
+ Built for care providers.
+
+
+ Midwife's Notebook is for midwives, doulas, and other care
+ providers who manage their own client lists on the go. It focuses
+ on allowing you to easily access & update your client notes
+ while visiting clients wherever they are. Whether you use it as a
+ staging-area before you transfer your notes to Accuro, or as the
+ primary place you track your client information, it provides
+ focused, private record-keeping.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Launching August 2026
+
+
+ Stay in the loop.
+
+
+ Leave your email and we'll send you one message when Midwife's
+ Notebook is available. No newsletter, no marketing sequence.
+
+
+
+
+
+
+
+
+
+ FAQ
+
+ Common questions.
+
+
+
+
+ {
+ faqs.map((faq, i) => (
+
+ ))
+ }
+
+
+
+
+
+
+
+
+
diff --git a/apps/web/src/styles/global.css b/apps/web/src/styles/global.css
new file mode 100644
index 0000000..0cd1b23
--- /dev/null
+++ b/apps/web/src/styles/global.css
@@ -0,0 +1,119 @@
+@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Newsreader:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&display=swap");
+
+@import "tailwindcss";
+
+@custom-variant dark (&:is(.dark *));
+
+@theme inline {
+ --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
+ --font-heading: "Newsreader", ui-serif, Georgia, serif;
+
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --color-card: var(--card);
+ --color-card-foreground: var(--card-foreground);
+ --color-primary: var(--primary);
+ --color-primary-foreground: var(--primary-foreground);
+ --color-secondary: var(--secondary);
+ --color-secondary-foreground: var(--secondary-foreground);
+ --color-muted: var(--muted);
+ --color-muted-foreground: var(--muted-foreground);
+ --color-accent: var(--accent);
+ --color-accent-foreground: var(--accent-foreground);
+ --color-destructive: var(--destructive);
+ --color-border: var(--border);
+ --color-input: var(--input);
+ --color-ring: var(--ring);
+
+ --radius-sm: calc(var(--radius) * 0.6);
+ --radius-md: calc(var(--radius) * 0.8);
+ --radius-lg: var(--radius);
+ --radius-xl: calc(var(--radius) * 1.4);
+ --radius-2xl: calc(var(--radius) * 1.8);
+ --radius-3xl: calc(var(--radius) * 2.2);
+ --radius-4xl: calc(var(--radius) * 2.6);
+}
+
+:root {
+ /* background: warm off-white #fcfbf7 */
+ --background: oklch(0.988 0.005 95.1);
+ /* foreground: near-black #0f172a */
+ --foreground: oklch(0.208 0.04 265.8);
+ /* card: light cream #f5f4ee */
+ --card: oklch(0.966 0.008 98.9);
+ --card-foreground: oklch(0.208 0.04 265.8);
+ /* primary: deep forest green #1a4331 */
+ --primary: oklch(0.348 0.056 162.4);
+ --primary-foreground: oklch(0.988 0.005 95.1);
+ /* secondary: emerald #059669 */
+ --secondary: oklch(0.596 0.127 163.2);
+ --secondary-foreground: oklch(0.988 0.005 95.1);
+ /* muted: warm light gray #edecea */
+ --muted: oklch(0.943 0.003 84.6);
+ /* muted-foreground: slate-gray */
+ --muted-foreground: oklch(0.717 0.034 255.6);
+ /* accent: very light teal #edf7f3 */
+ --accent: oklch(0.968 0.012 170.3);
+ --accent-foreground: oklch(0.348 0.056 162.4);
+ --destructive: oklch(0.577 0.245 27.325);
+ /* border: warm gray #e2e0d8 */
+ --border: oklch(0.906 0.011 95.2);
+ --input: oklch(0.906 0.011 95.2);
+ --ring: oklch(0.348 0.056 162.4);
+ --radius: 0.5rem;
+}
+
+.dark {
+ /* dark-bg #0d1f18 */
+ --background: oklch(0.221 0.028 166.8);
+ --foreground: oklch(0.966 0.008 98.9);
+ /* dark-card #162b1f */
+ --card: oklch(0.267 0.035 157.8);
+ --card-foreground: oklch(0.966 0.008 98.9);
+ /* emerald-400 as primary in dark */
+ --primary: oklch(0.773 0.153 163.2);
+ --primary-foreground: oklch(0.221 0.028 166.8);
+ --secondary: oklch(0.596 0.127 163.2);
+ --secondary-foreground: oklch(0.988 0.005 95.1);
+ /* dark-muted #1e3329 */
+ --muted: oklch(0.3 0.033 163);
+ --muted-foreground: oklch(0.717 0.034 255.6);
+ --accent: oklch(0.3 0.033 163);
+ --accent-foreground: oklch(0.773 0.153 163.2);
+ --destructive: oklch(0.704 0.191 22.216);
+ --border: oklch(1 0 0 / 10%);
+ --input: oklch(1 0 0 / 15%);
+ --ring: oklch(0.773 0.153 163.2);
+}
+
+@layer base {
+ * {
+ border-color: var(--border);
+ box-sizing: border-box;
+ }
+ body {
+ background-color: var(--background);
+ color: var(--foreground);
+ font-family: var(--font-sans);
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ }
+}
+
+@keyframes accordion-down {
+ from {
+ grid-template-rows: 0fr;
+ }
+ to {
+ grid-template-rows: 1fr;
+ }
+}
+
+@keyframes accordion-up {
+ from {
+ grid-template-rows: 1fr;
+ }
+ to {
+ grid-template-rows: 0fr;
+ }
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 123f917..d0276fb 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -124,15 +124,24 @@ importers:
apps/web:
dependencies:
+ '@astrojs/sitemap':
+ specifier: ^3.7.2
+ version: 3.7.2
'@midwifes-notebook/server':
specifier: workspace:*
version: link:../server
+ '@tailwindcss/vite':
+ specifier: ^4.3.0
+ version: 4.3.0(vite@7.3.3(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.22.2)(yaml@2.9.0))
'@trpc/client':
specifier: ^11.12.0
version: 11.12.0(@trpc/server@11.12.0(typescript@6.0.3))(typescript@6.0.3)
astro:
specifier: ^6.3.5
version: 6.3.5(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.60.4)(terser@5.46.0)(tsx@4.22.2)(yaml@2.9.0)
+ tailwindcss:
+ specifier: ^4.2.1
+ version: 4.2.1
devDependencies:
'@astrojs/check':
specifier: ^0.9.9
@@ -189,6 +198,9 @@ packages:
resolution: {integrity: sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA==}
engines: {node: '>=22.12.0'}
+ '@astrojs/sitemap@3.7.2':
+ resolution: {integrity: sha512-PqkzkcZTb5ICiyIR8VoKbIAP/laNRXi5tw616N1Ckk+40oNB8Can1AzVV56lrbC5GKSZFCyJYUVYqVivMisvpA==}
+
'@astrojs/telemetry@3.3.2':
resolution: {integrity: sha512-j8DNruA8ors99Al39RYZPJK4DC1bKkoNm93mAMuBhY9TCNC4R8n1q7ovFnJ5qhGh5Lsh7pa1gpQVpYpsJPeTHQ==}
engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0}
@@ -2125,6 +2137,96 @@ packages:
'@sinonjs/fake-timers@10.3.0':
resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==}
+ '@tailwindcss/node@4.3.0':
+ resolution: {integrity: sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==}
+
+ '@tailwindcss/oxide-android-arm64@4.3.0':
+ resolution: {integrity: sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [android]
+
+ '@tailwindcss/oxide-darwin-arm64@4.3.0':
+ resolution: {integrity: sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-darwin-x64@4.3.0':
+ resolution: {integrity: sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-freebsd-x64@4.3.0':
+ resolution: {integrity: sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0':
+ resolution: {integrity: sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==}
+ engines: {node: '>= 20'}
+ cpu: [arm]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.3.0':
+ resolution: {integrity: sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.3.0':
+ resolution: {integrity: sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.3.0':
+ resolution: {integrity: sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-x64-musl@4.3.0':
+ resolution: {integrity: sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tailwindcss/oxide-wasm32-wasi@4.3.0':
+ resolution: {integrity: sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+ bundledDependencies:
+ - '@napi-rs/wasm-runtime'
+ - '@emnapi/core'
+ - '@emnapi/runtime'
+ - '@tybys/wasm-util'
+ - '@emnapi/wasi-threads'
+ - tslib
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.3.0':
+ resolution: {integrity: sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.3.0':
+ resolution: {integrity: sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/oxide@4.3.0':
+ resolution: {integrity: sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==}
+ engines: {node: '>= 20'}
+
+ '@tailwindcss/vite@4.3.0':
+ resolution: {integrity: sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==}
+ peerDependencies:
+ vite: ^5.2.0 || ^6 || ^7 || ^8
+
'@trpc/client@11.12.0':
resolution: {integrity: sha512-zTwFKQdE99pvNm7kXFdHo5xIQpGqpQJHtqVkT9o+i8h/0fbDOUBEEbFVICiMsNA+GiXskoaDRX2l+z6ir+Ug3w==}
peerDependencies:
@@ -2190,12 +2292,18 @@ packages:
'@types/nlcst@2.0.3':
resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==}
+ '@types/node@24.12.4':
+ resolution: {integrity: sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==}
+
'@types/node@25.5.0':
resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==}
'@types/react@19.2.14':
resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==}
+ '@types/sax@1.2.7':
+ resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==}
+
'@types/stack-utils@2.0.3':
resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==}
@@ -3239,6 +3347,10 @@ packages:
end-of-stream@1.4.5:
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
+ enhanced-resolve@5.21.4:
+ resolution: {integrity: sha512-wE4fDO8OjJhrPFH69HUQStq5oKvGRTNXEyW+k5C/pUQLASSsTu7obd2V3GvCDgPcY9AWjhJ4jz9Kh7iRvrxhJg==}
+ engines: {node: '>=10.13.0'}
+
entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
@@ -5623,6 +5735,11 @@ packages:
sisteransi@1.0.5:
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
+ sitemap@9.0.1:
+ resolution: {integrity: sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==}
+ engines: {node: '>=20.19.5', npm: '>=10.8.2'}
+ hasBin: true
+
slash@3.0.0:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'}
@@ -5701,6 +5818,9 @@ packages:
resolution: {integrity: sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==}
engines: {node: '>= 0.10.0'}
+ stream-replace-string@2.0.0:
+ resolution: {integrity: sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==}
+
stream-shift@1.0.3:
resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==}
@@ -5803,6 +5923,13 @@ packages:
tailwindcss@4.2.1:
resolution: {integrity: sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==}
+ tailwindcss@4.3.0:
+ resolution: {integrity: sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==}
+
+ tapable@2.3.3:
+ resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
+ engines: {node: '>=6'}
+
terminal-link@2.1.1:
resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==}
engines: {node: '>=8'}
@@ -5998,6 +6125,9 @@ packages:
undefsafe@2.0.5:
resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
+ undici-types@7.16.0:
+ resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
+
undici-types@7.18.2:
resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
@@ -6537,6 +6667,12 @@ snapshots:
dependencies:
prismjs: 1.30.0
+ '@astrojs/sitemap@3.7.2':
+ dependencies:
+ sitemap: 9.0.1
+ stream-replace-string: 2.0.0
+ zod: 4.3.6
+
'@astrojs/telemetry@3.3.2':
dependencies:
ci-info: 4.4.0
@@ -8519,6 +8655,74 @@ snapshots:
dependencies:
'@sinonjs/commons': 3.0.1
+ '@tailwindcss/node@4.3.0':
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ enhanced-resolve: 5.21.4
+ jiti: 2.6.1
+ lightningcss: 1.32.0
+ magic-string: 0.30.21
+ source-map-js: 1.2.1
+ tailwindcss: 4.3.0
+
+ '@tailwindcss/oxide-android-arm64@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-arm64@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-x64@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-freebsd-x64@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-musl@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-wasm32-wasi@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide@4.3.0':
+ optionalDependencies:
+ '@tailwindcss/oxide-android-arm64': 4.3.0
+ '@tailwindcss/oxide-darwin-arm64': 4.3.0
+ '@tailwindcss/oxide-darwin-x64': 4.3.0
+ '@tailwindcss/oxide-freebsd-x64': 4.3.0
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.0
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.3.0
+ '@tailwindcss/oxide-linux-arm64-musl': 4.3.0
+ '@tailwindcss/oxide-linux-x64-gnu': 4.3.0
+ '@tailwindcss/oxide-linux-x64-musl': 4.3.0
+ '@tailwindcss/oxide-wasm32-wasi': 4.3.0
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.3.0
+ '@tailwindcss/oxide-win32-x64-msvc': 4.3.0
+
+ '@tailwindcss/vite@4.3.0(vite@7.3.3(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.22.2)(yaml@2.9.0))':
+ dependencies:
+ '@tailwindcss/node': 4.3.0
+ '@tailwindcss/oxide': 4.3.0
+ tailwindcss: 4.3.0
+ vite: 7.3.3(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.22.2)(yaml@2.9.0)
+
'@trpc/client@11.12.0(@trpc/server@11.12.0(typescript@6.0.3))(typescript@6.0.3)':
dependencies:
'@trpc/server': 11.12.0(typescript@6.0.3)
@@ -8598,6 +8802,10 @@ snapshots:
dependencies:
'@types/unist': 3.0.3
+ '@types/node@24.12.4':
+ dependencies:
+ undici-types: 7.16.0
+
'@types/node@25.5.0':
dependencies:
undici-types: 7.18.2
@@ -8606,6 +8814,10 @@ snapshots:
dependencies:
csstype: 3.2.3
+ '@types/sax@1.2.7':
+ dependencies:
+ '@types/node': 25.5.0
+
'@types/stack-utils@2.0.3': {}
'@types/unist@3.0.3': {}
@@ -9860,6 +10072,11 @@ snapshots:
dependencies:
once: 1.4.0
+ enhanced-resolve@5.21.4:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.3.3
+
entities@4.5.0: {}
entities@6.0.1: {}
@@ -11223,8 +11440,7 @@ snapshots:
jimp-compact@0.16.1: {}
- jiti@2.6.1:
- optional: true
+ jiti@2.6.1: {}
js-tokens@4.0.0: {}
@@ -13156,6 +13372,13 @@ snapshots:
sisteransi@1.0.5: {}
+ sitemap@9.0.1:
+ dependencies:
+ '@types/node': 24.12.4
+ '@types/sax': 1.2.7
+ arg: 5.0.2
+ sax: 1.5.0
+
slash@3.0.0: {}
slice-ansi@7.1.2:
@@ -13216,6 +13439,8 @@ snapshots:
stream-buffers@2.2.0: {}
+ stream-replace-string@2.0.0: {}
+
stream-shift@1.0.3: {}
string-argv@0.3.2: {}
@@ -13347,6 +13572,10 @@ snapshots:
tailwindcss@4.2.1: {}
+ tailwindcss@4.3.0: {}
+
+ tapable@2.3.3: {}
+
terminal-link@2.1.1:
dependencies:
ansi-escapes: 4.3.2
@@ -13550,6 +13779,8 @@ snapshots:
undefsafe@2.0.5: {}
+ undici-types@7.16.0: {}
+
undici-types@7.18.2: {}
unicode-canonical-property-names-ecmascript@2.0.1: {}