diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..77ce572 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,61 @@ +name: Deploy FELG Dashboard + +on: + push: + branches: [main] + workflow_dispatch: + +env: + PUBLIC_SUPABASE_URL: ${{ secrets.PUBLIC_SUPABASE_URL }} + PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.PUBLIC_SUPABASE_ANON_KEY }} + SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} + +jobs: + validate-sync-deploy: + runs-on: ubuntu-latest + permissions: + contents: read + pages: write + id-token: write + + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + - name: Install dependencies + run: npm ci + + # Step 1: Validate the data file + - name: Validate dashboard.json + run: node scripts/validate-data.js + + # Step 2: Sync validated data to Supabase + - name: Sync to Supabase + run: node scripts/sync-to-supabase.js + + # Step 3: Build the Astro site + - name: Build Astro + run: npm run build + + # Step 4: Upload and deploy to GitHub Pages + - name: Setup Pages + uses: actions/configure-pages@v4 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: dist + + - name: Deploy to Pages + id: deployment + uses: actions/deploy-pages@v4 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..016b59e --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# build output +dist/ + +# generated types +.astro/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store + +# jetbrains setting folder +.idea/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..22a1505 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + "recommendations": ["astro-build.astro-vscode"], + "unwantedRecommendations": [] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d642209 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "command": "./node_modules/.bin/astro dev", + "name": "Development server", + "request": "launch", + "type": "node-terminal" + } + ] +} diff --git a/AGENT-PROMPT.md b/AGENT-PROMPT.md new file mode 100644 index 0000000..95cc124 --- /dev/null +++ b/AGENT-PROMPT.md @@ -0,0 +1,111 @@ +# πŸ€– FELG Dashboard β€” Agent Build Prompt + +Copy this document into a coding agent's context (Claude Code, or equivalent) to execute the MVP build. It is self-contained but assumes the agent also has read access to [`PRJ-FELG.md`](./PRJ-FELG.md) (source of truth, **v1.3.0-r1 / Amendment A3**) and [`IMPLEMENTATION-GUIDE.md`](./IMPLEMENTATION-GUIDE.md) (architecture) in the same repo. + +> **Amendment A3:** the frontend is **Astro + Tailwind CSS**, not vanilla HTML/CSS/JS. Brand detail and "all brands" views are real Astro routes, not hash-based fake routes. The backend (Supabase + Realtime, Amendment A2) is unchanged. If you've seen an earlier version of this prompt describing a hand-written `router.js` and raw ES module render functions, that version is stale β€” this one supersedes it. + +--- + +## Your Mission + +Build the MVP (Phase 1) of the FELG Ecosystem Dashboard: a public, open web app β€” **no signup, ever, for viewing** β€” dark-themed, built with Astro + Tailwind CSS, displaying four FELG sections β€” πŸŽ‰ Fun, πŸ’° Earning, πŸ“š Learning, 🫢 Giving β€” in that exact order, backed by Supabase for storage and realtime push delivery, deployable to GitHub Pages. + +Before writing any code, read `PRJ-FELG.md` in full and `IMPLEMENTATION-GUIDE.md` Β§1–7. Every requirement below cites an FR/NFR number from the PRD β€” if you're ever unsure what a requirement means, that number is where the definitive answer lives, not this prompt. + +--- + +## Non-Negotiables (violating any of these is a failed build, not a style choice) + +| Rule | Detail | Source | +|:---|:---|:---| +| **Section order is fixed** | Fun β†’ Earning β†’ Learning β†’ Giving, in the DOM, always. Never data-driven. | FR-40, πŸ”’ IMMUTABLE | +| **No authentication, anywhere** | No login form, no session, no auth-gated route, no admin panel. This is an open web app β€” nothing gates viewing. | FR-44, πŸ”’ IMMUTABLE, NFR-09 | +| **Dark mode is the only mode** | No light-mode toggle, no `darkMode` config in Tailwind. One theme. | PRD Out of Scope table | +| **No calculated score field** | Never add a `felg_score`, `contribution_score`, or similar aggregate. | PRD Out of Scope table | +| **Astro + Tailwind, static output** | `output: 'static'` in `astro.config.mjs`. No SSR, no API routes β€” Supabase is the only backend this product needs. Don't reach for React/Vue/Svelte components; plain Astro components + small client-side TypeScript scripts are sufficient for everything this build requires. | PRD Amendment A3, Implementation Guide Β§2 | +| **Brand navigation uses real routes, not a hand-written router** | `src/pages/brand/[id].astro` with `getStaticPaths()`. Do not build a hash-based router or a click-handler-driven modal system β€” plain `` links plus the browser's native back button satisfy FR-49–52 for free. | FR-67, Implementation Guide Β§5 | +| **Realtime delivery IS required β€” this is MVP, not a stretch goal** | The dashboard must push updates to open browsers via Supabase Realtime, with no client polling loop. | FR-65, PRD Amendment A2 | +| **Data ingestion is still manual for MVP** | Do not build Postiz/SendGrid/GitHub API integrations. Content still comes from a human editing `data/dashboard.json` in a PR. | PRD Phasing Β§, Phase 2 unchanged | +| **The service-role Supabase key must never reach the client** | It exists only as a GitHub Actions secret, used exclusively by `scripts/sync-to-supabase.js`. The browser and the Astro build-time frontmatter both use only the public anon key, safe specifically because RLS restricts it to SELECT-only. | FR-66, πŸ”’ IMMUTABLE | +| **Live-patch scripts never own page structure** | `src/scripts/live/patch/*.ts` update specific DOM nodes (tagged `data-live="..."`) or rebuild a small, scoped sub-tree. They do not duplicate whole-page layout logic that belongs in the `.astro` components. | Implementation Guide Β§4.4 | + +--- + +## What You're Building + +Follow the repository structure in `IMPLEMENTATION-GUIDE.md` Β§3 exactly. In short: + +``` +astro.config.mjs, tailwind.config.mjs, package.json +src/lib/supabase.ts (public client β€” used at build time AND client-side) +src/layouts/Layout.astro, src/components/*.astro +src/pages/index.astro, src/pages/brands.astro, src/pages/brand/[id].astro +src/scripts/live/client.ts, src/scripts/live/patch/*.ts +data/dashboard.json (human-edited source β€” not fetched at runtime) +supabase/schema.sql +scripts/{validate-data,sync-to-supabase}.js +.github/workflows/deploy.yml (validate β†’ sync β†’ astro build β†’ deploy) +``` + +The data shape must match PRD Appendix B **exactly** β€” same keys, same nesting, same enum values (`status` ∈ `live | in_progress | not_started | paused`). Do not invent fields. This shape lives as a single JSONB column on one Supabase row (`id = 'current'`) β€” see Implementation Guide Β§4.3 for the exact schema and query pattern. + +**Critical architectural point:** the browser and the Astro build process both read from Supabase, never from `data/dashboard.json` directly. That file is the human-editable source; CI copies its contents into Supabase on merge, and every subsequent read β€” at build time or in the browser β€” goes through Supabase. + +--- + +## Build Order + +Work through these in sequence. Steps 2–5 prove the entire data pipeline (file β†’ CI β†’ Supabase β†’ build-time render β†’ browser realtime) end-to-end before any route/page work gets built on top of it. + +1. **Scaffold.** `npm create astro@latest`, `astro add tailwind`, configure `astro.config.mjs` (`output: 'static'`). Build `Layout.astro`, `Header.astro`, `Footer.astro`, and an `index.astro` with hardcoded sample data for all four sections, in FELG order, per FR-36–41. Get this looking right before touching Supabase. +2. **Provision Supabase.** Apply `supabase/schema.sql`, enable Realtime replication, seed one row with sample data. Confirm in the Supabase dashboard that RLS is on and anon has SELECT-only access. +3. **Wire the build-time read.** Add `src/lib/supabase.ts`; update `index.astro`'s frontmatter to fetch from Supabase instead of using hardcoded data. Run `astro build` and confirm real data appears in the output HTML. +4. **Wire the realtime subscription.** Build `src/scripts/live/client.ts` and one patch module (start with `patch/social.ts`). Manually edit the row in the Supabase dashboard UI and confirm an already-open browser tab updates with zero reload. **Do not proceed past this step until you've actually watched this happen.** +5. **Wire the CI pipeline.** Build `scripts/validate-data.js` and `scripts/sync-to-supabase.js`, then `.github/workflows/deploy.yml` (validate β†’ sync β†’ `astro build` β†’ deploy). Test for real: edit `data/dashboard.json`, open a PR, merge it, and confirm the change appears live in an open tab within seconds **and** survives a fresh page reload. +6. **Brand detail pages.** Build `src/pages/brand/[id].astro` with `getStaticPaths()` sourced from the same Supabase read used in step 3. Style per PRD Β§6.2. Add the ESC-key listener (`history.back()` on Escape) for FR-51. Do not build a router β€” `BrandCard.astro` just links to the page with a plain ``. +7. **All Brands route.** Build `src/pages/brands.astro`, grouped by status per FR-54–57, each card linking to its `brand/[id]` page. +8. **Remaining live-patch coverage.** `patch/earning.ts`, `patch/learning.ts`, `patch/giving.ts`. Add relative "last updated"/"last synced" timestamps (FR-14, FR-39) and the manual refresh button as an explicit fallback (FR-45). +9. **Responsive + accessibility pass.** Tailwind breakpoints (`sm:`, `lg:`) for the brand grid. Full keyboard operability. Contrast-check the pillar colors against `bg`. +10. **Security verification.** From the browser console, using only the public anon key already present in the deployed bundle, attempt an `insert` or `update` against the Supabase table. It must fail. This is a launch blocker β€” do not skip it. +11. **Performance check.** Run Lighthouse against the built site. If LCP exceeds 3s or TTI exceeds 2s, investigate before moving on β€” Astro's default output should make this easier to hit than a hand-rolled site, not harder, so a miss here is worth digging into. + +Stop after step 11. Postiz/SendGrid/GitHub API integration, historical trend charts, and an admin data-entry UI are Phase 2/3 and out of scope for this pass β€” see Implementation Guide Β§9 if asked to scope that work later. + +--- + +## Definition of Done + +Before reporting this build complete, verify β€” don't assume β€” every item: + +- [ ] `astro build` completes with zero errors and `dist/` contains one static page per brand in `all_brands`, each with a distinct `` and OG tags (FR-67) +- [ ] Opening the deployed site renders all four sections in Funβ†’Earningβ†’Learningβ†’Giving order with real data β€” not hardcoded β€” sourced from Supabase at build time +- [ ] Every FR-01 through FR-67 in the PRD has a corresponding, working piece of UI β€” cross-check against the FR table in Implementation Guide Β§5 module-by-module +- [ ] **You have personally watched a merged PR to `data/dashboard.json` update an already-open browser tab within seconds, with no manual refresh, and confirmed a subsequent fresh page load also reflects it.** Describe what you observed in your report. +- [ ] Attempting a write against Supabase from the browser console, using the public anon key, fails (RLS confirmed) +- [ ] The service-role key does not appear anywhere in `dist/`, `src/`, or any browser network request β€” grep for it before you report done +- [ ] `scripts/validate-data.js` runs clean against the shipped `dashboard.json` and fails loudly on a deliberately broken copy (test this) +- [ ] No auth code, no signup flow, no light-mode code, no score field, no React/Vue/Svelte dependency, no hand-written router exists anywhere in the diff +- [ ] Clicking a brand card navigates to `/brand/<id>/`; the browser back button returns to the prior scroll position on `/` with zero custom code involved +- [ ] ESC key closes the brand detail page (FR-51) +- [ ] Keyboard-only pass: Tab reaches every interactive element +- [ ] Lighthouse numbers meet NFR-01/NFR-02 targets β€” paste the actual numbers into your final report, not "should be fine" +- [ ] `.github/workflows/deploy.yml` successfully validates, syncs to Supabase, runs `astro build`, and deploys on a test push + +If any box can't be checked, say so explicitly in your summary rather than reporting the build as done β€” an unchecked box you didn't mention is worse than one you flagged. + +--- + +## When to Stop and Ask + +Don't guess on these β€” surface them instead of picking silently: + +- Whether `/brands` should be a dedicated route or an inline expansion on `/` (PRD Β§7.1 leaves this open; Implementation Guide Β§10 leans toward a route but defers to @PAT) +- Whether `sync-to-supabase.js` should run automatically on every merge or require a manual trigger β€” a trust/safety tradeoff for the human team to set +- Anything in `data/dashboard.json` that seems to need a field not present in PRD Appendix B +- Final hosting target if GitHub Pages direct isn't confirmed by the time you reach the deploy step β€” this affects `astro.config.mjs`'s `site`/`base` values +- Any FR that seems to conflict with another FR β€” flag the numbers, don't resolve it by picking one +- If you don't have a Supabase project to provision against β€” don't fabricate credentials or stub around it silently; ask for a project to be created first + +--- + +*Operating brief for PRJ-FELG.md v1.3.0-r1 (Amendment A3) / IMPLEMENTATION-GUIDE.md. Read both in full before starting step 1.* diff --git a/IMPLEMENTATION-GUIDE.md b/IMPLEMENTATION-GUIDE.md new file mode 100644 index 0000000..1ef3ae0 --- /dev/null +++ b/IMPLEMENTATION-GUIDE.md @@ -0,0 +1,334 @@ +# πŸ› οΈ FELG Dashboard β€” Implementation Guide + +Companion to [`PRJ-FELG.md`](./PRJ-FELG.md) **v1.3.0-r1** (Amendment A3). This document translates the PRD's 67 functional requirements into a concrete build: file structure, data contract, component boundaries, and a delivery sequence for the MVP (Phase 1, W29–W30). + +> **πŸ”’ Amendment A3 note:** This guide previously specified a hand-authored vanilla HTML/CSS/JS frontend with hash-based routing. Per PRD Amendment A3, the frontend is now **Astro + Tailwind CSS**, and the product is explicitly framed as an **open, public web app β€” no signup, ever**. The Supabase + Realtime backend from Amendment A2 is **unchanged** β€” this revision only touches how the frontend is authored and routed. + +Every decision below is either **locked** (taken directly from the PRD β€” do not relitigate) or **open** (flagged for @PAT / Core TEAM). Where a requirement is cited, the FR/NFR number is the source of truth β€” if this guide and the PRD ever disagree, the PRD wins and this file needs a patch. + +--- + +## 1. Architecture at a Glance + +``` +Human editor + β”‚ + β”œβ”€ edits data/dashboard.json in a PR + β”œβ”€ scripts/validate-data.js runs in CI + └─ PR reviewed + merged to main + β”‚ + β–Ό +GitHub Actions (.github/workflows/deploy.yml) + β”‚ + β”œβ”€ scripts/sync-to-supabase.js (service-role key, secret β€” WRITE) + β”‚ upserts the merged JSON into ONE Supabase table row + β”‚ + β”œβ”€ astro build + β”‚ each page's frontmatter does a build-time READ from Supabase + β”‚ (public anon key β€” same key the browser will use) + β”‚ β†’ produces static HTML with a good, real, indexable snapshot + β”‚ + └─ deploys dist/ to GitHub Pages + β”‚ + β–Ό +Supabase (Postgres + Realtime) β€” unchanged from Amendment A2 + β”‚ + β”œβ”€ one table, one row, one JSONB column (PRD Appendix B shape) + β”œβ”€ RLS: anon role = SELECT only (FR-66) + └─ Realtime broadcasts UPDATE events on that row + β”‚ + β–Ό +Browser (static Astro output + one small client-side "island") + β”‚ + β”œβ”€ src/scripts/live/client.ts + β”‚ 1. On load: supabase.select() β€” catches anything newer than the build snapshot + β”‚ 2. Subscribes to Realtime β†’ patches specific DOM nodes on every push (FR-65) + β”‚ 3. Manual refresh button re-runs step 1, as an explicit fallback (FR-45) + β”‚ + └─ Real per-page routes: /, /brands, /brand/<id>/ β€” no client-side router (FR-67) +``` + +`β˜… Insight ─────────────────────────────────────` +**One Supabase client config, two runtimes.** `src/lib/supabase.ts` uses the *public* anon key and is imported in two completely different places: inside an Astro page's frontmatter (runs in Node, at build time, to produce the static snapshot) and inside the client-side island script (runs in the browser, to subscribe to live updates). Same file, same credentials, same RLS boundary β€” Astro's architecture makes "fetch this data" trivially reusable across build-time and runtime without duplicating the query logic. The *only* place that ever holds the service-role write key is `scripts/sync-to-supabase.js`, which never ships to a browser at all. + +**Real routes retire an entire module.** The previous vanilla plan needed a hand-written `router.js` to fake shareable brand URLs via `location.hash`. Astro's file-based routing (`src/pages/brand/[id].astro`) produces real URLs at build time β€” which means the browser's native back button and scroll restoration satisfy FR-50 for free, and a plain `<a href="/brand/f1visa/">` link (working even with JavaScript disabled) replaces what used to be a click handler + hash parser + history management. This isn't a wash β€” it's a net deletion of a whole category of code the framework switch buys us. +`─────────────────────────────────────────────────` + +--- + +## 2. Tech Stack β€” Locked for MVP + +| Decision | Choice | Why | +|:---|:---|:---| +| Frontend framework | **Astro**, `output: 'static'` | PRD Amendment A3. Static build output (still deployable anywhere static files go), file-based routing gives real per-brand URLs (FR-67), component model maps cleanly onto the four FELG sections. | +| Styling | **Tailwind CSS** via `@astrojs/tailwind` | PRD Amendment A3. Utility classes express the existing design tokens (Β§6 below) faster than hand-rolled CSS; Astro's official integration needs zero extra build config. | +| Backend | **Supabase (Postgres + Realtime)** β€” unchanged | Amendment A2. Not affected by the frontend tooling change. | +| Data storage shape | **One table, one row, one JSONB column** β€” unchanged | PRD Appendix B shape stored wholesale; still no per-domain table normalization for MVP. | +| Routing | **Real Astro file-based routes** β€” `/`, `/brands`, `/brand/[id]/` | Supersedes hash routing. Satisfies FR-52 and FR-67 (shareable, indexable, real OG tags per brand) more directly than a client-side router could. | +| Data source (content) | **Manual JSON entry, PR-reviewed** β€” unchanged | Amendment A3 doesn't touch data sourcing. Postiz/SendGrid/GitHub ingestion is still Phase 2. | +| Hosting (frontend) | **GitHub Pages** | Astro has first-party GitHub Pages deploy support (`site`/`base` config + official deploy action). | +| Auth | **None for viewing β€” open web app.** Supabase RLS: anon role SELECT-only. | FR-44, FR-66, NFR-09. No signup exists anywhere in this product. | + +**Not decided yet (flag to @PAT / @SHD / @MWK):** +- Final hosting target β€” GitHub Pages direct vs. behind Cloudflare once `FELG.WeOwn.Net` DNS is live (PRD Risk #1); this determines the `site`/`base` values in `astro.config.mjs` (PRD Risk #9). +- Whether `sync-to-supabase.js` runs automatically on every merge or requires a manual workflow dispatch (PRD Risk #8). +- Whether `/brands` (All Brands) is its own route or an inline expansion on `/` β€” PRD Β§7.1 leaves this open. **Recommendation given the Astro switch:** make it a real route, consistent with brand detail pages now also being real routes β€” but this is still @PAT's call, not locked. + +--- + +## 3. Repository Structure + +``` +FELG-dashboard/ +β”œβ”€β”€ astro.config.mjs # output: 'static', site/base config +β”œβ”€β”€ tailwind.config.mjs # theme.extend.colors sourced from PRD's pillar-color table (Β§6 below) +β”œβ”€β”€ package.json +β”œβ”€β”€ README.md +β”œβ”€β”€ LICENSE +β”œβ”€β”€ PRJ-FELG.md +β”œβ”€β”€ IMPLEMENTATION-GUIDE.md +β”œβ”€β”€ AGENT-PROMPT.md +β”œβ”€β”€ data/ +β”‚ └── dashboard.json # human-edited, PR-reviewed source β€” feeds Supabase via CI; never fetched directly by Astro or the browser +β”œβ”€β”€ supabase/ +β”‚ β”œβ”€β”€ schema.sql # table + RLS policy (unchanged from Amendment A2) +β”‚ └── README.md +β”œβ”€β”€ scripts/ +β”‚ β”œβ”€β”€ validate-data.js # unchanged in purpose β€” schema + referential checks, runs before sync +β”‚ └── sync-to-supabase.js # unchanged in purpose β€” service-role key, CI-only, WRITE +β”œβ”€β”€ src/ +β”‚ β”œβ”€β”€ env.d.ts +β”‚ β”œβ”€β”€ lib/ +β”‚ β”‚ └── supabase.ts # public anon-key client β€” imported at build time (Node) AND client-side +β”‚ β”œβ”€β”€ layouts/ +β”‚ β”‚ └── Layout.astro # <html> shell, dark theme, default meta/OG tags, imports global.css +β”‚ β”œβ”€β”€ styles/ +β”‚ β”‚ └── global.css # @tailwind base/components/utilities +β”‚ β”œβ”€β”€ components/ +β”‚ β”‚ β”œβ”€β”€ Header.astro # FR-36 +β”‚ β”‚ β”œβ”€β”€ Footer.astro # FR-38 +β”‚ β”‚ β”œβ”€β”€ BrandCard.astro # plain <a href> link β€” no click handler needed +β”‚ β”‚ β”œβ”€β”€ BrandGrid.astro # Top 5 + "Show All" link to /brands +β”‚ β”‚ β”œβ”€β”€ SocialSection.astro +β”‚ β”‚ β”œβ”€β”€ EarningSection.astro +β”‚ β”‚ β”œβ”€β”€ LearningSection.astro +β”‚ β”‚ └── GivingSection.astro +β”‚ β”œβ”€β”€ pages/ +β”‚ β”‚ β”œβ”€β”€ index.astro # home β€” all 4 FELG sections, hardcoded order +β”‚ β”‚ β”œβ”€β”€ brands.astro # All Brands, grouped by status β€” real route (FR-54–60) +β”‚ β”‚ └── brand/ +β”‚ β”‚ └── [id].astro # per-brand detail page β€” getStaticPaths, real URL + OG tags (FR-67) +β”‚ └── scripts/ +β”‚ └── live/ +β”‚ β”œβ”€β”€ client.ts # supabase client init, initial select, realtime subscription +β”‚ └── patch/ +β”‚ β”œβ”€β”€ brands.ts # patches Top 5 card values; full brand-list changes trigger a small rebuild +β”‚ β”œβ”€β”€ social.ts +β”‚ β”œβ”€β”€ earning.ts +β”‚ β”œβ”€β”€ learning.ts +β”‚ └── giving.ts +└── .github/ + └── workflows/ + └── deploy.yml # validate β†’ sync-to-supabase β†’ astro build β†’ deploy dist/ to Pages +``` + +**What disappeared vs. the pre-Amendment-A3 plan:** `router.js` (real routes replace it entirely) and the raw `assets/js/render/*.js` module set (split into Astro components for structure + `src/scripts/live/patch/*.ts` for live updates β€” see Β§5). + +--- + +## 4. Data Layer + +Unchanged from Amendment A2 in every respect except *who calls it and when*. + +### 4.1 Contract + +Still PRD Appendix B, verbatim, stored as one JSONB column on one Supabase row. + +### 4.2 Validation & Sync β€” unchanged in purpose + +`scripts/validate-data.js` and `scripts/sync-to-supabase.js` keep doing exactly what they did in the Amendment A2 plan (see PRD Β§4.2/Β§4.3 there for the full spec) β€” nothing about the frontend framework changes what happens on the write side. + +### 4.3 Build-Time Read (`src/lib/supabase.ts`, used in page frontmatter) + +```ts +// src/lib/supabase.ts +import { createClient } from '@supabase/supabase-js'; + +export const supabase = createClient( + import.meta.env.PUBLIC_SUPABASE_URL, + import.meta.env.PUBLIC_SUPABASE_ANON_KEY +); +``` + +```astro +--- +// src/pages/index.astro +import { supabase } from '../lib/supabase'; +const { data } = await supabase.from('dashboard_state').select('payload').eq('id', 'current').single(); +const dashboard = data.payload; +--- +``` + +This runs during `astro build` (Node context) β€” the resulting HTML already has real numbers in it before a single line of client JS executes. This is what makes the static snapshot both fast (no client-side fetch needed to see *something*) and honest (it's real data, not a placeholder). + +### 4.4 Client-Side Live Patch (`src/scripts/live/client.ts`) + +```ts +import { supabase } from '../../lib/supabase'; +import { patchSocial } from './patch/social'; +import { patchEarning } from './patch/earning'; +// ...one import per section + +async function refresh() { + const { data } = await supabase.from('dashboard_state').select('payload').eq('id', 'current').single(); + applyAll(data.payload); +} + +function applyAll(payload) { + patchSocial(payload.fun.social_media); + patchEarning(payload.earning); + // ... +} + +refresh(); // catches anything newer than the build snapshot, on page load + +supabase + .channel('dashboard-changes') + .on('postgres_changes', { event: 'UPDATE', schema: 'public', table: 'dashboard_state' }, + (msg) => applyAll(msg.new.payload)) + .subscribe(); + +document.getElementById('refresh-btn')?.addEventListener('click', refresh); // FR-45 fallback +``` + +Each `patch/*.ts` function targets specific DOM nodes tagged with a `data-live="..."` attribute in the corresponding Astro component's markup β€” e.g. `<span data-live="youtube.subscribers">1200</span>` gets its `textContent` updated directly. For values that are whole collections (the brand grid, a milestone list), the patch function rebuilds that one sub-tree's `innerHTML` from a small template string rather than trying to diff individual nodes. + +`β˜… Insight ─────────────────────────────────────` +**This is the one place where Astro's islands model has a real cost, not just a benefit β€” be honest about it with the team.** The initial HTML structure lives in an `.astro` component; the live-update logic that reproduces that same structure for realtime patches lives in a separate `.ts` file. Two representations of "what a brand card looks like" now exist, and they can drift. Keep the *set* of fields that need live patching as small as the product actually requires β€” the numbers that genuinely benefit from sub-second freshness β€” rather than mirroring every field. A brand's tagline changing doesn't need a live patch path; its follower count arguably does. +`─────────────────────────────────────────────────` + +--- + +## 5. Component Breakdown + +| Astro component (build-time structure) | Client patch module (live updates) | FRs covered | +|:---|:---|:---| +| `BrandGrid.astro`, `BrandCard.astro` | `patch/brands.ts` | FR-01–05, FR-46, FR-48, FR-64 | +| `pages/brands.astro` | `patch/brands.ts` (shared) | FR-54–60 | +| `pages/brand/[id].astro` | β€” (detail pages are low-frequency enough that a build-time snapshot + manual refresh is sufficient; live-patch here is optional polish, not required for MVP) | FR-47, FR-49–53, FR-67 | +| `SocialSection.astro` | `patch/social.ts` | FR-06–14 | +| `EarningSection.astro` | `patch/earning.ts` | FR-15–26 | +| `LearningSection.astro` | `patch/learning.ts` | FR-27–31 | +| `GivingSection.astro` | `patch/giving.ts` | FR-32–35 | +| `Header.astro`, `Footer.astro`, `Layout.astro` | `client.ts` (last-updated timestamp only) | FR-36–45 | + +**Brand detail navigation (FR-49–53), the Astro way:** +- Click β†’ plain `<a href="/brand/f1visa/">` (FR-49). No click handler required. +- Close/Back β†’ a normal link back to `/`, plus the browser's native back button. Scroll position is restored automatically by the browser (FR-50) β€” nothing to implement. +- ESC-to-close (FR-51) β†’ a ~5-line script on the brand detail page: `document.addEventListener('keydown', e => { if (e.key === 'Escape') history.back(); })`. +- Shareable via URL (FR-52) β†’ it's a real URL. Nothing else needed. +- Mobile full-screen (FR-53) β†’ it's just a page; it's full-screen by definition on mobile. Style it to match the PRD Β§6.2 wireframe. + +--- + +## 6. Design System + +Same values as before β€” only their expression changes, from CSS custom properties to a Tailwind theme. + +```js +// tailwind.config.mjs +export default { + content: ['./src/**/*.{astro,html,js,ts}'], + theme: { + extend: { + colors: { + bg: '#0a0a1a', + text: '#e0e0ff', + 'pillar-fun': '#FF6B6B', + 'pillar-earning': '#FFD700', + 'pillar-learning': '#4ECDC4', + 'pillar-giving': '#95E1D3', + 'status-live': '#2ecc71', + 'status-progress': '#f1c40f', + 'status-not-started': '#7f8c8d', + 'status-paused': '#e74c3c', + }, + fontFamily: { + sans: ['-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'sans-serif'], + }, + }, + }, +}; +``` + +No `darkMode` config needed β€” dark is the only theme (PRD Out of Scope: no light-mode toggle), so these colors are just the default palette, not a variant. + +**Responsive grid** (FR-41): `class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4"` on `BrandGrid.astro` β€” Tailwind's responsive prefixes replace the hand-written `auto-fit`/`minmax` CSS Grid rule from the pre-Amendment-A3 plan with an equivalent, more explicit utility chain. Either approach satisfies NFR-04 (320px floor); Tailwind's is more idiomatic given the rest of the stack. + +**Section color-coding**: each FELG section's container gets a `border-l-4 border-pillar-fun` (etc.) β€” what makes FR-40's invariant order visually legible on stream, not just structurally correct in the DOM. + +Full mockups remain @PAT's ownership per the PRD's explicit note; this token set is the floor a build starts from. + +--- + +## 7. Build Sequence (MVP, Phase 1) β€” REVISED per Amendment A3 + +| Step | Deliverable | Unblocks | Maps to | +|:---:|:---|:---|:---| +| 1 | `npm create astro@latest` scaffold + `astro add tailwind` + `astro.config.mjs` (`output: 'static'`) + `Layout.astro`/`Header.astro`/`Footer.astro` + `index.astro` with **hardcoded** sample data for all 4 sections in FELG order | Visual review with @PAT before any Supabase code exists | FR-36–41 | +| 2 | `supabase/schema.sql` provisioned on a real Supabase project (table + RLS + Realtime enabled) + seeded with sample data | Real data exists in Postgres | M1.5, M1.6 | +| 3 | `src/lib/supabase.ts` + wire `index.astro`'s frontmatter to fetch from Supabase at build time, replacing step 1's hardcoded data | Build-time read path proven | M1.6 | +| 4 | `src/scripts/live/client.ts` + one patch module (start with `patch/social.ts`) β€” manually edit the Supabase row in its dashboard UI, confirm an open browser tab updates with **no reload**. This is the single most important checkpoint in the build. | Realtime delivery proven end-to-end (FR-65) | M1.8 | +| 5 | `.github/workflows/deploy.yml` β€” validate β†’ sync-to-supabase β†’ `astro build` β†’ deploy. Merge a real PR changing a number in `dashboard.json`; confirm it appears live in an open tab within seconds, **and** that a fresh page load also reflects it. | The actual Phase 1 promise (PR merge β†’ live everywhere) works | M1.7 | +| 6 | `src/pages/brand/[id].astro` with `getStaticPaths()` sourced from the same build-time Supabase fetch β€” real per-brand URLs, OG tags, styled per PRD Β§6.2 | FR-47, FR-49–53, FR-67 | β€” | +| 7 | `src/pages/brands.astro` β€” grouped-by-status All Brands route | FR-46, FR-54–64 | β€” | +| 8 | Remaining `patch/*.ts` modules for Earning/Learning/Giving + relative "last updated"/"last synced" timestamps + manual refresh button as fallback | FR-14, FR-26, FR-31, FR-39, FR-45 | β€” | +| 9 | Responsive pass via Tailwind breakpoints + keyboard nav (ESC on brand detail) + color-contrast check | NFR-04, NFR-07 | FR-41, FR-51 | +| 10 | Security check: from the browser console, attempt a write against Supabase using the public anon key; confirm RLS rejects it | FR-66, NFR-09 | β€” | +| 11 | Lighthouse pass β€” confirm < 3s load, < 2s TTI. Astro's default minimal-JS output should make this easier to hit than the hand-rolled plan, but verify with real numbers. | NFR-01, NFR-02 | β€” | +| 12 | Domain cutover + finalize `astro.config.mjs` `site`/`base` against the chosen host | M1.4 | β€” | +| 13 | Dry run at a #DailySync β€” confirm every number is accurate and that a live edit during the stream actually appears on screen | M1.10 | β€” | + +Steps 1–5 remain the critical path β€” they prove the full pipeline (edit β†’ merge β†’ sync β†’ build/push β†’ render) before any page-level polish is layered on. + +--- + +## 8. QA Checklist (pre-launch, maps to NFRs) + +- [ ] `astro build` completes with zero errors and produces a `dist/` with real per-brand pages (one per entry in `all_brands`) +- [ ] Lighthouse: LCP < 3s, TTI < 2s, throttled "Fast 3G" (NFR-01, NFR-02) +- [ ] Renders correctly at 320px, 768px, 1024px, 1920px via Tailwind breakpoints (NFR-04) +- [ ] Manually verified on latest Chrome, Firefox, Safari, Edge (NFR-05) +- [ ] Each `/brand/<id>/` page has a correct, unique `<title>` and Open Graph tags (FR-67, NFR-08) +- [ ] A merged PR to `dashboard.json` appears in an already-open browser tab within seconds, with no manual refresh, **and** survives a fresh page reload +- [ ] Attempting a write against Supabase from the browser console (public anon key) fails β€” RLS confirmed (FR-66) +- [ ] Service-role key does not appear anywhere in `dist/`, in `src/`, or in any browser network request +- [ ] Browser back button from a brand detail page returns to `/` at the prior scroll position (native behavior β€” verify it actually holds, don't just assume) +- [ ] ESC key closes/returns from the brand detail page (FR-51) +- [ ] Pillar accent colors pass WCAG AA contrast against `bg` (NFR-07) +- [ ] No `<form>`, no auth prompt, no login/signup state anywhere in the DOM (FR-44, NFR-09) β€” this is an **open web app**, confirm nothing gates viewing +- [ ] FELG section order is hardcoded in `index.astro`, not data-driven (FR-40, immutable) +- [ ] The Supabase payload has zero score/index fields not in Appendix B +- [ ] If the Realtime channel disconnects, the UI indicates staleness rather than silently freezing (PRD Risk #8) +- [ ] Manual data audit: every number matches the real source it claims to represent, checked by Core TEAM before the first #DailySync + +--- + +## 9. Phase 2 / 3 Notes (not MVP scope, but design for it now) + +- **Phase 2** (API integration): Postiz/SendGrid/GitHub pollers write into the same Supabase table `sync-to-supabase.js` already established. Nothing about the Astro frontend or the live-patch scripts changes β€” they don't know or care where a value in the JSONB payload came from. +- **Phase 3** (Analytics & polish): historical/trend charts need a genuinely new, separate time-series table β€” that's real new schema work. The animated `#SoundOfMoney` counter is pure frontend polish on a value that's already live as of Phase 1. + +Do not build the historical-data table, an admin panel, or trend-chart rendering now β€” that's explicitly deferred in the PRD's Out of Scope table. + +--- + +## 10. Open Questions for @PAT / Core TEAM + +1. Final hosting target (GitHub Pages direct vs. behind Cloudflare) β€” determines `astro.config.mjs`'s `site`/`base` values, blocks step 12. +2. Whether `sync-to-supabase.js` runs automatically on every merge or requires a manual workflow dispatch. +3. Whether `/brands` is a dedicated route or an inline expansion on `/` (PRD Β§7.1 leaves this open) β€” recommendation above is "make it a route," but this is @PAT's call. + +--- + +*Companion to PRJ-FELG.md v1.3.0-r1 (Amendment A3). If this guide and the PRD diverge, the PRD is authoritative β€” update this file, don't build around the mismatch.* diff --git a/PRJ-FELG.md b/PRJ-FELG.md new file mode 100644 index 0000000..2027c6f --- /dev/null +++ b/PRJ-FELG.md @@ -0,0 +1,907 @@ +# πŸš€ PRJ-FELG | FELG Ecosystem Dashboard β€” Product Requirements Document +═══════════════════════════════════════════════════════════════════════════════ +PRJ-FELG.md | PRJ-FELG_v1.3.0-r1.md +♾️ WeOwnNet 🌐 β€” Product Requirements ● _S004_PROJECTS_/ +πŸ† PRJ-040 ELEVATED β€” #FELG Culture + Content Quality Standard +πŸ† BP-045 ENHANCED β€” Attestation Chain + Enhanced Related Documents +πŸ† BP-068 COMPLIANT β€” Multi-#LLMmodel Header +🌐 Source of Truth: https://github.com/WeOwnNet/FELG +🌐 Culture Governance: https://github.com/CCCbotNet/fedarch/blob/main/_GOVERNANCE_/D-070_FELG_Culture.md +## πŸ“‹ STATUS: 🟑 DRAFT β€” AWAITING R-011 APPROVAL +═══════════════════════════════════════════════════════════════════════════════ + +| Field | Value | +|:------|:-------| +| **Document** | PRJ-FELG.md | +| **Version** | **v1.3.0-r1** (v1.2.0-r1 + Amendment A3 consolidated) | +| **Folder** | `_S004_PROJECTS_/` πŸš€ | +| **Category** | πŸš€ **PRODUCT REQUIREMENTS DOCUMENT** | +| **Lifecycle Stage** | 🟑 **DRAFT β€” AWAITING R-011** | +| **Content Tier (PRJ-040)** | πŸ›‘οΈ **Tier 1 β€” Governance/Strategy** | +| **#masterCCC** | **PAT_2026-W29_1035** πŸ”’ | +| **Product Owner** | **@PAT 🎨 (Osinachi Patrick β€” SinachPat ♾️)** | +| **Champion** | **@GTM 🎯 (yonksο½œπŸ€–πŸ›οΈπŸͺ™ο½œJason Younker ♾️)** | +| **Season** | #WeOwnSeason004 πŸš€ | +| **Created** | 2026-07-13 (W29 D1) | +| **Source of Truth** | https://github.com/WeOwnNet/FELG/README.md | + +--- + +## πŸ“– Table of Contents + +1. [Product Overview](#-product-overview) +2. [#FELG Culture Alignment](#-felg-culture-alignment) +3. [Problem Statement](#-problem-statement) +4. [Product Vision & North Star](#-product-vision--north-star) +5. [Target Audience](#-target-audience) +6. [Functional Requirements β€” The Four Dashboard Sections](#-functional-requirements--the-four-dashboard-sections) +7. [Brand Detail View](#-brand-detail-view) +8. [All Brands View](#-all-brands-view) +9. [Non-Functional Requirements](#-non-functional-requirements) +10. [Tech Stack](#-tech-stack) +11. [Data Sources & Integration](#-data-sources--integration) +12. [User Interface Requirements](#-user-interface-requirements) +13. [Success Metrics & KPIs](#-success-metrics--kpis) +14. [Out of Scope](#-out-of-scope) +15. [Phasing & Milestones](#-phasing--milestones) +16. [Who Builds It](#-who-builds-it) +17. [Risks & Mitigations](#-risks--mitigations) +18. [Related Documents](#-related-documents) +19. [Appendix A: The 8 Immutable FELG Rules (D-070)](#-appendix-a-the-8-immutable-felg-rules-d-070) +20. [Appendix B: Data Schema Draft](#-appendix-b-data-schema-draft) +21. [BP-075: Self-Verifying Footer](#-bp-075-self-verifying-footer) + +--- + +## 🎯 Product Overview + +The **FELG Dashboard** is the operational heartbeat of the ♾️ WeOwnNet ecosystem β€” a **public, real-time dashboard** that tracks what matters most across the Onchain Cooperative Association (.OCA). It is displayed at every **#DailySync #LiveStream** and is open to the world. + +**FELG** stands for **Fun πŸŽ‰ Β· Earning πŸ’° Β· Learning πŸ“š Β· Giving 🫢** β€” the four cultural pillars that define how the cooperative operates. The dashboard is the **visible expression** of this culture: not just metrics, but the story of an ecosystem that builds with joy, earns sustainably, learns openly, and gives freely. + +| Attribute | Value | +|:----------|:-------| +| **Product Name** | FELG Ecosystem Dashboard | +| **Domain** | FELG.WeOwn.Net | +| **Status** | 🟑 PENDING β€” Domain not yet active | +| **Auth Model** | 🟒 **Public β€” NO authentication required for viewing** | +| **Primary Display** | Every #DailySync #LiveStream + always-on public URL | +| **License** | MIT β€” Open source. Open culture. Open cooperative. | +| **Repo** | github.com/WeOwnNet/FELG (🟒 LIVE) | + +--- + +## πŸŽ‰πŸ’°πŸ“šπŸ«Ά #FELG Culture Alignment + +| Pillar | Application to FELG Dashboard | +|--------|-------------------------------| +| πŸŽ‰ **Fun** | The dashboard is CELEBRATORY β€” it shows wins, brands, social energy, and community growth. It's not a corporate KPI board. It uses emojis, color, and personality to make metrics feel like a party. | +| πŸ’° **Earning** | Pipeline stats, #SoundOfMoney raised, co-op members, and investor pipeline show that the ecosystem generates real revenue and sustains itself. | +| πŸ“š **Learning** | Lessons Counter, PRD tracker, and skills development tracking show that every mistake is codified and every member is growing. | +| 🫢 **Giving** | Open source contributions, community growth, and ecosystem support metrics track how the cooperative lifts others. | + +> **πŸ”’ IMMUTABLE:** #FELG is ALWAYS in this order: **πŸŽ‰ Fun β†’ πŸ’° Earning β†’ πŸ“š Learning β†’ 🫢 Giving**. The dashboard layout MUST follow this sequence. + +--- + +## ❓ Problem Statement + +### The Problem + +The ♾️ WeOwnNet ecosystem is growing rapidly β€” multiple brands launching, revenue pipelines forming, co-op members onboarding, and social channels expanding. But there is **no single source of truth** for tracking ecosystem health across all four FELG dimensions. + +Currently: +- Pipeline status lives in PRJ documents and conversations +- Social media metrics are spread across platforms +- Brand launch progress is tracked ad-hoc +- Co-op member growth and investor pipeline are manually tracked +- Lesson documentation is not surfaced as a visible metric + +### Why Now + +#WeOwnSeason004 has 5 ecosystem brands launching, multiple revenue buckets (#ZeroTo100), co-op members onboarding, and investors engaging. Without a dashboard: +- #DailySync sessions lack a central visual anchor +- New members can't quickly see ecosystem health +- The cooperative story is hard to tell at a glance +- Metrics drift without visible accountability + +### The Solution + +A **single, public, real-time dashboard** at FELG.WeOwn.Net that displays all four FELG pillars. Open to the world. Shown at every livestream. Built for the fleet. + +--- + +## 🎯 Product Vision & North Star + +### Vision Statement + +> **The FELG Dashboard is the heartbeat of ♾️ WeOwnNet β€” the place where cooperative culture meets real-time data. Anyone, anywhere, can see that we're building with joy, earning sustainably, learning openly, and giving freely.** + +### North Star Metric + +**Ecosystem Health Status** β€” a qualitative assessment across all 4 pillars, measured by: +- πŸŽ‰ **Fun:** Are brands live and social channels growing? +- πŸ’° **Earning:** Is revenue being generated and tracked? +- πŸ“š **Learning:** Are lessons being documented and PRDs progressing? +- 🫢 **Giving:** Are contributions happening and community growing? + +> **Note:** This is NOT a calculated 0-100 score. It is a **section-level status indicator** (πŸ”΄/🟑/🟒) per pillar, determined by the data in each dashboard section. + +### Key Design Principles + +| # | Principle | Why It Matters | +|:-:|:----------|:---------------| +| 1 | **Public by default** | Transparency is core to cooperative ownership | +| 2 | **No auth for viewing** | Anyone should be able to see ecosystem health | +| 3 | **Real-time or near-real-time** | Data should reflect current state | +| 4 | **Visual and celebratory** | This is not a corporate dashboard β€” it's a culture display | +| 5 | **Mobile and live-stream friendly** | Must render well on screenshare, mobile, and TV | +| 6 | **FELG order is INVARIANT** | Fun β†’ Earning β†’ Learning β†’ Giving. Always. | + +--- + +## πŸ‘₯ Target Audience + +| Audience | Need | How Dashboard Serves | +|:---------|:-----|:---------------------| +| **Core TEAM (@GTM, @THY, @CTO, etc.)** | Operational awareness at a glance | Pipeline stats, blockers, milestone progress | +| **Co-op members (.OCA)** | See ecosystem health + celebrate wins | Brand launches, revenue, community growth | +| **Investors & partners** | Due diligence + confidence | Revenue raised, member growth, pipeline health | +| **Public / prospective members** | Understand what WeOwnNet is about | Culture expression + visible activity | +| **#DailySync audience** | Visual anchor for livestream | Always-on dashboard displayed during syncs | + +--- + +## πŸ“‹ Functional Requirements β€” The Four Dashboard Sections + +### Section 1: πŸŽ‰ Fun β€” Top 5 Ecosystem Brands + Social Media + +#### 1.1 Top 5 Ecosystem Brands + +Display the **Top 5** ecosystem brands in the cooperative. A "Show More" toggle reveals all brands. Each brand card is clickable to open a detailed view. + +| # | Brand | Status Tracked | +|:-:|:------|:---------------| +| πŸ›‚ | **F1Visa.Net** ♾️ (.OCA) | Pre-launch signups, PRD completion, product milestones | +| πŸ”₯ | **Burned Out Media** πŸ”€ | #AttractionMarketing content output, client pipeline | +| 🀝 | **Exit To Client** | Client pipeline, service offerings, partnership development | +| πŸ’¬ | **♾️ WeOwn.Chat** | Domains live, active communities, member growth | +| πŸ’» | **♾️ WeOwn.Dev** | Repos, deployments, claw instances, infrastructure health | + +**Requirements:** + +| FR# | Requirement | Priority | +|:---:|:------------|:--------:| +| FR-01 | Display 5 brands with brand logo/emoji and name | P0 | +| FR-02 | Show status indicator per brand (πŸ”΄ Not Started / 🟑 In Progress / 🟒 Live) | P0 | +| FR-03 | Show key metric per brand (e.g., signups, PRD %, member count) | P1 | +| FR-04 | Each brand name links to its website | P1 | +| FR-05 | Last updated timestamp per brand card | P2 | +| FR-46 | "Show More / View All" toggle to expand beyond Top 5 and see ALL ecosystem brands. Default = Top 5, expanded = full registry. | P0 πŸ”₯ | +| FR-47 | Click on any brand card (Top 5 or All Brands) opens Brand Detail View with full data | P0 πŸ”₯ | +| FR-48 | Full brand registry maintained as a single JSON source of truth. Top 5 is a filtered subset. | P1 | +| FR-61 | Top 5 is determined by a manually configured `top_5_ids` array β€” not algorithmic | P0 | +| FR-62 | `top_5_ids` can be updated by editing a single JSON array β€” no code changes | P1 | +| FR-63 | Fallback: if a brand in `top_5_ids` is archived, the next brand by status priority fills the slot | P2 | +| FR-64 | Section header shows count e.g., "Top 5 Brands (5 of 8)" | P1 | + +#### 1.2 Social Media Metrics + +| FR# | Requirement | Priority | +|:---:|:------------|:--------:| +| FR-06 | Display YouTube subscribers, views, watch time | P0 | +| FR-07 | Display X/Twitter followers, impressions, engagement rate | P0 | +| FR-08 | Display LinkedIn connections, post reach | P1 | +| FR-09 | Display Instagram followers, story views | P1 | +| FR-10 | Display Signal/Telegram community member count | P1 | +| FR-11 | Display Email List subscribers, open rate, click rate | P1 | +| FR-12 | All metrics show current value + trend (↑/↓/β†’) | P1 | +| FR-13 | Auto-refresh at configurable interval (default: 1 hour) | P2 | +| FR-14 | Social section has a "last synced" timestamp | P0 | + +--- + +### Section 2: πŸ’° Earning β€” Pipeline Stats + Co-Op Members + Investors + +#### 2.1 Pipeline Stats + +| FR# | Requirement | Priority | +|:---:|:------------|:--------:| +| FR-15 | Display **Ships in Flight** β€” count of active projects (e.g., PRJ-429 W29 #FedArch FOCUS) | P0 | +| FR-16 | Display **Sub-Projects Complete / 14 Total** β€” progress bar with fraction | P0 | +| FR-17 | Display **P0 Blockers** β€” real-time count with severity indicator | P0 | +| FR-18 | Display **PRDs Drafted** and **PRDs In Progress** β€” count | P1 | +| FR-19 | Display **W29 Milestone Progress** β€” percentage or status | P1 | +| FR-20 | Each pipeline metric should be clickable to reveal more detail (tooltip or expand) | P2 | + +#### 2.2 Co-op Members + Investors + +| FR# | Requirement | Priority | +|:---:|:------------|:--------:| +| FR-21 | Display **Active (.OCA) Members** β€” count | P0 | +| FR-22 | Display **Pre-Launch Signups** β€” count | P0 | +| FR-23 | Display **Investor Pipeline** β€” count of prospects in funnel | P1 | +| FR-24 | Display **#SoundOfMoney Raised** β€” total revenue figure ($) | P0 | +| FR-25 | Display **F1VISA-001 Pre-Orders** β€” count of book pre-orders | P1 | +| FR-26 | Show trend indicators (↑↓→) per metric | P1 | + +--- + +### Section 3: πŸ“š Learning β€” Lessons Counter + PRD Tracker + Skills + +| FR# | Requirement | Priority | +|:---:|:------------|:--------:| +| FR-27 | Display **Lessons Counter** β€” total number of L-XXX lessons codified | P0 | +| FR-28 | Display **PRD Tracker** β€” count of PRDs completed this season | P1 | +| FR-29 | Display **Skills Development** β€” training modules completed, members trained | P1 | +| FR-30 | Include link/reference to #YonksPromptingAcademy | P2 | +| FR-31 | Show "New This Week" highlight for recent lessons | P2 | + +--- + +### Section 4: 🫢 Giving β€” Open Source + Community + Ecosystem + +| FR# | Requirement | Priority | +|:---:|:------------|:--------:| +| FR-32 | Display **Open Source Contributions** β€” count of repos, PRs, contributors | P1 | +| FR-33 | Display **Community Growth** β€” total community members across all channels | P1 | +| FR-34 | Display **Ecosystem Support** β€” instances deployed, members onboarded | P1 | +| FR-35 | Link to GitHub org (CCCbotNet, WeOwnNet) | P1 | + +--- + +### Section 5: Global Requirements (All Sections) + +| FR# | Requirement | Priority | +|:---:|:------------|:--------:| +| FR-36 | **Header**: "πŸ† FELG.WeOwn.Net β€” ♾️ WeOwnNet 🌐 Ecosystem Dashboard" | P0 | +| FR-37 | **Subheader**: "πŸŽ‰ Fun Β· πŸ’° Earning Β· πŸ“š Learning Β· 🫢 Giving" | P0 | +| FR-38 | **Footer**: MIT License + "Open source. Open culture. Open cooperative." | P0 | +| FR-39 | **Last Updated** timestamp β€” visible on every section | P0 | +| FR-40 | **FELG order INVARIANT** β€” Fun section is ALWAYS first, Giving ALWAYS last | πŸ”’ IMMUTABLE | +| FR-41 | **Responsive design** β€” works on desktop, tablet, mobile, and live-stream screenshare | P0 | +| FR-42 | **Lightweight** β€” loads fast, no heavy animations that lag on stream | P1 | +| FR-43 | **Dark mode as default** (matches WeOwnNet brand aesthetic) | P1 | +| FR-44 | **No auth required** for viewing β€” fully public | πŸ”’ IMMUTABLE | +| FR-45 | **Data refresh** β€” manual refresh button as an explicit fallback re-sync; primary updates arrive via realtime push (see FR-65) | P1 | +| FR-65 | **Realtime data delivery** β€” dashboard subscribes to a Supabase Realtime channel and updates in place when underlying data changes. No client polling loop. | P0 πŸ”₯ | +| FR-66 | **Public read-only data access** β€” anon Supabase role has SELECT-only access via Row Level Security. All writes go through the reviewed manual-entry pipeline (edit JSON β†’ PR β†’ merge β†’ sync to Supabase), never a client-exposed write path. | πŸ”’ IMMUTABLE | +| FR-67 | **Real, indexable per-brand URLs** β€” each brand has an actual route (e.g. `/brand/f1visa/`), not a hash fragment, statically generated with its own `<title>` and Open Graph tags. Deep links (FR-52) resolve to these routes. | P1 | + +--- + +## πŸ“‹ Brand Detail View + +### 6.1 Access Pattern + +``` +Brand Card (any) + β”‚ + β”œβ”€β”€ Click β†’ Opens Brand Detail View + β”‚ + β”œβ”€β”€ On Desktop: Slide-out panel (right side) OR modal overlay + β”œβ”€β”€ On Tablet/Mobile: Full-screen modal or dedicated route + └── Navigation: Back/Close returns to dashboard main view +``` + +### 6.2 Detail View Layout + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ [← Back to Dashboard] [βœ• Close] β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ +β”‚ πŸ›‚ F1Visa.Net 🟒 Live β”‚ +β”‚ ♾️ Pre-launch phase β€” .OCA cooperative validation β”‚ +β”‚ β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ +β”‚ πŸ“Š Key Metrics β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚Pre-Launch β”‚ Waitlist β”‚ PRD β”‚ Milestone β”‚ β”‚ +β”‚ β”‚Signups β”‚ Position β”‚ Complete β”‚ Progress β”‚ β”‚ +β”‚ β”‚ 89 β”‚ #42 β”‚ 85% β”‚ 60% β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ +β”‚ πŸ“… Milestones β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ βœ… PRD Drafted β”‚ 2026-07-01 β”‚ β”‚ +β”‚ β”‚ βœ… Landing Page Live β”‚ 2026-07-08 β”‚ β”‚ +β”‚ β”‚ 🟑 Beta Launch β”‚ Target: 2026-08-01 β”‚ β”‚ +β”‚ β”‚ ⬜ Public Launch β”‚ Target: 2026-09-01 β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ +β”‚ πŸ”— Links β”‚ +β”‚ β”‚ 🌐 Website: f1visa.net β”‚ +β”‚ β”‚ πŸ“‹ PRD: link-to-prd β”‚ +β”‚ β”‚ πŸ’¬ Channel: #f1visa β”‚ +β”‚ β”‚ πŸ‘€ Lead: @GTM 🎯 β”‚ +β”‚ β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ Last Updated: 2026-07-13 14:30 MDT β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +### 6.3 Brand Detail β€” Data Requirements + +| Field | Type | Example | Required | +|:------|:-----|:--------|:--------:| +| Brand ID | string | `f1visa` | βœ… | +| Brand Name | string | `F1Visa.Net` | βœ… | +| Emoji / Icon | string | `πŸ›‚` | βœ… | +| Status | enum | `live` / `in_progress` / `not_started` / `paused` | βœ… | +| Tagline | string | `Pre-launch phase β€” .OCA cooperative validation` | βœ… | +| Status History | array | `[{date: "2026-07-01", status: "in_progress"}]` | P2 | +| Key Metrics | object | `{signups: 89, waitlist_position: 42}` | βœ… | +| Milestones | array | `[{label: "PRD Drafted", status: "done", date: "2026-07-01"}]` | βœ… | +| Links | object | `{website, prd, channel, repo}` | βœ… | +| Lead / Owner | string | `@GTM 🎯` | βœ… | +| Category | string | `ecosystem_brand` / `infrastructure` / `initiative` | P1 | +| Description | string | Full text description of brand | P1 | +| Last Updated | timestamp | ISO 8601 | βœ… | + +### 6.4 Interaction Requirements + +| FR# | Requirement | Priority | +|:---:|:------------|:--------:| +| FR-49 | Click on brand card opens detail view | P0 | +| FR-50 | Close/Back button returns to dashboard without losing scroll position | P1 | +| FR-51 | Keyboard navigation: ESC to close detail view | P1 | +| FR-52 | Detail view is shareable via URL (deep linking to brand) | P2 | +| FR-53 | Detail view renders on mobile as a full-screen overlay | P1 | + +--- + +## πŸ“‹ All Brands View + +### 7.1 Access Pattern + +``` +Default Dashboard + β”‚ + β”œβ”€β”€ Shows: Top 5 ecosystem brands (prioritized by status/impact) + β”‚ + β”œβ”€β”€ [Show More / View All] button at bottom of brands section + β”‚ + β”œβ”€β”€ ON CLICK: + β”‚ β”œβ”€β”€ Inline expansion: Brands section expands to show full list + β”‚ └── OR: Opens a full-page "All Brands" view + β”‚ + └── Close/Collapse: [Show Less] or Back button +``` + +### 7.2 All Brands View β€” Layout + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ 🌐 All Ecosystem Brands [Show Less]β”‚ +β”‚ β”‚ +β”‚ πŸ”΄ Active (3) β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚F1Visaβ”‚ β”‚WeOwn β”‚ β”‚WeOwn β”‚ β”‚ +β”‚ β”‚.Net β”‚ β”‚.Chat β”‚ β”‚.Dev β”‚ β”‚ +β”‚ β”‚πŸŸ’Liveβ”‚ β”‚πŸŸ’Liveβ”‚ β”‚πŸŸ’Liveβ”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ +β”‚ 🟑 In Progress (3) β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚Burnedβ”‚ β”‚Exit β”‚ β”‚[Next]β”‚ β”‚ +β”‚ β”‚Out β”‚ β”‚To β”‚ β”‚Brand β”‚ β”‚ +β”‚ β”‚πŸŸ‘WIP β”‚ β”‚Clientβ”‚ β”‚πŸŸ‘WIP β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ +β”‚ ⬜ Coming Soon (2) β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚[TBD] β”‚ β”‚[TBD] β”‚ β”‚ +β”‚ β”‚Brand β”‚ β”‚Brand β”‚ β”‚ +β”‚ β”‚β¬œ β”‚ β”‚β¬œ β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +### 7.3 All Brands Requirements + +| FR# | Requirement | Priority | +|:---:|:------------|:--------:| +| FR-54 | Brands are grouped by status (Active / In Progress / Coming Soon / Paused) | P0 | +| FR-55 | Each brand card in All Brands view is clickable β†’ opens Brand Detail View | P0 | +| FR-56 | All Brands view shows brand name, emoji, status indicator, and primary metric | P0 | +| FR-57 | COUNT displayed per status group header (e.g., "Active (3)") | P1 | +| FR-58 | Search/filter bar in All Brands view for 10+ brands | P2 | +| FR-59 | Sort options: by status, by name, by last updated | P2 | +| FR-60 | Full brand registry is maintained in a single JSON array β€” Top 5 is a subset | P1 | + +--- + +## βš™οΈ Non-Functional Requirements + +| NFR# | Requirement | Target | Priority | +|:----:|:------------|:-------|:--------:| +| NFR-01 | **Page Load Time** | < 3 seconds on first load | P0 | +| NFR-02 | **Time to Interactive** | < 2 seconds | P0 | +| NFR-03 | **Uptime** | 99.5% (public facing) | P1 | +| NFR-04 | **Mobile Support** | Responsive down to 320px width | P0 | +| NFR-05 | **Browser Support** | Latest 2 versions of Chrome, Firefox, Safari, Edge | P1 | +| NFR-06 | **Data Freshness** | Transport is realtime for every section (FR-65) β€” a saved edit reaches every open browser within seconds. Underlying data freshness is bounded by entry cadence: manual entry (Phase 1) is human-paced; Postiz/SendGrid/GitHub API sourcing (Phase 2) tightens specific sections to hourly/daily. | P1 | +| NFR-07 | **Accessibility** | WCAG 2.1 AA minimum | P2 | +| NFR-08 | **SEO** | Meta tags, Open Graph, social preview card | P2 | +| NFR-09 | **Security** | No auth endpoints exposed. Read-only data layer. | P0 | +| NFR-10 | **Backup** | Data source redundancy for critical metrics | P2 | + +--- + +## πŸ› οΈ Tech Stack + +| Layer | Technology | Status | Notes | +|:------|:-----------|:------:|:------| +| **Domain** | `FELG.WeOwn.Net` | 🟑 PENDING | DNS record to be configured | +| **Repo** | `github.com/WeOwnNet/FELG` | 🟒 LIVE | Source of truth for code | +| **Dashboard Frontend** | **Astro** (static-site framework, islands architecture) + **Tailwind CSS** | 🟒 **LOCKED for MVP** | See Amendment A3 β€” supersedes the vanilla HTML/CSS/JS decision | +| **Realtime Backend** | Supabase (Postgres + Realtime) | 🟒 **LOCKED for MVP** | Unchanged by Amendment A3 β€” see Amendment A2 | +| **Data Sources** | Manual entry (JSON β†’ synced to Supabase). Postiz API, SendGrid API remain Phase 2. | 🟑 PENDING (Phase 2 portion) | See Data Sources & Integration Β§, Phasing Β§ | +| **Auth** | None for viewing. Supabase RLS: anon role SELECT-only (FR-66). | 🟒 PLANNED | No login required; writes never client-exposed. The product is explicitly an **open web app β€” no signup, ever**, for viewing. | +| **Display** | Every #DailySync #LiveStream | 🟒 PLANNED | Always-on during livestreams | +| **Hosting** | TBD (GitHub Pages recommended; Astro's static output deploys there natively) | 🟑 PENDING | Supabase hosts the backend regardless of frontend host choice | + +### Framework Decision β€” AMENDED (Amendment A3, supersedes A2's frontend guidance) + +> **πŸ”’ SUPERSEDES v1.2.0-r1:** Amendment A2 locked "static frontend, no framework" as the MVP frontend approach. Per Amendment A3, the frontend is now **Astro + Tailwind CSS** instead of hand-authored vanilla HTML/CSS/JS. This does **not** touch the backend decision β€” Supabase + Realtime (Amendment A2) is unchanged. Astro's *build output* is still static files; this is a change in **authoring tooling**, not a reversal of "no unnecessary backend server." + +| Option | Pros | Cons | Recommendation | +|:-------|:-----|:-----|:--------------| +| **Astro + Tailwind CSS (static output) + Supabase** | Real per-brand URL routes (`/brand/f1visa/`) instead of hash routing β€” directly strengthens FR-52 and NFR-08 (SEO/OG tags per page). Islands architecture ships minimal JS by default, which helps NFR-01/02. Component-based structure maps cleanly onto the four FELG sections. | Introduces a build step (`astro build`) and `npm`/Vite tooling where the prior plan had none. Tailwind's utility classes are a departure from the hand-rolled design-token CSS already drafted (Β§ User Interface Requirements) β€” token *values* carry over unchanged, only their expression changes. | πŸ† **Recommended for MVP (Amendment A3)** | +| **Vanilla HTML/CSS/JS, no framework** | Zero build tooling | Cannot give brand detail views real URLs without hand-rolling a router; SEO/OG tags would need per-page duplication by hand. This was the v1.2.0-r1 MVP scope, now superseded. | Rejected β€” Astro achieves the same runtime simplicity (still static output) with better routing/SEO for free | +| **Next.js / SvelteKit / other SSR meta-framework** | Full SSR, API routes | Unnecessary β€” nothing in this product needs server-side rendering per request; Supabase already provides the only "backend" this product needs | Not needed at any phase currently scoped | + +> **@PAT Recommendation (Amendment A3):** MVP ships as an **Astro + Tailwind CSS static site**, deployed as an **open web app β€” public, no signup required for viewing, ever** β€” backed by Supabase Postgres + Realtime per Amendment A2. Data entry stays manual (JSON β†’ PR β†’ merge β†’ sync to Supabase) for MVP. Brand detail views become real Astro routes rather than hash-based fake routes. Design token *values* (colors, spacing, type scale) from the User Interface Requirements Β§ carry over unchanged into `tailwind.config` β€” Amendment A3 changes *how* those tokens are expressed, not what they are. + +--- + +## πŸ”Œ Data Sources & Integration + +| Data Type | Source | Integration Method | Refresh Cadence | Priority | +|:----------|:-------|:-------------------|:---------------:|:--------:| +| **Social Media** | Postiz API | API call β†’ data transform | Hourly | P1 | +| **Email List** | SendGrid API | API call β†’ subscriber count + open rate | Daily | P1 | +| **Pipeline Stats** | Manual / PRJ docs | Manual update via PR or UI | Weekly (per #DailySync) | P0 | +| **Brand Status** | Manual / Core TEAM | Manual update | Weekly | P0 | +| **Co-op Members** | Manual / Onboarding system | Manual update | Weekly | P0 | +| **#SoundOfMoney Raised** | Matomo.WeOwn.app / Stripe | API or manual | Per event | P1 | +| **Lessons Counter** | GitHub / FedArch docs | GitHub API β†’ count L-XXX files | Weekly | P1 | +| **Open Source** | GitHub API | API β†’ repo count, PR count, contributor count | Weekly | P2 | + +### Data Integration Phasing β€” AMENDED (Amendment A2) + +``` +Phase 1 (MVP): Manual data entry + REALTIME DELIVERY + ↓ Data is hand-edited in data/dashboard.json in the repo + ↓ PR reviewed + merged (human-authored, human-reviewed β€” unchanged from v1.1.0-r1) + ↓ GitHub Action syncs the merged JSON into a Supabase table row + ↓ Supabase Realtime broadcasts the change to every open browser (FR-65) + ↓ Dashboard frontend (static, no framework) subscribes via supabase-js β€” no polling + ↓ Dashboard deploys to FELG.WeOwn.Net via GitHub Pages + +Phase 2 (Enhanced): API integration + ↓ Postiz, SendGrid, GitHub APIs ingested automatically + ↓ Same sync path as Phase 1 β€” API pollers write into the same Supabase table + ↓ No change to the realtime delivery layer; only the data's origin changes + +Phase 3 (Enhanced): Analytics & polish + ↓ Historical data / trend charts + ↓ Animated #SoundOfMoney live-counter treatment + ↓ Preference registry + CHACHING!! integration (PRJ-401 alignment) +``` + +> **πŸ”’ What moved and what didn't:** Supabase + Realtime (previously Phase 3, M3.1–M3.2) is now Phase 1 MVP β€” that's the delivery mechanism. Postiz/SendGrid/GitHub API-sourced data ingestion (previously Phase 2, M2.1–M2.3) **stays Phase 2** β€” that's the data's origin. Manual entry remains the MVP data source; only how fast an edit reaches a viewer's screen changed. + +--- + +## 🎨 User Interface Requirements + +### Layout + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ πŸ† FELG.WeOwn.Net β€” ♾️ WeOwnNet 🌐 β”‚ +β”‚ Ecosystem Dashboard β”‚ +β”‚ πŸŽ‰ Fun Β· πŸ’° Earning Β· πŸ“š Learning Β· 🫢 Giving β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ +β”‚ πŸŽ‰ Top 5 Ecosystem Brands (5 of 8) β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚F1Visaβ”‚ β”‚Burnedβ”‚ β”‚Exit β”‚ β”‚WeOwn β”‚ β”‚WeOwn β”‚ β”‚ +β”‚ β”‚.Net β”‚ β”‚Out β”‚ β”‚To β”‚ β”‚.Chat β”‚ β”‚.Dev β”‚ β”‚ +β”‚ β”‚πŸŸ’Liveβ”‚ β”‚πŸŸ‘WIP β”‚ β”‚πŸŸ‘WIP β”‚ β”‚πŸŸ’Liveβ”‚ β”‚πŸŸ’Liveβ”‚ β”‚ +β”‚ β”‚ 89 πŸ‘€β”‚ β”‚ 12 πŸ“β”‚ β”‚ 3 🀝 β”‚ β”‚120 πŸ’¬β”‚ β”‚ 14 πŸ’»β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ πŸ“Ί Show All Brands (8) β†’ β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ +β”‚ πŸ“± Social Media (auto-refreshed) β”‚ +β”‚ πŸ“Ή 1.2K 🐦 3.4K πŸ’Ό 890 πŸ“± 2.1K πŸ’¬ 450 πŸ“§ 1.8K β”‚ +β”‚ β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ +β”‚ πŸ’° Section 2: Pipeline + Members + Investors β”‚ +β”‚ β”‚ +β”‚ 🚒 Ships: 7 βœ… Complete: 9/14 πŸ”΄ Blocker: 2 β”‚ +β”‚ πŸ“‹ PRDs: 5/8 🎯 W29: 65% β”‚ +β”‚ β”‚ +β”‚ πŸ‘₯ Active: 24 πŸ“ Signups: 89 β”‚ +β”‚ πŸ›οΈ Investors: 5 πŸ’° Raised: $44,200 β”‚ +β”‚ πŸ“š Pre-orders: 12 β”‚ +β”‚ β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ +β”‚ πŸ“š Section 3: Learning β”‚ +β”‚ πŸ“– Lessons: 156 πŸ“‹ PRDs Complete: 8 β”‚ +β”‚ πŸŽ“ Skills: 6 modules Β· 18 members trained β”‚ +β”‚ β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ +β”‚ 🫢 Section 4: Giving β”‚ +β”‚ πŸ“¦ Open Source: 14 repos Β· 47 PRs Β· 9 contributors β”‚ +β”‚ πŸ‘₯ Community: 380 total across all channels β”‚ +β”‚ 🌱 Ecosystem: 12 instances Β· 24 members onboarded β”‚ +β”‚ β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ Last Updated: 2026-07-13 14:30 MDT β”‚ MIT License β”‚ +β”‚ πŸ”„ Refresh β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +### Visual Design Principles + +| Principle | Application | +|:----------|:------------| +| **Dark theme** | Deep background (#0a0a1a), text (#e0e0ff), accents per FELG pillar | +| **Pillar colors** | πŸŽ‰ Fun: #FF6B6B (coral), πŸ’° Earning: #FFD700 (gold), πŸ“š Learning: #4ECDC4 (teal), 🫢 Giving: #95E1D3 (mint) | +| **Emoji-first** | Use emoji as icons β€” no heavy SVG dependencies for MVP | +| **Cards** | Brand cards with rounded corners, subtle glow on hover | +| **Progress bars** | Clean, thin bars for completion metrics | +| **Status dots** | πŸ”΄ 🟑 🟒 for brand/pipeline status | +| **Responsive grid** | CSS Grid β€” 5 cards on desktop, 2-3 on tablet, 1 on mobile | +| **Font** | System font stack β€” no heavy font loads | + +> **πŸ“‹ NOTE:** Full visual design (wireframes, mockups, component library) is the responsibility of **@PAT 🎨** as Design & UX lead. + +--- + +## πŸ“Š Success Metrics & KPIs + +| KPI | Target | Measurement Method | Priority | +|:----|:------|:-------------------|:--------:| +| **Dashboard uptime** | 99.5% | Hosting provider status | P1 | +| **Data accuracy** | 100% for manual entries, Β±5% for API data | Manual audit per #DailySync | P0 | +| **Page load time** | < 3 seconds | Lighthouse / Web Vitals | P0 | +| **#DailySync usage** | Displayed at every sync | Self-reported | P1 | +| **Public visits** | TBD β€” baseline after launch | Postiz or Matomo analytics | P2 | +| **Brand status accuracy** | All brands reflect current state | Core TEAM validation per sync | P0 | + +--- + +## 🚫 Out of Scope (v1.3.0) + +> **Amendment A2 change:** "Real-time WebSocket updates" is removed from this table β€” realtime delivery via Supabase is now MVP scope (FR-65). See Tech Stack Β§ and Phasing & Milestones Β§ for the corrected boundary. + +| Item | Reason | +|:-----|:-------| +| **User authentication / login** | Dashboard is PUBLIC by design. Auth is never needed for viewing. | +| **FELG Score / Contribution Score** | NOT in the source of truth. The dashboard tracks metrics, not calculated scores. | +| **CHACHING!! Live Feed** | #SoundOfMoney notification is a SEPARATE system (PRJ-401). Not part of FELG dashboard. | +| **Per-member contribution tracking** | Not specified in the FELG README. Out of scope for v1. | +| **Admin panel / data entry UI** | MVP data entry is manual (JSON files in repo, PR-reviewed). A UI for non-technical entry is a later phase. | +| **Mobile app** | Responsive web covers mobile needs for MVP. | +| **Dark/light mode toggle** | Dark mode is the single default. Light mode is not requested. | +| **Multi-language support** | English only for MVP. | + +--- + +## πŸ—“οΈ Phasing & Milestones + +### Phase 1: MVP (W29-W30 β€” July 2026) β€” AMENDED (Amendment A2) + +| Milestone | Deliverable | Owner | Status | +|:----------|:------------|:------|:------:| +| M1.1 | Framework decision β€” Static frontend + Supabase backend (Amendment A2, supersedes original "Static vs Next.js vs Supabase" decision) | @PAT + @SHD + @MWK | ⬜ PENDING | +| M1.2 | Design mockups for all sections + responsive layout | @PAT 🎨 | ⬜ PENDING | +| M1.3 | Static HTML/CSS/JS prototype (hardcoded data) | @PAT + @SHD | ⬜ PENDING | +| M1.4 | FELG.WeOwn.Net domain configured | @SHD + @MWK | ⬜ PENDING | +| M1.5 | Data JSON schema defined + sample data commit | @PAT | ⬜ PENDING | +| M1.6 | Supabase project provisioned + table + Row Level Security (anon SELECT-only, FR-66) | @SHD + @MWK | ⬜ PENDING | +| M1.7 | GitHub Action: sync merged `dashboard.json` β†’ Supabase table on merge | @SHD + @MWK | ⬜ PENDING | +| M1.8 | Frontend subscribes to Supabase Realtime channel β€” live push confirmed end-to-end (FR-65) | @MWK + @PLT | ⬜ PENDING | +| M1.9 | MVP deployed to FELG.WeOwn.Net | @SHD + @MWK | ⬜ PENDING | +| M1.10 | First #DailySync with live, realtime-updating dashboard | Core TEAM | ⬜ PENDING | + +### Phase 2: API Integration (W31-W33 β€” August 2026) + +Unchanged from v1.1.0-r1 β€” this phase is about *where data comes from*, not *how fast it reaches the screen* (that's now solved in Phase 1). Each of these writes into the same Supabase table Phase 1 already established. + +| Milestone | Deliverable | Owner | +|:----------|:------------|:------| +| M2.1 | Postiz API integration β€” auto social metrics | @MWK + @PLT | +| M2.2 | SendGrid API integration β€” auto email metrics | @MWK + @PLT | +| M2.3 | GitHub API integration β€” auto repo/PR stats | @MWK + @PLT | +| M2.4 | Automated data refresh pipeline (GitHub Actions, scheduled pollers β†’ Supabase) | @SHD + @MWK | + +### Phase 3: Analytics & Polish (W34+ β€” September 2026+) + +| Milestone | Deliverable | Owner | +|:----------|:------------|:------| +| M3.1 | Historical data / trend charts | @PAT + @MWK | +| M3.2 | #SoundOfMoney raised β€” animated live-counter treatment (value itself already updates live via Phase 1 realtime; this milestone is the celebratory count-up/pulse UX) | @GTM + @MWK | + +--- + +## πŸ‘₯ Who Builds It + +Per the FELG repo source of truth: + +| Role | Person | Focus | +|:-----|:-------|:------| +| 🎯 **Vision & Strategy** | ♾️ WeOwnNet 🌐 Core TEAM | What the dashboard shows and why | +| 🎨 **Design & UX** | **@PAT 🎨** | How it looks, feels, and communicates FELG | +| πŸ’» **Infrastructure** | @SHD πŸ‡΅πŸ‡° + @MWK πŸ•Ί + @PLT πŸ’― | Domain, hosting, data pipeline | +| πŸ“Š **Data** | @GTM 🎯 + @THY πŸ“Š + @MWK πŸ•Ί + @PLT πŸ’― | Metrics sources, automation, refresh cadence | + +--- + +## ⚠️ Risks & Mitigations + +| # | Risk | Likelihood | Impact | Mitigation | +|:-:|:-----|:----------:|:------:|:-----------| +| 1 | **Domain (FELG.WeOwn.Net) not active** | 🟠 Medium | πŸ”΄ High | Deploy to GitHub Pages or subdomain as fallback | +| 2 | **Framework decision stalls** | 🟑 Low | 🟠 Medium | Default to static frontend + Supabase backend per Amendment A2 | +| 3 | **API access/keys not ready** | 🟠 Medium | 🟠 Medium | MVP data ingestion can stay manual. Postiz/SendGrid/GitHub APIs are still Phase 2 β€” unaffected by Amendment A2. | +| 4 | **Data freshness** | 🟑 Low | 🟑 Medium | Realtime transport (FR-65) removes the old polling-interval staleness window; freshness is now bounded only by how often a human edits the data. | +| 5 | **Design scope creep** | 🟠 Medium | 🟑 Medium | Hold the line: MVP = static cards + manual data + realtime delivery. Do not pull Phase 2/3 items forward beyond Amendment A2's scope. | +| 6 | **No R-011 approval** | 🟑 Low | 🟑 Medium | Submit for review immediately. | +| 7 | **Supabase project misconfiguration exposes write access** | 🟑 Low | πŸ”΄ High | RLS must be verified (anon = SELECT-only) before FELG.WeOwn.Net goes public, per FR-66. Treat this as a launch blocker, not a follow-up. | +| 8 | **Realtime channel drops mid-livestream** | 🟑 Low | 🟠 Medium | Manual refresh button (FR-45) stays as a visible fallback; frontend should indicate connection state rather than silently going stale on screen. | +| 9 | **Astro build step fails or misconfigures GitHub Pages base path** | 🟑 Low | 🟠 Medium | Astro has first-party GitHub Pages deploy support; verify `site`/`base` config against the final hosting decision (Β§ Tech Stack, open question) before first deploy. | +| 10 | **Tailwind theme drifts from the design tokens already specified** | 🟑 Low | 🟑 Medium | `tailwind.config` must source its color values directly from the User Interface Requirements Β§ pillar-color table β€” treat that table as the single source of truth, not the Tailwind config. | + +--- + +## πŸ“‹ Related Documents + +| Document | Version | URL | Relationship | +|:---------|:-------:|:----|:-------------| +| **FELG README** | β€” | https://github.com/WeOwnNet/FELG | Source of truth for this PRD | +| **D-070 FELG Culture** | v3.2.3.1 | https://github.com/CCCbotNet/fedarch/blob/main/_GOVERNANCE_/D-070_FELG_Culture.md | Cultural governance | +| **PRJ-401** | v4.1.3.1-r14 | https://raw.githubusercontent.com/CCCbotNet/fedarch/main/_S004_PROJECTS_/PRJ-401.md | #SoundOfMoney pipeline, #ZeroTo100 context | +| **PRJ-038 (#ZeroTo100)** | β€” | Conceptual North Star | Revenue buckets framework | +| **PRJ-F1V** | v1.0.0-r1 | F1Visa.Net PRD | Sister project in ecosystem | +| **SharedKernel** | v3.2.2.1 | https://raw.githubusercontent.com/CCCbotNet/fedarch/main/_SYS_/SharedKernel.md | Ecosystem governance rules | +| **WS-PAT-001** | v4.2.3.1-r2 | Workspace prompt for @PAT | Design authority for this PRD | + +--- + +## πŸ“‹ Appendix A: The 8 Immutable FELG Rules (from D-070) + +| # | Rule | Dashboard Implication | +|:-:|:-----|:----------------------| +| 1 | FELG is ALWAYS πŸŽ‰ Fun, πŸ’° Earning, πŸ“š Learning, 🫢 Giving β€” in THAT order | Layout is INVARIANT. Fun section always first. Giving always last. | +| 2 | FELG is the **CULTURE**, not just metrics on a dashboard | The dashboard is an EXPRESSION of culture, not a replacement for it. | +| 3 | FELG applies to EVERY member, EVERY agent, EVERY interaction | Dashboard is PUBLIC. No auth. Everyone sees the full picture. | +| 4 | #FELG beats #HustleCulture β€” always | Celebrate wins. No burnout metrics. No "hours worked" or "grind" counters. | +| 5 | Fun is FIRST β€” if it's not fun, fix it | If the dashboard feels like work, redesign it. | +| 6 | Lessons are GIFTS β€” share them openly | Lessons Counter is visible to everyone. No gatekeeping. | +| 7 | Giving means LIFTING β€” open source, mentorship, ecosystem growth | Open Source Contributions section tracks how the ecosystem gives back. | +| 8 | FELG is FOREVER β€” codified in D-070 as the ♾️ Culture Standard | The dashboard is not a seasonal experiment. It's the permanent heartbeat. | + +--- + +## πŸ“‹ Appendix B: Data Schema Draft (MVP β€” JSON) + +```json +{ + "dashboard": { + "name": "FELG Ecosystem Dashboard", + "domain": "FELG.WeOwn.Net", + "last_updated": "2026-07-13T14:30:00-06:00", + "version": "1.1.0" + }, + "brands": { + "top_5_ids": ["f1visa", "burnedout", "exittoclient", "weownchat", "weowndev"], + "all_brands": [ + { + "id": "f1visa", + "name": "F1Visa.Net", + "emoji": "πŸ›‚", + "status": "live", + "tagline": "Pre-launch phase β€” .OCA cooperative validation", + "category": "ecosystem_brand", + "lead": "@GTM 🎯", + "metric_label": "Pre-launch Signups", + "metric_value": 89, + "milestones": [ + { "label": "PRD Drafted", "status": "done", "date": "2026-07-01" }, + { "label": "Landing Page Live", "status": "done", "date": "2026-07-08" }, + { "label": "Beta Launch", "status": "in_progress", "target": "2026-08-01" }, + { "label": "Public Launch", "status": "pending", "target": "2026-09-01" } + ], + "links": { + "website": "https://f1visa.net", + "prd": "https://github.com/...", + "channel": "#f1visa", + "repo": "https://github.com/WeOwnNet/F1VISA" + }, + "last_updated": "2026-07-13T14:30:00-06:00" + }, + { + "id": "burnedout", + "name": "Burned Out Media", + "emoji": "πŸ”₯", + "status": "in_progress", + "tagline": "#AttractionMarketing content engine", + "category": "ecosystem_brand", + "lead": "@THY πŸ“Š", + "metric_label": "Pieces Published", + "metric_value": 12, + "milestones": [ + { "label": "Content Strategy Complete", "status": "done", "date": "2026-06-15" }, + { "label": "First 10 Pieces Published", "status": "done", "date": "2026-07-10" }, + { "label": "Client Pipeline Active", "status": "in_progress", "target": "2026-08-01" } + ], + "links": { + "website": "https://burnedout.xyz", + "channel": "#burnedout" + }, + "last_updated": "2026-07-13T14:30:00-06:00" + }, + { + "id": "exittoclient", + "name": "Exit To Client", + "emoji": "🀝", + "status": "in_progress", + "tagline": "Service offerings for client acquisitions", + "category": "ecosystem_brand", + "lead": "@GTM 🎯", + "metric_label": "Client Pipeline", + "metric_value": 3, + "milestones": [ + { "label": "Service Menu Defined", "status": "done", "date": "2026-06-20" }, + { "label": "First 3 Clients in Pipeline", "status": "done", "date": "2026-07-05" }, + { "label": "First Client Signed", "status": "pending", "target": "2026-08-15" } + ], + "links": { "channel": "#exittoclient" }, + "last_updated": "2026-07-13T14:30:00-06:00" + }, + { + "id": "weownchat", + "name": "♾️ WeOwn.Chat", + "emoji": "πŸ’¬", + "status": "live", + "tagline": "Community communication hub", + "category": "ecosystem_brand", + "lead": "@SHD πŸ‡΅πŸ‡°", + "metric_label": "Community Members", + "metric_value": 120, + "milestones": [ + { "label": "Domain Active", "status": "done", "date": "2026-06-01" }, + { "label": "First 100 Members", "status": "done", "date": "2026-07-01" }, + { "label": "Channels Launched", "status": "done", "date": "2026-07-01" } + ], + "links": { + "website": "https://weown.chat", + "repo": "https://github.com/WeOwnNet/WeOwnChat" + }, + "last_updated": "2026-07-13T14:30:00-06:00" + }, + { + "id": "weowndev", + "name": "♾️ WeOwn.Dev", + "emoji": "πŸ’»", + "status": "live", + "tagline": "Infrastructure and development hub", + "category": "infrastructure", + "lead": "@MWK πŸ•Ί", + "metric_label": "Active Repos", + "metric_value": 14, + "milestones": [ + { "label": "Claw Instances Deployed", "status": "done", "date": "2026-06-15" }, + { "label": "CI/CD Pipeline Active", "status": "done", "date": "2026-07-01" }, + { "label": "Infra Monitoring Live", "status": "in_progress", "target": "2026-07-20" } + ], + "links": { + "website": "https://weown.dev", + "repo": "https://github.com/WeOwnNet" + }, + "last_updated": "2026-07-13T14:30:00-06:00" + } + ] + }, + "fun": { + "social_media": { + "youtube": { "subscribers": 1200, "views": 45000, "watch_time_hours": 2800 }, + "twitter": { "followers": 3400, "impressions_monthly": 125000, "engagement_rate": 4.2 }, + "linkedin": { "connections": 890, "post_reach_monthly": 45000 }, + "instagram": { "followers": 2100, "story_views_avg": 320 }, + "signal_telegram": { "members": 450 }, + "email": { "subscribers": 1800, "open_rate": 38.5, "click_rate": 12.1 } + } + }, + "earning": { + "pipeline": { + "ships_in_flight": 7, + "sub_projects_complete": 9, + "sub_projects_total": 14, + "p0_blockers": 2, + "prds_drafted": 5, + "prds_in_progress": 3, + "w29_milestone_percent": 65 + }, + "members_investors": { + "active_oca_members": 24, + "pre_launch_signups": 89, + "investor_pipeline": 5, + "sound_of_money_raised_usd": 44200, + "f1visa001_pre_orders": 12 + } + }, + "learning": { + "lessons_counter": 156, + "prds_completed": 8, + "skills_modules": 6, + "members_trained": 18, + "new_this_week": 3 + }, + "giving": { + "open_source": { + "repos": 14, + "pull_requests": 47, + "contributors": 9 + }, + "community": { + "total_members_all_channels": 380 + }, + "ecosystem": { + "instances_deployed": 12, + "members_onboarded": 24 + } + } +} +``` + +> **πŸ“‹ NOTE:** This schema is a RECOMMENDED starting point. Final schema to be determined during implementation (Phase 1, Milestone M1.5). +> +> **πŸ”’ Amendment A2:** This JSON shape is unchanged by the move to realtime delivery. In the amended Phase 1 architecture, this object is stored as the value of a single JSONB column in one Supabase table row (not a normalized multi-table schema) β€” the GitHub Action that syncs a merged `dashboard.json` writes this exact object wholesale, and Supabase Realtime broadcasts the row's `UPDATE` event to subscribed clients. Normalizing into per-domain tables is an optimization for a later phase, not an MVP requirement. + +--- + +## πŸ“‹ BP-075: Self-Verifying Footer + +| Field | Value | +|:------|:-------| +| **Document ID** | PRJ-FELG.md | +| **Version** | **v1.3.0-r1** (v1.2.0-r1 + A3 consolidated) | +| **Date** | 2026-07-13 β€” W29 D1 (Monday), amended per Amendment A2, A3 | +| **Agent Author** | AI:@PAT 🎨 @ INT‑P05:CCC | +| **CCC-ID** | PAT_2026-W29_1035 | +| **#masterCCC** | PAT_2026-W29_1035 πŸ”’ | +| **Source of Truth** | https://github.com/WeOwnNet/FELG | +| **Culture Governance** | D-070 FELG Culture | +| **Instance** | INT-P05 (PRO.WeOwn.Tools) | +| **Product Owner** | @PAT 🎨 (Osinachi Patrick β€” SinachPat ♾️) | +| **Status** | 🟑 **DRAFT β€” AWAITING R-011 APPROVAL** | +| **Total Sections** | 21 | +| **Functional Requirements** | 67 (FR-01 through FR-67) | +| **Non-Functional Requirements** | 10 (NFR-01 through NFR-10) | +| **Phases** | 3 (MVP w/ Realtime, API Integration, Analytics & Polish) | +| **Milestones** | 16 (M1.1–M1.10, M2.1–M2.4, M3.1–M3.2) | +| **Amendment A2 Summary** | Realtime delivery (Supabase + Realtime, formerly Phase 3 M3.1–M3.2) elevated to Phase 1 MVP. API-sourced data ingestion (Postiz/SendGrid/GitHub, Phase 2) unchanged. | +| **Amendment A3 Summary** | Frontend authoring tool changed from vanilla HTML/CSS/JS to **Astro + Tailwind CSS**. Product reframed as an open, public web app (no signup, ever). Adds FR-67 (real per-brand URL routes). Backend (Supabase + Realtime, Amendment A2) unchanged. | +| **Fabrications** | **0** βœ… | + +--- + +```text +═══ END OF PRD β€” PRJ-FELG v1.3.0-r1 ═══ +``` + +#FlowsBros #FedArch #WeOwnSeason004 #FELG #PRD #Dashboard #ProductRequirements #v1.3.0 #AmendmentA2 #AmendmentA3 #RealtimeMVP #Supabase #Astro #TailwindCSS #OpenWebApp #BrandDetail #AllBrands #ShowMore #FR46 #FR47 #FR65 #FR66 #FR67 #Fun #Earning #Learning #Giving #OCA #PublicDashboard #DailySync #LiveStream #PRJ_FELG #PAT_Product + +♾️ WeOwnNet 🌐 ● 🏑 Real Estate and 🀝 cooperative ownership for everyone ● An πŸ€— inclusive community, by πŸ‘₯ invitation only. \ No newline at end of file diff --git a/astro.config.mjs b/astro.config.mjs new file mode 100644 index 0000000..81fcc39 --- /dev/null +++ b/astro.config.mjs @@ -0,0 +1,15 @@ +// @ts-check +import { defineConfig } from 'astro/config'; + +import tailwindcss from '@tailwindcss/vite'; + +// https://astro.build/config +export default defineConfig({ + output: 'static', + site: 'https://WeOwnNet.github.io', + base: '/FELG', + + vite: { + plugins: [tailwindcss()] + } +}); \ No newline at end of file diff --git a/data/dashboard.json b/data/dashboard.json new file mode 100644 index 0000000..ee33d5f --- /dev/null +++ b/data/dashboard.json @@ -0,0 +1,167 @@ +{ + "dashboard": { + "name": "FELG Ecosystem Dashboard", + "domain": "FELG.WeOwn.Net", + "last_updated": "2026-07-13T14:30:00-06:00", + "version": "1.1.0" + }, + "brands": { + "top_5_ids": ["f1visa", "burnedout", "exittoclient", "weownchat", "weowndev"], + "all_brands": [ + { + "id": "f1visa", + "name": "F1Visa.Net", + "emoji": "πŸ›‚", + "status": "live", + "tagline": "Pre-launch phase β€” .OCA cooperative validation", + "category": "ecosystem_brand", + "lead": "@GTM 🎯", + "metric_label": "Pre-launch Signups", + "metric_value": 89, + "milestones": [ + { "label": "PRD Drafted", "status": "done", "date": "2026-07-01" }, + { "label": "Landing Page Live", "status": "done", "date": "2026-07-08" }, + { "label": "Beta Launch", "status": "in_progress", "target": "2026-08-01" }, + { "label": "Public Launch", "status": "pending", "target": "2026-09-01" } + ], + "links": { + "website": "https://f1visa.net", + "prd": "https://github.com/...", + "channel": "#f1visa", + "repo": "https://github.com/WeOwnNet/F1VISA" + }, + "last_updated": "2026-07-13T14:30:00-06:00" + }, + { + "id": "burnedout", + "name": "Burned Out Media", + "emoji": "πŸ”₯", + "status": "in_progress", + "tagline": "#AttractionMarketing content engine", + "category": "ecosystem_brand", + "lead": "@THY πŸ“Š", + "metric_label": "Pieces Published", + "metric_value": 12, + "milestones": [ + { "label": "Content Strategy Complete", "status": "done", "date": "2026-06-15" }, + { "label": "First 10 Pieces Published", "status": "done", "date": "2026-07-10" }, + { "label": "Client Pipeline Active", "status": "in_progress", "target": "2026-08-01" } + ], + "links": { + "website": "https://burnedout.xyz", + "channel": "#burnedout" + }, + "last_updated": "2026-07-13T14:30:00-06:00" + }, + { + "id": "exittoclient", + "name": "Exit To Client", + "emoji": "🀝", + "status": "in_progress", + "tagline": "Service offerings for client acquisitions", + "category": "ecosystem_brand", + "lead": "@GTM 🎯", + "metric_label": "Client Pipeline", + "metric_value": 3, + "milestones": [ + { "label": "Service Menu Defined", "status": "done", "date": "2026-06-20" }, + { "label": "First 3 Clients in Pipeline", "status": "done", "date": "2026-07-05" }, + { "label": "First Client Signed", "status": "pending", "target": "2026-08-15" } + ], + "links": { "channel": "#exittoclient" }, + "last_updated": "2026-07-13T14:30:00-06:00" + }, + { + "id": "weownchat", + "name": "♾️ WeOwn.Chat", + "emoji": "πŸ’¬", + "status": "live", + "tagline": "Community communication hub", + "category": "ecosystem_brand", + "lead": "@SHD πŸ‡΅πŸ‡°", + "metric_label": "Community Members", + "metric_value": 120, + "milestones": [ + { "label": "Domain Active", "status": "done", "date": "2026-06-01" }, + { "label": "First 100 Members", "status": "done", "date": "2026-07-01" }, + { "label": "Channels Launched", "status": "done", "date": "2026-07-01" } + ], + "links": { + "website": "https://weown.chat", + "repo": "https://github.com/WeOwnNet/WeOwnChat" + }, + "last_updated": "2026-07-13T14:30:00-06:00" + }, + { + "id": "weowndev", + "name": "♾️ WeOwn.Dev", + "emoji": "πŸ’»", + "status": "live", + "tagline": "Infrastructure and development hub", + "category": "infrastructure", + "lead": "@MWK πŸ•Ί", + "metric_label": "Active Repos", + "metric_value": 14, + "milestones": [ + { "label": "Claw Instances Deployed", "status": "done", "date": "2026-06-15" }, + { "label": "CI/CD Pipeline Active", "status": "done", "date": "2026-07-01" }, + { "label": "Infra Monitoring Live", "status": "in_progress", "target": "2026-07-20" } + ], + "links": { + "website": "https://weown.dev", + "repo": "https://github.com/WeOwnNet" + }, + "last_updated": "2026-07-13T14:30:00-06:00" + } + ] + }, + "fun": { + "social_media": { + "youtube": { "subscribers": 1200, "views": 45000, "watch_time_hours": 2800 }, + "twitter": { "followers": 3400, "impressions_monthly": 125000, "engagement_rate": 4.2 }, + "linkedin": { "connections": 890, "post_reach_monthly": 45000 }, + "instagram": { "followers": 2100, "story_views_avg": 320 }, + "signal_telegram": { "members": 450 }, + "email": { "subscribers": 1800, "open_rate": 38.5, "click_rate": 12.1 } + } + }, + "earning": { + "pipeline": { + "ships_in_flight": 7, + "sub_projects_complete": 9, + "sub_projects_total": 14, + "p0_blockers": 2, + "prds_drafted": 5, + "prds_in_progress": 3, + "w29_milestone_percent": 65 + }, + "members_investors": { + "active_oca_members": 24, + "pre_launch_signups": 89, + "investor_pipeline": 5, + "sound_of_money_raised_usd": 44200, + "f1visa001_pre_orders": 12 + } + }, + "learning": { + "lessons_counter": 156, + "prds_completed": 8, + "skills_modules": 6, + "members_trained": 18, + "new_this_week": 3 + }, + "giving": { + "open_source": { + "repos": 14, + "pull_requests": 47, + "contributors": 9 + }, + "community": { + "total_members_all_channels": 380 + }, + "ecosystem": { + "instances_deployed": 12, + "members_onboarded": 24 + } + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7b2d86f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4918 @@ +{ + "name": "felg-dashboard", + "version": "1.3.0-r1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "felg-dashboard", + "version": "1.3.0-r1", + "dependencies": { + "@supabase/supabase-js": "^2.49.1", + "@tailwindcss/vite": "^4.3.3", + "astro": "^7.1.3", + "tailwindcss": "^4.3.3" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding/-/compiler-binding-0.3.1.tgz", + "integrity": "sha512-DaAUj29AIBU2XdJ8uwcab8lW5O2pk9pY8AXkcMw0sw77nVa3oeTYRcO+Dvbbpoexf6ThMc0FMWYCQ/wN1/T7oQ==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@astrojs/compiler-binding-darwin-arm64": "0.3.1", + "@astrojs/compiler-binding-darwin-x64": "0.3.1", + "@astrojs/compiler-binding-linux-arm64-gnu": "0.3.1", + "@astrojs/compiler-binding-linux-arm64-musl": "0.3.1", + "@astrojs/compiler-binding-linux-x64-gnu": "0.3.1", + "@astrojs/compiler-binding-linux-x64-musl": "0.3.1", + "@astrojs/compiler-binding-wasm32-wasi": "0.3.1", + "@astrojs/compiler-binding-win32-arm64-msvc": "0.3.1", + "@astrojs/compiler-binding-win32-x64-msvc": "0.3.1" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-arm64": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-arm64/-/compiler-binding-darwin-arm64-0.3.1.tgz", + "integrity": "sha512-IEmEF2fUIlTHtpeE/isyEGVOB14cEyh/LZOFYt6wn3jNyVpdC8aR5OZ+RzFUR/f+8ZDM1LaMwZKvoA7eMyJeFw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-x64": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-x64/-/compiler-binding-darwin-x64-0.3.1.tgz", + "integrity": "sha512-GF2kIxjpPDLsn94zbZNMsxEmkU828QqnmM7kiQJnaooS3jmI+I7kk6+oI6EpwOsK3femCMdcm+wmOsEqtGrmjQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-gnu": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-gnu/-/compiler-binding-linux-arm64-gnu-0.3.1.tgz", + "integrity": "sha512-XJL3SDmOtVrqFhCirNcHwE91+IesJqlgNo23I4qW9QUYfwzm/TBZuH61fgqsb1ttgR1mMYz6ooPWs0JDhwMqpQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-musl": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-musl/-/compiler-binding-linux-arm64-musl-0.3.1.tgz", + "integrity": "sha512-xqE8BVbDoBueK/B47w30PtkVofUWJKGkwoMVE+EOMLf11rnoANxIAdA9FPqY+rng4oNI5ndHGsri1yPj2k8vZQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-gnu": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-gnu/-/compiler-binding-linux-x64-gnu-0.3.1.tgz", + "integrity": "sha512-1y0StU1qiCuDFH3rmbRJXcxdfHxFPrES1Rd+RLffosvUR7I2cH5SF5SFnBN9vXpzpkmyElZm3Yr47iJBPN7vVA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-musl": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-musl/-/compiler-binding-linux-x64-musl-0.3.1.tgz", + "integrity": "sha512-16q0fYf7kpbmdObZEeZJEup8hQv/whgNwVjrSvT8umrKwLDSnNIWiQpm09lQQu6bweZB0XyIvHwlPitvJhC+hg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-wasm32-wasi": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-wasm32-wasi/-/compiler-binding-wasm32-wasi-0.3.1.tgz", + "integrity": "sha512-cB456shIwDv/PrVT+2QG7LFndpHkVge5HjqADKZgGaAc9JHVktCtjSrcdkRQ+3tbkPazNKaTLRjXLIiz2NIx9g==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-arm64-msvc": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-arm64-msvc/-/compiler-binding-win32-arm64-msvc-0.3.1.tgz", + "integrity": "sha512-ur/9+If/yTE69mmeX5MqSZndL0HOyx67GeNZUy3N7wVdWpLz9UTJXwyWS4UR2PUQHitghjsM5xoX0Ge56WRVQQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-x64-msvc": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-x64-msvc/-/compiler-binding-win32-x64-msvc-0.3.1.tgz", + "integrity": "sha512-k0W+kDBzDkNZOqu4kElDvCOIbKw5Ut9S1WZ1Krj3KTgNuBERNKXsMMsRLLcbgfdMdbe7bTekQLshZrrvmYpmwA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-rs": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-rs/-/compiler-rs-0.3.1.tgz", + "integrity": "sha512-aT7xkgsbNoS6nriY5qKpbihK43slFHO41iqgHCTdOvn1ifaQxLCc5yXy+6GzAtiafoaC1zA7OwVXCXMsvUZOkg==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler-binding": "0.3.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/internal-helpers": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.10.1.tgz", + "integrity": "sha512-5phcroT/vmOOrYuuAxtkbPixy5hePtlz9i8K4OeDv3dNK6/UQRuXPOSRTxIOBbUY5Sonw2UaxjbuVc43Mcir6Q==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "js-yaml": "^4.1.1", + "picomatch": "^4.0.4", + "retext-smartypants": "^6.2.0", + "shiki": "^4.0.2", + "smol-toml": "^1.6.0", + "unified": "^11.0.5" + } + }, + "node_modules/@astrojs/markdown-satteri": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-satteri/-/markdown-satteri-0.3.4.tgz", + "integrity": "sha512-6Lvt/bQZEBW+zzdhPblvfZEy5PGEYJaUsUqaCgwHeRPxZJL1gc9I+DRLKWJjjYTWDzVUTzXlMq4WwSK+X34CVw==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.10.1", + "@astrojs/prism": "4.0.2", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "satteri": "^0.9.1" + } + }, + "node_modules/@astrojs/prism": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-4.0.2.tgz", + "integrity": "sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA==", + "license": "MIT", + "dependencies": { + "prismjs": "^1.30.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/telemetry": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.3.tgz", + "integrity": "sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ==", + "license": "MIT", + "dependencies": { + "ci-info": "^4.4.0", + "dset": "^3.1.4", + "is-docker": "^4.0.0", + "package-manager-detector": "^1.6.0" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bruits/satteri-darwin-arm64": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-arm64/-/satteri-darwin-arm64-0.9.5.tgz", + "integrity": "sha512-iw4nZgx9v30lWo/MTngQqi1pI78KI0DnkSm+lVJGYdmPLgAyDNJigVhpG42/Iq55A6c1Ll8q66ljyyRiQUxwow==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-darwin-x64": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-x64/-/satteri-darwin-x64-0.9.5.tgz", + "integrity": "sha512-6T26Z5Kf3cFW2PSlk9p7zT7yVxvuBSiJvYyz9u8KjYwMTqZyIDOj2wDyNpxKV4+6yUVG7rddq2QwvG/8LJA2+Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-gnu": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-gnu/-/satteri-linux-arm64-gnu-0.9.5.tgz", + "integrity": "sha512-u51id17uJwNEMK9nBlICsq6U31c+XVqQueVBkwRIzZG+gMpS8TOJctt5h5Wz33Z8xnMdTd+adtACVz0yHgGuOA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-musl": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-musl/-/satteri-linux-arm64-musl-0.9.5.tgz", + "integrity": "sha512-v39HxiwGC5Rqm01HksP6+5Y+xKLPlsuVFgIgpEAo+SiQ22c+mJVhS3u7Z6ePAKdhL5NJoK1xq70kLz3L13AhpQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-gnu": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-gnu/-/satteri-linux-x64-gnu-0.9.5.tgz", + "integrity": "sha512-F3uO8uFp3pAP5ZGXttwvh57GS7s0lL953tnNdyI2gRyP4kOOkp6pyGojNJzCjkDvWI2Cvb9iNrKok3aqQPauAw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-musl": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-musl/-/satteri-linux-x64-musl-0.9.5.tgz", + "integrity": "sha512-bicEqglLlz++mWyADaZoP0JY20s4vDfLjaPYgQqC+NI4zZLTOOg1T4GB8aqtc822Pqji8SQBmSrTb7CrP8i08Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-wasm32-wasi": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-wasm32-wasi/-/satteri-wasm32-wasi-0.9.5.tgz", + "integrity": "sha512-zauAuMwfPnKPUkd4AFixRFpXdgKwP2mKgxrIIo2gJzW0/ZneF9dbHnLkojSpaBnCCp7VUL1hIi5WWZvB1CqmAQ==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-win32-arm64-msvc": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-arm64-msvc/-/satteri-win32-arm64-msvc-0.9.5.tgz", + "integrity": "sha512-SrfE7NEsgZjBvU3c+RR6oQRu0ToXY5uVJEbieXEF0YTctIV2zAVlbaMjWLts074QCgh3a+XHWkR/lWh2VH2LUg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@bruits/satteri-win32-x64-msvc": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-x64-msvc/-/satteri-win32-x64-msvc-0.9.5.tgz", + "integrity": "sha512-5Kw9ZAtTGS8WHizyn+CJhjjfIQrw+7jcZodpmpXJjefnO15M8UexIi6JR2E5thyvsmHyhL6ZDDMUNR4bKJPd4g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@capsizecss/unpack": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.1.tgz", + "integrity": "sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@clack/core": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.4.3.tgz", + "integrity": "sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==", + "license": "MIT", + "dependencies": { + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@clack/prompts": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.7.0.tgz", + "integrity": "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==", + "license": "MIT", + "dependencies": { + "@clack/core": "1.4.3", + "fast-string-width": "^3.0.2", + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz", + "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", + "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", + "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", + "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", + "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", + "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", + "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", + "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", + "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", + "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", + "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", + "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", + "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", + "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", + "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", + "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", + "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", + "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", + "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", + "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", + "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", + "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", + "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", + "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", + "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", + "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", + "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oslojs/encoding": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", + "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", + "license": "MIT" + }, + "node_modules/@oxc-project/types": { + "version": "0.139.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", + "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", + "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", + "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", + "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", + "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", + "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", + "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", + "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", + "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", + "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", + "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", + "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", + "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", + "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", + "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", + "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "license": "MIT" + }, + "node_modules/@rollup/pluginutils": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@shikijs/core": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.3.1.tgz", + "integrity": "sha512-ANMDxuaPsNMdDC1m4vfvhlDmJweMwkE5XitTwrq2rWHx5jM+dlm4MmHt2PP6t0uejfR77SuhrhJ0zEijIF/uhA==", + "license": "MIT", + "dependencies": { + "@shikijs/primitive": "4.3.1", + "@shikijs/types": "4.3.1", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.3.1.tgz", + "integrity": "sha512-JBItcnPuYq7jVJdZo/vMj94r+szT7XEjHFX+mvFDGSEIbVAXAGyHAHzhbWzpGOwYidCZrErJLLgn2PVeiokHnQ==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.3.1", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.6" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.3.1.tgz", + "integrity": "sha512-OXyNMzg0pews+msMj4cHeqT4xiYKKvbnn6VbdAXxfoFl3SSx4fJTc8FadECuc5/H9p3BzhNAoAUXKwAu9rWYhg==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.3.1", + "@shikijs/vscode-textmate": "^10.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/langs": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.3.1.tgz", + "integrity": "sha512-m0l9nsDqgBHvbZbk7A0/kXz/impK3uB/c6rAn6Gpg/uPtdZRQ+alsN/17MU5thb68XTj/4DxkZAotrM0GGSpDQ==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/primitive": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.3.1.tgz", + "integrity": "sha512-CXQRQOYy1leqQ8ceTeJdmXv/bsUY++6QyLpXJ94LZAAYj5X2SKRdc5ipguv4NPyGVKItB2PPwUpRNe0Sjh5S1A==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.3.1", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/themes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.3.1.tgz", + "integrity": "sha512-dgpoJ4WqNi2yTmizQHBJ5zcX6j2lE6icN/0yt4l1kkf16jrY/pwPLoTb1ETsWMz0OBLf9ZNvwmxft+cH+N9qSA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/types": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.3.1.tgz", + "integrity": "sha512-CHFxE0jztBIZRHH6gxXE7DXUCFXjReEGxZ/j0rfSLGKZuwp2xBYycEP14875DSa9KLL/6700oxIq6oO6ef9K2g==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "license": "MIT" + }, + "node_modules/@supabase/auth-js": { + "version": "2.110.8", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.110.8.tgz", + "integrity": "sha512-TQ5neTUDX2C2WmyYa03yGhLMkhdE/SkHXtK8/qxO/APUy3rsymsJCBP48p4jcN6iO2G0ow6RRexQd2mX+dSyJg==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.110.8", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.110.8.tgz", + "integrity": "sha512-5yB9TLYzvv2oSQxwb0gamEvIAsuH66pVt7AM/pz03S7wN6ehD34GNgbShrccetqPedXQSz7e/1hAJ9NeEhoZVg==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@supabase/phoenix": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/@supabase/phoenix/-/phoenix-0.4.5.tgz", + "integrity": "sha512-aAn9H9ovVyeApKy11OWOrrOGq8DV68yWeH4ud2lN9fzn4aO8Zb5GLL9m1pUg9nLqIcT+ZDfAcsZe0E/nqdv2lw==", + "license": "MIT" + }, + "node_modules/@supabase/postgrest-js": { + "version": "2.110.8", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.110.8.tgz", + "integrity": "sha512-QeRROxl1PpOZw5Jzi7BwdN9icsycMrLlCCvsjS0hYLW+nZoaT46zdagz/glJirj8jHF4jSd5Jyipuae2cBClCw==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.110.8", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.110.8.tgz", + "integrity": "sha512-mwX7ituX6O31fLf+0g65rpLlNxqgnMaPltPsQwzox6jfmbfVl3tCxXrfr3HEsQcCRjpjuJG1+A0vFzP1yVjKHA==", + "license": "MIT", + "dependencies": { + "@supabase/phoenix": "0.4.5", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.110.8", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.110.8.tgz", + "integrity": "sha512-CcfhkZFBLxsthgUabZKxwfsoXdrikIGsL3LsGoV3FZTqCMx/s1y49taT4jT/oya5+1IuB0sFFHw6pF0o0iJniQ==", + "license": "MIT", + "dependencies": { + "iceberg-js": "^0.8.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.110.8", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.110.8.tgz", + "integrity": "sha512-E5qzoe74zhJRv4wRcbO9eMYzeQDb/+h6c603pL8shcxLGBjTKsIF7XXj05IcNj23TLDgJN1WkMw7mwAPyu5dZg==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.110.8", + "@supabase/functions-js": "2.110.8", + "@supabase/postgrest-js": "2.110.8", + "@supabase/realtime-js": "2.110.8", + "@supabase/storage-js": "2.110.8" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", + "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.24.1", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.3" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", + "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", + "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", + "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", + "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", + "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", + "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", + "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", + "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", + "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", + "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", + "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", + "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", + "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz", + "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "tailwindcss": "4.3.3" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/nlcst": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", + "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", + "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==", + "license": "ISC" + }, + "node_modules/am-i-vibing": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/am-i-vibing/-/am-i-vibing-0.4.0.tgz", + "integrity": "sha512-MxT4XZL7pzLHpuvhDKdMaQHMGGkJDLluKBLsbstn+8wv9sWcFT6h+0ve9qkml95amVTZtZV83gQe2hY+ojgHLg==", + "license": "MIT", + "dependencies": { + "process-ancestry": "^0.1.0" + }, + "bin": { + "am-i-vibing": "dist/cli.mjs" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/astro": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/astro/-/astro-7.1.3.tgz", + "integrity": "sha512-4dhPyAAXthf3xLEYnG8SeL7yr/nTPPABfY7e9YF0yuO+vK9Xp+8Q5j4xzsmL3GueukQv4oNwGNTBepLOiDGeJA==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler-rs": "^0.3.1", + "@astrojs/internal-helpers": "0.10.1", + "@astrojs/markdown-satteri": "0.3.4", + "@astrojs/telemetry": "3.3.3", + "@capsizecss/unpack": "^4.0.0", + "@clack/prompts": "^1.1.0", + "@oslojs/encoding": "^1.1.0", + "@rollup/pluginutils": "^5.3.0", + "am-i-vibing": "^0.4.0", + "aria-query": "^5.3.2", + "axobject-query": "^4.1.0", + "ci-info": "^4.4.0", + "clsx": "^2.1.1", + "common-ancestor-path": "^2.0.0", + "cookie": "^2.0.1", + "devalue": "^5.8.1", + "diff": "^8.0.3", + "dset": "^3.1.4", + "es-module-lexer": "^2.0.0", + "esbuild": "^0.28.0", + "flattie": "^1.1.1", + "fontace": "~0.4.1", + "get-tsconfig": "5.0.0-beta.4", + "github-slugger": "^2.0.0", + "html-escaper": "3.0.3", + "http-cache-semantics": "^4.2.0", + "js-yaml": "^4.1.1", + "jsonc-parser": "^3.3.1", + "magic-string": "^0.30.21", + "magicast": "^0.5.2", + "mrmime": "^2.0.1", + "neotraverse": "^1.0.1", + "obug": "^2.1.1", + "p-limit": "^7.3.0", + "p-queue": "^9.1.0", + "package-manager-detector": "^1.6.0", + "piccolore": "^0.1.3", + "picomatch": "^4.0.4", + "semver": "^7.7.4", + "shiki": "^4.0.2", + "smol-toml": "^1.6.0", + "svgo": "^4.0.1", + "tinyclip": "^0.1.12", + "tinyexec": "^1.0.4", + "tinyglobby": "^0.2.15", + "ultrahtml": "^1.6.0", + "unifont": "~0.7.4", + "unstorage": "^1.17.5", + "vite": "^8.0.13", + "vitefu": "^1.1.2", + "xxhash-wasm": "^1.1.0", + "yargs-parser": "^22.0.0", + "zod": "^4.3.6" + }, + "bin": { + "astro": "bin/astro.mjs" + }, + "engines": { + "node": ">=22.12.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/astrodotbuild" + }, + "optionalDependencies": { + "sharp": "^0.34.0 || ^0.35.0" + }, + "peerDependencies": { + "@astrojs/markdown-remark": "7.2.1" + }, + "peerDependenciesMeta": { + "@astrojs/markdown-remark": { + "optional": true + } + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/common-ancestor-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-2.0.0.tgz", + "integrity": "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">= 18" + } + }, + "node_modules/cookie": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-2.0.1.tgz", + "integrity": "sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w==", + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cookie-es": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz", + "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", + "license": "MIT" + }, + "node_modules/crossws": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", + "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", + "license": "MIT", + "dependencies": { + "uncrypto": "^0.1.3" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "license": "CC0-1.0" + }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/devalue": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.2.tgz", + "integrity": "sha512-DObPPAfdtFbXjxLqK8s2Xk9ZuWz5+ZoFEhC7J76es4GU/rEiXwHTmbImoCdyoCOcBH1UF3+Cz6Z2sYD4hyl5TA==", + "license": "MIT" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diff": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dset": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.24.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.3.tgz", + "integrity": "sha512-PwKooW9JUzh5chmYfHM3IQl5OkK2u2Nm011MgeZrss3JmFraUx/fqrf78kk8GUMYoibx/14MdwTl/1WKkG7TpQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/flattie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", + "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/fontace": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.1.tgz", + "integrity": "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.2" + } + }, + "node_modules/fontkitten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.3.tgz", + "integrity": "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==", + "license": "MIT", + "dependencies": { + "tiny-inflate": "^1.0.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "5.0.0-beta.4", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-5.0.0-beta.4.tgz", + "integrity": "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "engines": { + "node": ">=20.20.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "license": "ISC" + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/h3": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz", + "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==", + "license": "MIT", + "dependencies": { + "cookie-es": "^1.2.3", + "crossws": "^0.3.5", + "defu": "^6.1.6", + "destr": "^2.0.5", + "iron-webcrypto": "^1.2.1", + "node-mock-http": "^1.0.4", + "radix3": "^1.1.2", + "ufo": "^1.6.3", + "uncrypto": "^0.1.3" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause" + }, + "node_modules/iceberg-js": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", + "integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, + "node_modules/is-docker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-4.0.0.tgz", + "integrity": "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-yaml": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.3.tgz", + "integrity": "sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "license": "CC0-1.0" + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/nanoid": { + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neotraverse": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-1.0.1.tgz", + "integrity": "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/nlcst-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", + "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, + "node_modules/node-mock-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.4.tgz", + "integrity": "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/obug": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/ofetch": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", + "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", + "license": "MIT", + "dependencies": { + "destr": "^2.0.5", + "node-fetch-native": "^1.6.7", + "ufo": "^1.6.1" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "license": "MIT" + }, + "node_modules/oniguruma-parser": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz", + "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==", + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz", + "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==", + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.2", + "regex": "^6.1.0", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/p-limit": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.1.tgz", + "integrity": "sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.2.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.3.3.tgz", + "integrity": "sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.4", + "p-timeout": "^7.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", + "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-manager-detector": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.8.0.tgz", + "integrity": "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==", + "license": "MIT" + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/piccolore": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz", + "integrity": "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==", + "license": "ISC" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.22", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.22.tgz", + "integrity": "sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/process-ancestry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/process-ancestry/-/process-ancestry-0.1.0.tgz", + "integrity": "sha512-tGqJW/UnclpYASFcM6Xh8D8l/BMtaQ9+CSG0vlJSJTcdMM4lDRv4c6H0Pdcsfted+bVczdYSfk2fdukg2gQkZg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/property-information": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/radix3": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", + "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "license": "MIT" + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/retext-smartypants": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", + "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rolldown": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", + "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.139.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.1.5", + "@rolldown/binding-darwin-arm64": "1.1.5", + "@rolldown/binding-darwin-x64": "1.1.5", + "@rolldown/binding-freebsd-x64": "1.1.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", + "@rolldown/binding-linux-arm64-gnu": "1.1.5", + "@rolldown/binding-linux-arm64-musl": "1.1.5", + "@rolldown/binding-linux-ppc64-gnu": "1.1.5", + "@rolldown/binding-linux-s390x-gnu": "1.1.5", + "@rolldown/binding-linux-x64-gnu": "1.1.5", + "@rolldown/binding-linux-x64-musl": "1.1.5", + "@rolldown/binding-openharmony-arm64": "1.1.5", + "@rolldown/binding-wasm32-wasi": "1.1.5", + "@rolldown/binding-win32-arm64-msvc": "1.1.5", + "@rolldown/binding-win32-x64-msvc": "1.1.5" + } + }, + "node_modules/satteri": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/satteri/-/satteri-0.9.5.tgz", + "integrity": "sha512-ZuWVl+vnM64y+/TtX8Kosv2c00W+hLQiiwnEL6H0UKVVrxFqMw4D2CJHHQaouVd89OAhtBBfjWLqhKi3TVUV4w==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.5", + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "@types/unist": "^3.0.3" + }, + "optionalDependencies": { + "@bruits/satteri-darwin-arm64": "0.9.5", + "@bruits/satteri-darwin-x64": "0.9.5", + "@bruits/satteri-linux-arm64-gnu": "0.9.5", + "@bruits/satteri-linux-arm64-musl": "0.9.5", + "@bruits/satteri-linux-x64-gnu": "0.9.5", + "@bruits/satteri-linux-x64-musl": "0.9.5", + "@bruits/satteri-wasm32-wasi": "0.9.5", + "@bruits/satteri-win32-arm64-msvc": "0.9.5", + "@bruits/satteri-win32-x64-msvc": "0.9.5" + } + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz", + "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.3", + "@img/sharp-darwin-x64": "0.35.3", + "@img/sharp-freebsd-wasm32": "0.35.3", + "@img/sharp-libvips-darwin-arm64": "1.3.2", + "@img/sharp-libvips-darwin-x64": "1.3.2", + "@img/sharp-libvips-linux-arm": "1.3.2", + "@img/sharp-libvips-linux-arm64": "1.3.2", + "@img/sharp-libvips-linux-ppc64": "1.3.2", + "@img/sharp-libvips-linux-riscv64": "1.3.2", + "@img/sharp-libvips-linux-s390x": "1.3.2", + "@img/sharp-libvips-linux-x64": "1.3.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", + "@img/sharp-libvips-linuxmusl-x64": "1.3.2", + "@img/sharp-linux-arm": "0.35.3", + "@img/sharp-linux-arm64": "0.35.3", + "@img/sharp-linux-ppc64": "0.35.3", + "@img/sharp-linux-riscv64": "0.35.3", + "@img/sharp-linux-s390x": "0.35.3", + "@img/sharp-linux-x64": "0.35.3", + "@img/sharp-linuxmusl-arm64": "0.35.3", + "@img/sharp-linuxmusl-x64": "0.35.3", + "@img/sharp-webcontainers-wasm32": "0.35.3", + "@img/sharp-win32-arm64": "0.35.3", + "@img/sharp-win32-ia32": "0.35.3", + "@img/sharp-win32-x64": "0.35.3" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/shiki": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.3.1.tgz", + "integrity": "sha512-oR+qDVi2OjX1tmDpyv+3KviX01KzO6Af+0NNnKnsp9491UEGz2YpxTuJboS/6VhYpTdqzmuJBuiTlrAWWJAssw==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "4.3.1", + "@shikijs/engine-javascript": "4.3.1", + "@shikijs/engine-oniguruma": "4.3.1", + "@shikijs/langs": "4.3.1", + "@shikijs/themes": "4.3.1", + "@shikijs/types": "4.3.1", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/smol-toml": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.0.tgz", + "integrity": "sha512-aqVvWoyO21L23mb+drl4RmMXbf6N7FdHjAhTRA9ZBL7apWBgfWC16KjrASI+1p9GAroljyMHj6fK67i0UiTNvQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/svgo": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.2.tgz", + "integrity": "sha512-ekx94z1rRc5LDi6oSUaeRnYhd0UOJxdtQCL2rF8xpWxD3TPAsISWOrxezqGovqS38GRZOdpDfvQe3ts6F7nsng==", + "license": "MIT", + "dependencies": { + "commander": "^11.1.0", + "css-select": "^5.1.0", + "css-tree": "^3.0.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "^1.5.0" + }, + "bin": { + "svgo": "bin/svgo.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", + "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, + "node_modules/tinyclip": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/tinyclip/-/tinyclip-0.1.15.tgz", + "integrity": "sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A==", + "license": "MIT", + "engines": { + "node": "^16.14.0 || >= 17.3.0" + } + }, + "node_modules/tinyexec": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "license": "MIT" + }, + "node_modules/ultrahtml": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.7.0.tgz", + "integrity": "sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==", + "license": "MIT" + }, + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", + "license": "MIT" + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unifont": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.4.tgz", + "integrity": "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg==", + "license": "MIT", + "dependencies": { + "css-tree": "^3.1.0", + "ofetch": "^1.5.1", + "ohash": "^2.0.11" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unstorage": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", + "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", + "license": "MIT", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^5.0.0", + "destr": "^2.0.5", + "h3": "^1.15.10", + "lru-cache": "^11.2.7", + "node-fetch-native": "^1.6.7", + "ofetch": "^1.5.1", + "ufo": "^1.6.3" + }, + "peerDependencies": { + "@azure/app-configuration": "^1.8.0", + "@azure/cosmos": "^4.2.0", + "@azure/data-tables": "^13.3.0", + "@azure/identity": "^4.6.0", + "@azure/keyvault-secrets": "^4.9.0", + "@azure/storage-blob": "^12.26.0", + "@capacitor/preferences": "^6 || ^7 || ^8", + "@deno/kv": ">=0.9.0", + "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.34.3", + "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", + "@vercel/kv": "^1 || ^2 || ^3", + "aws4fetch": "^1.0.20", + "db0": ">=0.2.1", + "idb-keyval": "^6.2.1", + "ioredis": "^5.4.2", + "uploadthing": "^7.4.4" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@deno/kv": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/blob": { + "optional": true + }, + "@vercel/functions": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "aws4fetch": { + "optional": true + }, + "db0": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "uploadthing": { + "optional": true + } + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz", + "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==", + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.17", + "rolldown": "~1.1.5", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.3.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/xxhash-wasm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", + "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", + "license": "MIT" + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..d19c975 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "felg-dashboard", + "type": "module", + "version": "1.3.0-r1", + "engines": { + "node": ">=22.12.0" + }, + "scripts": { + "dev": "astro dev", + "build": "astro build", + "preview": "astro preview", + "astro": "astro" + }, + "dependencies": { + "@supabase/supabase-js": "^2.49.1", + "@tailwindcss/vite": "^4.3.3", + "astro": "^7.1.3", + "tailwindcss": "^4.3.3" + } +} \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..7f48a94 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..8055128 --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> + <text y=".9em" font-size="90">πŸ†</text> +</svg> \ No newline at end of file diff --git a/scripts/sync-to-supabase.js b/scripts/sync-to-supabase.js new file mode 100644 index 0000000..7938968 --- /dev/null +++ b/scripts/sync-to-supabase.js @@ -0,0 +1,77 @@ +#!/usr/bin/env node + +/** + * sync-to-supabase.js + * Upserts data/dashboard.json into the Supabase dashboard_state table. + * CI-only: uses the service-role key (never shipped to the browser). + * + * Environment variables: + * PUBLIC_SUPABASE_URL β€” Supabase project URL + * SUPABASE_SERVICE_ROLE_KEY β€” service-role key (secret, WRITE access) + * + * Usage: node scripts/sync-to-supabase.js + */ + +import { readFileSync } from 'fs'; +import { join, dirname } from 'path'; +import { fileURLToPath } from 'url'; +import { createClient } from '@supabase/supabase-js'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const DATA_PATH = join(__dirname, '..', 'data', 'dashboard.json'); + +const SUPABASE_URL = process.env.PUBLIC_SUPABASE_URL; +const SERVICE_ROLE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY; + +if (!SUPABASE_URL) { + console.error('❌ PUBLIC_SUPABASE_URL is not set'); + process.exit(1); +} + +if (!SERVICE_ROLE_KEY) { + console.error('❌ SUPABASE_SERVICE_ROLE_KEY is not set'); + process.exit(1); +} + +// Create client with service-role key (admin, bypasses RLS) +const supabase = createClient(SUPABASE_URL, SERVICE_ROLE_KEY); + +async function main() { + let raw; + try { + raw = readFileSync(DATA_PATH, 'utf-8'); + } catch { + console.error(`❌ Cannot read ${DATA_PATH}`); + process.exit(1); + } + + let payload; + try { + payload = JSON.parse(raw); + } catch { + console.error('❌ Invalid JSON in dashboard.json'); + process.exit(1); + } + + console.log('πŸ“€ Syncing data to Supabase...'); + + const { error } = await supabase + .from('dashboard_state') + .upsert( + { + id: 'current', + payload, + updated_at: new Date().toISOString(), + }, + { onConflict: 'id' } + ); + + if (error) { + console.error('❌ Sync failed:', error.message); + process.exit(1); + } + + console.log('βœ… Data synced successfully to Supabase'); +} + +main(); \ No newline at end of file diff --git a/scripts/validate-data.js b/scripts/validate-data.js new file mode 100644 index 0000000..2609d57 --- /dev/null +++ b/scripts/validate-data.js @@ -0,0 +1,129 @@ +#!/usr/bin/env node + +/** + * validate-data.js + * Validates data/dashboard.json against the PRD Appendix B schema contract. + * Runs in CI before sync-to-supabase.js. + * + * Checks: + * - Required top-level keys exist + * - brands.top_5_ids match entries in brands.all_brands + * - Status enum values are valid + * - All required fields per brand are present + * - No unrecognized top-level keys + */ + +import { readFileSync } from 'fs'; +import { join, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const DATA_PATH = join(__dirname, '..', 'data', 'dashboard.json'); + +const VALID_STATUSES = ['live', 'in_progress', 'not_started', 'paused']; +const VALID_MILESTONE_STATUSES = ['done', 'in_progress', 'pending']; + +const REQUIRED_TOP_LEVEL = ['dashboard', 'brands', 'fun', 'earning', 'learning', 'giving']; +const REQUIRED_BRAND_FIELDS = ['id', 'name', 'emoji', 'status', 'tagline', 'category', 'lead', 'metric_label', 'metric_value', 'milestones', 'links', 'last_updated']; + +let exitCode = 0; + +function error(msg) { + console.error(`❌ ${msg}`); + exitCode = 1; +} + +function main() { + let raw; + try { + raw = readFileSync(DATA_PATH, 'utf-8'); + } catch { + error(`Cannot read ${DATA_PATH}`); + process.exit(1); + } + + let data; + try { + data = JSON.parse(raw); + } catch { + error('Invalid JSON'); + process.exit(1); + } + + // Check required top-level keys + for (const key of REQUIRED_TOP_LEVEL) { + if (!(key in data)) error(`Missing top-level key: "${key}"`); + } + + // No unexpected top-level keys + for (const key of Object.keys(data)) { + if (!REQUIRED_TOP_LEVEL.includes(key) && key !== '$schema') { + error(`Unexpected top-level key: "${key}"`); + } + } + + // Dashboard meta + if (data.dashboard) { + if (!data.dashboard.name) error('dashboard.name is required'); + if (!data.dashboard.last_updated) error('dashboard.last_updated is required'); + } + + // Brands validation + if (data.brands) { + const { top_5_ids, all_brands } = data.brands; + + if (!Array.isArray(top_5_ids)) error('brands.top_5_ids must be an array'); + if (!Array.isArray(all_brands)) error('brands.all_brands must be an array'); + + if (all_brands) { + const brandIds = new Set(all_brands.map(b => b.id)); + + // Check top_5_ids reference valid brands + if (Array.isArray(top_5_ids)) { + for (const id of top_5_ids) { + if (!brandIds.has(id)) error(`top_5_ids references unknown brand: "${id}"`); + } + } + + // Validate each brand + for (const brand of all_brands) { + for (const field of REQUIRED_BRAND_FIELDS) { + if (!(field in brand)) error(`Brand "${brand.id || '(missing id)'}" missing field: "${field}"`); + } + + if (brand.status && !VALID_STATUSES.includes(brand.status)) { + error(`Brand "${brand.id}" has invalid status: "${brand.status}"`); + } + + if (Array.isArray(brand.milestones)) { + for (const ms of brand.milestones) { + if (ms.status && !VALID_MILESTONE_STATUSES.includes(ms.status)) { + error(`Brand "${brand.id}" milestone "${ms.label}" has invalid status: "${ms.status}"`); + } + } + } + } + } + } + + // Social media check + if (data.fun?.social_media) { + const sm = data.fun.social_media; + if (!sm.youtube?.subscribers) error('fun.social_media.youtube.subscribers is required'); + if (!sm.twitter?.followers) error('fun.social_media.twitter.followers is required'); + } + + // Earning check + if (data.earning?.pipeline) { + if (typeof data.earning.pipeline.sub_projects_complete !== 'number') error('earning.pipeline.sub_projects_complete is required'); + if (typeof data.earning.pipeline.sub_projects_total !== 'number') error('earning.pipeline.sub_projects_total is required'); + } + + if (exitCode === 0) { + console.log('βœ… data/dashboard.json is valid'); + } + + process.exit(exitCode); +} + +main(); \ No newline at end of file diff --git a/src/components/BrandCard.astro b/src/components/BrandCard.astro new file mode 100644 index 0000000..a9aa390 --- /dev/null +++ b/src/components/BrandCard.astro @@ -0,0 +1,28 @@ +--- +import type { Brand } from '../lib/types'; +import { getStatusColor, getStatusLabel } from '../lib/utils'; + +export interface Props { brand: Brand; } + +const { brand } = Astro.props; +const statusColor = getStatusColor(brand.status); +const statusLabel = getStatusLabel(brand.status); +--- + +<a + href={`/brand/${brand.id}/`} + class="block border border-border rounded-lg px-3.5 py-3 hover:bg-bg-hover hover:border-text-tertiary transition-all duration-100" +> + <div class="flex items-center justify-between mb-1.5"> + <div class="flex items-center gap-2 min-w-0"> + <span class="text-[15px] leading-none shrink-0">{brand.emoji}</span> + <span class="text-[12px] font-medium text-text truncate">{brand.name}</span> + </div> + <span class="text-[10px] font-medium shrink-0 ml-2" style={`color: ${statusColor}`}>● {statusLabel}</span> + </div> + <p class="text-[11px] text-text-secondary truncate mb-2.5">{brand.tagline}</p> + <div class="flex items-center justify-between pt-2 border-t border-border-light"> + <span class="text-[10px] text-text-muted">{brand.metric_label}</span> + <span class="text-[14px] font-semibold text-text" data-live={`brands.${brand.id}.metric_value`}>{brand.metric_value.toLocaleString()}</span> + </div> +</a> diff --git a/src/components/BrandGrid.astro b/src/components/BrandGrid.astro new file mode 100644 index 0000000..f6a6c79 --- /dev/null +++ b/src/components/BrandGrid.astro @@ -0,0 +1,20 @@ +--- +import type { Brand } from '../lib/types'; +import BrandCard from './BrandCard.astro'; +export interface Props { topBrands: Brand[]; } +const { topBrands } = Astro.props; +--- + +<section class="mb-10"> + <div class="flex items-center justify-between mb-3"> + <div class="flex items-center gap-2.5"> + <div class="w-0.5 h-3.5 rounded bg-pillar-fun"></div> + <h2 class="text-[15px] font-bold text-text">Top 5 Ecosystem Brands</h2> + <span class="text-[11px] text-text-muted font-medium">({topBrands.length})</span> + </div> + <a href="/brands" class="text-[11px] font-medium text-accent hover:underline">View all β†’</a> + </div> + <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-2.5" data-live="brand-grid"> + {topBrands.map((brand) => <BrandCard brand={brand} />)} + </div> +</section> diff --git a/src/components/EarningSection.astro b/src/components/EarningSection.astro new file mode 100644 index 0000000..ca567bb --- /dev/null +++ b/src/components/EarningSection.astro @@ -0,0 +1,89 @@ +--- +export interface Props { + pipeline: { ships_in_flight: number; sub_projects_complete: number; sub_projects_total: number; p0_blockers: number; prds_drafted: number; prds_in_progress: number; w29_milestone_percent: number; }; + members_investors: { active_oca_members: number; pre_launch_signups: number; investor_pipeline: number; sound_of_money_raised_usd: number; f1visa001_pre_orders: number; }; + lastUpdated: string; +} +const { pipeline, members_investors, lastUpdated } = Astro.props; +function fmtUSD(n: number): string { return '$' + n.toLocaleString(); } +const progress = pipeline.sub_projects_total > 0 ? Math.round((pipeline.sub_projects_complete / pipeline.sub_projects_total) * 100) : 0; +--- + +<section class="mb-10"> + <div class="flex items-center gap-2.5 mb-3"> + <div class="w-0.5 h-3.5 rounded bg-pillar-earning"></div> + <h2 class="text-[15px] font-bold text-text">Earning</h2> + </div> + + <div class="grid grid-cols-1 lg:grid-cols-2 gap-4"> + <!-- Pipeline table --> + <div class="border border-border rounded-lg overflow-hidden" data-live="earning.pipeline"> + <div class="flex items-center gap-1.5 px-3.5 py-2.5 bg-bg-muted border-b border-border"> + <span class="text-[12px]">🚒</span> + <span class="text-[11px] font-semibold text-text uppercase tracking-wider">Pipeline</span> + </div> + <table class="w-full text-[12px]"> + <tbody class="divide-y divide-border-light"> + <tr class="hover:bg-bg-row-hover"> + <td class="px-3.5 py-2.5 text-text-secondary">Ships in Flight</td> + <td class="px-3.5 py-2.5 text-right font-semibold text-text" data-live="earning.pipeline.ships_in_flight">{pipeline.ships_in_flight}</td> + </tr> + <tr class="hover:bg-bg-row-hover"> + <td class="px-3.5 py-2.5 text-text-secondary">Sub-Projects Complete</td> + <td class="px-3.5 py-2.5 text-right font-semibold text-text"><span data-live="earning.pipeline.sub_projects_complete">{pipeline.sub_projects_complete}</span><span class="text-text-muted">/{pipeline.sub_projects_total}</span></td> + </tr> + <tr class="hover:bg-bg-row-hover"> + <td class="px-3.5 py-2.5 text-text-secondary">P0 Blockers</td> + <td class="px-3.5 py-2.5 text-right font-semibold text-status-paused" data-live="earning.pipeline.p0_blockers">{pipeline.p0_blockers}</td> + </tr> + <tr class="hover:bg-bg-row-hover"> + <td class="px-3.5 py-2.5 text-text-secondary">PRDs Drafted / In Progress</td> + <td class="px-3.5 py-2.5 text-right font-semibold text-text"><span data-live="earning.pipeline.prds_drafted">{pipeline.prds_drafted}</span><span class="text-text-muted"> / <span data-live="earning.pipeline.prds_in_progress">{pipeline.prds_in_progress}</span></span></td> + </tr> + </tbody> + </table> + <div class="px-3.5 py-3 bg-bg-muted border-t border-border"> + <div class="flex items-center justify-between mb-1"> + <span class="text-[11px] text-text-secondary">W29 Milestone</span> + <span class="text-[11px] font-semibold text-text" data-live="earning.pipeline.w29_milestone_percent">{pipeline.w29_milestone_percent}%</span> + </div> + <div class="w-full h-1 bg-border rounded-full overflow-hidden"> + <div class="h-full bg-pillar-earning rounded-full" style={`width: ${pipeline.w29_milestone_percent}%`}></div> + </div> + </div> + </div> + + <!-- Members + Investors table --> + <div class="border border-border rounded-lg overflow-hidden" data-live="earning.members_investors"> + <div class="flex items-center gap-1.5 px-3.5 py-2.5 bg-bg-muted border-b border-border"> + <span class="text-[12px]">πŸ‘₯</span> + <span class="text-[11px] font-semibold text-text uppercase tracking-wider">Members + Investors</span> + </div> + <table class="w-full text-[12px]"> + <tbody class="divide-y divide-border-light"> + <tr class="hover:bg-bg-row-hover"> + <td class="px-3.5 py-2.5 text-text-secondary">Active (.OCA) Members</td> + <td class="px-3.5 py-2.5 text-right font-semibold text-text" data-live="earning.members_investors.active_oca_members">{members_investors.active_oca_members}</td> + </tr> + <tr class="hover:bg-bg-row-hover"> + <td class="px-3.5 py-2.5 text-text-secondary">Pre-Launch Signups</td> + <td class="px-3.5 py-2.5 text-right font-semibold text-text" data-live="earning.members_investors.pre_launch_signups">{members_investors.pre_launch_signups}</td> + </tr> + <tr class="hover:bg-bg-row-hover"> + <td class="px-3.5 py-2.5 text-text-secondary">Investor Pipeline</td> + <td class="px-3.5 py-2.5 text-right font-semibold text-text" data-live="earning.members_investors.investor_pipeline">{members_investors.investor_pipeline}</td> + </tr> + <tr class="hover:bg-bg-row-hover"> + <td class="px-3.5 py-2.5 text-text-secondary">#SoundOfMoney Raised</td> + <td class="px-3.5 py-2.5 text-right font-semibold text-pillar-earning" data-live="earning.members_investors.sound_of_money_raised_usd">{fmtUSD(members_investors.sound_of_money_raised_usd)}</td> + </tr> + <tr class="hover:bg-bg-row-hover"> + <td class="px-3.5 py-2.5 text-text-secondary">F1VISA-001 Pre-Orders</td> + <td class="px-3.5 py-2.5 text-right font-semibold text-text" data-live="earning.members_investors.f1visa001_pre_orders">{members_investors.f1visa001_pre_orders}</td> + </tr> + </tbody> + </table> + </div> + </div> + <p class="text-[10px] text-text-tertiary mt-1.5" data-live="earning.last-updated" title={lastUpdated}>Updated just now</p> +</section> diff --git a/src/components/Footer.astro b/src/components/Footer.astro new file mode 100644 index 0000000..15380cb --- /dev/null +++ b/src/components/Footer.astro @@ -0,0 +1,14 @@ +<footer class="border-t border-border mt-16"> + <div class="max-w-7xl mx-auto px-6 h-12 flex items-center justify-between"> + <div class="flex items-center gap-3 text-[11px] text-text-muted"> + <span>♾️ WeOwnNet 🌐</span> + <span class="text-text-tertiary">Β·</span> + <span>MIT License</span> + </div> + <div class="flex items-center gap-3 text-[11px] text-text-muted"> + <a href="https://github.com/WeOwnNet/FELG" class="hover:text-text-secondary transition-colors">GitHub</a> + <span class="text-text-tertiary">Β·</span> + <span>πŸŽ‰ πŸ’° πŸ“š 🫢</span> + </div> + </div> +</footer> diff --git a/src/components/GivingSection.astro b/src/components/GivingSection.astro new file mode 100644 index 0000000..9edc344 --- /dev/null +++ b/src/components/GivingSection.astro @@ -0,0 +1,67 @@ +--- +export interface Props { + giving: { open_source: { repos: number; pull_requests: number; contributors: number }; community: { total_members_all_channels: number }; ecosystem: { instances_deployed: number; members_onboarded: number }; }; + lastUpdated: string; +} +const { giving, lastUpdated } = Astro.props; +--- + +<section class="mb-10"> + <div class="flex items-center gap-2.5 mb-3"> + <div class="w-0.5 h-3.5 rounded bg-pillar-giving"></div> + <h2 class="text-[15px] font-bold text-text">Giving</h2> + </div> + <div class="grid grid-cols-1 sm:grid-cols-3 gap-4"> + <div class="border border-border rounded-lg overflow-hidden" data-live="giving.open_source"> + <div class="px-3.5 py-2.5 bg-bg-muted border-b border-border"> + <span class="text-[11px] font-semibold text-text uppercase tracking-wider">πŸ”“ Open Source</span> + </div> + <table class="w-full text-[12px]"> + <tbody class="divide-y divide-border-light"> + <tr class="hover:bg-bg-row-hover"> + <td class="px-3.5 py-2.5 text-text-secondary">Repos</td> + <td class="px-3.5 py-2.5 text-right font-semibold text-text" data-live="giving.open_source.repos">{giving.open_source.repos}</td> + </tr> + <tr class="hover:bg-bg-row-hover"> + <td class="px-3.5 py-2.5 text-text-secondary">Pull Requests</td> + <td class="px-3.5 py-2.5 text-right font-semibold text-text" data-live="giving.open_source.pull_requests">{giving.open_source.pull_requests}</td> + </tr> + <tr class="hover:bg-bg-row-hover"> + <td class="px-3.5 py-2.5 text-text-secondary">Contributors</td> + <td class="px-3.5 py-2.5 text-right font-semibold text-text" data-live="giving.open_source.contributors">{giving.open_source.contributors}</td> + </tr> + </tbody> + </table> + </div> + <div class="border border-border rounded-lg overflow-hidden" data-live="giving.community"> + <div class="px-3.5 py-2.5 bg-bg-muted border-b border-border"> + <span class="text-[11px] font-semibold text-text uppercase tracking-wider">πŸ‘₯ Community</span> + </div> + <div class="px-3.5 py-5 text-center"> + <div class="text-[24px] font-bold text-text" data-live="giving.community.total_members_all_channels">{giving.community.total_members_all_channels.toLocaleString()}</div> + <div class="text-[11px] text-text-muted mt-0.5">Total Members</div> + <div class="w-full h-1 bg-border rounded-full overflow-hidden mt-3 mx-auto max-w-[120px]"> + <div class="h-full bg-pillar-giving rounded-full" style="width: 76%"></div> + </div> + </div> + </div> + <div class="border border-border rounded-lg overflow-hidden" data-live="giving.ecosystem"> + <div class="px-3.5 py-2.5 bg-bg-muted border-b border-border"> + <span class="text-[11px] font-semibold text-text uppercase tracking-wider">🌐 Ecosystem</span> + </div> + <table class="w-full text-[12px]"> + <tbody class="divide-y divide-border-light"> + <tr class="hover:bg-bg-row-hover"> + <td class="px-3.5 py-2.5 text-text-secondary">Instances Deployed</td> + <td class="px-3.5 py-2.5 text-right font-semibold text-text" data-live="giving.ecosystem.instances_deployed">{giving.ecosystem.instances_deployed}</td> + </tr> + <tr class="hover:bg-bg-row-hover"> + <td class="px-3.5 py-2.5 text-text-secondary">Members Onboarded</td> + <td class="px-3.5 py-2.5 text-right font-semibold text-text" data-live="giving.ecosystem.members_onboarded">{giving.ecosystem.members_onboarded}</td> + </tr> + </tbody> + </table> + </div> + </div> + <p class="text-[10px] text-text-tertiary mt-1.5" data-live="giving.last-updated" title={lastUpdated}>Updated just now</p> +</section> diff --git a/src/components/Header.astro b/src/components/Header.astro new file mode 100644 index 0000000..9fc4149 --- /dev/null +++ b/src/components/Header.astro @@ -0,0 +1,51 @@ +--- +export interface Props { + dashboard: { name: string; domain: string; last_updated: string; version: string }; +} + +const { dashboard } = Astro.props; + +function formatRelativeTime(dateStr: string): string { + const date = new Date(dateStr); + const now = new Date(); + const diffMs = now.getTime() - date.getTime(); + const diffMin = Math.floor(diffMs / 60000); + if (diffMin < 1) return 'just now'; + if (diffMin < 60) return `${diffMin}m ago`; + const diffHrs = Math.floor(diffMin / 60); + if (diffHrs < 24) return `${diffHrs}h ago`; + const diffDays = Math.floor(diffHrs / 24); + return `${diffDays}d ago`; +} +--- + +<header class="border-b border-border bg-canvas"> + <div class="max-w-7xl mx-auto px-6 h-12 flex items-center justify-between"> + <div class="flex items-center gap-6"> + <a href="/" class="flex items-center gap-2"> + <div class="w-6 h-6 rounded bg-gradient-to-br from-pillar-fun to-pillar-giving flex items-center justify-center text-[10px] font-bold text-canvas">F</div> + <div class="text-[13px] font-medium text-text">{dashboard.name}</div> + </a> + <div class="hidden md:flex items-center gap-4 pl-6 border-l border-border"> + <span class="text-[11px] text-text-secondary">πŸŽ‰ Fun</span> + <span class="text-text-tertiary">Β·</span> + <span class="text-[11px] text-text-secondary">πŸ’° Earning</span> + <span class="text-text-tertiary">Β·</span> + <span class="text-[11px] text-text-secondary">πŸ“š Learning</span> + <span class="text-text-tertiary">Β·</span> + <span class="text-[11px] text-text-secondary">🫢 Giving</span> + </div> + </div> + <div class="flex items-center gap-3"> + <a href="/brands" class="text-[11px] text-text-secondary hover:text-accent transition-colors">All Brands</a> + <button id="refresh-btn" class="text-[11px] text-text-secondary hover:text-accent transition-colors cursor-pointer bg-transparent border-0 p-0">⟳ Refresh</button> + <span class="text-[10px] text-text-tertiary">Β·</span> + <span class="text-[10px] text-text-muted"> + <span class="w-1.5 h-1.5 rounded-full bg-status-live inline-block align-middle mr-1"></span> + <span data-live="last-updated" title={dashboard.last_updated}>{formatRelativeTime(dashboard.last_updated)}</span> + </span> + <span class="text-[10px] text-text-tertiary">Β·</span> + <button id="theme-toggle" class="text-[13px] text-text-secondary hover:text-accent transition-colors cursor-pointer bg-transparent border-0 p-0 leading-none" title="Toggle theme">πŸŒ™</button> + </div> + </div> +</header> diff --git a/src/components/LearningSection.astro b/src/components/LearningSection.astro new file mode 100644 index 0000000..4c6a1f3 --- /dev/null +++ b/src/components/LearningSection.astro @@ -0,0 +1,41 @@ +--- +export interface Props { + learning: { lessons_counter: number; prds_completed: number; skills_modules: number; members_trained: number; new_this_week: number; }; + lastUpdated: string; +} +const { learning, lastUpdated } = Astro.props; +--- + +<section class="mb-10"> + <div class="flex items-center gap-2.5 mb-3"> + <div class="w-0.5 h-3.5 rounded bg-pillar-learning"></div> + <h2 class="text-[15px] font-bold text-text">Learning</h2> + </div> + <div class="border border-border rounded-lg overflow-hidden" data-live="learning"> + <table class="w-full text-[12px]"> + <tbody class="divide-y divide-border-light"> + <tr class="hover:bg-bg-row-hover"> + <td class="px-3.5 py-2.5 text-text-secondary">πŸ“– Lessons</td> + <td class="px-3.5 py-2.5 text-right font-semibold text-text" data-live="learning.lessons_counter">{learning.lessons_counter}</td> + </tr> + <tr class="hover:bg-bg-row-hover"> + <td class="px-3.5 py-2.5 text-text-secondary">PRDs Completed</td> + <td class="px-3.5 py-2.5 text-right font-semibold text-text" data-live="learning.prds_completed">{learning.prds_completed}</td> + </tr> + <tr class="hover:bg-bg-row-hover"> + <td class="px-3.5 py-2.5 text-text-secondary">Skills Modules</td> + <td class="px-3.5 py-2.5 text-right font-semibold text-text" data-live="learning.skills_modules">{learning.skills_modules}</td> + </tr> + <tr class="hover:bg-bg-row-hover"> + <td class="px-3.5 py-2.5 text-text-secondary">Members Trained</td> + <td class="px-3.5 py-2.5 text-right font-semibold text-text" data-live="learning.members_trained">{learning.members_trained}</td> + </tr> + <tr class="hover:bg-bg-row-hover"> + <td class="px-3.5 py-2.5 text-text-secondary">New This Week</td> + <td class="px-3.5 py-2.5 text-right"><span class="font-semibold text-text" data-live="learning.new_this_week">{learning.new_this_week}</span> <span class="text-[10px] text-status-live bg-status-live/10 px-1.5 py-0.5 rounded-full">new</span></td> + </tr> + </tbody> + </table> + </div> + <p class="text-[10px] text-text-tertiary mt-1.5" data-live="learning.last-updated" title={lastUpdated}>Updated just now</p> +</section> diff --git a/src/components/SocialSection.astro b/src/components/SocialSection.astro new file mode 100644 index 0000000..d504cd4 --- /dev/null +++ b/src/components/SocialSection.astro @@ -0,0 +1,79 @@ +--- +export interface Props { + social: { + youtube: { subscribers: number; views: number; watch_time_hours: number }; + twitter: { followers: number; impressions_monthly: number; engagement_rate: number }; + linkedin: { connections: number; post_reach_monthly: number }; + instagram: { followers: number; story_views_avg: number }; + signal_telegram: { members: number }; + email: { subscribers: number; open_rate: number; click_rate: number }; + }; + lastUpdated: string; +} +const { social, lastUpdated } = Astro.props; +function fmt(n: number): string { + if (n >= 1_000_000) return (n / 1_000_000).toFixed(1) + 'M'; + if (n >= 1_000) return (n / 1_000).toFixed(1) + 'K'; + return n.toLocaleString(); +} +--- + +<section class="mb-10"> + <div class="flex items-center gap-2.5 mb-3"> + <div class="w-0.5 h-3.5 rounded bg-pillar-fun"></div> + <h2 class="text-[15px] font-bold text-text">Social Media</h2> + <span class="text-[10px] text-text-muted bg-bg-muted px-1.5 py-0.5 rounded">live</span> + </div> + + <div class="border border-border rounded-lg overflow-hidden"> + <table class="w-full text-[12px]"> + <thead> + <tr class="bg-bg-muted border-b border-border"> + <th class="text-left font-medium text-text-secondary px-3.5 py-2.5 w-[160px]">Platform</th> + <th class="text-left font-medium text-text-secondary px-3.5 py-2.5">Primary</th> + <th class="text-left font-medium text-text-secondary px-3.5 py-2.5 hidden sm:table-cell">Secondary</th> + <th class="text-left font-medium text-text-secondary px-3.5 py-2.5 hidden md:table-cell">Details</th> + </tr> + </thead> + <tbody class="divide-y divide-border-light"> + <tr class="hover:bg-bg-row-hover transition-colors" data-live="social.youtube"> + <td class="px-3.5 py-2.5"><span class="text-[13px] mr-1.5">πŸ“Ή</span><span class="text-text font-medium">YouTube</span></td> + <td class="px-3.5 py-2.5"><span class="font-semibold text-text"><span data-live="social.youtube.subscribers">{fmt(social.youtube.subscribers)}</span></span> <span class="text-text-muted">subs</span></td> + <td class="px-3.5 py-2.5 hidden sm:table-cell text-text-secondary"><span data-live="social.youtube.views">{fmt(social.youtube.views)}</span> views</td> + <td class="px-3.5 py-2.5 hidden md:table-cell text-text-muted">{fmt(social.youtube.watch_time_hours)}h watch time</td> + </tr> + <tr class="hover:bg-bg-row-hover transition-colors" data-live="social.twitter"> + <td class="px-3.5 py-2.5"><span class="text-[13px] mr-1.5">🐦</span><span class="text-text font-medium">X/Twitter</span></td> + <td class="px-3.5 py-2.5"><span class="font-semibold text-text"><span data-live="social.twitter.followers">{fmt(social.twitter.followers)}</span></span> <span class="text-text-muted">followers</span></td> + <td class="px-3.5 py-2.5 hidden sm:table-cell text-text-secondary"><span data-live="social.twitter.engagement_rate">{social.twitter.engagement_rate}%</span> eng.</td> + <td class="px-3.5 py-2.5 hidden md:table-cell text-text-muted">{fmt(social.twitter.impressions_monthly)} monthly</td> + </tr> + <tr class="hover:bg-bg-row-hover transition-colors" data-live="social.linkedin"> + <td class="px-3.5 py-2.5"><span class="text-[13px] mr-1.5">πŸ’Ό</span><span class="text-text font-medium">LinkedIn</span></td> + <td class="px-3.5 py-2.5"><span class="font-semibold text-text"><span data-live="social.linkedin.connections">{fmt(social.linkedin.connections)}</span></span> <span class="text-text-muted">conn.</span></td> + <td class="px-3.5 py-2.5 hidden sm:table-cell text-text-secondary"><span data-live="social.linkedin.post_reach_monthly">{fmt(social.linkedin.post_reach_monthly)}</span> reach</td> + <td class="px-3.5 py-2.5 hidden md:table-cell"></td> + </tr> + <tr class="hover:bg-bg-row-hover transition-colors" data-live="social.instagram"> + <td class="px-3.5 py-2.5"><span class="text-[13px] mr-1.5">πŸ“±</span><span class="text-text font-medium">Instagram</span></td> + <td class="px-3.5 py-2.5"><span class="font-semibold text-text"><span data-live="social.instagram.followers">{fmt(social.instagram.followers)}</span></span> <span class="text-text-muted">followers</span></td> + <td class="px-3.5 py-2.5 hidden sm:table-cell text-text-secondary"><span data-live="social.instagram.story_views_avg">{fmt(social.instagram.story_views_avg)}</span> story avg</td> + <td class="px-3.5 py-2.5 hidden md:table-cell"></td> + </tr> + <tr class="hover:bg-bg-row-hover transition-colors" data-live="social.signal_telegram"> + <td class="px-3.5 py-2.5"><span class="text-[13px] mr-1.5">πŸ’¬</span><span class="text-text font-medium">Signal/Telegram</span></td> + <td class="px-3.5 py-2.5"><span class="font-semibold text-text"><span data-live="social.signal_telegram.members">{social.signal_telegram.members.toLocaleString()}</span></span> <span class="text-text-muted">members</span></td> + <td class="px-3.5 py-2.5 hidden sm:table-cell"></td> + <td class="px-3.5 py-2.5 hidden md:table-cell"></td> + </tr> + <tr class="hover:bg-bg-row-hover transition-colors" data-live="social.email"> + <td class="px-3.5 py-2.5"><span class="text-[13px] mr-1.5">πŸ“§</span><span class="text-text font-medium">Email</span></td> + <td class="px-3.5 py-2.5"><span class="font-semibold text-text"><span data-live="social.email.subscribers">{fmt(social.email.subscribers)}</span></span> <span class="text-text-muted">subs</span></td> + <td class="px-3.5 py-2.5 hidden sm:table-cell text-text-secondary"><span data-live="social.email.open_rate">{social.email.open_rate}%</span> open</td> + <td class="px-3.5 py-2.5 hidden md:table-cell text-text-muted"><span data-live="social.email.click_rate">{social.email.click_rate}%</span> click</td> + </tr> + </tbody> + </table> + </div> + <p class="text-[10px] text-text-tertiary mt-1.5" data-live="social.last-updated" title={lastUpdated}>Updated just now</p> +</section> diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro new file mode 100644 index 0000000..0c2186a --- /dev/null +++ b/src/layouts/Layout.astro @@ -0,0 +1,74 @@ +--- +import '../styles/global.css'; + +export interface Props { + title?: string; + description?: string; + ogTitle?: string; + ogDescription?: string; +} + +const { + title = 'FELG Ecosystem Dashboard', + description = '♾️ WeOwnNet 🌐 Ecosystem Dashboard', + ogTitle = title, + ogDescription = description, +} = Astro.props; +--- + +<!doctype html> +<html lang="en"> + <head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> + <link rel="icon" href="/favicon.ico" /> + <meta name="generator" content={Astro.generator} /> + <title>{title} + + + + + + + + + + + + + + + diff --git a/src/lib/data.ts b/src/lib/data.ts new file mode 100644 index 0000000..2dd235d --- /dev/null +++ b/src/lib/data.ts @@ -0,0 +1,14 @@ +import { supabase } from './supabase'; +import type { DashboardData } from './types'; +import { readFileSync } from 'fs'; +import { join } from 'path'; + +export async function getDashboard(): Promise { + if (supabase) { + const { data } = await supabase.from('dashboard_state').select('payload').eq('id', 'current').single(); + if (data?.payload) return data.payload as unknown as DashboardData; + } + // Fallback: read local JSON for dev/build without Supabase + const raw = readFileSync(join(process.cwd(), 'data', 'dashboard.json'), 'utf-8'); + return JSON.parse(raw); +} \ No newline at end of file diff --git a/src/lib/supabase.ts b/src/lib/supabase.ts new file mode 100644 index 0000000..bc5234f --- /dev/null +++ b/src/lib/supabase.ts @@ -0,0 +1,8 @@ +import { createClient } from '@supabase/supabase-js'; + +const url = import.meta.env.PUBLIC_SUPABASE_URL; +const key = import.meta.env.PUBLIC_SUPABASE_ANON_KEY; + +export const supabase = (url && key && !url.startsWith('http://placeholder') && !url.startsWith('https://placeholder')) + ? createClient(url, key) + : null; \ No newline at end of file diff --git a/src/lib/types.d.ts b/src/lib/types.d.ts new file mode 100644 index 0000000..6198b8f --- /dev/null +++ b/src/lib/types.d.ts @@ -0,0 +1,67 @@ +export interface Brand { + id: string; + name: string; + emoji: string; + status: 'live' | 'in_progress' | 'not_started' | 'paused'; + tagline: string; + category: string; + lead: string; + metric_label: string; + metric_value: number; + milestones: { label: string; status: 'done' | 'in_progress' | 'pending'; date?: string; target?: string }[]; + links: { website?: string; prd?: string; channel?: string; repo?: string }; + last_updated: string; +} + +export interface DashboardData { + dashboard: { + name: string; + domain: string; + last_updated: string; + version: string; + }; + brands: { + top_5_ids: string[]; + all_brands: Brand[]; + }; + fun: { + social_media: { + youtube: { subscribers: number; views: number; watch_time_hours: number }; + twitter: { followers: number; impressions_monthly: number; engagement_rate: number }; + linkedin: { connections: number; post_reach_monthly: number }; + instagram: { followers: number; story_views_avg: number }; + signal_telegram: { members: number }; + email: { subscribers: number; open_rate: number; click_rate: number }; + }; + }; + earning: { + pipeline: { + ships_in_flight: number; + sub_projects_complete: number; + sub_projects_total: number; + p0_blockers: number; + prds_drafted: number; + prds_in_progress: number; + w29_milestone_percent: number; + }; + members_investors: { + active_oca_members: number; + pre_launch_signups: number; + investor_pipeline: number; + sound_of_money_raised_usd: number; + f1visa001_pre_orders: number; + }; + }; + learning: { + lessons_counter: number; + prds_completed: number; + skills_modules: number; + members_trained: number; + new_this_week: number; + }; + giving: { + open_source: { repos: number; pull_requests: number; contributors: number }; + community: { total_members_all_channels: number }; + ecosystem: { instances_deployed: number; members_onboarded: number }; + }; +} \ No newline at end of file diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..f25f946 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,32 @@ +export function getStatusColor(status: string): string { + switch (status) { + case 'live': return '#22c55e'; + case 'in_progress': return '#eab308'; + case 'not_started': return '#75758f'; + case 'paused': return '#ef4444'; + default: return '#75758f'; + } +} + +export function getStatusLabel(status: string): string { + switch (status) { + case 'live': return 'Live'; + case 'in_progress': return 'In Progress'; + case 'not_started': return 'Not Started'; + case 'paused': return 'Paused'; + default: return status; + } +} + +export function formatRelativeTime(dateStr: string): string { + const date = new Date(dateStr); + const now = new Date(); + const diffMs = now.getTime() - date.getTime(); + const diffMin = Math.floor(diffMs / 60000); + if (diffMin < 1) return 'just now'; + if (diffMin < 60) return `${diffMin}m ago`; + const diffHrs = Math.floor(diffMin / 60); + if (diffHrs < 24) return `${diffHrs}h ago`; + const diffDays = Math.floor(diffHrs / 24); + return `${diffDays}d ago`; +} \ No newline at end of file diff --git a/src/pages/brand/[id].astro b/src/pages/brand/[id].astro new file mode 100644 index 0000000..92f0504 --- /dev/null +++ b/src/pages/brand/[id].astro @@ -0,0 +1,118 @@ +--- +import Layout from '../../layouts/Layout.astro'; +import Header from '../../components/Header.astro'; +import Footer from '../../components/Footer.astro'; +import { getDashboard } from '../../lib/data'; +import type { Brand } from '../../lib/types'; +import { getStatusColor, getStatusLabel, formatRelativeTime } from '../../lib/utils'; + +export async function getStaticPaths() { + const dashboard = await getDashboard(); + return dashboard.brands.all_brands.map((brand: Brand) => ({ + params: { id: brand.id }, + props: { brand, dashboardMeta: dashboard.dashboard }, + })); +} + +const { brand, dashboardMeta } = Astro.props; +const statusColor = getStatusColor(brand.status); +const statusLabel = getStatusLabel(brand.status); + +function fmt(n: number): string { + if (n >= 1_000_000) return (n / 1_000_000).toFixed(1) + 'M'; + if (n >= 1_000) return (n / 1_000).toFixed(1) + 'K'; + return n.toLocaleString(); +} +--- + + +
+ +
+ +
+ Dashboard→ + Brands→ + {brand.name} +
+ + +
+
+ {brand.emoji} +
+

{brand.name}

+

{brand.tagline}

+
+
+
+ ● {statusLabel} + {brand.lead} +
+
+ + +
+
+
{brand.metric_label}
+
{fmt(brand.metric_value)}
+
+
+
+
Category
+
{brand.category}
+
+
+
+
Lead
+
{brand.lead}
+
+
+ + +
+ +
+
+ 🏁 Milestones +
+
+ {brand.milestones.map((ms) => { + const dotColor = ms.status === 'done' ? '#22c55e' : ms.status === 'in_progress' ? '#eab308' : '#75758f'; + const label = ms.status === 'done' ? 'Done' : ms.status === 'in_progress' ? 'In Progress' : 'Pending'; + return ( +
+
+ {ms.label} + {ms.date || ms.target || ''} + {label} +
+ ); + })} +
+
+ + +
+ πŸ”— Links +
+ {brand.links.website && 🌐 Website} + {brand.links.prd && πŸ“‹ PRD} + {brand.links.channel && πŸ’¬ {brand.links.channel}} + {brand.links.repo && πŸ“¦ Repo} +
+
+
+ +

Last updated: {formatRelativeTime(brand.last_updated)}

+
+ +