diff --git a/pof-cayman-website/.env.example b/pof-cayman-website/.env.example new file mode 100644 index 00000000..e590cc7b --- /dev/null +++ b/pof-cayman-website/.env.example @@ -0,0 +1,21 @@ +# Copy to .env for local work. On Cloudflare Pages, set these under +# Settings > Environment variables. All are optional; each section +# falls back to a safe, honest state when its value is missing. + +# Formspree form IDs (https://formspree.io). Route every form to the +# shared POF inbox, never to a personal address. +PUBLIC_FORMSPREE_CONTACT= +PUBLIC_FORMSPREE_VOLUNTEER= +PUBLIC_FORMSPREE_CHAPTER= + +# Mailchimp embedded form action URL (Audience > Signup forms > Embedded). +PUBLIC_MAILCHIMP_ACTION= + +# Fygaro payment link issued by Sustainable Cayman for POF donations. +PUBLIC_FYGARO_URL= + +# CAF America / GlobalGiving project page, once one exists. +PUBLIC_OVERSEAS_GIVING_URL= + +# Cloudflare Web Analytics token (Cloudflare dashboard > Analytics > Web Analytics). +PUBLIC_CF_ANALYTICS_TOKEN= diff --git a/pof-cayman-website/.gitignore b/pof-cayman-website/.gitignore new file mode 100644 index 00000000..1fb5a999 --- /dev/null +++ b/pof-cayman-website/.gitignore @@ -0,0 +1,8 @@ +node_modules/ +dist/ +.astro/ +.env +.env.* +!.env.example +.wrangler/ +screenshots/ diff --git a/pof-cayman-website/BLOCKERS.md b/pof-cayman-website/BLOCKERS.md new file mode 100644 index 00000000..007ffb8f --- /dev/null +++ b/pof-cayman-website/BLOCKERS.md @@ -0,0 +1,26 @@ +# What POF still has to supply + +The site is built and passes its checks. Each item below is something only POF can provide. Nothing here has been invented or filled in with a placeholder that could be mistaken for the real thing. + +| Item | Where it goes | What the site shows until then | +|---|---|---| +| Vector logo files | `public/press/`, `src/components/Wordmark.astro` | A typographic wordmark in Bricolage Grotesque | +| Official colours and fonts, if any exist | `src/styles/tokens.css` | Palette chosen from Cayman ocean, reef, sand and mangrove tones, checked at 4.5:1 | +| Rights-cleared photography, with consent on file for every identifiable person and parental consent for under-18s | `public/images/` via `scripts/images.mjs`; set `image` and `imageAlt` on campaigns | Solid colour blocks from the palette, hidden from screen readers | +| Confirmation of use rights for photos credited to Heidi Bassett Blair and Courtney Platt | As above | Not used | +| Current leadership roster | `src/content/team/*.md`, `holder` and `school` | Role name with "Current holder to be named for this school year" | +| Per-chapter meeting times, leader names and current projects | `src/content/chapters/*.md` | "Time confirmed each term. Email to be introduced." | +| 2026 to 2027 event schedule | `src/content/events/` (copy `_TEMPLATE-copy-me.md`) | "The next event is being scheduled" plus newsletter signup | +| Confirmed links to press coverage | `src/content/news/*.md`, `url` | Outlet, date and summary without a link | +| Date of the World Cleanup Day figures (13 sites, 300 volunteers, over two tons) | `src/lib/site.ts` STATS source | Source shown without a year | +| Dates for the Mission Blue Hope Spot partnership and the National Conservation Act and National Energy Policy letters | `src/content/news/` | Mentioned without dates | +| Formal statement of legal status and donation handling, agreed with Sustainable Cayman | `/support`, footer | Wording drawn from the brief: not a registered NPO, donations via Sustainable Cayman (NPO-612) | +| Fygaro payment link from Sustainable Cayman | Cloudflare variable `PUBLIC_FYGARO_URL` | "Email to give" button | +| CAF America or GlobalGiving page | `PUBLIC_OVERSEAS_GIVING_URL` | Email instruction | +| Formspree form IDs (three forms), routed to the shared inbox | `PUBLIC_FORMSPREE_CONTACT`, `PUBLIC_FORMSPREE_VOLUNTEER`, `PUBLIC_FORMSPREE_CHAPTER` | Forms open the visitor's email app with the fields filled in | +| Mailchimp embedded form URL | `PUBLIC_MAILCHIMP_ACTION` | Email instruction | +| Cloudflare Web Analytics token | `PUBLIC_CF_ANALYTICS_TOKEN` | No analytics | +| An adult custodian (faculty or alumni) with access to the shared inbox, so under-16 sign-ups are never read by students alone | Privacy page wording, `docs/HANDOVER.md` | Privacy page says POF's current leadership reads the inbox | +| Role-based email addresses on the domain (president@, chapters@, events@, press@) | `src/lib/site.ts`, `docs/HANDOVER.md` | The shared inbox protectourfuturecayman@gmail.com everywhere | +| Domain access for protectourfuturecayman.org, pofcaribbean.org and protectourfuture-eco.com | Cloudflare Pages custom domains | Redirect rules are written in `public/_redirects` and fire once the domains are attached | +| Google Search Console access for the canonical domain | Launch checklist in README | Sitemap is generated at `/sitemap-index.xml` | diff --git a/pof-cayman-website/README.md b/pof-cayman-website/README.md new file mode 100644 index 00000000..48736149 --- /dev/null +++ b/pof-cayman-website/README.md @@ -0,0 +1,192 @@ +# Protect Our Future Cayman website + +## What this site is + +This is the website for Protect Our Future (POF) Cayman, served at protectourfuturecayman.org. It is a static site built with [Astro](https://astro.build) and hosted on Cloudflare Pages. All the content that changes (events, news, chapters, campaigns, team roles) lives in plain Markdown files under `src/content`. You do not need to install anything to update it. You edit a file on github.com, commit it to the `main` branch, and Cloudflare rebuilds the live site in about two minutes. + +The one address you need is `protectourfuturecayman@gmail.com`. Every form and every "email us" link on the site points there. + +## How to add an event + +Screenshots to be added by the first student who does this. + +1. Go to the repository on github.com and open the folder `src/content/events`. +2. Open `_TEMPLATE-copy-me.md`. Click the "Raw" button, select everything, and copy it. +3. Go back to the `src/content/events` folder. Click "Add file", then "Create new file". +4. Name the file `YYYY-MM-DD-short-name.md`. For example `2026-11-07-barkers-cleanup.md`. Use the date the event starts. Use lower-case letters and hyphens, no spaces. +5. The file name must NOT start with an underscore. The site ignores any file that starts with `_`. That is how the template stays hidden. +6. Paste the template into the editor and change each field. The fields are explained below. +7. Scroll down, write a one-line commit message such as "Add Barkers cleanup", choose "Commit directly to the main branch", and click "Commit changes". +8. Wait about two minutes. Refresh protectourfuturecayman.org/events and the event is there. + +Every event file starts with a block between two lines of `---`. Each line in that block is a field. + +| Field | Required | What to put | +| --- | --- | --- | +| `title` | Yes | The name of the event. Keep it short. | +| `date` | Yes | Start date as `YYYY-MM-DD`, for example `2026-11-07`. | +| `endDate` | No | Last day of a multi-day event, same format. Leave it out for a one-day event. | +| `time` | No | Plain text, for example `8:00 am to 11:00 am`. | +| `location` | Yes | Where to meet, for example `Barkers National Park, West Bay`. | +| `district` | No | The district, for example `West Bay`. | +| `summary` | Yes | One or two sentences, up to 220 characters. This shows on the event list. | +| `action` | No | A button. It has two parts on their own indented lines: `label` (the words on the button) and `href` (where it goes, for example `/get-involved`). Remove all three lines if there is no button. | +| `bring` | No | A list of things to bring, one per line, each starting with `- `. Remove the whole list if it is not needed. | +| `cancelled` | No | Write `cancelled: true` if the event is called off. The event stays listed with a "Cancelled" label and its button is hidden. Do not delete the file. | + +Anything you write below the second `---` line shows on the event's own page. Say where to meet, who to look for, and what happens if it rains. + +If the build log says `The collection "events" does not exist or is empty`, that is only because no event file exists yet. It is not an error. + +Past events archive themselves. An event is "upcoming" until the end of its last day, Cayman time. The day after, it moves to the archive page on its own. Nobody deletes anything. The site is also rebuilt weekly so this happens even when nobody commits (see "Weekly rebuild" below). + +## How to add a news item + +News items live in `src/content/news`. Create a new file the same way as an event. Name it by date and a short slug, for example `2026-04-12-compass-cleanup.md`. It must not start with an underscore. + +| Field | Required | What to put | +| --- | --- | --- | +| `title` | Yes | The headline. | +| `outlet` | Yes | Who published it, for example `Cayman News Service`. For POF's own milestones write `Protect Our Future`. | +| `date` | Yes | `YYYY-MM-DD`. If you only know the month, use the first of the month. If you only know the year, use `YYYY-01-01`. | +| `datePrecision` | No | How much of the date is real: `day` (default), `month`, or `year`. With `month` the site shows "March 2026". With `year` it shows "2026". This is how you avoid inventing a day. | +| `url` | No | The link to the article. Leave it out until you have confirmed the link works. Without a url the headline shows as plain text. With one it becomes a link. | +| `kind` | No | `coverage` (default), `award`, or `milestone`. | +| `summary` | Yes | One or two sentences, up to 280 characters. | + +News files usually have nothing below the second `---`. + +## How to update a chapter + +Chapters live in `src/content/chapters`, one file per school. Open the file, click the pencil icon to edit, change the fields, and commit. + +| Field | Required | What to put | +| --- | --- | --- | +| `school` | Yes | The full school name. | +| `shortName` | No | A shorter version for tight spaces. | +| `status` | Yes | `active` or `forming`. Only active chapters count on the About page. | +| `meeting` | No | When and where the chapter meets, for example `Tuesdays, 3:15 pm, Room 12`. | +| `leaderRole` | No | The title of the chapter lead. Defaults to `Chapter Lead`. | +| `leaderName` | No | The current student lead. Only add a name with that student's permission. | +| `project` | No | What the chapter is working on this term. | +| `order` | No | A number that sets the order on the page. Lower comes first. Defaults to 50. | + +Blank fields are a normal state, not a mistake. This is what the site shows when they are empty: + +- `meeting` blank: "Time confirmed each term. Email to be introduced." +- `leaderName` blank: "A current student. Ask by email and we will connect you." +- `project` blank: "Project for this term not yet posted. Contact: protectourfuturecayman@gmail.com" (only shown for active chapters). + +To add a new school, create a new file in the folder using an existing one as a guide. + +## How to update a campaign status + +Campaigns live in `src/content/campaigns`. Each one has a status that the President signs off each term. + +| Field | Required | What to put | +| --- | --- | --- | +| `title` | Yes | The campaign name. | +| `status` | Yes | `active` (POF is working on it now), `won` (the outcome POF asked for happened), or `ongoing` (a long-running issue with no single finish line). | +| `statusNote` | Yes | One line saying what the status means right now. This is shown next to the status label. Update it whenever the status changes. | +| `started` | No | The year the campaign began. | +| `summary` | Yes | One or two sentences, up to 220 characters. | +| `action` | Yes | A button with `label` and `href`, same as events. | +| `featured` | No | `true` puts this campaign at the top of the Our Work page and on the home page. Only one campaign should be featured at a time. If none is, the site picks the first by `order`. | +| `image` | No | The base name of a rights-cleared photo (see "Photos"). Leave out until one exists. | +| `imageAlt` | No | A description of the photo for screen readers. Required if `image` is set. | +| `order` | No | Sort order. Lower comes first. | + +Everything below the second `---` is the campaign's full page. Use `##` headings for sections. + +## How to update team roles + +Team roles live in `src/content/team`, one file per role. The role stays the same from year to year. The holder changes every school year. + +| Field | Required | What to put | +| --- | --- | --- | +| `role` | Yes | The role title, for example `President`. Do not rename roles without a handover meeting. | +| `holder` | No | The current student's name. Leave blank until the roster is confirmed and the student agrees to be named. Blank shows "Current holder to be named for this school year." | +| `school` | No | The holder's school. Shown after the name. | +| `responsibility` | Yes | What the role does, in one or two sentences. | +| `order` | No | Sort order. Lower comes first. | + +Each September, update `holder` and `school` in every file. Each June, clear the graduating students' names. + +## How to update the numbers + +The figures on the home page and the About page come from `STATS` in `src/lib/site.ts`. Each one has a `value`, a `label`, and a `source`. Only change a figure when you have a source you can name: a news article, a government result, or a POF count with a date. Put that source in the `source` field. If you cannot name a source, do not change the number. + +The same file holds `SITE.boilerplate`, the standard paragraph about POF used on the press page. Keep it in step with the numbers. + +## Photos + +There are no photographs in the repository yet. Every photo on the site goes through the `Photo` component in `src/components/Photo.astro`. It expects each image in six files inside `public/images`: + +``` +/images/-400.avif /images/-800.avif /images/-1600.avif +/images/-400.webp /images/-800.webp /images/-1600.webp +/images/-400.jpg /images/-800.jpg /images/-1600.jpg +``` + +The `image` field in a campaign file (or the `src` prop in a page) is just ``, with no size and no extension. + +Before any photo goes in the repository: + +- Every identifiable person in it must have agreed to be on the website. +- Anyone under 18 needs a parent or guardian's written consent. +- Keep the consent record with the shared inbox, not on a personal phone. + +Until an image exists, the site shows a solid colour block in its place. That is on purpose. It is never a broken-image icon and nothing needs fixing. + +## Settings that live in Cloudflare + +Some settings are not in the repository. They are environment variables set in Cloudflare Pages under Settings, then Environment variables. The full list is in `.env.example`. All of them are optional. When one is blank, the site falls back to something honest instead of a dead button. + +- `PUBLIC_FORMSPREE_CONTACT`: Formspree form ID for the contact form. Blank: the form opens the visitor's email app with the message filled in. +- `PUBLIC_FORMSPREE_VOLUNTEER`: Formspree form ID for the volunteer form. Blank: same email fallback. +- `PUBLIC_FORMSPREE_CHAPTER`: Formspree form ID for the "start a chapter" form. Blank: same email fallback. +- `PUBLIC_MAILCHIMP_ACTION`: the Mailchimp embedded form action URL. Blank: the newsletter box shows "email us with the word newsletter" instead of a form. +- `PUBLIC_FYGARO_URL`: the Fygaro payment link issued by Sustainable Cayman. Blank: the donate button becomes "Email to give". +- `PUBLIC_OVERSEAS_GIVING_URL`: a CAF America or GlobalGiving page, once one exists. Blank: the support page says to email us to arrange it. +- `PUBLIC_CF_ANALYTICS_TOKEN`: Cloudflare Web Analytics token. Blank: no analytics script is loaded. + +Every Formspree form must deliver to the shared POF inbox, never to a personal address. After changing a variable, trigger a new deployment in Cloudflare so it takes effect. + +## Deploying on Cloudflare Pages + +1. In Cloudflare, go to Workers and Pages, then Create, then Pages, then Connect to Git. +2. Choose the GitHub repository. Set the production branch to `main`. +3. Build settings: root directory `pof-cayman-website`, build command `npm run build`, build output directory `dist`. +4. Under environment variables, set `NODE_VERSION` to `20` (or newer). Add any of the `PUBLIC_*` variables above that you have values for. +5. Save and deploy. The first build takes a few minutes. +6. Under Custom domains, add `protectourfuturecayman.org`. Also add `www.protectourfuturecayman.org`. +7. On the same Pages project, also add `pofcaribbean.org`, `www.pofcaribbean.org`, `protectourfuture-eco.com` and `www.protectourfuture-eco.com` as custom domains. The redirect rules in `public/_redirects` only fire for domains attached to this project. Once attached, every old address and every `www` address sends visitors to the matching page on protectourfuturecayman.org. + +`public/_redirects` also maps old Squarespace and Wix paths (for example `/about-us`) to the new pages, so old press links keep working. + +## Weekly rebuild + +Past events disappear from the upcoming list because the site is rebuilt, not because anyone edits a file. If nobody commits for a month, the site is not rebuilt and a past event would sit on the list. This repository does not use `.github/workflows` for this. Instead: + +1. In the Cloudflare Pages project, go to Settings, then Builds and deployments, then Deploy hooks. Create a hook called "weekly" and copy its URL. +2. Call that URL once a week. A Cloudflare Worker with a Cron Trigger can do it, or a free scheduler service such as cron-job.org. Set it to Monday early morning, Cayman time. +3. Store the hook URL in the shared password manager. Anyone with it can trigger a build, so do not paste it in chat. + +## Running locally + +Almost nobody needs this. If you do, install Node 20 or newer, then in the `pof-cayman-website` folder: + +``` +npm install # once +npm run dev # live preview at http://localhost:4321 +npm test # date and archive logic tests +npm run build # builds the site into dist/ +npm run verify # checks the built site for broken links, missing titles, and past events +``` + +Copy `.env.example` to `.env` if you want to test the Formspree, Mailchimp or Fygaro settings locally. + +## Where to get help + +Email `protectourfuturecayman@gmail.com`. That is the only verified POF address. For account and handover matters, see `docs/HANDOVER.md`. + diff --git a/pof-cayman-website/astro.config.mjs b/pof-cayman-website/astro.config.mjs new file mode 100644 index 00000000..88b9d932 --- /dev/null +++ b/pof-cayman-website/astro.config.mjs @@ -0,0 +1,41 @@ +import { defineConfig } from 'astro/config'; +import sitemap from '@astrojs/sitemap'; +import fs from 'node:fs'; +import path from 'node:path'; + +// Canonical domain. Both retired domains 301 here (see public/_redirects). +const SITE = 'https://protectourfuturecayman.org'; + +// Past events stay reachable (and noindexed) but leave the sitemap on their own. +function pastEventIds() { + const dir = path.resolve('./src/content/events'); + const today = new Date(Date.now() - 5 * 3600 * 1000).toISOString().slice(0, 10); + const ids = new Set(); + if (!fs.existsSync(dir)) return ids; + for (const f of fs.readdirSync(dir)) { + if (!f.endsWith('.md') || f.startsWith('_')) continue; + const src = fs.readFileSync(path.join(dir, f), 'utf8'); + const end = src.match(/^endDate:\s*(\d{4}-\d{2}-\d{2})/m)?.[1]; + const start = src.match(/^date:\s*(\d{4}-\d{2}-\d{2})/m)?.[1]; + if ((end ?? start ?? '9999') < today) ids.add(f.replace(/\.md$/, '')); + } + return ids; +} +const past = pastEventIds(); + +export default defineConfig({ + site: SITE, + trailingSlash: 'never', + build: { format: 'file', inlineStylesheets: 'auto' }, + integrations: [ + sitemap({ + filter: (page) => { + const p = new URL(page).pathname; + if (p === '/404' || p === '/events/archive') return false; + const id = p.match(/^\/events\/(.+)$/)?.[1]; + return !(id && past.has(id)); + }, + }), + ], + prefetch: false, +}); diff --git a/pof-cayman-website/deploy/rebuild-weekly.yml b/pof-cayman-website/deploy/rebuild-weekly.yml new file mode 100644 index 00000000..8dfc9c55 --- /dev/null +++ b/pof-cayman-website/deploy/rebuild-weekly.yml @@ -0,0 +1,16 @@ +# Weekly rebuild so past events archive even when nobody commits. +# Move this file to .github/workflows/rebuild-weekly.yml in the site's own +# GitHub repository, and add the Cloudflare Pages Deploy Hook URL as a +# repository secret named CF_PAGES_DEPLOY_HOOK +# (Cloudflare dashboard > Workers & Pages > the project > Settings > Builds > Deploy hooks). +name: Weekly rebuild +on: + schedule: + - cron: '0 10 * * 1' # Mondays 10:00 UTC, 5:00 am in Cayman + workflow_dispatch: +jobs: + rebuild: + runs-on: ubuntu-latest + steps: + - name: Trigger Cloudflare Pages build + run: curl -fsS -X POST "${{ secrets.CF_PAGES_DEPLOY_HOOK }}" diff --git a/pof-cayman-website/docs/DESIGN.md b/pof-cayman-website/docs/DESIGN.md new file mode 100644 index 00000000..35083426 --- /dev/null +++ b/pof-cayman-website/docs/DESIGN.md @@ -0,0 +1,45 @@ +# Visual system + +Direction: Youth Voice, Island Ground. Hand-painted protest energy over a restrained editorial base. + +## The three distinctive moments + +1. The homepage hero: deep ocean, the mission in the founders' words, and the painted sheet. +2. The slogan sheet, `SloganBlock`, once per page at most, on the single most important line. It is drawn from the banner series: white bedsheets, hand-painted capitals, torn edges. +3. The stat band: the first verified number set at 96px on ocean. Nothing else on the site is set that large. + +Everything else is quiet type on a twelve-column grid. + +## Palette + +| Token | Value | Use | +|---|---|---| +| ocean | #0B2E42 | Anchor. Dark sections, primary buttons on light | +| ocean-deep | #07202F | Footer | +| reef | #3ED2C3 | Accent fill on dark surfaces. Interactive only | +| reef-ink | #0C6E6B | Accent for link text and the Donate button on light surfaces (5.7:1 on paper) | +| reef-on-dark | #5EE0D2 | Link text on ocean (8.8:1) | +| sand | #F1EADC | Warm neutral background | +| mangrove | #2C4A3C | Secondary blocks only | +| ink / paper | #15202B / #FBF8F2 | Type. Never #000 or #FFF | +| sheet / paint | #F6F1E6 / #1B2A38 | The slogan device only | + +Rule: if the accent appears on anything that is not clickable, the site has lost its signal. + +These tones were chosen against Cayman references (open water off the West Bay wall, shallow water over sand at Seven Mile Beach, ironshore, the Central Mangrove Wetland). When POF supplies rights-cleared photographs, re-sample and confirm against them, then re-run `node scripts/contrast.mjs`. + +## Type + +Display: Bricolage Grotesque, self-hosted, weights 400 to 800. Body: Newsreader, self-hosted, optical sizes. Two families, no more. Scale 16 / 20 / 25 / 31 / 39 / 49 / 61 at a 1.25 ratio. Body line-height 1.6, headings 1.15, hero 1.1. Measure 68ch. + +## Space + +4 / 8 / 16 / 24 / 40 / 64 / 96 / 160. Nothing else. Section rhythm alternates 64 and 96 on desktop, 40 and 64 on mobile. + +## Layout + +Twelve columns. Left-weighted and right-weighted sections alternate down each page. Three items are always one dominant and two supporting. Radius is 2px on controls, 6px on panels, and nowhere else. + +## Motion + +Reveal on scroll only: 320ms, opacity and a 12px rise from scale 0.985, `cubic-bezier(0.23, 1, 0.32, 1)`, 50ms stagger. Everything is off under `prefers-reduced-motion: reduce`. diff --git a/pof-cayman-website/docs/HANDOVER.md b/pof-cayman-website/docs/HANDOVER.md new file mode 100644 index 00000000..7049ed38 --- /dev/null +++ b/pof-cayman-website/docs/HANDOVER.md @@ -0,0 +1,103 @@ +# Graduation handover checklist + +POF is run by students, and students graduate. Every year the people who know how the website, the domain and the accounts work leave school. If a login sits in one person's personal email, it leaves with them. This checklist exists so that nothing is lost at graduation. The rule underneath all of it: every account belongs to a role, not a person. + +## Accounts to make role-based + +Once email is set up on the organisation's domain, create these addresses and use them for every service: + +- [ ] `president@` on the organisation's domain +- [ ] `chapters@` on the organisation's domain +- [ ] `events@` on the organisation's domain +- [ ] `press@` on the organisation's domain + +Until domain email exists, the shared inbox `protectourfuturecayman@gmail.com` is the role address for everything. + +- [ ] No service is ever registered to a personal email. If one is found, move it to a role address before the holder graduates. + +## Shared password manager + +Use one shared password manager vault for the organisation. These logins go in it: + +- [ ] Domain registrar (where protectourfuturecayman.org is registered) +- [ ] Cloudflare +- [ ] GitHub organisation +- [ ] Formspree +- [ ] Mailchimp +- [ ] Fygaro / Sustainable Cayman contact details for donations +- [ ] Instagram (@protectourfuture.eco) +- [ ] Facebook (@protectourfuture.eco) +- [ ] Google account for the shared inbox +- [ ] Cloudflare Pages deploy hook URL (see README, "Weekly rebuild") + +Rules for the vault: + +- [ ] The custodian holds the master password. The President has a second copy. +- [ ] Two-factor recovery codes for every account are stored in the vault next to the login. +- [ ] Two-factor is turned on for every account that offers it, using the shared inbox or an authenticator app the custodian can recover. + +## Custodian + +- [ ] A faculty member or an alumnus is named custodian. +- [ ] The custodian is not a current student. +- [ ] The custodian has owner access to the GitHub organisation, the Cloudflare account, and the domain registrar. +- [ ] The custodian's name and how to reach them are recorded in the vault, not in this file. + +## Domain + +- [ ] `protectourfuturecayman.org` is registered to the organisation, not to a person. +- [ ] The renewal date is written in the vault and in the shared calendar. +- [ ] Auto-renew is on and the payment method belongs to the organisation. +- [ ] `pofcaribbean.org` and `protectourfuture-eco.com` are kept for at least two years so old links keep redirecting. +- [ ] Both retired domains are attached to the Cloudflare Pages project (see README, "Deploying on Cloudflare Pages"). + +## Each May or June, before graduation + +- [ ] Hold a dated handover meeting with the outgoing and incoming holder of each role. +- [ ] Transfer each role. Walk through what the role does using the README. +- [ ] Rotate every password in the vault. Update the vault. +- [ ] Update the `holder` and `school` fields in `src/content/team/*.md`. Clear the names of graduating students. +- [ ] Update the chapter directory in `src/content/chapters/*.md`: status, meeting time, leader name, current project. +- [ ] Review every campaign status and `statusNote` in `src/content/campaigns/*.md`. +- [ ] Remove graduating students from the GitHub organisation. +- [ ] Remove graduating students as admins on Instagram and Facebook. +- [ ] Confirm the custodian can still log in to GitHub, Cloudflare and the domain registrar. +- [ ] Test every form on the live site: contact, volunteer, start a chapter, newsletter. Confirm each one arrives in the shared inbox. +- [ ] Test the donate button on the support page. + +## Each September, for new leadership + +- [ ] Confirm the roster and add the new `holder` and `school` to each `src/content/team/*.md` file, with each student's permission. +- [ ] Update the chapter directory for the new term. +- [ ] Review campaign statuses with the new President. +- [ ] Add new leads to the GitHub organisation with write access. Do not give owner access to students. +- [ ] Add new leads as social admins. Remove anyone who has left. +- [ ] Confirm the custodian still has access. +- [ ] Test every form and the donate button on the live site. +- [ ] Check the weekly rebuild is still firing (Cloudflare Pages, Deployments tab). + +## Who to call + +| Question | Role to ask | +| --- | --- | +| The site is down or a build failed | Communications Lead, then the custodian | +| Domain renewal or DNS | Custodian | +| A login does not work | President (vault access), then the custodian | +| Adding or fixing an event | Events Lead | +| Chapter directory | Chapters Lead | +| Press, news items, social accounts | Communications Lead | +| Campaign status or the numbers on the site | President | +| Donations, Fygaro, Sustainable Cayman | President | +| Photo consent | Communications Lead | + +## Sign-off + +Complete this block at each handover meeting and keep a copy in the shared inbox. + +| | Name | Signature | Date | +| --- | --- | --- | --- | +| Outgoing role holder (role: ______________) | | | | +| Incoming role holder (role: ______________) | | | | +| Custodian | | | | + +Date of handover meeting: ______________ diff --git a/pof-cayman-website/package-lock.json b/pof-cayman-website/package-lock.json new file mode 100644 index 00000000..b963e37c --- /dev/null +++ b/pof-cayman-website/package-lock.json @@ -0,0 +1,4185 @@ +{ + "name": "pof-cayman-website", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pof-cayman-website", + "version": "1.0.0", + "dependencies": { + "@astrojs/sitemap": "^3.7.4", + "astro": "^7.2.10" + }, + "devDependencies": { + "sharp": "^0.35.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@astrojs/compiler-binding": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding/-/compiler-binding-0.4.0.tgz", + "integrity": "sha512-x2RjDUuWfwLNtc3mjAdSRInwqh/rqbLar9cm/5FOMbHvmYZB7yfKewzSclAxWjIZsypJDXv1lhaP2WG+P8TK3g==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@astrojs/compiler-binding-darwin-arm64": "0.4.0", + "@astrojs/compiler-binding-darwin-x64": "0.4.0", + "@astrojs/compiler-binding-linux-arm64-gnu": "0.4.0", + "@astrojs/compiler-binding-linux-arm64-musl": "0.4.0", + "@astrojs/compiler-binding-linux-x64-gnu": "0.4.0", + "@astrojs/compiler-binding-linux-x64-musl": "0.4.0", + "@astrojs/compiler-binding-wasm32-wasi": "0.4.0", + "@astrojs/compiler-binding-win32-arm64-msvc": "0.4.0", + "@astrojs/compiler-binding-win32-x64-msvc": "0.4.0" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-arm64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-arm64/-/compiler-binding-darwin-arm64-0.4.0.tgz", + "integrity": "sha512-ZVUwHundaQyFNjE6uoa0usaC0WOCitDCLS/4mdb4rOiJXwVUuKJBMxI5WMzXLWmamsXtK/Z//ifLXvV5Yeh4Hw==", + "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.4.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-x64/-/compiler-binding-darwin-x64-0.4.0.tgz", + "integrity": "sha512-FI6G8AY8u6fR1SI/QRR5yGMwtvZwP34CDmZpZ5HwJGa50UM1VISTLhqkhV4a476pmgd25X1Aur2dqw6hUnrlKA==", + "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.4.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-gnu/-/compiler-binding-linux-arm64-gnu-0.4.0.tgz", + "integrity": "sha512-lB9gLFJK7m82EnjaU8nlRBEfcwGNeHidW3sSjODTUjMNaoewVuUz9fwwdY5M4jiSXIqWLH3yl6TX8FTDKA74Sw==", + "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.4.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-musl/-/compiler-binding-linux-arm64-musl-0.4.0.tgz", + "integrity": "sha512-HPbvWqbxFxyaoQJhLxCaSjtYBx9KBo7JGVzEFZCmMl968a2PsSH0UfiODYgYPXofTOIsIH2aoCcrHXML0IA3ig==", + "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.4.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-gnu/-/compiler-binding-linux-x64-gnu-0.4.0.tgz", + "integrity": "sha512-tQKolMxoJ/+0AmLWm1PmJ/i+z3i10ZU1bNuVjEDulCf48azEMtUNjTZgHJ5MPtpYRNc7dlETr8QujUfduzoC7Q==", + "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.4.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-musl/-/compiler-binding-linux-x64-musl-0.4.0.tgz", + "integrity": "sha512-5v5YymudsxMHp3NBLCS8BUlu5CRqeLtWD9cKS/4nIhIEHCbpz9okmVV6I0HWqmBAPhWYcDa3vw/vltYPrOQCTA==", + "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.4.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-wasm32-wasi/-/compiler-binding-wasm32-wasi-0.4.0.tgz", + "integrity": "sha512-m/phuH3x3PREvv1OnkM44NoPh4MatUadix1fB1u5SvMLCyDTUZykDJbKnWf1cjnYmHdlB8HcjTjl6JrCqAIXcw==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.2.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-arm64-msvc": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-arm64-msvc/-/compiler-binding-win32-arm64-msvc-0.4.0.tgz", + "integrity": "sha512-B9zYf3okEY83kM8gydlpH2BHP00w4ifxPqlYlWrgTwuD6wnkrJDCwBlgy1q31cERjCJRXN1lrE2VmkLvFjv/6g==", + "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.4.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-x64-msvc/-/compiler-binding-win32-x64-msvc-0.4.0.tgz", + "integrity": "sha512-zB0Nrv0dGc0zZWPGDRmmETTPhDRqyZjAjk+gWMlVrJX5U89obpB3VUUE1ZiHxOCN5LQojeLK6O8L/dnoHolvNQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-rs": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-rs/-/compiler-rs-0.4.0.tgz", + "integrity": "sha512-koVikeon1kreEy+/JzLQRy3vzHHQVOjycs4degg4vFufKApZOwMZvSSAEztYNhmcQVfNVsVZZI4cEge3cexAbQ==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler-binding": "0.4.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/internal-helpers": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.11.0.tgz", + "integrity": "sha512-3rzxJ+xbo0+8YyqOzLziIN32wmsHdCjEVz2sGOpRxJ+Ben/KiLph4ItxBy1abEL+E8fkRzqjg0rfXmaHJGw9JA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "js-yaml": "^4.3.0", + "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.4.0", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-satteri/-/markdown-satteri-0.4.0.tgz", + "integrity": "sha512-wykOOW9KsUVcZweOpY/CeXpdKcCKZy6fQbdcteWFuI75+sQCiqxYM7VKsGa5b+aGl3cYQscFY37rsbbyal5MRw==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.11.0", + "@astrojs/prism": "4.0.2", + "github-slugger": "^2.0.0", + "satteri": "^0.10.3" + } + }, + "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/sitemap": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.7.4.tgz", + "integrity": "sha512-LbKNC24bdUWcQf/pThB6qLlSqHojxGjZDURIzFocY8rlWnAn2t74nnhnK6S5x0NHriHoAduLEpVjRykmeGiVvA==", + "license": "MIT", + "dependencies": { + "sitemap": "^9.0.0", + "zod": "^4.3.6" + } + }, + "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.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "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.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-arm64/-/satteri-darwin-arm64-0.10.5.tgz", + "integrity": "sha512-27KTVl4TJkVahMy/ohyA7qd4938G5UNneFUz/PsScYfpIhj0IVAS23mpcJXdPF44sa6nva198lmV/cKIb2YPyA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-darwin-x64": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-x64/-/satteri-darwin-x64-0.10.5.tgz", + "integrity": "sha512-IjnLe3nKspq6qaeqGgjT7MT8VrTV74yWRlaag7ZdNsI8TDAYZ0iPxMCo+9KQZHUk5EyVB+reBI/PFWL5KuFw9Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-gnu": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-gnu/-/satteri-linux-arm64-gnu-0.10.5.tgz", + "integrity": "sha512-glkYXZCJywjP13v67eAyAMSJdF+ncvEbYvgi/wOtffL9tQ27lr/zsyzUfgs+ovjJ9d8JNQKiXeiArJcX8PJL9w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-musl": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-musl/-/satteri-linux-arm64-musl-0.10.5.tgz", + "integrity": "sha512-yWdgG1g17Nh2QyGVlFUxGRa3FEFwiMcpZEyMNWkbM3deC94cmVc+/i9OuyFpdKuWo3GkgoCtYVOoxk1uCnCZIA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-gnu": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-gnu/-/satteri-linux-x64-gnu-0.10.5.tgz", + "integrity": "sha512-FVaLoPT1fBgGl0J+AYebyyXJYBachGl8Oyyrf1lye4RTqCB4S0Gwkj1uM9RJyThUOvx5VUmAT1CnNh1SFHA+kw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-musl": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-musl/-/satteri-linux-x64-musl-0.10.5.tgz", + "integrity": "sha512-EHpVAx2bqW3GINHTKkljtxVfQmVDGWIuwOYOP5YghTj+0PkBa2o8oKPRtQ9Kbsr1Fye8jtUcDjhwj2jMNugZKg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-wasm32-wasi": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-wasm32-wasi/-/satteri-wasm32-wasi-0.10.5.tgz", + "integrity": "sha512-ypz8c/Zmipxp4IoeDa228Gstv6TLzVmNs3yC6wKCoNSOjx1iwpgzu87Y3hTkXFdwChVGU85qeUDuOIarGUZQLw==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.2.3" + }, + "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-wasm32-wasi/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/@bruits/satteri-win32-arm64-msvc": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-arm64-msvc/-/satteri-win32-arm64-msvc-0.10.5.tgz", + "integrity": "sha512-siTV88nb0LRqNpkL2gXboqCwVdq95sLtzMHS1/3eONV2gLbB3NAK46wmSMvCO/yquBvI2lvaFIfd8P12ecsxBw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@bruits/satteri-win32-x64-msvc": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-x64-msvc/-/satteri-win32-x64-msvc-0.10.5.tgz", + "integrity": "sha512-C3IfPvfvMXmlzBxaMPKFS1XiuV9pu2mC7YqkPk7PSvTgPZ8gbdASIpHpztDLvTTQjqZ0z1Ol8tK5X+V6XXC0wQ==", + "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.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz", + "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", + "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "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==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.4.tgz", + "integrity": "sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==", + "cpu": [ + "arm64" + ], + "dev": true, + "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.3" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.4.tgz", + "integrity": "sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==", + "cpu": [ + "x64" + ], + "dev": true, + "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.3" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.4.tgz", + "integrity": "sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.4" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.3.tgz", + "integrity": "sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==", + "cpu": [ + "arm64" + ], + "dev": true, + "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.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.3.tgz", + "integrity": "sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==", + "cpu": [ + "x64" + ], + "dev": true, + "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.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.3.tgz", + "integrity": "sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==", + "cpu": [ + "arm" + ], + "dev": true, + "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.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.3.tgz", + "integrity": "sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "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.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.3.tgz", + "integrity": "sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "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.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.3.tgz", + "integrity": "sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "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.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.3.tgz", + "integrity": "sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "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.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.3.tgz", + "integrity": "sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==", + "cpu": [ + "x64" + ], + "dev": true, + "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.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.3.tgz", + "integrity": "sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==", + "cpu": [ + "arm64" + ], + "dev": true, + "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.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.3.tgz", + "integrity": "sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==", + "cpu": [ + "x64" + ], + "dev": true, + "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.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.4.tgz", + "integrity": "sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==", + "cpu": [ + "arm" + ], + "dev": true, + "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.3" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.4.tgz", + "integrity": "sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "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.3" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.4.tgz", + "integrity": "sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "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.3" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.4.tgz", + "integrity": "sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "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.3" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.4.tgz", + "integrity": "sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "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.3" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.4.tgz", + "integrity": "sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==", + "cpu": [ + "x64" + ], + "dev": true, + "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.3" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.4.tgz", + "integrity": "sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==", + "cpu": [ + "arm64" + ], + "dev": true, + "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.3" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.4.tgz", + "integrity": "sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==", + "cpu": [ + "x64" + ], + "dev": true, + "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.3" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz", + "integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==", + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.4.tgz", + "integrity": "sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.4" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.4.tgz", + "integrity": "sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "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.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.4.tgz", + "integrity": "sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==", + "cpu": [ + "ia32" + ], + "dev": true, + "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.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.4.tgz", + "integrity": "sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==", + "cpu": [ + "x64" + ], + "dev": true, + "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/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "license": "MIT" + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.3.tgz", + "integrity": "sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" + } + }, + "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.148.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.148.0.tgz", + "integrity": "sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.7.tgz", + "integrity": "sha512-EypzgnYCwyVY4NDHKzGmNJT5b+XaQEBniHxsMdeIQLB/tcCzZnhqrzHpZFbX9iaxx+5RiB8caATBtfvZP7zVxQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.7.tgz", + "integrity": "sha512-l17HE9EweWaqJZhuUuNBN/FzM62xw+DECVnJyvMsxn8vJFAGLy5QfLDoYAcronkAN8VxKZHezDpulHDPx95vFw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.7.tgz", + "integrity": "sha512-8ED8ELFvHXc6OCETIn4gXObPiaR6bckM/ipXtbzlPVDRMBfEGjCKgO90F9YtfdpDatVx/ZQw7aZ1vUMf/+T3Mw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.7.tgz", + "integrity": "sha512-/WPripjtiAIZ2tWY7ddijORT0Ujg87wxWW/qcoFVCKAWVDPhtY0xr7Dj0M3GyNGz60jGwTElhro/mkF9dT7dDQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.7.tgz", + "integrity": "sha512-14DI4NcqpvbICxSnGLx3PmtDaWqRP/KGSGb6C+JLLVPeZRl6dKdHba3pGsqT3vpdTqhEYIPG0MMQ8c0xYqoJxA==", + "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.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.7.tgz", + "integrity": "sha512-bxrWIRvHWQvbJwi+VIie/kDJmQxcNE6xxWwZdqF/ExVAigtHkv54WTLQPb+QsZdnFy18fg7JPfWGL0RH6vwIlQ==", + "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.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.7.tgz", + "integrity": "sha512-toOY2BChBZyuxU7OYX6Tn389di4IzAqPTycVcci0O7FSfBqzRB3RZn+K5Is6ANf4tmgRd/K1yZTsNTXbkXsnLg==", + "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.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.7.tgz", + "integrity": "sha512-lAIXTH/aiLRLxsTgQvfhjo4K1ydWIp00+V0voOr9beb/9ZmkUFrSIb03dXNFRgMNvkE6oGsF10ioQ6UsI+vS5Q==", + "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.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.7.tgz", + "integrity": "sha512-kdnwS28Pkenp/mZMRwjXXXwxQ7pIsm+bF919LUK93BOyhcLsrVKdP2p9fxpiPNPAbNuch8ypQt0pm2P2LYCAGg==", + "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.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.7.tgz", + "integrity": "sha512-516OdsyLdr5E65paF3yBF55t8mfm9+gmtCsK3xI7XKXIT7EfRlHhxL8K/NR6Hu8BWSgF5+1w74lTL0+nxcc8Qw==", + "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.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.7.tgz", + "integrity": "sha512-r8/z8n7GFaYRln3xmP1Cxy0HH/HLM0uBUPkEuSVEfKGDA89M0FsZRZJRSwe/tJjRx+fpH/gjorfhB8tmEbSFLA==", + "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.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.7.tgz", + "integrity": "sha512-pAsE8iiDxUg1xBqdhrTfg45AVDVpirjz00sblEYClGNNcMnDb+e8beQgqIAw6LvauX/APvgxUnwrgun/YYGBhw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.7.tgz", + "integrity": "sha512-lTcIYmmnQQA8Or/2DatS6oSqcdLHvendjS+zLu+FwgToynWMRSmQdpM65fTANJgIS4mjbMOo5KT2lnT9SAb96w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.7.tgz", + "integrity": "sha512-e3Gu3WxbNk/UqQhxqU7YIYO+9ZBvWNz3U+h/qRFosscMFzdRPbXYSaSWgSnklv2fz1TgzBTcti2z35c/7irsHw==", + "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.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.7.tgz", + "integrity": "sha512-W/jg5qoRSqjsEv0+dZi4e687mcHqmVuU0P4fK6qS/xjetW2Gmc1W8j//z5nAeNcC8Ttm0hV46IjcYeuVwYhuiw==", + "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/@shikijs/core": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.4.3.tgz", + "integrity": "sha512-QCR4q2ZO/ILJEuwiBMel4wdcTDb1JGwfjKTxPDF6x8ixOaluPrVqIn06C99AcRPhmYlBR56d/Fb+GN58GzExpg==", + "license": "MIT", + "dependencies": { + "@shikijs/primitive": "4.4.3", + "@shikijs/types": "4.4.3", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5", + "hast-util-to-html": "^9.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.4.3.tgz", + "integrity": "sha512-FbOjFJp9VLdo1Wevs10BBtVxiTWwNLqZh5Gkhjgda/ioL15YOgeSl9n+6XMa3qRlPQzfhFNe641SrynFHYG0nQ==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.3", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.6" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.4.3.tgz", + "integrity": "sha512-EcOQkxdxGQrc1Row/cC2c96/v1dbZqGnEVu1qTuT/MJmp6+cXCvQussowVmCv5Tqr3KuY3c7IbM6HTW3LJ1k9w==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.3", + "@shikijs/vscode-textmate": "^10.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/langs": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.4.3.tgz", + "integrity": "sha512-ePic0yfAJGOF83D5wBHK/00EjK65oahBYxFk5epgq33WRv7X9UuxLEV8PtR0szC0z8dl7INIpIodB99JRFlR+A==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/primitive": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.4.3.tgz", + "integrity": "sha512-m0wBeLDQDeIxRdUmrCPdQqfuUamDwRL5isCfYbguKD6NiaKpVbsv+3J81DyIKgNW5h4WAIIr8T4EkgQrBBxvaQ==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.3", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/themes": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.4.3.tgz", + "integrity": "sha512-w8UHjeUnIR965KMWJHUPXOc2mNJUnK3vpVLYLvw5IYU2mnTTJ89E24OrJDBNiJDQ0qzb0tc4l7mrIXx5cFeIyw==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/types": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.4.3.tgz", + "integrity": "sha512-UEJxmRR++MAGR6hugn0vgVS2W/6lWAts84FFSrnlH9sP0LNol7E5+NQ792pH8liWUhyMyjhTgSUH3k7iD7tc5g==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "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/@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/node": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/sax": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", + "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "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.4.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz", + "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==", + "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/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "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.2.10", + "resolved": "https://registry.npmjs.org/astro/-/astro-7.2.10.tgz", + "integrity": "sha512-uPtD+nK6kQXugyq4kiMPwj9OI3Sae6HSerA5X+fuv2CPaDwxmokFPPFlGRKIAXH0QAKu+zot2q6yrLG61wFmqg==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler-rs": "^0.4.0", + "@astrojs/internal-helpers": "0.11.0", + "@astrojs/markdown-satteri": "0.4.0", + "@astrojs/telemetry": "3.3.3", + "@capsizecss/unpack": "^4.0.0", + "@clack/prompts": "^1.1.0", + "@oslojs/encoding": "^1.1.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": "^9.0.0", + "dset": "^3.1.4", + "es-module-lexer": "^2.0.0", + "esbuild": "^0.28.0", + "find-proc": "0.1.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.3.0", + "jsonc-parser": "^3.3.1", + "magic-string": "^1.0.0", + "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.5", + "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.35.4" + }, + "peerDependencies": { + "@astrojs/markdown-remark": "^7.3.0" + }, + "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": "6.0.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-6.0.0.tgz", + "integrity": "sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^7.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "nth-check": "^2.1.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": "7.0.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-7.0.0.tgz", + "integrity": "sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==", + "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.9.2", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.2.tgz", + "integrity": "sha512-po4PAY5c53tw5XMocSnf8A/5OHhbbUftpr93aEN6BBoAdntUmK7vu7wOATqvt7cXO7m1Cl4gMVn6p7n6n4mj0w==", + "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": "9.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", + "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", + "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/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/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/es-module-lexer": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "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/find-proc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/find-proc/-/find-proc-0.1.0.tgz", + "integrity": "sha512-OaOpEYv2PiQ7SQ5LIrl+deA1XaWcxEjnpM6VuWXTUvn+teIXxeFTLDmu18/zDQpFmHN4o3oDBX+BT0AGwEhemg==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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/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-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/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/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/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "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": "1.2.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.2.3.tgz", + "integrity": "sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.4.tgz", + "integrity": "sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "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.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "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.5", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.5.tgz", + "integrity": "sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==", + "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.12", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.12.tgz", + "integrity": "sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==", + "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.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.2.tgz", + "integrity": "sha512-Ll0w3fU24vYpXoZmjjZIee6bJQDgG0oAyo1PdmFYI8UDwJJddaHAypxIH9avUu+t+lSsAwKVsb1jDCMIIChliw==", + "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/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.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "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.17", + "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.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "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.2.7", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.7.tgz", + "integrity": "sha512-g0EtLvBjTUB7jhyV0S/TCup3v/XSVl45vUIGbOGU4QPiyjTenCe4mKuFvW9fEgYmS2Fo42AUssRmNuMziXdrig==", + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.148.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.7", + "@rolldown/binding-android-arm64": "1.2.7", + "@rolldown/binding-darwin-arm64": "1.2.7", + "@rolldown/binding-darwin-x64": "1.2.7", + "@rolldown/binding-freebsd-x64": "1.2.7", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.7", + "@rolldown/binding-linux-arm64-gnu": "1.2.7", + "@rolldown/binding-linux-arm64-musl": "1.2.7", + "@rolldown/binding-linux-ppc64-gnu": "1.2.7", + "@rolldown/binding-linux-s390x-gnu": "1.2.7", + "@rolldown/binding-linux-x64-gnu": "1.2.7", + "@rolldown/binding-linux-x64-musl": "1.2.7", + "@rolldown/binding-openharmony-arm64": "1.2.7", + "@rolldown/binding-win32-arm64-msvc": "1.2.7", + "@rolldown/binding-win32-x64-msvc": "1.2.7" + } + }, + "node_modules/satteri": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/satteri/-/satteri-0.10.5.tgz", + "integrity": "sha512-Ao1LKpAEa9Wdg0otgbVKViZHEq9ebdXe4DMrp3s9vQAU0HNIuHnFEuMuOcm0ZIXyV0Yzxj91NvhLpvXZJO/5ZQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.5", + "@types/hast": "^3.0.5", + "@types/mdast": "^4.0.4", + "@types/unist": "^3.0.3" + }, + "optionalDependencies": { + "@bruits/satteri-darwin-arm64": "0.10.5", + "@bruits/satteri-darwin-x64": "0.10.5", + "@bruits/satteri-linux-arm64-gnu": "0.10.5", + "@bruits/satteri-linux-arm64-musl": "0.10.5", + "@bruits/satteri-linux-x64-gnu": "0.10.5", + "@bruits/satteri-linux-x64-musl": "0.10.5", + "@bruits/satteri-wasm32-wasi": "0.10.5", + "@bruits/satteri-win32-arm64-msvc": "0.10.5", + "@bruits/satteri-win32-x64-msvc": "0.10.5" + } + }, + "node_modules/sax": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", + "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.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.4.tgz", + "integrity": "sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==", + "devOptional": true, + "license": "Apache-2.0", + "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.4", + "@img/sharp-darwin-x64": "0.35.4", + "@img/sharp-freebsd-wasm32": "0.35.4", + "@img/sharp-libvips-darwin-arm64": "1.3.3", + "@img/sharp-libvips-darwin-x64": "1.3.3", + "@img/sharp-libvips-linux-arm": "1.3.3", + "@img/sharp-libvips-linux-arm64": "1.3.3", + "@img/sharp-libvips-linux-ppc64": "1.3.3", + "@img/sharp-libvips-linux-riscv64": "1.3.3", + "@img/sharp-libvips-linux-s390x": "1.3.3", + "@img/sharp-libvips-linux-x64": "1.3.3", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.3", + "@img/sharp-libvips-linuxmusl-x64": "1.3.3", + "@img/sharp-linux-arm": "0.35.4", + "@img/sharp-linux-arm64": "0.35.4", + "@img/sharp-linux-ppc64": "0.35.4", + "@img/sharp-linux-riscv64": "0.35.4", + "@img/sharp-linux-s390x": "0.35.4", + "@img/sharp-linux-x64": "0.35.4", + "@img/sharp-linuxmusl-arm64": "0.35.4", + "@img/sharp-linuxmusl-x64": "0.35.4", + "@img/sharp-webcontainers-wasm32": "0.35.4", + "@img/sharp-win32-arm64": "0.35.4", + "@img/sharp-win32-ia32": "0.35.4", + "@img/sharp-win32-x64": "0.35.4" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/shiki": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.4.3.tgz", + "integrity": "sha512-Mb/GvXPHBAXdgGIcnfU5L3ldpn1XcxrGkPHwqgRx17/I2XRfqlFKk2vGkHWINn1kdXvzJZeuO3is6I9KLPFm0g==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "4.4.3", + "@shikijs/engine-javascript": "4.4.3", + "@shikijs/engine-oniguruma": "4.4.3", + "@shikijs/langs": "4.4.3", + "@shikijs/themes": "4.4.3", + "@shikijs/types": "4.4.3", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "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/sitemap": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-9.0.1.tgz", + "integrity": "sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==", + "license": "MIT", + "dependencies": { + "@types/node": "^24.9.2", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.4.1" + }, + "bin": { + "sitemap": "dist/esm/cli.js" + }, + "engines": { + "node": ">=20.19.5", + "npm": ">=10.8.2" + } + }, + "node_modules/smol-toml": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.8.0.tgz", + "integrity": "sha512-kCZr2V3ch9i00x8zXRhjUNVcjG9ijES5dDudkXvUVCT5QlJNQWElSJdZqyPemffHoLNUYwOcou0Fy+ojN0uHSQ==", + "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.1.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.1.0.tgz", + "integrity": "sha512-bkxnTg1kSU0guhIBmibA6UUhrQmPVA1XsQLN+ylCd+UWzbnLkySOcXpyk1mrl05f+pcaCx2eHb+sp6BgMZWX+Q==", + "license": "MIT", + "dependencies": { + "commander": "^11.1.0", + "css-select": "^6.0.0", + "css-tree": "^3.0.1", + "css-what": "^7.0.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "1.6.1" + }, + "bin": { + "svgo": "bin/svgo.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "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.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "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", + "optional": true + }, + "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/undici": { + "version": "8.10.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.10.1.tgz", + "integrity": "sha512-YQ3WlbqjYMmNpdvDH64jAgLjxuAR9+649calDWhbshYaeQGO2bR4nI94ORJmwI3J9YhoKQnpyGOK+0zlWS5N5Q==", + "license": "MIT", + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "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.5", + "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.5.tgz", + "integrity": "sha512-ULe/Cs+ZIsq+dcFofNkhqielCrUJnb5mr+Yc4EBM2VlL+6OZR6+cjtI2mT1bJvRBrVncqHAbLURxmPLcCXzWMg==", + "license": "MIT", + "dependencies": { + "css-tree": "^3.1.0", + "ohash": "^2.0.11", + "undici": "^8.0.0" + } + }, + "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-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.2.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", + "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==", + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.26", + "rolldown": "~1.2.4", + "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.4.0 || ^0.5.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/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.5.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.5.4.tgz", + "integrity": "sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==", + "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/pof-cayman-website/package.json b/pof-cayman-website/package.json new file mode 100644 index 00000000..9d11cb69 --- /dev/null +++ b/pof-cayman-website/package.json @@ -0,0 +1,26 @@ +{ + "name": "pof-cayman-website", + "version": "1.0.0", + "private": true, + "type": "module", + "description": "Website for Protect Our Future Cayman. Built with Astro, deployed to Cloudflare Pages.", + "scripts": { + "dev": "astro dev", + "build": "astro build", + "preview": "astro preview", + "check": "astro check", + "test": "node --test tests/*.test.mjs", + "verify": "node scripts/verify-build.mjs", + "og": "node scripts/generate-og.mjs" + }, + "dependencies": { + "@astrojs/sitemap": "^3.7.4", + "astro": "^7.2.10" + }, + "devDependencies": { + "sharp": "^0.35.4" + }, + "engines": { + "node": ">=20" + } +} diff --git a/pof-cayman-website/public/_headers b/pof-cayman-website/public/_headers new file mode 100644 index 00000000..31ac837a --- /dev/null +++ b/pof-cayman-website/public/_headers @@ -0,0 +1,12 @@ +/* + X-Content-Type-Options: nosniff + X-Frame-Options: DENY + Referrer-Policy: strict-origin-when-cross-origin + Permissions-Policy: camera=(), microphone=(), geolocation=() + Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://static.cloudflareinsights.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self' https://formspree.io https://cloudflareinsights.com; form-action 'self' https://formspree.io https://*.list-manage.com; frame-ancestors 'none'; base-uri 'self' +/fonts/* + Cache-Control: public, max-age=31536000, immutable +/_astro/* + Cache-Control: public, max-age=31536000, immutable +/images/* + Cache-Control: public, max-age=2592000 diff --git a/pof-cayman-website/public/_redirects b/pof-cayman-website/public/_redirects new file mode 100644 index 00000000..e0e2f1ef --- /dev/null +++ b/pof-cayman-website/public/_redirects @@ -0,0 +1,27 @@ +# Cloudflare Pages redirects. Both retired domains 301 to the canonical +# domain, preserving the path. The site itself is served at +# protectourfuturecayman.org (see the custom-domain notes in README.md). +https://pofcaribbean.org/* https://protectourfuturecayman.org/:splat 301 +https://www.pofcaribbean.org/* https://protectourfuturecayman.org/:splat 301 +https://protectourfuture-eco.com/* https://protectourfuturecayman.org/:splat 301 +https://www.protectourfuture-eco.com/* https://protectourfuturecayman.org/:splat 301 +https://www.protectourfuturecayman.org/* https://protectourfuturecayman.org/:splat 301 + +# Old Squarespace and Wix paths that still receive traffic from press links. +/home / 301 +/about-us /about 301 +/about-1 /about 301 +/our-work-1 /our-work 301 +/campaigns /our-work 301 +/get-involved-1 /get-involved 301 +/volunteer /get-involved 301 +/join /get-involved 301 +/donate /support 301 +/events-1 /events 301 +/upcoming-events /events 301 +/press-1 /press 301 +/media /press 301 +/blog /news 301 +/news-1 /news 301 +/contact-us /contact 301 +/generation-green-1 /generation-green 301 diff --git a/pof-cayman-website/public/apple-touch-icon.png b/pof-cayman-website/public/apple-touch-icon.png new file mode 100644 index 00000000..e9767c2a Binary files /dev/null and b/pof-cayman-website/public/apple-touch-icon.png differ diff --git a/pof-cayman-website/public/favicon.svg b/pof-cayman-website/public/favicon.svg new file mode 100644 index 00000000..8ea1910a --- /dev/null +++ b/pof-cayman-website/public/favicon.svg @@ -0,0 +1 @@ + diff --git a/pof-cayman-website/public/fonts/BricolageGrotesque.woff2 b/pof-cayman-website/public/fonts/BricolageGrotesque.woff2 new file mode 100644 index 00000000..fcc4eb1e Binary files /dev/null and b/pof-cayman-website/public/fonts/BricolageGrotesque.woff2 differ diff --git a/pof-cayman-website/public/fonts/Newsreader-Italic.woff2 b/pof-cayman-website/public/fonts/Newsreader-Italic.woff2 new file mode 100644 index 00000000..d8b3029d Binary files /dev/null and b/pof-cayman-website/public/fonts/Newsreader-Italic.woff2 differ diff --git a/pof-cayman-website/public/fonts/Newsreader.woff2 b/pof-cayman-website/public/fonts/Newsreader.woff2 new file mode 100644 index 00000000..c6e5b0ec Binary files /dev/null and b/pof-cayman-website/public/fonts/Newsreader.woff2 differ diff --git a/pof-cayman-website/public/og/cruise-berthing-legacy.png b/pof-cayman-website/public/og/cruise-berthing-legacy.png new file mode 100644 index 00000000..3016c542 Binary files /dev/null and b/pof-cayman-website/public/og/cruise-berthing-legacy.png differ diff --git a/pof-cayman-website/public/og/default.png b/pof-cayman-website/public/og/default.png new file mode 100644 index 00000000..5a51d2ba Binary files /dev/null and b/pof-cayman-website/public/og/default.png differ diff --git a/pof-cayman-website/public/og/heritage-homes.png b/pof-cayman-website/public/og/heritage-homes.png new file mode 100644 index 00000000..494ec0fc Binary files /dev/null and b/pof-cayman-website/public/og/heritage-homes.png differ diff --git a/pof-cayman-website/public/og/managed-retreat-seven-mile-beach.png b/pof-cayman-website/public/og/managed-retreat-seven-mile-beach.png new file mode 100644 index 00000000..75a16c09 Binary files /dev/null and b/pof-cayman-website/public/og/managed-retreat-seven-mile-beach.png differ diff --git a/pof-cayman-website/public/og/mangroves-and-wetlands.png b/pof-cayman-website/public/og/mangroves-and-wetlands.png new file mode 100644 index 00000000..b2e3cd4c Binary files /dev/null and b/pof-cayman-website/public/og/mangroves-and-wetlands.png differ diff --git a/pof-cayman-website/public/og/reef-safe-sunscreen.png b/pof-cayman-website/public/og/reef-safe-sunscreen.png new file mode 100644 index 00000000..cf0b402f Binary files /dev/null and b/pof-cayman-website/public/og/reef-safe-sunscreen.png differ diff --git a/pof-cayman-website/public/og/single-use-plastics.png b/pof-cayman-website/public/og/single-use-plastics.png new file mode 100644 index 00000000..81294622 Binary files /dev/null and b/pof-cayman-website/public/og/single-use-plastics.png differ diff --git a/pof-cayman-website/public/og/youth-climate-march.png b/pof-cayman-website/public/og/youth-climate-march.png new file mode 100644 index 00000000..551fd3a3 Binary files /dev/null and b/pof-cayman-website/public/og/youth-climate-march.png differ diff --git a/pof-cayman-website/public/press/pof-wordmark-ocean.png b/pof-cayman-website/public/press/pof-wordmark-ocean.png new file mode 100644 index 00000000..18f1ff6d Binary files /dev/null and b/pof-cayman-website/public/press/pof-wordmark-ocean.png differ diff --git a/pof-cayman-website/public/press/pof-wordmark-ocean.svg b/pof-cayman-website/public/press/pof-wordmark-ocean.svg new file mode 100644 index 00000000..934933c0 --- /dev/null +++ b/pof-cayman-website/public/press/pof-wordmark-ocean.svg @@ -0,0 +1,6 @@ + +Protect Our Future + + + + \ No newline at end of file diff --git a/pof-cayman-website/public/press/pof-wordmark-paper.png b/pof-cayman-website/public/press/pof-wordmark-paper.png new file mode 100644 index 00000000..865965fd Binary files /dev/null and b/pof-cayman-website/public/press/pof-wordmark-paper.png differ diff --git a/pof-cayman-website/public/press/pof-wordmark-paper.svg b/pof-cayman-website/public/press/pof-wordmark-paper.svg new file mode 100644 index 00000000..afc8f00f --- /dev/null +++ b/pof-cayman-website/public/press/pof-wordmark-paper.svg @@ -0,0 +1,6 @@ + +Protect Our Future + + + + \ No newline at end of file diff --git a/pof-cayman-website/public/robots.txt b/pof-cayman-website/public/robots.txt new file mode 100644 index 00000000..3af2b0b0 --- /dev/null +++ b/pof-cayman-website/public/robots.txt @@ -0,0 +1,5 @@ +User-agent: * +Allow: / +Disallow: /404 + +Sitemap: https://protectourfuturecayman.org/sitemap-index.xml diff --git a/pof-cayman-website/scripts/contrast.mjs b/pof-cayman-website/scripts/contrast.mjs new file mode 100644 index 00000000..a476815d --- /dev/null +++ b/pof-cayman-website/scripts/contrast.mjs @@ -0,0 +1,24 @@ +/* Checks every text-on-surface pair in tokens.css. Run: node scripts/contrast.mjs */ +import fs from 'node:fs'; +const css = fs.readFileSync(new URL('../src/styles/tokens.css', import.meta.url), 'utf8'); +const P = Object.fromEntries([...css.matchAll(/--c-([\w-]+):\s*(#[0-9A-Fa-f]{6})/g)].map((m) => [m[1], m[2]])); +const lin = (c) => (c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4); +const L = (h) => [1, 3, 5].map((i) => lin(parseInt(h.slice(i, i + 2), 16) / 255)).reduce((a, v, i) => a + v * [0.2126, 0.7152, 0.0722][i], 0); +const ratio = (a, b) => (Math.max(L(a), L(b)) + 0.05) / (Math.min(L(a), L(b)) + 0.05); +const pairs = [ + ['ink', 'paper', 4.5], ['ink', 'sand', 4.5], ['ink-muted', 'paper', 4.5], ['ink-muted', 'sand', 4.5], + ['paper', 'ocean', 4.5], ['paper-muted', 'ocean', 4.5], ['paper', 'ocean-deep', 4.5], ['paper-muted', 'ocean-deep', 4.5], + ['paper', 'mangrove', 4.5], ['paper-muted', 'mangrove', 4.5], + ['reef-ink', 'paper', 4.5], ['reef-ink', 'sand', 4.5], ['reef-on-dark', 'ocean', 4.5], ['reef-on-dark', 'ocean-deep', 4.5], ['reef-on-dark', 'mangrove', 4.5], + ['ink', 'reef', 4.5], ['paper', 'reef-ink', 4.5], ['paint', 'sheet', 4.5], ['error', 'paper', 4.5], ['error', 'error-bg', 4.5], + ['reef', 'ocean', 3], ['reef-ink', 'paper', 3], ['ocean', 'sand', 3], ['ocean', 'paper', 3], ['ink-muted', 'paper', 3], +]; +let bad = 0; +for (const [a, b, min] of pairs) { + const r = ratio(P[a], P[b]); + const ok = r >= min; + if (!ok) bad++; + console.log(`${ok ? 'ok ' : 'FAIL'} ${a.padEnd(13)} on ${b.padEnd(11)} ${r.toFixed(2)} (min ${min})`); +} +if (bad) { console.log(`${bad} pair(s) fail`); process.exit(1); } +console.log('All pairs pass.'); diff --git a/pof-cayman-website/scripts/generate-og.mjs b/pof-cayman-website/scripts/generate-og.mjs new file mode 100644 index 00000000..92d17afa --- /dev/null +++ b/pof-cayman-website/scripts/generate-og.mjs @@ -0,0 +1,44 @@ +/* + Regenerates the Open Graph share images in public/og from scripts/og-template.html. + Optional: the PNGs are committed, so only run this when a title changes. + Needs `npm install --no-save playwright-core` and a local Chrome or Chromium. + Run: node scripts/generate-og.mjs +*/ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); +const tpl = `file://${root}/scripts/og-template.html`; +const images = [ + ['default', 'Keep Cayman for our grandchildren', 'The largest youth-led environmental advocacy group in the Cayman Islands. Founded by students in 2018.'], + ['cruise-berthing-legacy', 'Cruise berthing: 63.66% said no', 'Referendum, 30 April 2025. POF campaigned on it since 2018.'], + ['mangroves-and-wetlands', 'Mangroves secure our future', 'POF argues for mangrove protection every time the law is reopened.'], + ['single-use-plastics', 'Our future is not single-use', 'Two tons of plastic removed in one day. POF wants less of it arriving.'], + ['reef-safe-sunscreen', 'Reef-safe sunscreen', 'Choose sunscreen that does not harm coral. Ask your school chapter to take it on.'], + ['heritage-homes', 'Protecting our future is protecting our past', "Cayman's traditional homes belong in the same conversation as its reefs."], + ['managed-retreat-seven-mile-beach', 'Managed retreat for Seven Mile Beach', 'Plan where the beach moves now, instead of paying for it later.'], + ['youth-climate-march', 'Youth Climate March', 'Once a year, students from across Grand Cayman march for the climate.'], +]; + +let chromium; +try { ({ chromium } = await import('playwright-core')); } +catch { console.error('Run: npm install --no-save playwright-core'); process.exit(1); } + +const candidates = [process.env.CHROME_PATH, '/opt/pw-browsers/chromium', '/usr/bin/chromium', '/usr/bin/google-chrome', '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'].filter(Boolean); +const executablePath = candidates.find((p) => fs.existsSync(p)); +const browser = await chromium.launch(executablePath ? { executablePath } : { channel: 'chrome' }); +const page = await browser.newPage({ viewport: { width: 1200, height: 630 } }); +fs.mkdirSync(`${root}/public/og`, { recursive: true }); +for (const [name, title, sub] of images) { + await page.goto(tpl); + await page.evaluate(({ title, sub }) => { + document.getElementById('title').textContent = title; + document.getElementById('sub').textContent = sub; + document.getElementById('sheet').classList.toggle('long', title.length > 30); + }, { title, sub }); + await page.evaluate(() => document.fonts.ready); + await page.screenshot({ path: `${root}/public/og/${name}.png` }); + console.log(`public/og/${name}.png`); +} +await browser.close(); diff --git a/pof-cayman-website/scripts/images.mjs b/pof-cayman-website/scripts/images.mjs new file mode 100644 index 00000000..0ddb0dc9 --- /dev/null +++ b/pof-cayman-website/scripts/images.mjs @@ -0,0 +1,39 @@ +/* + Image pipeline. Put rights-cleared originals in ./photos (JPEG or PNG, any size). + Run: node scripts/images.mjs + Output: public/images/-{400,800,1600}.{avif,webp,jpg}, one grade applied to all. + Then reference the base name (without size or extension) in content, e.g. image: banner-march + and write real alt text in imageAlt. Do not ship any image without consent on file + for every identifiable person, and parental consent for anyone under 18. +*/ +import fs from 'node:fs'; +import path from 'node:path'; +import sharp from 'sharp'; + +const IN = path.resolve('photos'); +const OUT = path.resolve('public/images'); +const SIZES = [400, 800, 1600]; +if (!fs.existsSync(IN)) { console.log('No ./photos folder. Create it and add originals.'); process.exit(0); } +fs.mkdirSync(OUT, { recursive: true }); + +// One grading preset so mixed contributors look like one shoot: +// slight warmth, lifted shadows, restrained saturation. +const grade = (img) => img + .modulate({ saturation: 0.92, brightness: 1.02 }) + .linear(0.96, 6) + .tint({ r: 255, g: 250, b: 242 }) + .sharpen({ sigma: 0.6 }); + +for (const file of fs.readdirSync(IN)) { + if (!/\.(jpe?g|png|tiff?)$/i.test(file)) continue; + const name = file.replace(/\.[^.]+$/, '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''); + const src = path.join(IN, file); + for (const w of SIZES) { + const base = grade(sharp(src).rotate().resize({ width: w, withoutEnlargement: true })); + await base.clone().avif({ quality: 55 }).toFile(path.join(OUT, `${name}-${w}.avif`)); + await base.clone().webp({ quality: 78 }).toFile(path.join(OUT, `${name}-${w}.webp`)); + await base.clone().jpeg({ quality: 80, mozjpeg: true }).toFile(path.join(OUT, `${name}-${w}.jpg`)); + } + console.log(`${file} -> ${name}-{400,800,1600}.{avif,webp,jpg}`); +} +console.log('Done. Add alt text where each image is used.'); diff --git a/pof-cayman-website/scripts/og-template.html b/pof-cayman-website/scripts/og-template.html new file mode 100644 index 00000000..fc36a5fe --- /dev/null +++ b/pof-cayman-website/scripts/og-template.html @@ -0,0 +1,22 @@ + + + + +
ProtectOur Future
+

Keep Cayman for our grandchildren

+
The largest youth-led environmental advocacy group in the Cayman Islands.
+
protectourfuturecayman.org
+ diff --git a/pof-cayman-website/scripts/verify-build.mjs b/pof-cayman-website/scripts/verify-build.mjs new file mode 100644 index 00000000..964349c8 --- /dev/null +++ b/pof-cayman-website/scripts/verify-build.mjs @@ -0,0 +1,84 @@ +/* + Checks every built page for the things that quietly break trust: + one h1, headings in order, canonical + unique title/description/OG, + no #000/#FFF, no broken internal links, forms have honeypots, and + no events older than today survive the build. + Run after `npm run build`: `npm run verify`. +*/ +import fs from 'node:fs'; +import path from 'node:path'; + +const DIST = new URL('../dist/', import.meta.url).pathname; +const files = []; +(function walk(d) { for (const f of fs.readdirSync(d)) { const p = path.join(d, f); fs.statSync(p).isDirectory() ? walk(p) : p.endsWith('.html') && files.push(p); } })(DIST); + +const problems = []; +const titles = new Map(), descs = new Map(); +const attr = (html, re) => (html.match(re) || [])[1]; + +for (const file of files) { + const rel = '/' + path.relative(DIST, file).replace(/\\/g, '/'); + const html = fs.readFileSync(file, 'utf8'); + const h1s = (html.match(/]/g) || []).length; + if (h1s !== 1) problems.push(`${rel}: ${h1s}

elements`); + + // heading order: never skip a level going down + let last = 1; + for (const m of html.matchAll(/]/g)) { + const lvl = Number(m[1]); + if (lvl > last + 1) problems.push(`${rel}: heading jumps from h${last} to h${lvl}`); + last = lvl; + } + + const title = attr(html, /([^<]*)<\/title>/); + const desc = attr(html, /<meta name="description" content="([^"]*)"/); + const canonical = attr(html, /<link rel="canonical" href="([^"]*)"/); + if (!title) problems.push(`${rel}: missing <title>`); + if (!desc) problems.push(`${rel}: missing meta description`); + if (!canonical) problems.push(`${rel}: missing canonical`); + if (!/property="og:image"/.test(html)) problems.push(`${rel}: missing og:image`); + if (!/name="twitter:card"/.test(html)) problems.push(`${rel}: missing twitter card`); + if (title) { if (titles.has(title)) problems.push(`${rel}: duplicate title with ${titles.get(title)}`); titles.set(title, rel); } + if (desc) { if (descs.has(desc)) problems.push(`${rel}: duplicate description with ${descs.get(desc)}`); descs.set(desc, rel); } + if (title && title.length > 70) problems.push(`${rel}: title over 70 chars (${title.length})`); + if (desc && desc.length > 165) problems.push(`${rel}: description over 165 chars (${desc.length})`); + + if (/#000\b|#fff\b|#000000|#ffffff/i.test(html)) problems.push(`${rel}: pure black or white in inline CSS`); + if (/<img(?![^>]*\balt=)/i.test(html)) problems.push(`${rel}: <img> without alt`); + + for (const m of html.matchAll(/<form[^>]*>/g)) { + const formEnd = html.indexOf('</form>', m.index); + const form = html.slice(m.index, formEnd); + if (!/_gotcha|b_honeypot/.test(form)) problems.push(`${rel}: form without honeypot`); + if (/action="mailto:/.test(form) && !/data-mode="mailto"/.test(form)) problems.push(`${rel}: mailto form not marked as fallback`); + } + + for (const m of html.matchAll(/href="(\/[^"#?]*)/g)) { + const href = m[1]; + if (href.startsWith('/fonts/') || href.startsWith('/press/') || href.startsWith('/og/') || href === '/sitemap-index.xml' || href === '/') continue; + const candidates = [href, href + '.html', href + '/index.html'].map((h) => path.join(DIST, h)); + if (!candidates.some((c) => fs.existsSync(c))) problems.push(`${rel}: broken internal link ${href}`); + } + + const today = new Date(Date.now() - 5 * 3600 * 1000).toISOString().slice(0, 10); + for (const m of html.matchAll(/data-event-date="([^"]+)"/g)) { + if (m[1] < today && !rel.includes('archive')) problems.push(`${rel}: past event ${m[1]} still rendered as upcoming`); + } +} + +for (const f of ['_redirects', '_headers', 'robots.txt', 'sitemap-index.xml', 'favicon.svg', 'og/default.png', 'fonts/BricolageGrotesque.woff2', 'press/pof-wordmark-ocean.svg']) { + if (!fs.existsSync(path.join(DIST, f))) problems.push(`dist/${f} missing`); +} +const redirects = fs.readFileSync(path.join(DIST, '_redirects'), 'utf8'); +for (const d of ['pofcaribbean.org', 'protectourfuture-eco.com']) if (!redirects.includes(d)) problems.push(`_redirects lacks 301 for ${d}`); + +// CSS checks on the built stylesheet(s) +const cssFiles = []; +(function walk(d) { for (const f of fs.readdirSync(d)) { const p = path.join(d, f); fs.statSync(p).isDirectory() ? walk(p) : p.endsWith('.css') && cssFiles.push(p); } })(DIST); +const css = cssFiles.map((f) => fs.readFileSync(f, 'utf8')).join('\n') + files.map((f) => (fs.readFileSync(f, 'utf8').match(/<style>[\s\S]*?<\/style>/g) || []).join('\n')).join('\n'); +if (/font-family:[^;]*\bInter\b/i.test(css)) problems.push('CSS uses Inter'); +if (/fonts\.googleapis\.com/.test(css) || files.some((f) => /fonts\.googleapis\.com/.test(fs.readFileSync(f, 'utf8')))) problems.push('Google Fonts API referenced at runtime'); + +console.log(`${files.length} pages checked`); +if (problems.length) { console.log(problems.join('\n')); process.exit(1); } +console.log('All checks passed.'); diff --git a/pof-cayman-website/src/components/ChapterCard.astro b/pof-cayman-website/src/components/ChapterCard.astro new file mode 100644 index 00000000..a472c3eb --- /dev/null +++ b/pof-cayman-website/src/components/ChapterCard.astro @@ -0,0 +1,39 @@ +--- +/* One school. School, meeting time, leader role, current project. Empty fields have defined states. */ +import { SITE } from '../lib/site'; +interface Props { + chapter: { id: string; data: { school: string; shortName?: string; status: 'active' | 'forming'; meeting?: string; leaderRole: string; leaderName?: string; project?: string } }; + index?: number; +} +const { chapter, index = 0 } = Astro.props; +const { data } = chapter; +const mail = `mailto:${SITE.email}?subject=${encodeURIComponent(`${data.school} chapter`)}`; +--- +<article class="chapter reveal" style={`--i: ${index % 6}`}> + <h3 class="chapter__school">{data.school}</h3> + <p><span class:list={['chip', { 'chip--ongoing': data.status === 'forming' }]}>{data.status === 'active' ? 'Active chapter' : 'Forming'}</span></p> + <dl class="chapter__facts"> + <div> + <dt>Meets</dt> + <dd>{data.meeting ?? 'Time confirmed each term. Email to be introduced.'}</dd> + </div> + <div> + <dt>{data.leaderRole}</dt> + <dd>{data.leaderName ?? 'A current student. Ask by email and we will connect you.'}</dd> + </div> + {data.status === 'active' && ( + <div> + <dt>Now working on</dt> + <dd>{data.project ?? `Project for this term not yet posted. Contact: ${SITE.email}`}</dd> + </div> + )} + </dl> + <p><a class="link-arrow" href={mail}>Email about this chapter</a></p> +</article> +<style> + .chapter { display: grid; gap: var(--s-3); padding-top: var(--s-4); border-top: 1px solid var(--rule); align-content: start; } + .chapter__school { font-size: var(--fs-2); } + .chapter__facts { display: grid; gap: var(--s-3); } + .chapter__facts dt { font-family: var(--font-display); font-size: var(--fs-small); font-weight: 600; letter-spacing: var(--tracking-caps); text-transform: uppercase; color: var(--text-muted); } + .chapter__facts dd { margin-top: var(--s-1); max-width: 40ch; } +</style> diff --git a/pof-cayman-website/src/components/EventCard.astro b/pof-cayman-website/src/components/EventCard.astro new file mode 100644 index 00000000..60b9f3a0 --- /dev/null +++ b/pof-cayman-website/src/components/EventCard.astro @@ -0,0 +1,54 @@ +--- +/* Upcoming event. Date, location, action. The list that renders it archives automatically. */ +import { formatRange, weekday, isoDate } from '../lib/dates.js'; +import Icon from './Icon.astro'; +interface Props { + event: { id: string; data: { title: string; date: Date; endDate?: Date; time?: string; location: string; summary: string; action?: { label: string; href: string }; cancelled: boolean } }; + headingLevel?: 'h2' | 'h3'; + featured?: boolean; +} +const { event, headingLevel: H = 'h3', featured = false } = Astro.props; +const { data } = event; +const day = data.date.getUTCDate(); +const month = data.date.toLocaleString('en-GB', { month: 'short', timeZone: 'UTC' }); +--- +<article class:list={['event', 'reveal', { 'event--featured': featured, 'event--cancelled': data.cancelled }]} data-event-date={isoDate(data.endDate ?? data.date)}> + <div class="event__date tnum" aria-hidden="true"> + <span class="event__day">{day}</span> + <span class="event__month">{month}</span> + </div> + <div class="event__body"> + <H class="event__title"> + <a href={`/events/${event.id}`}>{data.title}</a> + {data.cancelled && <span class="chip"> Cancelled</span>} + </H> + <p class="event__meta meta"> + <span><Icon name="calendar" /> {weekday(data.date)}, {formatRange(data.date, data.endDate)}{data.time ? `, ${data.time}` : ''}</span> + <span><Icon name="map-pin" /> {data.location}</span> + </p> + <p class="event__summary">{data.summary}</p> + {data.action && !data.cancelled && ( + <a class:list={['btn', { 'btn--ghost': !featured }]} href={data.action.href}>{data.action.label} <Icon name="arrow-right" /></a> + )} + </div> +</article> +<style> + .event { display: grid; grid-template-columns: auto 1fr; gap: var(--s-4); padding-block: var(--s-4); border-top: 1px solid var(--rule); } + .event__date { + display: grid; justify-items: center; align-content: start; + min-width: 3.5rem; padding-top: var(--s-1); + font-family: var(--font-display); line-height: 1; + } + .event__day { font-size: var(--fs-4); font-weight: 700; letter-spacing: var(--tracking-display); } + .event__month { font-size: var(--fs-small); text-transform: uppercase; letter-spacing: var(--tracking-caps); margin-top: var(--s-1); } + .event__body { display: grid; gap: var(--s-2); justify-items: start; } + .event__title a { color: inherit; text-decoration: none; } + .event__title a:hover { text-decoration: underline; } + .event__meta { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-4); } + .event__meta span { display: inline-flex; align-items: center; gap: var(--s-1); } + .event__summary { max-width: 55ch; } + .event .btn { margin-top: var(--s-2); } + .event--featured { border-top: 3px solid var(--text); } + .event--featured .event__day { font-size: var(--fs-5); } + .event--cancelled .event__title a { text-decoration: line-through; } +</style> diff --git a/pof-cayman-website/src/components/FeatureBlock.astro b/pof-cayman-website/src/components/FeatureBlock.astro new file mode 100644 index 00000000..383504b0 --- /dev/null +++ b/pof-cayman-website/src/components/FeatureBlock.astro @@ -0,0 +1,47 @@ +--- +/* One campaign or initiative. Asymmetric, image plus text, one action. */ +import Photo from './Photo.astro'; +import Icon from './Icon.astro'; +interface Props { + eyebrow?: string; + title: string; + action: { label: string; href: string }; + image?: string; + imageAlt?: string; + tone?: 'ocean' | 'mangrove' | 'sand'; + align?: 'left' | 'right'; // which side the image sits on at desktop + surface?: 'paper' | 'sand'; + status?: { label: string; kind: 'active' | 'won' | 'ongoing' }; + headingLevel?: 'h2' | 'h3'; +} +const { eyebrow, title, action, image, imageAlt = '', tone = 'mangrove', align = 'right', surface = 'paper', status, headingLevel: H = 'h2' } = Astro.props; +--- +<section class:list={['feature', `surface-${surface}`, `feature--${align}`]}> + <div class="container feature__inner"> + <div class="feature__media reveal"> + <Photo src={image} alt={imageAlt} aspect="4 / 3" aspectDesktop="4 / 5" tone={tone} sizes="(min-width: 64em) 45vw, 100vw" /> + </div> + <div class="feature__copy reveal" style="--i: 1"> + {eyebrow && <p class="eyebrow">{eyebrow}</p>} + <H class="feature__title">{title}</H> + {status && <p><span class:list={['chip', `chip--${status.kind}`]}>{status.label}</span></p>} + <div class="feature__body flow"><slot /></div> + <p><a class="btn" href={action.href}>{action.label} <Icon name="arrow-right" /></a></p> + </div> + </div> +</section> +<style> + .feature { padding-block: var(--s-6); } + .feature__inner { display: grid; gap: var(--s-4); } + .feature__copy { display: grid; gap: var(--s-3); align-content: center; } + .feature__media { margin-inline: calc(-1 * var(--gutter)); } + @media (min-width: 64em) { + .feature { padding-block: var(--s-7); } + .feature__inner { grid-template-columns: repeat(12, minmax(0, 1fr)); gap: var(--s-4); align-items: center; } + .feature__media { margin-inline: 0; grid-row: 1; } + .feature--right .feature__media { grid-column: 7 / 13; } + .feature--right .feature__copy { grid-column: 1 / 6; grid-row: 1; } + .feature--left .feature__media { grid-column: 1 / 7; } + .feature--left .feature__copy { grid-column: 8 / 13; grid-row: 1; } + } +</style> diff --git a/pof-cayman-website/src/components/Footer.astro b/pof-cayman-website/src/components/Footer.astro new file mode 100644 index 00000000..787cb41c --- /dev/null +++ b/pof-cayman-website/src/components/Footer.astro @@ -0,0 +1,82 @@ +--- +import { SITE, NAV } from '../lib/site'; +import Wordmark from './Wordmark.astro'; +import Icon from './Icon.astro'; +import Newsletter from './Newsletter.astro'; +const year = new Date().getFullYear(); +--- +<footer class="site-footer surface-ocean-deep"> + <div class="container"> + <div class="site-footer__grid"> + <div class="site-footer__brand"> + <Wordmark class="site-footer__mark" /> + <p>A youth-led grassroots environmental organisation in the Cayman Islands. Founded in 2018 by students. Still run by students.</p> + <ul class="site-footer__social" role="list"> + <li><a href={SITE.instagram.url} rel="me noopener"><Icon name="instagram" /> Instagram</a></li> + <li><a href={SITE.facebook.url} rel="me noopener"><Icon name="facebook" /> Facebook</a></li> + <li><a href={`mailto:${SITE.email}`}><Icon name="mail" /> {SITE.email}</a></li> + </ul> + </div> + + <nav class="site-footer__nav" aria-label="Footer"> + <ul role="list"> + {NAV.map((n) => <li><a href={n.href}>{n.label}</a></li>)} + <li><a href="/generation-green">Generation Green</a></li> + <li><a href="/support">Support POF</a></li> + <li><a href="/press">Press</a></li> + <li><a href="/contact">Contact</a></li> + </ul> + </nav> + + <div class="site-footer__newsletter"> + <Newsletter compact id="footer-newsletter" /> + </div> + </div> + + <div class="site-footer__trust"> + <h2 class="eyebrow">How POF is set up</h2> + <p> + Protect Our Future is a youth-led grassroots organisation. It is not a registered non-profit in the Cayman Islands.{' '} + {SITE.fiscalPartner.name}, a registered non-profit ({SITE.fiscalPartner.npo}), receives donations for POF.{' '} + Cayman has no charitable tax deduction. <a href="/support">How giving works</a>.{' '} + Questions: <a href={`mailto:${SITE.email}`}>{SITE.email}</a>. + </p> + </div> + + <div class="site-footer__legal"> + <p class="meta">© {year} Protect Our Future Cayman. Grand Cayman, Cayman Islands.</p> + <ul class="site-footer__legal-links meta" role="list"> + <li><a href="/accessibility">Accessibility</a></li> + <li><a href="/privacy">Privacy</a></li> + </ul> + </div> + </div> +</footer> +<style> + .site-footer { padding-block: var(--s-6) var(--s-5); } + .site-footer__grid { display: grid; gap: var(--s-5); } + .site-footer__brand p { max-width: 34ch; margin-top: var(--s-3); } + .site-footer__mark { font-size: var(--fs-2); } + .site-footer__social { display: grid; gap: var(--s-2); margin-top: var(--s-4); } + .site-footer__social a, .site-footer__nav a { + display: inline-flex; align-items: center; gap: var(--s-2); + min-height: var(--touch); color: var(--text); text-decoration: none; + font-family: var(--font-display); + } + .site-footer__social a:hover, .site-footer__nav a:hover { text-decoration: underline; } + .site-footer__nav ul { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--s-4); } + .site-footer__trust { + margin-top: var(--s-6); padding-top: var(--s-4); + border-top: 1px solid var(--rule); + } + .site-footer__trust p { margin-top: var(--s-2); max-width: var(--measure); } + .site-footer__legal { + margin-top: var(--s-5); display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-5); align-items: center; + } + .site-footer__legal-links { display: flex; gap: var(--s-4); } + .site-footer__legal-links a { color: var(--text); } + @media (min-width: 64em) { + .site-footer__grid { grid-template-columns: 4fr 4fr 4fr; gap: var(--s-6); } + .site-footer__nav ul { grid-template-columns: 1fr 1fr; } + } +</style> diff --git a/pof-cayman-website/src/components/Form.astro b/pof-cayman-website/src/components/Form.astro new file mode 100644 index 00000000..bfd69089 --- /dev/null +++ b/pof-cayman-website/src/components/Form.astro @@ -0,0 +1,125 @@ +--- +/* + Contact, volunteer and chapter forms share this shell. + - Posts to Formspree when an endpoint is configured (plain HTML POST, enhanced with fetch for inline status). + - Without an endpoint, submitting opens the visitor's email app with the fields filled in. + - Honeypot field. Visible labels. Minimum fields. Inline success and error states. +*/ +import { SITE } from '../lib/site'; +import Icon from './Icon.astro'; +interface Props { + id: string; + endpoint?: string; // Formspree form id + subject: string; // email subject, also Formspree _subject + submitLabel: string; + successTitle: string; + successBody: string; // what happens next, and when +} +const { id, endpoint, subject, submitLabel, successTitle, successBody } = Astro.props; +const action = endpoint ? `https://formspree.io/f/${endpoint}` : `mailto:${SITE.email}`; +--- +<form + class="form" id={id} action={action} method="post" + data-form data-mode={endpoint ? 'formspree' : 'mailto'} data-email={SITE.email} data-subject={subject} + novalidate +> + <input type="hidden" name="_subject" value={subject} /> + <div class="visually-hidden" aria-hidden="true"> + <label for={`${id}-website`}>Leave this field empty</label> + <input id={`${id}-website`} type="text" name="_gotcha" tabindex="-1" autocomplete="off" /> + </div> + + <div class="form__fields"> + <slot /> + </div> + + <p class="form__consent meta"> + By sending this you agree to POF keeping your details to reply to you. If you are under 16, a parent or guardian should know you are getting in touch.{' '} + <a href="/privacy">How we handle your details</a>. + </p> + + <div class="form__actions"> + <button class="btn" type="submit">{submitLabel} <Icon name="arrow-right" /></button> + {!endpoint && <span class="meta">Opens your email app.</span>} + </div> + + <div class="form-status form-status--ok" data-status="ok" hidden role="status" tabindex="-1"> + <p><strong>{successTitle}</strong></p> + <p>{successBody}</p> + </div> + <div class="form-status form-status--error" data-status="error" hidden role="alert" tabindex="-1"> + <p><strong>That did not send.</strong> Please check the fields marked below, or email us directly at <a href={`mailto:${SITE.email}`}>{SITE.email}</a> so nothing is lost.</p> + </div> +</form> +<style> + .form { display: grid; gap: var(--s-4); max-width: 36rem; } + .form__fields { display: grid; gap: var(--s-4); } + .form__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); } + .form :global(.field.is-invalid input), .form :global(.field.is-invalid select), .form :global(.field.is-invalid textarea) { border-color: var(--c-error); border-width: 2px; } + .form :global(.field .error) { color: var(--c-error); font-family: var(--font-display); font-size: var(--fs-small); } +</style> +<script> + for (const form of document.querySelectorAll<HTMLFormElement>('[data-form]')) { + const ok = form.querySelector<HTMLElement>('[data-status="ok"]')!; + const err = form.querySelector<HTMLElement>('[data-status="error"]')!; + const mode = form.dataset.mode; + + const validate = () => { + let valid = true; + for (const el of form.querySelectorAll<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>('[required]')) { + const field = el.closest('.field'); + const msg = field?.querySelector('.error'); + const good = el.checkValidity(); + field?.classList.toggle('is-invalid', !good); + if (msg) msg.remove(); + if (!good && field) { + const p = document.createElement('p'); + p.className = 'error'; + p.id = `${el.id}-error`; + p.textContent = el.type === 'email' ? 'Enter an email address we can reply to.' : 'This one is needed.'; + field.appendChild(p); + el.setAttribute('aria-describedby', p.id); + el.setAttribute('aria-invalid', 'true'); + if (valid) el.focus(); + } else { + el.removeAttribute('aria-invalid'); + el.removeAttribute('aria-describedby'); + } + valid = valid && good; + } + return valid; + }; + + form.addEventListener('submit', async (e) => { + e.preventDefault(); + ok.hidden = true; err.hidden = true; + if (!validate()) { err.hidden = false; err.focus(); return; } + const data = new FormData(form); + if (data.get('_gotcha')) { ok.hidden = false; return; } // silently drop bots + + if (mode === 'mailto') { + const lines: string[] = []; + for (const [k, v] of data.entries()) if (!k.startsWith('_') && String(v).trim()) lines.push(`${k}: ${v}`); + const href = `mailto:${form.dataset.email}?subject=${encodeURIComponent(form.dataset.subject || '')}&body=${encodeURIComponent(lines.join('\n'))}`; + window.location.href = href; + ok.hidden = false; ok.focus(); + return; + } + + const btn = form.querySelector<HTMLButtonElement>('button[type="submit"]')!; + btn.disabled = true; + try { + const res = await fetch(form.action, { method: 'POST', body: data, headers: { Accept: 'application/json' } }); + if (!res.ok) throw new Error(String(res.status)); + form.querySelector<HTMLElement>('.form__fields')!.hidden = true; + form.querySelector<HTMLElement>('.form__actions')!.hidden = true; + form.querySelector<HTMLElement>('.form__consent')!.hidden = true; + ok.hidden = false; ok.focus(); + } catch { + err.hidden = false; err.focus(); + } finally { + btn.disabled = false; + } + }); + } +</script> diff --git a/pof-cayman-website/src/components/Header.astro b/pof-cayman-website/src/components/Header.astro new file mode 100644 index 00000000..935e6ae3 --- /dev/null +++ b/pof-cayman-website/src/components/Header.astro @@ -0,0 +1,97 @@ +--- +import { NAV } from '../lib/site'; +import Wordmark from './Wordmark.astro'; +import Icon from './Icon.astro'; +const path = Astro.url.pathname.replace(/\/$/, '') || '/'; +const isCurrent = (href: string) => path === href || path.startsWith(href + '/'); +--- +<header class="site-header surface-paper"> + <div class="container site-header__inner"> + <a class="site-header__brand" href="/" aria-label="Protect Our Future Cayman, home"> + <Wordmark /> + </a> + <button class="site-header__toggle" type="button" aria-expanded="false" aria-controls="site-nav" data-nav-toggle> + <Icon name="menu" class="i-open" /> + <Icon name="x" class="i-close" /> + <span class="visually-hidden">Menu</span> + </button> + <nav class="site-nav" id="site-nav" aria-label="Main"> + <ul role="list"> + {NAV.map((item) => ( + <li><a href={item.href} aria-current={isCurrent(item.href) ? 'page' : undefined}>{item.label}</a></li> + ))} + </ul> + <a class="btn btn--donate site-nav__donate" href="/support">Donate</a> + </nav> + </div> +</header> +<style> + .site-header { + position: sticky; top: 0; z-index: 50; + border-bottom: 1px solid var(--rule); + } + .site-header__inner { + display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); + min-height: var(--header-h); + } + .site-header__brand { color: var(--text); text-decoration: none; display: inline-flex; align-items: center; min-height: var(--touch); } + .site-header__toggle { + display: inline-flex; align-items: center; justify-content: center; + width: var(--touch); height: var(--touch); + background: none; border: 0; cursor: pointer; color: var(--text); + border-radius: var(--r-1); + } + .site-header__toggle :global(.i-close) { display: none; } + .site-header__toggle[aria-expanded="true"] :global(.i-open) { display: none; } + .site-header__toggle[aria-expanded="true"] :global(.i-close) { display: block; } + + .site-nav { display: none; } + .site-nav.is-open, html:not(.js) .site-nav { + display: flex; flex-direction: column; gap: var(--s-3); + position: absolute; left: 0; right: 0; top: 100%; + padding: var(--s-3) var(--gutter) var(--s-5); + background: var(--c-paper); + border-bottom: 1px solid var(--rule); + } + .site-nav ul { display: flex; flex-direction: column; } + .site-nav a:not(.btn) { + display: flex; align-items: center; + min-height: var(--touch); + font-family: var(--font-display); font-weight: 500; font-size: var(--fs-1); + color: var(--text); text-decoration: none; + border-bottom: 1px solid var(--rule); + } + .site-nav a[aria-current="page"] { font-weight: 700; } + .site-nav__donate { align-self: flex-start; } + .btn--donate { background: var(--c-reef-ink); color: var(--c-paper); } + .btn--donate:hover { background: var(--c-ocean); color: var(--c-paper); } + + @media (min-width: 64em) { + .site-header__toggle { display: none; } + .site-nav, .site-nav.is-open, html:not(.js) .site-nav { + display: flex; flex-direction: row; align-items: center; gap: var(--s-4); + position: static; padding: 0; border: 0; background: transparent; + } + .site-nav ul { flex-direction: row; gap: var(--s-4); } + .site-nav a:not(.btn) { + font-size: var(--fs-0); border-bottom: 0; padding: var(--s-2) 0; position: relative; + } + .site-nav a:not(.btn)::after { + content: ""; position: absolute; left: 0; right: 0; bottom: 0.35rem; height: 2px; + background: currentColor; transform: scaleX(0); transform-origin: left; + transition: transform var(--dur-ui) var(--ease-out); + } + .site-nav a:not(.btn):hover::after, .site-nav a[aria-current="page"]::after { transform: scaleX(1); } + .site-nav__donate { align-self: center; } + } +</style> +<script> + const toggle = document.querySelector<HTMLButtonElement>('[data-nav-toggle]'); + const nav = document.getElementById('site-nav'); + if (toggle && nav) { + const set = (open: boolean) => { toggle.setAttribute('aria-expanded', String(open)); nav.classList.toggle('is-open', open); }; + toggle.addEventListener('click', () => set(toggle.getAttribute('aria-expanded') !== 'true')); + document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && nav.classList.contains('is-open')) { set(false); toggle.focus(); } }); + matchMedia('(min-width: 64em)').addEventListener('change', (e) => { if (e.matches) set(false); }); + } +</script> diff --git a/pof-cayman-website/src/components/Hero.astro b/pof-cayman-website/src/components/Hero.astro new file mode 100644 index 00000000..14e6f748 --- /dev/null +++ b/pof-cayman-website/src/components/Hero.astro @@ -0,0 +1,60 @@ +--- +/* + Page opener. Full-bleed photo, one headline, one CTA maximum. + Home uses the split layout with the slogan device. Inner pages use `compact`. +*/ +import Photo from './Photo.astro'; +interface Props { + eyebrow?: string; + title: string; + image?: string; + imageAlt?: string; + compact?: boolean; + surface?: 'paper' | 'sand' | 'ocean'; +} +const { eyebrow, title, image, imageAlt = '', compact = false, surface = compact ? 'sand' : 'ocean' } = Astro.props; +--- +<section class:list={['hero', `surface-${surface}`, { 'hero--compact': compact, 'hero--split': !compact && image, 'hero--type': !compact && !image }]}> + <div class="container hero__inner"> + <div class="hero__copy"> + {eyebrow && <p class="eyebrow hero__eyebrow">{eyebrow}</p>} + <h1 class="hero__title">{title}</h1> + <slot /> + </div> + {!compact && !image && Astro.slots.has('banner') && ( + <div class="hero__banner"> + <slot name="banner" /> + </div> + )} + {!compact && image && ( + <div class="hero__media"> + <Photo src={image} alt={imageAlt} aspect="4 / 5" aspectDesktop="4 / 5" sizes="(min-width: 64em) 50vw, 100vw" priority /> + </div> + )} + </div> +</section> +<style> + .hero { padding-block: var(--s-6) var(--s-6); overflow-x: clip; } + .hero__inner { display: grid; gap: var(--s-5); } + .hero__copy { display: grid; gap: var(--s-4); align-content: center; } + .hero__eyebrow { color: var(--text-muted); } + .hero__title { max-width: 18ch; font-size: var(--fs-4); } + .hero__banner { display: grid; align-content: center; order: -1; } + .hero__banner :global(.slogan) { font-size: var(--fs-4); transform: rotate(-2deg); } + .hero--compact { padding-block: var(--s-5) var(--s-5); border-bottom: 1px solid var(--rule); } + .hero--compact .hero__title { max-width: 20ch; font-size: var(--fs-4); } + .hero--compact .hero__copy { gap: var(--s-3); } + @media (min-width: 64em) { + .hero { padding-block: var(--s-7) var(--s-7); } + .hero--compact { padding-block: var(--s-6); } + .hero--compact .hero__title { font-size: var(--fs-5); } + .hero__title { font-size: var(--fs-6); } + .hero--split .hero__inner { grid-template-columns: 7fr 5fr; gap: var(--s-6); align-items: center; } + .hero--type .hero__inner { grid-template-columns: 6fr 6fr; gap: var(--s-6); align-items: center; } + .hero--type .hero__banner { order: 0; justify-items: end; padding-left: var(--s-5); } + .hero--type .hero__banner :global(.slogan) { font-size: var(--fs-5); transform-origin: right center; } + } + /* Full-bleed photo on mobile: the image crops to portrait, not a stripe. */ + .hero__media { margin-inline: calc(-1 * var(--gutter)); } + @media (min-width: 64em) { .hero__media { margin-inline: 0; } } +</style> diff --git a/pof-cayman-website/src/components/Icon.astro b/pof-cayman-website/src/components/Icon.astro new file mode 100644 index 00000000..b25dfefe --- /dev/null +++ b/pof-cayman-website/src/components/Icon.astro @@ -0,0 +1,34 @@ +--- +/* + One icon set (Lucide), one stroke width, inlined. Decorative by default. + Instagram and Facebook are drawn on the same 24px grid because Lucide + no longer ships brand marks. +*/ +interface Props { name: keyof typeof ICONS; label?: string; class?: string } +const { name, label, class: cls } = Astro.props; +const ICONS = { + 'arrow-right': '<path d="M5 12h14"/><path d="m12 5 7 7-7 7"/>', + 'arrow-up-right': '<path d="M7 7h10v10"/><path d="M7 17 17 7"/>', + 'external': '<path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>', + 'mail': '<path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7"/><rect x="2" y="4" width="20" height="16" rx="2"/>', + 'calendar': '<path d="M8 2v3"/><path d="M16 2v3"/><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18"/>', + 'clock': '<circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>', + 'map-pin': '<path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"/><circle cx="12" cy="10" r="3"/>', + 'menu': '<path d="M4 5h16"/><path d="M4 12h16"/><path d="M4 19h16"/>', + 'x': '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>', + 'check': '<path d="M20 6 9 17l-5-5"/>', + 'download': '<path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"/><path d="M14 2v5a1 1 0 0 0 1 1h5"/><path d="M12 18v-6"/><path d="m9 15 3 3 3-3"/>', + 'instagram': '<rect x="3" y="3" width="18" height="18" rx="5"/><circle cx="12" cy="12" r="4"/><path d="M17.5 6.5h.01"/>', + 'facebook': '<path d="M14 3h3v4h-3a1 1 0 0 0-1 1v3h4l-.5 4H13v6H9v-6H6v-4h3V8a5 5 0 0 1 5-5z"/>', +}; +--- +<svg + class:list={['icon', cls]} + xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" + fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" + aria-hidden={label ? undefined : 'true'} role={label ? 'img' : undefined} focusable="false" + set:html={(label ? `<title>${label}` : '') + ICONS[name]} +/> + diff --git a/pof-cayman-website/src/components/NewsItem.astro b/pof-cayman-website/src/components/NewsItem.astro new file mode 100644 index 00000000..8a8f8b7d --- /dev/null +++ b/pof-cayman-website/src/components/NewsItem.astro @@ -0,0 +1,40 @@ +--- +/* Press coverage or award. Outlet, date, headline, link. */ +import { formatDate } from '../lib/dates.js'; +import Icon from './Icon.astro'; +interface Props { + item: { id: string; data: { title: string; outlet: string; date: Date; datePrecision: 'day' | 'month' | 'year'; url?: string; kind: 'coverage' | 'award' | 'milestone'; summary: string } }; + index?: number; +} +const { item, index = 0 } = Astro.props; +const { data } = item; +const KIND = { coverage: 'Coverage', award: 'Award', milestone: 'Milestone' }; +--- +
+

+ + + {data.outlet} + + {KIND[data.kind]} +

+

+ {data.url ? {data.title} : data.title} +

+

{data.summary}

+
+ diff --git a/pof-cayman-website/src/components/Newsletter.astro b/pof-cayman-website/src/components/Newsletter.astro new file mode 100644 index 00000000..42eb61af --- /dev/null +++ b/pof-cayman-website/src/components/Newsletter.astro @@ -0,0 +1,38 @@ +--- +/* Mailchimp embed when configured; email fallback when not. Never a dead form. */ +import { SITE, INTEGRATIONS } from '../lib/site'; +import Icon from './Icon.astro'; +interface Props { heading?: string; compact?: boolean; id?: string; short?: boolean } +const { heading = 'Hear about the next event first', compact = false, id = 'newsletter', short = false } = Astro.props; +const action = INTEGRATIONS.mailchimp; +--- +
+ + {action ? ( + + ) : ( + + )} +
+ diff --git a/pof-cayman-website/src/components/PathSelector.astro b/pof-cayman-website/src/components/PathSelector.astro new file mode 100644 index 00000000..ea508927 --- /dev/null +++ b/pof-cayman-website/src/components/PathSelector.astro @@ -0,0 +1,40 @@ +--- +/* Split audiences. Three destinations maximum, distinct labels. One dominant. */ +import Icon from './Icon.astro'; +interface Path { audience: string; title: string; body: string; href: string; label: string } +interface Props { paths: Path[]; heading?: string } +const { paths, heading = 'Three ways in' } = Astro.props; +if (paths.length > 3) throw new Error('PathSelector: three destinations maximum.'); +--- +
+
+

{heading}

+
    + {paths.map((p, i) => ( +
  • +

    {p.audience}

    +

    {p.title}

    +

    {p.body}

    + {i === 0 + ? {p.label} + : {p.label} } +
  • + ))} +
+
+
+ diff --git a/pof-cayman-website/src/components/Photo.astro b/pof-cayman-website/src/components/Photo.astro new file mode 100644 index 00000000..efd7e4cb --- /dev/null +++ b/pof-cayman-website/src/components/Photo.astro @@ -0,0 +1,54 @@ +--- +/* + Every photograph on the site goes through this component. + - With a rights-cleared image: responsive with AVIF/WebP/JPEG and srcset. + - Without one: a solid palette block, never a broken-image icon. + Images live in public/images/-{400,800,1600}.{avif,webp,jpg} + (see scripts/images.mjs). `src` is the base name without size or extension. +*/ +interface Props { + src?: string; + alt?: string; + aspect?: string; // "4 / 5", "16 / 9", "3 / 2" + aspectDesktop?: string; // wider crop above 1024px + sizes?: string; + priority?: boolean; // above the fold: eager + high fetch priority + tone?: 'ocean' | 'mangrove' | 'sand' | 'ocean-deep'; + class?: string; + caption?: string; +} +const { + src, alt = '', aspect = '4 / 5', aspectDesktop, sizes = '100vw', + priority = false, tone = 'ocean', class: cls, caption, +} = Astro.props; +const base = src ? `/images/${src}` : ''; +const set = (ext: string) => [400, 800, 1600].map((w) => `${base}-${w}.${ext} ${w}w`).join(', '); +const style = `--aspect: ${aspect}; --aspect-desktop: ${aspectDesktop ?? aspect};`; +--- +{src ? ( +
+ + + + {alt} + + {caption &&
{caption}
} +
+) : ( + +)} + diff --git a/pof-cayman-website/src/components/SloganBlock.astro b/pof-cayman-website/src/components/SloganBlock.astro new file mode 100644 index 00000000..db87dace --- /dev/null +++ b/pof-cayman-website/src/components/SloganBlock.astro @@ -0,0 +1,48 @@ +--- +/* + The signature device. Derived from POF's banner series: hand-painted + slogans on white bedsheets. Rendered as a torn-edge sheet with painted + capitals. Use exactly once per page, on the single most important line. +*/ +interface Props { as?: 'span' | 'p' | 'h1' | 'h2'; class?: string; size?: 'md' | 'lg' } +const { as: Tag = 'span', class: cls, size = 'lg' } = Astro.props; +--- + + + + + diff --git a/pof-cayman-website/src/components/StatBand.astro b/pof-cayman-website/src/components/StatBand.astro new file mode 100644 index 00000000..e59edc33 --- /dev/null +++ b/pof-cayman-website/src/components/StatBand.astro @@ -0,0 +1,44 @@ +--- +/* Verified numbers. Number, label, date or source. Never more than four. */ +interface Stat { value: string; label: string; source: string } +interface Props { stats: readonly Stat[]; heading?: string } +const { stats, heading = 'With sources' } = Astro.props; +if (stats.length > 4) throw new Error('StatBand: four numbers maximum.'); +--- +
+
+

{heading}

+
+ {stats.map((s, i) => ( +
+
{s.value}
+
{s.label}
+
{s.source}
+
+ ))} +
+
+
+ diff --git a/pof-cayman-website/src/components/Wordmark.astro b/pof-cayman-website/src/components/Wordmark.astro new file mode 100644 index 00000000..62c37a4a --- /dev/null +++ b/pof-cayman-website/src/components/Wordmark.astro @@ -0,0 +1,22 @@ +--- +/* Typographic wordmark. POF's official vector logo replaces this once supplied (see BLOCKERS.md). */ +interface Props { class?: string } +const { class: cls } = Astro.props; +--- + + Protect + Our Future + + diff --git a/pof-cayman-website/src/content.config.ts b/pof-cayman-website/src/content.config.ts new file mode 100644 index 00000000..3a3e8b50 --- /dev/null +++ b/pof-cayman-website/src/content.config.ts @@ -0,0 +1,90 @@ +import { defineCollection, z } from 'astro:content'; +import { glob } from 'astro/loaders'; + +/* + Content model. Students edit these files on GitHub in the browser. + See README.md for step-by-step instructions with no local setup. +*/ + +const events = defineCollection({ + loader: glob({ pattern: '**/[^_]*.md', base: './src/content/events' }), + schema: z.object({ + title: z.string(), + date: z.coerce.date(), // YYYY-MM-DD. Past dates archive automatically. + endDate: z.coerce.date().optional(), + time: z.string().optional(), // "9:00 am to 12:00 pm" + location: z.string(), // "Barkers Beach, West Bay" + district: z.string().optional(), + summary: z.string().max(220), + action: z.object({ label: z.string(), href: z.string() }).optional(), + bring: z.array(z.string()).optional(), + cancelled: z.boolean().default(false), + }), +}); + +const news = defineCollection({ + loader: glob({ pattern: '**/[^_]*.md', base: './src/content/news' }), + schema: z.object({ + title: z.string(), + outlet: z.string(), // "Cayman News Service" + date: z.coerce.date(), + datePrecision: z.enum(['day', 'month', 'year']).default('day'), + url: z.string().url().optional(), // add when the link is confirmed + kind: z.enum(['coverage', 'award', 'milestone']).default('coverage'), + summary: z.string().max(280), + }), +}); + +const chapters = defineCollection({ + loader: glob({ pattern: '**/[^_]*.md', base: './src/content/chapters' }), + schema: z.object({ + school: z.string(), + shortName: z.string().optional(), + status: z.enum(['active', 'forming']), + meeting: z.string().optional(), // "Tuesdays, 3:15 pm, Room 12" + leaderRole: z.string().default('Chapter Lead'), + leaderName: z.string().optional(), + project: z.string().optional(), // current project; empty is a defined state + order: z.number().default(50), + }), +}); + +const campaigns = defineCollection({ + loader: glob({ pattern: '**/[^_]*.md', base: './src/content/campaigns' }), + schema: z.object({ + title: z.string(), + status: z.enum(['active', 'won', 'ongoing']), + statusNote: z.string(), // one line: what the status means right now + started: z.number().optional(), // year + summary: z.string().max(220), + action: z.object({ label: z.string(), href: z.string() }), + featured: z.boolean().default(false), + image: z.string().optional(), // /images/... once rights-cleared photography exists + imageAlt: z.string().optional(), + order: z.number().default(50), + }), +}); + +const team = defineCollection({ + loader: glob({ pattern: '**/[^_]*.md', base: './src/content/team' }), + schema: z.object({ + role: z.string(), // role survives graduation; the name changes + holder: z.string().optional(), // current student, blank until confirmed + school: z.string().optional(), + responsibility: z.string(), + order: z.number().default(50), + }), +}); + +const partners = defineCollection({ + loader: glob({ pattern: '**/[^_]*.md', base: './src/content/partners' }), + schema: z.object({ + name: z.string(), + url: z.string().url().optional(), + note: z.string().optional(), + logo: z.string().optional(), // /partners/... once each partner supplies a file + order: z.number().default(50), + }), +}); + +export const collections = { events, news, chapters, campaigns, team, partners }; diff --git a/pof-cayman-website/src/content/campaigns/cruise-berthing-legacy.md b/pof-cayman-website/src/content/campaigns/cruise-berthing-legacy.md new file mode 100644 index 00000000..5eac3f5a --- /dev/null +++ b/pof-cayman-website/src/content/campaigns/cruise-berthing-legacy.md @@ -0,0 +1,27 @@ +--- +title: Cruise berthing legacy +status: won +statusNote: "Referendum held 30 April 2025. 63.66% voted against cruise berthing. The next step is making sure the result holds." +started: 2018 +summary: "POF campaigned for years against a cruise berthing facility in George Town Harbour. On 30 April 2025, voters said no." +action: + label: See where each campaign stands + href: /our-work +featured: true +order: 1 +--- +Protect Our Future started in 2018 as a campaign against overdevelopment. The proposed cruise berthing facility in George Town Harbour became the fight that defined the organisation. + +On 30 April 2025, the Cayman Islands held a referendum. 11,993 people, 63.66% of those who voted, said no to cruise berthing. 5,417 people, 28.75%, said yes. + +George Town Harbour is a Mission Blue Hope Spot, recognised for its reef and marine life. POF's position was that the piers would have destroyed reef the islands cannot replace. + +## What POF did + +- Campaigned against the piers for years, from its founding in 2018 to the vote in 2025. +- Painted the banner series that became POF's best-known image. +- Represented the Cayman Islands at COP25, COP26, COP27 and COP28. + +## What happens next + +A referendum result is a starting point. POF keeps watching development proposals for George Town Harbour and reports on them here and on Instagram. diff --git a/pof-cayman-website/src/content/campaigns/heritage-homes.md b/pof-cayman-website/src/content/campaigns/heritage-homes.md new file mode 100644 index 00000000..d000136c --- /dev/null +++ b/pof-cayman-website/src/content/campaigns/heritage-homes.md @@ -0,0 +1,19 @@ +--- +title: Heritage homes +status: ongoing +statusNote: "POF argues that Cayman's traditional homes belong in the same conversation as its reefs and mangroves." +summary: "Protecting Our Future Is Protecting Our Past. Cayman's traditional homes are disappearing to development, and POF wants them kept." +action: + label: Read POF's position + href: /our-work/heritage-homes +order: 5 +--- +"Protecting Our Future Is Protecting Our Past." Cayman's traditional wooden homes were built for this climate by the people who lived here before the tourism economy. They are being lost to the same development pressure that threatens the coastline. + +## Why an environmental group cares + +Heritage and environment are the same argument here. Both ask what the islands keep for the next generation, and both are decided by the same planning process. + +## What you can do + +If your family has a heritage home or a story about one, POF wants to hear it. Write to us through the Contact page. diff --git a/pof-cayman-website/src/content/campaigns/managed-retreat-seven-mile-beach.md b/pof-cayman-website/src/content/campaigns/managed-retreat-seven-mile-beach.md new file mode 100644 index 00000000..a84f73a8 --- /dev/null +++ b/pof-cayman-website/src/content/campaigns/managed-retreat-seven-mile-beach.md @@ -0,0 +1,23 @@ +--- +title: Managed retreat for Seven Mile Beach +status: active +statusNote: "POF calls for a managed retreat plan as the beach narrows and sea walls fail to hold sand." +summary: "Seven Mile Beach is narrowing. POF argues for planning a managed retreat now rather than paying for it later." +action: + label: Read POF's position + href: /our-work/managed-retreat-seven-mile-beach +order: 6 +--- +Seven Mile Beach is the reason most visitors come to Grand Cayman. Parts of it are losing sand, and sea walls built to protect buildings push the loss further along the shore. + +Managed retreat means deciding in advance where the beach will be allowed to move, instead of defending every building until the sand is gone. + +## What a managed retreat plan involves + +- Deciding in public which stretches of Seven Mile Beach will be defended and which will be given room to move. +- Rules on how close new buildings can sit to the water. +- Clear public information about where the sand is going. + +## What you can do + +Follow the campaign on Instagram and come to the next POF event to hear where it stands. diff --git a/pof-cayman-website/src/content/campaigns/mangroves-and-wetlands.md b/pof-cayman-website/src/content/campaigns/mangroves-and-wetlands.md new file mode 100644 index 00000000..11a74105 --- /dev/null +++ b/pof-cayman-website/src/content/campaigns/mangroves-and-wetlands.md @@ -0,0 +1,22 @@ +--- +title: Mangroves and wetlands +status: ongoing +statusNote: "POF submitted a formal letter on the National Conservation Act amendments and keeps pressing for mangrove protection." +summary: "Mangroves hold the coastline together and shelter young fish. POF argues for their protection each time the law is reopened." +action: + label: Read POF's position + href: /our-work/mangroves-and-wetlands +order: 2 +--- +Grand Cayman's mangroves protect the island from storm surge, filter the water that reaches the reef, and give young fish somewhere to grow before they move out to open water. Clearing them for development removes all three at once. + +"Mangroves Secure Our Future" is one of the slogans painted on POF's banners. It is also the argument in plain terms. + +## What POF has done + +- Submitted a formal letter on amendments to the National Conservation Act. +- Partnered with the Mangrove Action Project and the National Trust for the Cayman Islands. + +## What you can do + +Join a school chapter and help with the next mangrove outing. If you are not a student, watch the Events page for the next one that is open to everyone. diff --git a/pof-cayman-website/src/content/campaigns/reef-safe-sunscreen.md b/pof-cayman-website/src/content/campaigns/reef-safe-sunscreen.md new file mode 100644 index 00000000..8a2372e1 --- /dev/null +++ b/pof-cayman-website/src/content/campaigns/reef-safe-sunscreen.md @@ -0,0 +1,20 @@ +--- +title: Reef-safe sunscreen +status: active +statusNote: "POF asks residents, visitors and retailers to choose mineral sunscreens that do not harm coral." +summary: "Some sunscreen ingredients harm coral. POF asks Cayman's swimmers and shops to choose products that do not." +action: + label: Read POF's position + href: /our-work/reef-safe-sunscreen +order: 4 +--- +Sunscreen washes off every swimmer at Seven Mile Beach and Stingray City. Some common ingredients harm coral. Mineral sunscreens do the same job for skin without the same cost to the reef. + +## What POF asks for + +- Swimmers and divers: choose a reef-safe sunscreen and check the label. +- Retailers and hotels: stock and offer reef-safe options. + +## What you can do + +Check the ingredients on the bottle you already own. Then ask your school chapter to take this campaign on for a term. diff --git a/pof-cayman-website/src/content/campaigns/single-use-plastics.md b/pof-cayman-website/src/content/campaigns/single-use-plastics.md new file mode 100644 index 00000000..53b59cca --- /dev/null +++ b/pof-cayman-website/src/content/campaigns/single-use-plastics.md @@ -0,0 +1,22 @@ +--- +title: Single-use plastics +status: ongoing +statusNote: "POF continues to call for a national ban on single-use plastics and runs cleanups that show what is washing up." +summary: "On one World Cleanup Day, 300 volunteers at 13 locations removed over two tons of plastic from Cayman's coast. POF wants less of it arriving." +action: + label: Read POF's position + href: /our-work/single-use-plastics +order: 3 +--- +"Our Future is Not Single-Use" is painted on one of POF's banners. It is the campaign in five words. + +On World Cleanup Day, POF and its partners put 300 volunteers on 13 locations around the islands and removed over two tons of plastic. Cleanups matter, and they also show what a ban would stop at the source. + +## What POF has done + +- Called publicly for a national single-use plastic ban. +- Partnered with Plastic Free Cayman on cleanups. + +## What you can do + +Bring a reusable bottle. Then come to the next cleanup on the Events page and see what a morning's work looks like. diff --git a/pof-cayman-website/src/content/campaigns/youth-climate-march.md b/pof-cayman-website/src/content/campaigns/youth-climate-march.md new file mode 100644 index 00000000..c5c16409 --- /dev/null +++ b/pof-cayman-website/src/content/campaigns/youth-climate-march.md @@ -0,0 +1,17 @@ +--- +title: Youth Climate March +status: ongoing +statusNote: "Held annually. The next date is posted on the Events page when it is confirmed." +summary: "Once a year, students from across Grand Cayman march for the climate. Everyone is welcome." +action: + label: Watch the Events page + href: /events +order: 7 +--- +The Youth Climate March is POF's annual public event. Students from every chapter, their families, and anyone who wants to walk with them march together. + +Banners are welcome. Hand-painted ones are encouraged. + +## When it happens + +The date is confirmed each year and posted on the Events page. Sign up for the newsletter on the same page to hear first. diff --git a/pof-cayman-website/src/content/chapters/calvary-baptist-christian-academy.md b/pof-cayman-website/src/content/chapters/calvary-baptist-christian-academy.md new file mode 100644 index 00000000..87a3007c --- /dev/null +++ b/pof-cayman-website/src/content/chapters/calvary-baptist-christian-academy.md @@ -0,0 +1,5 @@ +--- +school: Calvary Baptist Christian Academy +status: forming +order: 10 +--- diff --git a/pof-cayman-website/src/content/chapters/cayman-international-school.md b/pof-cayman-website/src/content/chapters/cayman-international-school.md new file mode 100644 index 00000000..85c101a4 --- /dev/null +++ b/pof-cayman-website/src/content/chapters/cayman-international-school.md @@ -0,0 +1,8 @@ +--- +school: Cayman International School +shortName: CIS +status: active +leaderRole: Chapter Lead +order: 1 +--- +Where POF began in 2018. Four CIS students founded the organisation as a campaign against overdevelopment. diff --git a/pof-cayman-website/src/content/chapters/cayman-prep-and-high-school.md b/pof-cayman-website/src/content/chapters/cayman-prep-and-high-school.md new file mode 100644 index 00000000..c2be32ba --- /dev/null +++ b/pof-cayman-website/src/content/chapters/cayman-prep-and-high-school.md @@ -0,0 +1,7 @@ +--- +school: Cayman Prep and High School +shortName: Cayman Prep +status: active +leaderRole: Chapter Lead +order: 2 +--- diff --git a/pof-cayman-website/src/content/chapters/clifton-hunter-high-school.md b/pof-cayman-website/src/content/chapters/clifton-hunter-high-school.md new file mode 100644 index 00000000..27e7a6a4 --- /dev/null +++ b/pof-cayman-website/src/content/chapters/clifton-hunter-high-school.md @@ -0,0 +1,5 @@ +--- +school: Clifton Hunter High School +status: forming +order: 11 +--- diff --git a/pof-cayman-website/src/content/chapters/first-baptist-christian-school.md b/pof-cayman-website/src/content/chapters/first-baptist-christian-school.md new file mode 100644 index 00000000..abce6923 --- /dev/null +++ b/pof-cayman-website/src/content/chapters/first-baptist-christian-school.md @@ -0,0 +1,7 @@ +--- +school: First Baptist Christian School +shortName: First Baptist +status: active +leaderRole: Chapter Lead +order: 3 +--- diff --git a/pof-cayman-website/src/content/chapters/grace-christian-academy.md b/pof-cayman-website/src/content/chapters/grace-christian-academy.md new file mode 100644 index 00000000..1ea51413 --- /dev/null +++ b/pof-cayman-website/src/content/chapters/grace-christian-academy.md @@ -0,0 +1,5 @@ +--- +school: Grace Christian Academy +status: forming +order: 12 +--- diff --git a/pof-cayman-website/src/content/chapters/st-ignatius-catholic-school.md b/pof-cayman-website/src/content/chapters/st-ignatius-catholic-school.md new file mode 100644 index 00000000..73f89e4f --- /dev/null +++ b/pof-cayman-website/src/content/chapters/st-ignatius-catholic-school.md @@ -0,0 +1,7 @@ +--- +school: St. Ignatius Catholic School +shortName: St. Ignatius +status: active +leaderRole: Chapter Lead +order: 4 +--- diff --git a/pof-cayman-website/src/content/events/_TEMPLATE-copy-me.md b/pof-cayman-website/src/content/events/_TEMPLATE-copy-me.md new file mode 100644 index 00000000..f928b444 --- /dev/null +++ b/pof-cayman-website/src/content/events/_TEMPLATE-copy-me.md @@ -0,0 +1,19 @@ +--- +# Copy this file. Name the copy YYYY-MM-DD-short-name.md (no leading underscore). +# The site ignores any file that starts with an underscore. +title: Beach cleanup at Barkers +date: 2026-10-17 +time: 8:00 am to 11:00 am +location: Barkers National Park, West Bay +district: West Bay +summary: Two hours on the beach with gloves and bags provided. Counts toward school community service hours. +action: + label: Volunteer with POF + href: /get-involved +bring: + - Reusable water bottle + - Hat and reef-safe sunscreen + - Closed shoes +--- +Anything written below the dashes shows on the event's own page. Keep it short. +Say where to meet, who to look for, and what happens if it rains. diff --git a/pof-cayman-website/src/content/news/2018-founded.md b/pof-cayman-website/src/content/news/2018-founded.md new file mode 100644 index 00000000..4560ef80 --- /dev/null +++ b/pof-cayman-website/src/content/news/2018-founded.md @@ -0,0 +1,8 @@ +--- +title: Four students at Cayman International School found Protect Our Future +outlet: Protect Our Future +date: 2018-01-01 +datePrecision: year +kind: milestone +summary: POF began as a student campaign against overdevelopment. It is now the largest youth-led environmental advocacy group in the Cayman Islands. +--- diff --git a/pof-cayman-website/src/content/news/2019-12-cop25.md b/pof-cayman-website/src/content/news/2019-12-cop25.md new file mode 100644 index 00000000..270eceb8 --- /dev/null +++ b/pof-cayman-website/src/content/news/2019-12-cop25.md @@ -0,0 +1,8 @@ +--- +title: POF attends COP25 in Spain +outlet: UN Climate Change Conference +date: 2019-12-01 +datePrecision: month +kind: milestone +summary: POF's first UN climate conference, one year after the organisation was founded. +--- diff --git a/pof-cayman-website/src/content/news/2021-11-cop26.md b/pof-cayman-website/src/content/news/2021-11-cop26.md new file mode 100644 index 00000000..9d5bc417 --- /dev/null +++ b/pof-cayman-website/src/content/news/2021-11-cop26.md @@ -0,0 +1,8 @@ +--- +title: POF attends COP26 in Glasgow +outlet: UN Climate Change Conference +date: 2021-11-01 +datePrecision: month +kind: milestone +summary: POF members travelled to Glasgow to represent the Cayman Islands at the UN climate conference. +--- diff --git a/pof-cayman-website/src/content/news/2022-11-cop27.md b/pof-cayman-website/src/content/news/2022-11-cop27.md new file mode 100644 index 00000000..f4e8955a --- /dev/null +++ b/pof-cayman-website/src/content/news/2022-11-cop27.md @@ -0,0 +1,8 @@ +--- +title: POF attends COP27 in Egypt +outlet: UN Climate Change Conference +date: 2022-11-01 +datePrecision: month +kind: milestone +summary: Cayman students took the islands' case to the UN climate conference for the third year running. +--- diff --git a/pof-cayman-website/src/content/news/2022-diana-award.md b/pof-cayman-website/src/content/news/2022-diana-award.md new file mode 100644 index 00000000..53e9613d --- /dev/null +++ b/pof-cayman-website/src/content/news/2022-diana-award.md @@ -0,0 +1,8 @@ +--- +title: Dejea Lyons receives the Diana Award +outlet: The Diana Award +date: 2022-01-01 +datePrecision: year +kind: award +summary: The Diana Award recognises young people for social action and humanitarian work. Lyons received it in 2022 for her work with POF. +--- diff --git a/pof-cayman-website/src/content/news/2023-12-cop28.md b/pof-cayman-website/src/content/news/2023-12-cop28.md new file mode 100644 index 00000000..8d27df50 --- /dev/null +++ b/pof-cayman-website/src/content/news/2023-12-cop28.md @@ -0,0 +1,8 @@ +--- +title: POF attends COP28 in the United Arab Emirates +outlet: UN Climate Change Conference +date: 2023-12-01 +datePrecision: month +kind: milestone +summary: POF's fourth consecutive UN climate conference, after COP25 in Spain, COP26 in Glasgow and COP27 in Egypt. +--- diff --git a/pof-cayman-website/src/content/news/2024-03-diana-legacy-award.md b/pof-cayman-website/src/content/news/2024-03-diana-legacy-award.md new file mode 100644 index 00000000..f09fe6eb --- /dev/null +++ b/pof-cayman-website/src/content/news/2024-03-diana-legacy-award.md @@ -0,0 +1,8 @@ +--- +title: Dejea Lyons receives the Diana Legacy Award, the first Caymanian to do so +outlet: The Diana Award +date: 2024-03-01 +datePrecision: month +kind: award +summary: POF's Dejea Lyons was presented with the Diana Legacy Award by Prince William in March 2024. Lyons is also a Planeteer Alliance ambassador and a Captain Planet Young Superhero for Earth. +--- diff --git a/pof-cayman-website/src/content/news/2025-04-30-referendum-result.md b/pof-cayman-website/src/content/news/2025-04-30-referendum-result.md new file mode 100644 index 00000000..960e39c2 --- /dev/null +++ b/pof-cayman-website/src/content/news/2025-04-30-referendum-result.md @@ -0,0 +1,7 @@ +--- +title: Cruise berthing referendum passes with 63.66% against +outlet: Elections Office, Cayman Islands +date: 2025-04-30 +kind: milestone +summary: 11,993 voters (63.66%) said no to cruise berthing in George Town Harbour, against 5,417 (28.75%) in favour. POF had campaigned on the issue for years. +--- diff --git a/pof-cayman-website/src/content/news/2026-03-cns-180-members.md b/pof-cayman-website/src/content/news/2026-03-cns-180-members.md new file mode 100644 index 00000000..dc67d69c --- /dev/null +++ b/pof-cayman-website/src/content/news/2026-03-cns-180-members.md @@ -0,0 +1,8 @@ +--- +title: Cayman News Service reports around 180 active POF members across four high schools +outlet: Cayman News Service +date: 2026-03-01 +datePrecision: month +kind: coverage +summary: Coverage of Protect Our Future's growth to roughly 180 active members across four high schools. +--- diff --git a/pof-cayman-website/src/content/partners/cayocean.md b/pof-cayman-website/src/content/partners/cayocean.md new file mode 100644 index 00000000..7fbe4356 --- /dev/null +++ b/pof-cayman-website/src/content/partners/cayocean.md @@ -0,0 +1,4 @@ +--- +name: CayOcean +order: 8 +--- diff --git a/pof-cayman-website/src/content/partners/ccmi.md b/pof-cayman-website/src/content/partners/ccmi.md new file mode 100644 index 00000000..bdffb5dc --- /dev/null +++ b/pof-cayman-website/src/content/partners/ccmi.md @@ -0,0 +1,5 @@ +--- +name: Central Caribbean Marine Institute +url: https://reefresearch.org +order: 3 +--- diff --git a/pof-cayman-website/src/content/partners/department-of-environment.md b/pof-cayman-website/src/content/partners/department-of-environment.md new file mode 100644 index 00000000..3cf72610 --- /dev/null +++ b/pof-cayman-website/src/content/partners/department-of-environment.md @@ -0,0 +1,5 @@ +--- +name: Department of Environment +url: https://doe.ky +order: 5 +--- diff --git a/pof-cayman-website/src/content/partners/guy-harvey-foundation.md b/pof-cayman-website/src/content/partners/guy-harvey-foundation.md new file mode 100644 index 00000000..05187c14 --- /dev/null +++ b/pof-cayman-website/src/content/partners/guy-harvey-foundation.md @@ -0,0 +1,5 @@ +--- +name: Guy Harvey Foundation +url: https://guyharveyfoundation.org +order: 9 +--- diff --git a/pof-cayman-website/src/content/partners/mangrove-action-project.md b/pof-cayman-website/src/content/partners/mangrove-action-project.md new file mode 100644 index 00000000..d3400a66 --- /dev/null +++ b/pof-cayman-website/src/content/partners/mangrove-action-project.md @@ -0,0 +1,5 @@ +--- +name: Mangrove Action Project +url: https://mangroveactionproject.org +order: 7 +--- diff --git a/pof-cayman-website/src/content/partners/mission-blue.md b/pof-cayman-website/src/content/partners/mission-blue.md new file mode 100644 index 00000000..cceb2d9a --- /dev/null +++ b/pof-cayman-website/src/content/partners/mission-blue.md @@ -0,0 +1,6 @@ +--- +name: Mission Blue +url: https://mission-blue.org +note: George Town Harbour Hope Spot partner. +order: 2 +--- diff --git a/pof-cayman-website/src/content/partners/national-trust.md b/pof-cayman-website/src/content/partners/national-trust.md new file mode 100644 index 00000000..88ff16c7 --- /dev/null +++ b/pof-cayman-website/src/content/partners/national-trust.md @@ -0,0 +1,5 @@ +--- +name: National Trust for the Cayman Islands +url: https://nationaltrust.org.ky +order: 4 +--- diff --git a/pof-cayman-website/src/content/partners/plastic-free-cayman.md b/pof-cayman-website/src/content/partners/plastic-free-cayman.md new file mode 100644 index 00000000..1526a68e --- /dev/null +++ b/pof-cayman-website/src/content/partners/plastic-free-cayman.md @@ -0,0 +1,4 @@ +--- +name: Plastic Free Cayman +order: 6 +--- diff --git a/pof-cayman-website/src/content/partners/sustainable-cayman.md b/pof-cayman-website/src/content/partners/sustainable-cayman.md new file mode 100644 index 00000000..29e5ed35 --- /dev/null +++ b/pof-cayman-website/src/content/partners/sustainable-cayman.md @@ -0,0 +1,5 @@ +--- +name: Sustainable Cayman +note: Registered non-profit, NPO-612. Receives donations on POF's behalf. +order: 1 +--- diff --git a/pof-cayman-website/src/content/team/chapters-lead.md b/pof-cayman-website/src/content/team/chapters-lead.md new file mode 100644 index 00000000..4b56a03f --- /dev/null +++ b/pof-cayman-website/src/content/team/chapters-lead.md @@ -0,0 +1,5 @@ +--- +role: Chapters Lead +responsibility: Runs Generation Green. Keeps the chapter directory current each school term and helps new schools start. +order: 2 +--- diff --git a/pof-cayman-website/src/content/team/communications-lead.md b/pof-cayman-website/src/content/team/communications-lead.md new file mode 100644 index 00000000..0ca9c860 --- /dev/null +++ b/pof-cayman-website/src/content/team/communications-lead.md @@ -0,0 +1,5 @@ +--- +role: Communications Lead +responsibility: Runs Instagram and Facebook, answers the press, and adds coverage to the News page when it runs. +order: 4 +--- diff --git a/pof-cayman-website/src/content/team/events-lead.md b/pof-cayman-website/src/content/team/events-lead.md new file mode 100644 index 00000000..5d03763e --- /dev/null +++ b/pof-cayman-website/src/content/team/events-lead.md @@ -0,0 +1,5 @@ +--- +role: Events Lead +responsibility: Plans cleanups, outings and the Youth Climate March, and posts each one on the Events page before it happens. +order: 3 +--- diff --git a/pof-cayman-website/src/content/team/president.md b/pof-cayman-website/src/content/team/president.md new file mode 100644 index 00000000..a13b815e --- /dev/null +++ b/pof-cayman-website/src/content/team/president.md @@ -0,0 +1,5 @@ +--- +role: President +responsibility: Leads POF, speaks for it in public, and signs off campaign statuses and impact numbers each term. +order: 1 +--- diff --git a/pof-cayman-website/src/layouts/Base.astro b/pof-cayman-website/src/layouts/Base.astro new file mode 100644 index 00000000..e76c6f23 --- /dev/null +++ b/pof-cayman-website/src/layouts/Base.astro @@ -0,0 +1,88 @@ +--- +import '../styles/global.css'; +import Header from '../components/Header.astro'; +import Footer from '../components/Footer.astro'; +import { SITE, INTEGRATIONS } from '../lib/site'; + +interface Props { + title: string; // page subject; "Protect Our Future Cayman" is appended + description: string; + ogImage?: string; // /og/... 1200x630 + schema?: Record | Record[]; + noindex?: boolean; +} +const { title, description, ogImage = '/og/default.png', schema, noindex = false } = Astro.props; +const fullTitle = title === SITE.name ? title : `${title} · ${SITE.name}`; +const canonical = new URL(Astro.url.pathname === '/' ? '/' : Astro.url.pathname.replace(/\/$/, ''), SITE.url).href; +const ogAbs = new URL(ogImage, SITE.url).href; + +const orgSchema = { + '@context': 'https://schema.org', + '@type': 'Organization', + name: SITE.shortName, + alternateName: ['POF', 'Protect Our Future Cayman'], + url: SITE.url, + email: SITE.email, + foundingDate: '2018', + areaServed: 'Cayman Islands', + sameAs: [SITE.instagram.url, SITE.facebook.url], + description: SITE.boilerplate, +}; +const schemas = [orgSchema, ...(Array.isArray(schema) ? schema : schema ? [schema] : [])]; +--- + + + + + {fullTitle} + + + {noindex && } + + + + + + + + + + + + + + + + + + + + + {schemas.map((s) => + + + +
+
+ +
+