diff --git a/.dockerignore b/.dockerignore index 8e41aa5..16fc485 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,11 @@ +.env +.env.* +!.env.example node_modules dist .git -.DS_Store +.gitignore +*.log npm-debug.log* +.DS_Store +.astro diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..069fe4b --- /dev/null +++ b/.env.example @@ -0,0 +1,11 @@ +SES_ACCESS_KEY_ID= +SES_SECRET_ACCESS_KEY= +SES_REGION=ap-southeast-1 +META_ACCESS_TOKEN= +GA4_API_SECRET= +SES_FROM_EMAIL=kunthawat@moreminimore.com +CONTACT_TO_EMAIL=kunthawat@moreminimore.com +META_PIXEL_ID=418349260078648 +GA4_MEASUREMENT_ID=G-74BHREDLC3 +TRUST_PROXY=0 +PORT=4321 diff --git a/Dockerfile b/Dockerfile index 07a3514..bb51b8c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,3 @@ -# Build the static Astro site FROM node:22-alpine AS build WORKDIR /app COPY package.json package-lock.json ./ @@ -6,9 +5,13 @@ RUN npm ci COPY . . RUN npm run build -# Serve static files with a small, unprivileged web server -FROM nginxinc/nginx-unprivileged:1.27-alpine AS production -COPY nginx.conf /etc/nginx/conf.d/default.conf -COPY --from=build /app/dist /usr/share/nginx/html -EXPOSE 8080 -CMD ["nginx", "-g", "daemon off;"] +FROM node:22-alpine AS production +ENV NODE_ENV=production +WORKDIR /app +COPY package.json package-lock.json ./ +RUN npm ci --omit=dev && npm cache clean --force +COPY --from=build /app/dist ./dist +COPY server.js ./server.js +USER node +EXPOSE 4321 +CMD ["node", "server.js"] diff --git a/README.md b/README.md index 3016fb1..56c68f5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MoreminiMore redesign -A static Astro redesign for MoreminiMore, retaining verified service, case study, portfolio, and contact information from moreminimore.com. +An Astro website for MoreminiMore with four service landing pages and a same-origin Express contact API delivered through Amazon SES. ## Local development @@ -13,13 +13,18 @@ npm run dev ```sh npm run build +npm start ``` ## Docker deployment ```sh docker build -t moreminimore-site . -docker run --rm -p 8080:8080 moreminimore-site +docker run --rm --env-file .env -p 4321:4321 moreminimore-site ``` -The site is served at `http://localhost:8080`. +The site is served at `http://localhost:4321`. Copy `.env.example` to `.env`. Production email delivery requires `SES_ACCESS_KEY_ID`, `SES_SECRET_ACCESS_KEY`, and `SES_REGION`; the sender and recipient both default to `kunthawat@moreminimore.com` and can be overridden with `SES_FROM_EMAIL` and `CONTACT_TO_EMAIL`. + +`META_ACCESS_TOKEN` and `GA4_API_SECRET` are optional. When configured, a successful email submission also sends consent-scoped server conversions. `META_PIXEL_ID` defaults to `418349260078648` and `GA4_MEASUREMENT_ID` defaults to `G-74BHREDLC3`. Provider timeouts or failures never change the successful form response. In non-production mode, valid submissions are accepted without sending email or provider conversions so the flow can be tested safely. + +`TRUST_PROXY` defaults to `0`, so forwarded client-IP headers are ignored. Behind a known reverse proxy, set it to the explicit hop count (`1`–`3`) that matches the deployment path; never set it to `true`. Contact requests are rate-limited per resolved client IP. diff --git a/TRACKING.md b/TRACKING.md new file mode 100644 index 0000000..ec2c3b2 --- /dev/null +++ b/TRACKING.md @@ -0,0 +1,36 @@ +# MoreminiMore tracking + +Tracking is enabled in production builds and omitted from the Astro development server so local previews do not pollute production data. Production starts with every non-essential Google Consent Mode signal denied. No analytics, advertising, or recorder script is requested until ConsentOS emits the verified `consentos:consent-change` event with the corresponding accepted category. + +## Installed tools + +- ConsentOS — the loader follows a non-networking denied-default bootstrap and runs before any tracking tag. Its `analytics` category gates GA4, Umami, and the recorder; `marketing` gates Meta Pixel. +- Google Analytics 4 — measurement ID `G-74BHREDLC3`. +- Meta Pixel — pixel ID `418349260078648`, including PageView event IDs. +- Umami Analytics — website ID `b2e87a6c-0b64-43c8-bb09-e406ffca0af1`. +- Umami Recorder — 15% sample rate, strict masking, five-minute maximum sessions. `.contact-form` is excluded through both the recorder block selector and rrweb's `rr-block` class. + +If a visitor withdraws analytics or marketing consent after a tag was loaded, the page reloads after ConsentOS stores the new choice. The reloaded page stays tag-free for the withdrawn category. The Meta noscript fallback is intentionally omitted because it cannot be safely consent-gated without JavaScript. + +## Events + +| Event | Trigger | Properties | +| --- | --- | --- | +| `page_view` / `PageView` | Each production page load | Automatic page data | +| `contact_clicked` / `Contact` | Phone, email, or LINE link | `method`, `location` | +| `service_selected` / `ViewContent` | Service landing-page link | `service_path`, `location` | +| `form_started` | First focus inside a contact form | `form_location` | +| `form_submitted` / `Lead` | Contact API confirms a successful submission | `form_location`, shared random event ID for Meta deduplication | + +No names, phone numbers, email addresses, form values, or other personally identifiable information are included in these client-side events. + +## Contact-form tracking + +Each page uses the same short contact form. `form_started` is emitted once on the first focus. The client creates a UUID event ID for each validated submission and sends a boolean snapshot of the already-applied ConsentOS state. The same ID is retained across network or ambiguous-response retries; it rotates only after confirmed success or an actual edit to the form contents. `form_submitted` and the Meta standard `Lead` browser event are emitted only after `/api/contact` returns success. The browser event contains only the page or service location and reuses the same event ID as Meta CAPI for deduplication. + +The contact endpoint sends email through Amazon SES first. Only after SES succeeds: + +- With marketing consent and `META_ACCESS_TOKEN`, Meta CAPI receives `Lead`, the shared event ID, server IP/user agent, validated `_fbp`/`_fbc`, and normalized name/contact values hashed with SHA-256. Thai phone numbers are normalized to country code `66` before hashing. Raw form values are not sent to Meta. +- With analytics consent, `GA4_API_SECRET`, and a safely retrieved GA client ID, Google Measurement Protocol receives `form_submitted`, session/context identifiers, explicit advertising consent state, and no PII, hashes, or user properties. The browser helper skips its GA copy to avoid a duplicate event; Umami still receives the PII-free event. + +Provider requests have short timeouts and are handled with `Promise.allSettled`, so missing tokens, upstream errors, or timeouts never change a successful form response. There is no public conversion relay route. Event IDs are UUID-v4 validated and retained in a bounded 15-minute pending/completed registry. Concurrent pending requests receive a processing response; a retry of a completed delivery receives success without sending a second email, while failed SES attempts release the ID for retry. Delivery and provider logs contain status only and never include form values, credentials, tokens, or upstream response bodies. diff --git a/astro.config.mjs b/astro.config.mjs index 4053420..24b83b5 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -5,5 +5,5 @@ export default defineConfig({ site: 'https://moreminimore.com', output: 'static', integrations: [sitemap()], - build: { format: 'file' }, + build: { format: 'directory' }, }); diff --git a/package-lock.json b/package-lock.json index 05418f6..9cb380b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,12 @@ "version": "1.0.0", "dependencies": { "@astrojs/sitemap": "^3.2.1", - "astro": "^5.10.0" + "@aws-sdk/client-sesv2": "^3.883.0", + "astro": "^7.1.1", + "express": "^5.1.0", + "express-rate-limit": "^8.2.1", + "gsap": "^3.15.0", + "nodemailer": "^9.0.3" }, "devDependencies": { "@astrojs/check": "^0.9.4", @@ -17,16 +22,16 @@ } }, "node_modules/@astrojs/check": { - "version": "0.9.9", - "resolved": "https://registry.npmjs.org/@astrojs/check/-/check-0.9.9.tgz", - "integrity": "sha512-A5UW8uIuErLWEoRQvzgXpO1gTjUFtK8r7nU2Z7GewAMxUb7bPvpk11qaKKgxqXlHJWlAvaaxy+Xg28A6bmQ1Tg==", + "version": "0.9.10", + "resolved": "https://registry.npmjs.org/@astrojs/check/-/check-0.9.10.tgz", + "integrity": "sha512-zgx/UQMozdjOa3bOxjgeCFdtpE3c9rRX6xHwa+2QXvy8z8Akifu2AtubHyv/zzC2znO8dl8fFWL4K+Ba9kS8HQ==", "dev": true, "license": "MIT", "dependencies": { "@astrojs/language-server": "^2.16.7", "chokidar": "^4.0.3", "kleur": "^4.1.5", - "yargs": "^17.7.2" + "yargs": "^18.0.0" }, "bin": { "astro-check": "bin/astro-check.js" @@ -35,62 +40,221 @@ "typescript": "^5.0.0 || ^6.0.0" } }, - "node_modules/@astrojs/check/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@astrojs/check/node_modules/kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/@astrojs/check/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@astrojs/compiler": { "version": "2.13.1", "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.13.1.tgz", "integrity": "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==", + "dev": true, "license": "MIT" }, + "node_modules/@astrojs/compiler-binding": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding/-/compiler-binding-0.3.2.tgz", + "integrity": "sha512-8w/9CWmYrAJJ8N0SY3O43ws2BgxoW6u3QsD8u2mE140lMYAlwh+tlNoUeSBq22wVheFuiBbR212l6ixZ2IIgCQ==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@astrojs/compiler-binding-darwin-arm64": "0.3.2", + "@astrojs/compiler-binding-darwin-x64": "0.3.2", + "@astrojs/compiler-binding-linux-arm64-gnu": "0.3.2", + "@astrojs/compiler-binding-linux-arm64-musl": "0.3.2", + "@astrojs/compiler-binding-linux-x64-gnu": "0.3.2", + "@astrojs/compiler-binding-linux-x64-musl": "0.3.2", + "@astrojs/compiler-binding-wasm32-wasi": "0.3.2", + "@astrojs/compiler-binding-win32-arm64-msvc": "0.3.2", + "@astrojs/compiler-binding-win32-x64-msvc": "0.3.2" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-arm64": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-arm64/-/compiler-binding-darwin-arm64-0.3.2.tgz", + "integrity": "sha512-MM8tn8CSimcfytaOla4b6acN8mKWiL/rlAA1fpT3/Wl7dNGSE4y8FjTN/zJVNnb63CsLWG5zZwCt01TXtDKh9g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-x64": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-x64/-/compiler-binding-darwin-x64-0.3.2.tgz", + "integrity": "sha512-2lXOlzf8xb7jLomRsf/aswh61/NnGusynB2OwFkK6k4pmOtpfXMYnG0PLfXrEvxXYj69NdCnmUYXtHDd+JOOag==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-gnu": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-gnu/-/compiler-binding-linux-arm64-gnu-0.3.2.tgz", + "integrity": "sha512-BmU3kWj7qnLrd4vzm49zFEPJ5oFnn1tCT4Vt9hZbqdU5Cmb8GZl7fn6VFsnNfe7B18a2gIFtVzbLINtYl5kBjQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-musl": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-musl/-/compiler-binding-linux-arm64-musl-0.3.2.tgz", + "integrity": "sha512-f0heT9ZZEseSu5bHCeb80eL2DH07ArE6U9xi1WT/PEusNjzPmEr3GJsjG1tRLo5VYUUYX7h3ScaqGmGrMOVGmw==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-gnu": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-gnu/-/compiler-binding-linux-x64-gnu-0.3.2.tgz", + "integrity": "sha512-M8fOUt0itRpqiGyoEA/ij184s8O+hqbCz3+YozRusOOM3osgGljpDThhbKAJjqh82wOo6FioQ4w8PBvU1XMD5Q==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-musl": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-musl/-/compiler-binding-linux-x64-musl-0.3.2.tgz", + "integrity": "sha512-/Kebk8sO6HnLeSd691JkaAPfN7CqR9/KEXmWvyNPkaKNGmj8rTZ/lf2uXtnPu92Lan84UrKdIKVPy1fSo2encQ==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-wasm32-wasi": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-wasm32-wasi/-/compiler-binding-wasm32-wasi-0.3.2.tgz", + "integrity": "sha512-pUA6xbcOSB7DhfzIArB8BCAkFfAIqriiR7zl5zOStd6oU2G0kIKj+GUdGnyXyhfiv881Hffyk5tC0mR18sDjDw==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-arm64-msvc": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-arm64-msvc/-/compiler-binding-win32-arm64-msvc-0.3.2.tgz", + "integrity": "sha512-ESruf+6Qkl1trHUFxI6GSf6t52j8yN2kCNSzMWdzt7V/T09tFHrYzrVaJQohb2C9bJUH76pNvX6Zb51+xCQc9Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-x64-msvc": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-x64-msvc/-/compiler-binding-win32-x64-msvc-0.3.2.tgz", + "integrity": "sha512-wzzVrEbOwbsLWOdEbocskjMRx2aZPxJ7ZbmL+jnpBamFwmigm+2M/wzuM6JWncocgYwLic1csSpalBh96kQKXA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-rs": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-rs/-/compiler-rs-0.3.2.tgz", + "integrity": "sha512-xlx/T7JovIKduu4ucbTQUxQ5+Q8wxkHxhLjnZk3VlJbhbQ9RLvvuDk1p2YYFYFQ5y14dVm3FGGO4isQXa4F+Tg==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler-binding": "0.3.2" + }, + "engines": { + "node": ">=22.12.0" + } + }, "node_modules/@astrojs/internal-helpers": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.6.tgz", - "integrity": "sha512-GOle7smBWKfMSP8osUIGOlB5kaHdQLV3foCsf+5Q9Wsuu+C6Fs3Ez/ttXmhjZ1HkSgsogcM1RXSjjOVieHq16Q==", - "license": "MIT" + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.10.2.tgz", + "integrity": "sha512-yt7fMgPYqSM4Tmr+taTW6Per+hjJ8Pk6lA1PAcDyqzOt8HzJ6Kje5WzCxA2Sd+9wsUW7uhkLeoTMK0cXPwH9rQ==", + "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/language-server": { - "version": "2.16.12", - "resolved": "https://registry.npmjs.org/@astrojs/language-server/-/language-server-2.16.12.tgz", - "integrity": "sha512-3LpFphBCzveUgm5ZVDINB/v3YA4TgPa1EMOEFn3Zt/Ww6jojR25iN+kmzeUz7v/b9xkmq+hMACTX4hizN3VCEQ==", + "version": "2.16.13", + "resolved": "https://registry.npmjs.org/@astrojs/language-server/-/language-server-2.16.13.tgz", + "integrity": "sha512-ekOa+CYprEq5n4EJC1qTIAhLk49HZIUQuFwrEuF+3JK/pdMaYnWoREFUI2A0KEPOJiFA2kamBzKzbYljDvUxLg==", "dev": true, "license": "MIT", "dependencies": { @@ -129,45 +293,29 @@ } } }, - "node_modules/@astrojs/markdown-remark": { - "version": "6.3.11", - "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.11.tgz", - "integrity": "sha512-hcaxX/5aC6lQgHeGh1i+aauvSwIT6cfyFjKWvExYSxUhZZBBdvCliOtu06gbQyhbe0pGJNoNmqNlQZ5zYUuIyQ==", + "node_modules/@astrojs/markdown-satteri": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-satteri/-/markdown-satteri-0.3.5.tgz", + "integrity": "sha512-CvWVEFAbay7YO+i9SaqDJubipA5ckiVB89QWoMJ5XC0m5CtFg8JwZ7Kau6X9sYY7FZURH0w2l03ISH2jOS/RDQ==", "license": "MIT", "dependencies": { - "@astrojs/internal-helpers": "0.7.6", - "@astrojs/prism": "3.3.0", + "@astrojs/internal-helpers": "0.10.2", + "@astrojs/prism": "4.0.2", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", - "hast-util-to-text": "^4.0.2", - "import-meta-resolve": "^4.2.0", - "js-yaml": "^4.1.1", - "mdast-util-definitions": "^6.0.0", - "rehype-raw": "^7.0.0", - "rehype-stringify": "^10.0.1", - "remark-gfm": "^4.0.1", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.1.2", - "remark-smartypants": "^3.0.2", - "shiki": "^3.21.0", - "smol-toml": "^1.6.0", - "unified": "^11.0.5", - "unist-util-remove-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "unist-util-visit-parents": "^6.0.2", - "vfile": "^6.0.3" + "satteri": "^0.9.1" } }, "node_modules/@astrojs/prism": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-3.3.0.tgz", - "integrity": "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==", + "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": "18.20.8 || ^20.3.0 || >=22.0.0" + "node": ">=22.12.0" } }, "node_modules/@astrojs/sitemap": { @@ -182,18 +330,15 @@ } }, "node_modules/@astrojs/telemetry": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.0.tgz", - "integrity": "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==", + "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.2.0", - "debug": "^4.4.0", - "dlv": "^1.1.3", + "ci-info": "^4.4.0", "dset": "^3.1.4", - "is-docker": "^3.0.0", - "is-wsl": "^3.1.0", - "which-pm-runs": "^1.1.0" + "is-docker": "^4.0.0", + "package-manager-detector": "^1.6.0" }, "engines": { "node": "18.20.8 || ^20.3.0 || >=22.0.0" @@ -209,6 +354,279 @@ "yaml": "^2.8.3" } }, + "node_modules/@aws-sdk/client-sesv2": { + "version": "3.1106.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sesv2/-/client-sesv2-3.1106.0.tgz", + "integrity": "sha512-cbPngGGYk8JIBVB9qcisq4hcN0WegWHO6G3IOD1cc3CLVE5RqWKRrko7STO+tS6RXcyHQzErMELuTSwswDtsRw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.6", + "@aws-sdk/credential-provider-node": "^3.972.78", + "@aws-sdk/signature-v4-multi-region": "^3.996.43", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/fetch-http-handler": "^5.6.13", + "@smithy/node-http-handler": "^4.9.13", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.977.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.977.6.tgz", + "integrity": "sha512-QiaJV4/zDrB4ZY2mfeSXSzSTc36W16sZXcGz+SPFk0CJ26gziO0cS+4LjJUMAbdeeBOvS0k0Aq1cZpfGdUXxSw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.2", + "@aws-sdk/xml-builder": "^3.972.37", + "@aws/lambda-invoke-store": "^0.3.0", + "@smithy/core": "^3.31.1", + "@smithy/signature-v4": "^5.6.12", + "@smithy/types": "^4.16.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.67", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.67.tgz", + "integrity": "sha512-rcIpk5kxUqDaaNa6Xk23pQ6ViY7jlqzmfFWCahQcBT97ddXaXYYwzCen9Tz1Jvo6aJft6wDl5bN44/Jw5B4oLA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.6", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.69", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.69.tgz", + "integrity": "sha512-nggwJtZ4eeNsUw5IeWBMXsi1ryct5idi0K+/SCRF3kybLubOMaNTb3XCihXpWMiVpyzyPeIrl0zTkzhBH9porA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.6", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/fetch-http-handler": "^5.6.13", + "@smithy/node-http-handler": "^4.9.13", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.973.12", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.12.tgz", + "integrity": "sha512-pNEf/OeyN5X3VmLKlgSO6TqaWmW10CvI3TfwL1XhsuhYjSLT2VDaxFnCPHnOeQXSaFisMX4jNhpETriqN8DOmg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.6", + "@aws-sdk/credential-provider-env": "^3.972.67", + "@aws-sdk/credential-provider-http": "^3.972.69", + "@aws-sdk/credential-provider-login": "^3.972.74", + "@aws-sdk/credential-provider-process": "^3.972.67", + "@aws-sdk/credential-provider-sso": "^3.973.11", + "@aws-sdk/credential-provider-web-identity": "^3.972.73", + "@aws-sdk/nested-clients": "^3.997.41", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/credential-provider-imds": "^4.4.16", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.74", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.74.tgz", + "integrity": "sha512-0AQfDcf99TNmqVKv0owHrw/TQs6i4ZE5t9qmz6NvO53bE/sA/tpXhXL9AAcEP1qHc6Zzjd1UMb69+/9zdhvY3g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.6", + "@aws-sdk/nested-clients": "^3.997.41", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.78", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.78.tgz", + "integrity": "sha512-OgPAnfvbGAMWac6yvxJ1ihslrvDpPVwR68D2csospdNCCyPvHk9JLzYKwz48SNiS1T2znDwHauywRKRFfpyYng==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.67", + "@aws-sdk/credential-provider-http": "^3.972.69", + "@aws-sdk/credential-provider-ini": "^3.973.12", + "@aws-sdk/credential-provider-process": "^3.972.67", + "@aws-sdk/credential-provider-sso": "^3.973.11", + "@aws-sdk/credential-provider-web-identity": "^3.972.73", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/credential-provider-imds": "^4.4.16", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.67", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.67.tgz", + "integrity": "sha512-IlUEejorGTWKb4/Dm7K5Yw4QxUmXLThLhrvBmzVBqZFTbW72cv9LTcITmo1dsnYriALE4h68mOq4LB99x6sQ7Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.6", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.973.11", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.11.tgz", + "integrity": "sha512-gAQBkBZxUB84d71+pPcI9L+jh2ujhuAVxc/4FgGiWFDjkPBlMKxzd5XDtkSXTFX8Ro7ansnT88+XadasxMeCRw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.6", + "@aws-sdk/nested-clients": "^3.997.41", + "@aws-sdk/token-providers": "3.1103.0", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.73", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.73.tgz", + "integrity": "sha512-SnlEmQa6SjOgs6iOPLUQl1Eyq4AKiAdPQlkOhFhqNfDtDCwibMGvL6QlkSmf3o6vAUSImzdPCxowT5dfQUZP1A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.6", + "@aws-sdk/nested-clients": "^3.997.41", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.997.41", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.41.tgz", + "integrity": "sha512-RDHqPGQWlF6tatA/Tp3rg6oIwtgN9IVderxE+9av2Y93Dfyu+mO1hZ5Bu2jpfZg2rwdNbsssnwM+sLafIczMlQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.6", + "@aws-sdk/signature-v4-multi-region": "^3.996.43", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/fetch-http-handler": "^5.6.13", + "@smithy/node-http-handler": "^4.9.13", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.996.43", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.43.tgz", + "integrity": "sha512-lKekx8bLBXSv4O+cslk9Zfnw2XKSkWBs3uWL5QGhH2ZAQfNS7FE0vcSSN2vD/AhxX54ZTywWxR4STThoeOXlBA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.2", + "@smithy/signature-v4": "^5.6.12", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.1103.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1103.0.tgz", + "integrity": "sha512-N4wy26MNn31ItGVHYHPrEuCIFY4MBBjC+C5v1lJKqIUSA7OZBdhleCY53zCCrXn27hsk7YNOaTuhQu807S4AfQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.6", + "@aws-sdk/nested-clients": "^3.997.41", + "@aws-sdk/types": "^3.974.2", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.974.2", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.2.tgz", + "integrity": "sha512-3W6IUtSxFbH6X7Wb7DzGCV5QiFQsd0g8bOfntpmDxQlzBoKWUMBu/JPQR0DwkE+Hpnxd6db1tXbOwdeHddG6cA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.972.37", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.37.tgz", + "integrity": "sha512-zKq4HQum8JwDyEuyfuI4bbiAcU0KxP6qy+9PR/IsR92IyE/DaBAikzAS50tjxip4bqIIANpCcG+Yyj6CVhXupg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.3.0.tgz", + "integrity": "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.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", @@ -228,12 +646,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", - "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "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.7" + "@babel/types": "^7.29.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -243,9 +661,9 @@ } }, "node_modules/@babel/types": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", - "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "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", @@ -255,6 +673,171 @@ "node": ">=6.9.0" } }, + "node_modules/@bruits/satteri-darwin-arm64": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-arm64/-/satteri-darwin-arm64-0.9.5.tgz", + "integrity": "sha512-iw4nZgx9v30lWo/MTngQqi1pI78KI0DnkSm+lVJGYdmPLgAyDNJigVhpG42/Iq55A6c1Ll8q66ljyyRiQUxwow==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-darwin-x64": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-x64/-/satteri-darwin-x64-0.9.5.tgz", + "integrity": "sha512-6T26Z5Kf3cFW2PSlk9p7zT7yVxvuBSiJvYyz9u8KjYwMTqZyIDOj2wDyNpxKV4+6yUVG7rddq2QwvG/8LJA2+Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-gnu": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-gnu/-/satteri-linux-arm64-gnu-0.9.5.tgz", + "integrity": "sha512-u51id17uJwNEMK9nBlICsq6U31c+XVqQueVBkwRIzZG+gMpS8TOJctt5h5Wz33Z8xnMdTd+adtACVz0yHgGuOA==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-musl": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-musl/-/satteri-linux-arm64-musl-0.9.5.tgz", + "integrity": "sha512-v39HxiwGC5Rqm01HksP6+5Y+xKLPlsuVFgIgpEAo+SiQ22c+mJVhS3u7Z6ePAKdhL5NJoK1xq70kLz3L13AhpQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-gnu": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-gnu/-/satteri-linux-x64-gnu-0.9.5.tgz", + "integrity": "sha512-F3uO8uFp3pAP5ZGXttwvh57GS7s0lL953tnNdyI2gRyP4kOOkp6pyGojNJzCjkDvWI2Cvb9iNrKok3aqQPauAw==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-musl": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-musl/-/satteri-linux-x64-musl-0.9.5.tgz", + "integrity": "sha512-bicEqglLlz++mWyADaZoP0JY20s4vDfLjaPYgQqC+NI4zZLTOOg1T4GB8aqtc822Pqji8SQBmSrTb7CrP8i08Q==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-wasm32-wasi": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-wasm32-wasi/-/satteri-wasm32-wasi-0.9.5.tgz", + "integrity": "sha512-zauAuMwfPnKPUkd4AFixRFpXdgKwP2mKgxrIIo2gJzW0/ZneF9dbHnLkojSpaBnCCp7VUL1hIi5WWZvB1CqmAQ==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-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.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-arm64-msvc/-/satteri-win32-arm64-msvc-0.9.5.tgz", + "integrity": "sha512-SrfE7NEsgZjBvU3c+RR6oQRu0ToXY5uVJEbieXEF0YTctIV2zAVlbaMjWLts074QCgh3a+XHWkR/lWh2VH2LUg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@bruits/satteri-win32-x64-msvc": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-x64-msvc/-/satteri-win32-x64-msvc-0.9.5.tgz", + "integrity": "sha512-5Kw9ZAtTGS8WHizyn+CJhjjfIQrw+7jcZodpmpXJjefnO15M8UexIi6JR2E5thyvsmHyhL6ZDDMUNR4bKJPd4g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@capsizecss/unpack": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.1.tgz", @@ -267,6 +850,34 @@ "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/@emmetio/abbreviation": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/@emmetio/abbreviation/-/abbreviation-2.3.3.tgz", @@ -329,20 +940,43 @@ "dev": true, "license": "MIT" }, + "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.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", - "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", + "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.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", - "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "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" ], @@ -356,9 +990,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", - "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", "cpu": [ "arm" ], @@ -372,9 +1006,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", - "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "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" ], @@ -388,9 +1022,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", - "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", "cpu": [ "x64" ], @@ -404,9 +1038,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", - "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", "cpu": [ "arm64" ], @@ -420,9 +1054,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", - "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "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" ], @@ -436,9 +1070,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", - "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "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" ], @@ -452,9 +1086,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", - "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "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" ], @@ -468,9 +1102,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", - "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "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" ], @@ -484,9 +1118,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", - "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", "cpu": [ "arm64" ], @@ -500,9 +1134,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", - "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "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" ], @@ -516,9 +1150,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", - "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "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" ], @@ -532,9 +1166,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", - "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "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" ], @@ -548,9 +1182,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", - "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "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" ], @@ -564,9 +1198,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", - "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "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" ], @@ -580,9 +1214,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", - "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", "cpu": [ "s390x" ], @@ -596,9 +1230,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", - "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "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" ], @@ -612,9 +1246,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", - "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "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" ], @@ -628,9 +1262,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", - "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "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" ], @@ -644,9 +1278,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", - "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "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" ], @@ -660,9 +1294,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", - "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", "cpu": [ "x64" ], @@ -676,9 +1310,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", - "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", "cpu": [ "arm64" ], @@ -692,9 +1326,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", - "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", "cpu": [ "x64" ], @@ -708,9 +1342,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", - "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "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" ], @@ -724,9 +1358,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", - "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "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" ], @@ -740,9 +1374,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", - "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "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" ], @@ -766,9 +1400,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", + "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==", "cpu": [ "arm64" ], @@ -778,19 +1412,19 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "@img/sharp-libvips-darwin-arm64": "1.3.2" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", + "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==", "cpu": [ "x64" ], @@ -800,19 +1434,38 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "@img/sharp-libvips-darwin-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", + "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", + "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==", "cpu": [ "arm64" ], @@ -826,9 +1479,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", + "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==", "cpu": [ "x64" ], @@ -842,12 +1495,15 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", + "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==", "cpu": [ "arm" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -858,12 +1514,15 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", + "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -874,12 +1533,15 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", + "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==", "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -890,12 +1552,15 @@ } }, "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", + "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==", "cpu": [ "riscv64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -906,12 +1571,15 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", + "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==", "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -922,12 +1590,15 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", + "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -938,12 +1609,15 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", + "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -954,12 +1628,15 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", + "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -970,204 +1647,244 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", + "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==", "cpu": [ "arm" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" + "@img/sharp-libvips-linux-arm": "1.3.2" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", + "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" + "@img/sharp-libvips-linux-arm64": "1.3.2" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", + "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==", "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" + "@img/sharp-libvips-linux-ppc64": "1.3.2" } }, "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", + "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==", "cpu": [ "riscv64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "@img/sharp-libvips-linux-riscv64": "1.3.2" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", + "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==", "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" + "@img/sharp-libvips-linux-s390x": "1.3.2" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", + "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" + "@img/sharp-libvips-linux-x64": "1.3.2" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", + "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", + "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "1.3.2" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", + "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==", "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.7.0" + "@emnapi/runtime": "^1.11.1" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", + "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", + "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==", "cpu": [ "arm64" ], @@ -1177,16 +1894,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", + "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==", "cpu": [ "ia32" ], @@ -1196,16 +1913,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "^20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", + "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==", "cpu": [ "x64" ], @@ -1215,7 +1932,7 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" @@ -1227,57 +1944,46 @@ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "license": "MIT" }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.2.tgz", + "integrity": "sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==", + "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.3", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.3" + } + }, "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/@rollup/pluginutils": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", - "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", + "node_modules/@oxc-project/types": { + "version": "0.143.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.143.0.tgz", + "integrity": "sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==", "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/Boshen" } }, - "node_modules/@rollup/pluginutils/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", - "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", - "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==", + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.3.tgz", + "integrity": "sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw==", "cpu": [ "arm64" ], @@ -1285,12 +1991,15 @@ "optional": true, "os": [ "android" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", - "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.3.tgz", + "integrity": "sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA==", "cpu": [ "arm64" ], @@ -1298,12 +2007,15 @@ "optional": true, "os": [ "darwin" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", - "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==", + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.3.tgz", + "integrity": "sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA==", "cpu": [ "x64" ], @@ -1311,25 +2023,15 @@ "optional": true, "os": [ "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", - "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==", - "cpu": [ - "arm64" ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", - "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==", + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.3.tgz", + "integrity": "sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg==", "cpu": [ "x64" ], @@ -1337,12 +2039,15 @@ "optional": true, "os": [ "freebsd" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", - "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==", + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.3.tgz", + "integrity": "sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw==", "cpu": [ "arm" ], @@ -1350,181 +2055,129 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", - "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==", - "cpu": [ - "arm" ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", - "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.3.tgz", + "integrity": "sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", - "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.3.tgz", + "integrity": "sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q==", "cpu": [ "arm64" ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", - "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==", - "cpu": [ - "loong64" + "libc": [ + "musl" ], "license": "MIT", "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", - "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==", - "cpu": [ - "loong64" ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", - "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.3.tgz", + "integrity": "sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w==", "cpu": [ "ppc64" ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", - "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==", - "cpu": [ - "ppc64" + "libc": [ + "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", - "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==", - "cpu": [ - "riscv64" ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", - "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", - "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.3.tgz", + "integrity": "sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg==", "cpu": [ "s390x" ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", - "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==", - "cpu": [ - "x64" + "libc": [ + "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz", - "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.3.tgz", + "integrity": "sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", - "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.3.tgz", + "integrity": "sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ - "openbsd" - ] + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", - "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.3.tgz", + "integrity": "sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug==", "cpu": [ "arm64" ], @@ -1532,12 +2185,15 @@ "optional": true, "os": [ "openharmony" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", - "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==", + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.3.tgz", + "integrity": "sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw==", "cpu": [ "arm64" ], @@ -1545,25 +2201,15 @@ "optional": true, "os": [ "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", - "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==", - "cpu": [ - "ia32" ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", - "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.3.tgz", + "integrity": "sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg==", "cpu": [ "x64" ], @@ -1571,80 +2217,109 @@ "optional": true, "os": [ "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", - "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==", - "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": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.23.0.tgz", - "integrity": "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.4.2.tgz", + "integrity": "sha512-StyzbAyxg2/tBGf78gwbBkGyeQ73lf8UiJArFaQhTQIDqQOCKPCQFanvrs4/Yv3Yfyc+ONInJM6K+FMIf+P+kA==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.23.0", + "@shikijs/primitive": "4.4.2", + "@shikijs/types": "4.4.2", "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4", + "@types/hast": "^3.0.5", "hast-util-to-html": "^9.0.5" + }, + "engines": { + "node": ">=20" } }, "node_modules/@shikijs/engine-javascript": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.23.0.tgz", - "integrity": "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.4.2.tgz", + "integrity": "sha512-MnIkeqWdVPUWsxlx8gKLVCJFTsqrQJgpTPBPpQwaFeJ56lOnJxj5aN2LUFnfxUEcvOQuNocmbaMnVrCEln6rkw==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.23.0", + "@shikijs/types": "4.4.2", "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^4.3.4" + "oniguruma-to-es": "^4.3.6" + }, + "engines": { + "node": ">=20" } }, "node_modules/@shikijs/engine-oniguruma": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz", - "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.4.2.tgz", + "integrity": "sha512-GLhowz1+jixjz+wiZ3wMnOn1jTxiFCGl2PkXufivbnwPHKuyw1AYqu5/hbWhZZ2oAb0NP05WUJhYeigY14drnw==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.23.0", + "@shikijs/types": "4.4.2", "@shikijs/vscode-textmate": "^10.0.2" + }, + "engines": { + "node": ">=20" } }, "node_modules/@shikijs/langs": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz", - "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.4.2.tgz", + "integrity": "sha512-8DfeusD+Zdv/eYIDdXyJTUnSMHt+aAWjAOCXV20HNGAHRlInXpG8wh421v6B91WOm9TFwRLN+b/LG5F2NAIojg==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.23.0" + "@shikijs/types": "4.4.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/primitive": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.4.2.tgz", + "integrity": "sha512-l6fQQKsOMlz72n38fztmSgZ76MO6KSWuw8o+GJ+FhmqrpC9pIOJNQNXGgbb5yX2AwpzlEHwsaLPnk/8o4Fm+rA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.2", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" } }, "node_modules/@shikijs/themes": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz", - "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.4.2.tgz", + "integrity": "sha512-H0CFoL07ddDC2Dd6EdrPYNkRhUR6YCkJlnuYFceYYUJJA5TIm2b5B33qqiDYryBExgbKMndFJPb2u1gTuqO37g==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.23.0" + "@shikijs/types": "4.4.2" + }, + "engines": { + "node": ">=20" } }, "node_modules/@shikijs/types": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz", - "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.4.2.tgz", + "integrity": "sha512-PFYitV4vpDr/iPCIhnHp+Q4ftic5N5VeNJ3KQ1O8gn3h2ar8qgwMAXF7tq4m1CWaMS60fV4VqF6vfnWH4F7vqQ==", "license": "MIT", "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" } }, "node_modules/@shikijs/vscode-textmate": { @@ -1653,13 +2328,95 @@ "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", "license": "MIT" }, - "node_modules/@types/debug": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", - "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", - "license": "MIT", + "node_modules/@smithy/core": { + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.31.1.tgz", + "integrity": "sha512-CyogUINxvi7C7LDsh8Syo6hVJOT9ckz4rG8dRZfTJ8r91HkMY59PnNooaj7WcHyxEkxPfBAmbgztZU+xTo76lg==", + "license": "Apache-2.0", "dependencies": { - "@types/ms": "*" + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.4.16", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.4.16.tgz", + "integrity": "sha512-QfuLWAkLzptffFW980AFeHZFdqds2B64rpEd3uJ6lgs3xVn9QegGMUgUcj+4d7dRrAsya3r58ZKpku97WcFb4w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.6.13", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.6.13.tgz", + "integrity": "sha512-4fW86pEUOMbrD5nkbyl/tTvPHHWJFbuB2odl6ps9lWfHoXf9HWh3Q/Smh59qH1g7+c/BSZghX6bbUk4gsiMs8A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.9.13", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.9.13.tgz", + "integrity": "sha512-Nmd/Nl35zfYrd+a6OO2cDJb3GPh9bgTjIUhcM+JFfjpp8/osCgboDV5nCT1I01Pv6R13eSKDKLSoVa5ZB6Zsfw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.6.12", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.6.12.tgz", + "integrity": "sha512-I6KLtq3H0qqSuV9vLglfi8puHqzygzWHOnI4z/Rdoo+q50vvo18vBRdPAvvEtcaKROz7Zn6qnPa14kRfPH6PcQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.16.1.tgz", + "integrity": "sha512-0JFs3V2y2M9tKW5na/qxe69Zv+uxLMO7QBbhxF/FHu/Gp2NFZAAL9tWl9PU02xxo07pb3G9FTyjNc6D5uZrJIg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "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": { @@ -1668,6 +2425,15 @@ "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", @@ -1686,12 +2452,6 @@ "@types/unist": "*" } }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" - }, "node_modules/@types/nlcst": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", @@ -1822,6 +2582,13 @@ "vscode-uri": "^3.0.8" } }, + "node_modules/@vscode/emmet-helper/node_modules/jsonc-parser": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", + "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==", + "dev": true, + "license": "MIT" + }, "node_modules/@vscode/l10n": { "version": "0.0.18", "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", @@ -1829,16 +2596,17 @@ "dev": true, "license": "MIT" }, - "node_modules/acorn": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", - "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" }, "engines": { - "node": ">=0.4.0" + "node": ">= 0.6" } }, "node_modules/ajv": { @@ -1883,60 +2651,23 @@ "ajv": "^8.0.0-beta.0" } }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "license": "ISC", - "dependencies": { - "string-width": "^4.1.0" - } - }, - "node_modules/ansi-align/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-align/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "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": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "process-ancestry": "^0.1.0" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-align/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" + "bin": { + "am-i-vibing": "dist/cli.mjs" } }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -1949,6 +2680,7 @@ "version": "6.2.3", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -2003,91 +2735,70 @@ "node": ">= 0.4" } }, - "node_modules/array-iterate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", - "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/astro": { - "version": "5.18.2", - "resolved": "https://registry.npmjs.org/astro/-/astro-5.18.2.tgz", - "integrity": "sha512-TnFwLnAXty5MXKPDGuKXqK4AMBXG+FH6RUdK7Oyc3gyfNoFIthT+4eRbzOK43bdRlLaZuxgciDSjgtggZ3OtGQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/astro/-/astro-7.2.0.tgz", + "integrity": "sha512-lLTYzx3fOvCmtwD3JVBLQcbORbIOW1/j0R+3IvJx/XKwMGrk7mFnF0BYSOeRiNw1qHUR5mdA6+hRnyvyDfqrWQ==", "license": "MIT", "dependencies": { - "@astrojs/compiler": "^2.13.0", - "@astrojs/internal-helpers": "0.7.6", - "@astrojs/markdown-remark": "6.3.11", - "@astrojs/telemetry": "3.3.0", + "@astrojs/compiler-rs": "^0.3.2", + "@astrojs/internal-helpers": "0.10.2", + "@astrojs/markdown-satteri": "0.3.5", + "@astrojs/telemetry": "3.3.3", "@capsizecss/unpack": "^4.0.0", + "@clack/prompts": "^1.1.0", "@oslojs/encoding": "^1.1.0", - "@rollup/pluginutils": "^5.3.0", - "acorn": "^8.15.0", + "am-i-vibing": "^0.4.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", - "boxen": "8.0.1", - "ci-info": "^4.3.1", + "ci-info": "^4.4.0", "clsx": "^2.1.1", - "common-ancestor-path": "^1.0.1", - "cookie": "^1.1.1", - "cssesc": "^3.0.0", - "debug": "^4.4.3", - "deterministic-object-hash": "^2.0.2", - "devalue": "^5.6.2", + "common-ancestor-path": "^2.0.0", + "cookie": "^2.0.1", + "devalue": "^5.8.1", "diff": "^8.0.3", - "dlv": "^1.1.3", "dset": "^3.1.4", - "es-module-lexer": "^1.7.0", - "esbuild": "^0.27.3", - "estree-walker": "^3.0.3", + "es-module-lexer": "^2.0.0", + "esbuild": "^0.28.0", "flattie": "^1.1.1", - "fontace": "~0.4.0", + "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", - "import-meta-resolve": "^4.2.0", - "js-yaml": "^4.1.1", - "magic-string": "^0.30.21", - "magicast": "^0.5.1", + "js-yaml": "^4.3.0", + "jsonc-parser": "^3.3.1", + "magic-string": "^1.0.0", + "magicast": "^0.5.2", "mrmime": "^2.0.1", - "neotraverse": "^0.6.18", - "p-limit": "^6.2.0", - "p-queue": "^8.1.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.3", - "prompts": "^2.4.2", - "rehype": "^13.0.2", - "semver": "^7.7.3", - "shiki": "^3.21.0", + "picomatch": "^4.0.4", + "semver": "^7.7.4", + "shiki": "^4.0.2", "smol-toml": "^1.6.0", - "svgo": "^4.0.0", - "tinyexec": "^1.0.2", + "svgo": "^4.0.1", + "tinyclip": "^0.1.12", + "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", - "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", - "unifont": "~0.7.3", - "unist-util-visit": "^5.0.0", - "unstorage": "^1.17.4", - "vfile": "^6.0.3", - "vite": "^6.4.1", - "vitefu": "^1.1.1", + "unifont": "~0.7.4", + "unstorage": "^1.17.5", + "vite": "^8.0.13", + "vitefu": "^1.1.2", "xxhash-wasm": "^1.1.0", - "yargs-parser": "^21.1.1", - "yocto-spinner": "^0.2.3", - "zod": "^3.25.76", - "zod-to-json-schema": "^3.25.1", - "zod-to-ts": "^1.2.0" + "yargs-parser": "^22.0.0", + "zod": "^4.3.6" }, "bin": { - "astro": "astro.js" + "astro": "bin/astro.mjs" }, "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0", + "node": ">=22.12.0", "npm": ">=9.6.5", "pnpm": ">=7.1.0" }, @@ -2096,25 +2807,15 @@ "url": "https://opencollective.com/astrodotbuild" }, "optionalDependencies": { - "sharp": "^0.34.0" - } - }, - "node_modules/astro/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/astro/node_modules/zod-to-ts": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/zod-to-ts/-/zod-to-ts-1.2.0.tgz", - "integrity": "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==", + "sharp": "^0.34.0 || ^0.35.0" + }, "peerDependencies": { - "typescript": "^4.9.4 || ^5.0.2", - "zod": "^3" + "@astrojs/markdown-remark": "7.2.2" + }, + "peerDependenciesMeta": { + "@astrojs/markdown-remark": { + "optional": true + } } }, "node_modules/axobject-query": { @@ -2136,11 +2837,42 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/base-64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", - "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", - "license": "MIT" + "node_modules/body-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } }, "node_modules/boolbase": { "version": "1.0.0", @@ -2148,38 +2880,48 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "license": "ISC" }, - "node_modules/boxen": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz", - "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==", + "node_modules/bowser": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^8.0.0", - "chalk": "^5.3.0", - "cli-boxes": "^3.0.0", - "string-width": "^7.2.0", - "type-fest": "^4.21.0", - "widest-line": "^5.0.0", - "wrap-ansi": "^9.0.0" - }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.8" } }, - "node_modules/camelcase": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", - "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, "engines": { - "node": ">=16" + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/ccount": { @@ -2192,28 +2934,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "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", @@ -2235,15 +2955,16 @@ } }, "node_modules/chokidar": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", - "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, "license": "MIT", "dependencies": { - "readdirp": "^5.0.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 20.19.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" @@ -2264,110 +2985,37 @@ "node": ">=8" } }, - "node_modules/cli-boxes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", "dev": true, "license": "ISC", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": ">=12" + "node": ">=20" } }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/clsx": { @@ -2379,26 +3027,6 @@ "node": ">=6" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", @@ -2419,15 +3047,18 @@ } }, "node_modules/common-ancestor-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", - "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", - "license": "ISC" + "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": "1.1.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", - "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", "license": "MIT", "engines": { "node": ">=18" @@ -2437,12 +3068,43 @@ "url": "https://opencollective.com/express" } }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "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/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, "node_modules/crossws": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", @@ -2493,18 +3155,6 @@ "url": "https://github.com/sponsors/fb55" } }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/csso": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", @@ -2555,25 +3205,21 @@ } } }, - "node_modules/decode-named-character-reference": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", - "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", - "license": "MIT", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "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/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -2594,27 +3240,14 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "license": "Apache-2.0", - "optional": true, "engines": { "node": ">=8" } }, - "node_modules/deterministic-object-hash": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/deterministic-object-hash/-/deterministic-object-hash-2.0.2.tgz", - "integrity": "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==", - "license": "MIT", - "dependencies": { - "base-64": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/devalue": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.1.tgz", - "integrity": "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==", + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.0.tgz", + "integrity": "sha512-RWrqdArjvPbsATEhOPUo6Wndc/iWnkWKlhIrdlF3zMMYo/c3CVtoaVAyLtWxz5h8nSlkHzxnzV2uLydPXmtF+A==", "license": "MIT" }, "node_modules/devlop": { @@ -2639,12 +3272,6 @@ "node": ">=0.3.1" } }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "license": "MIT" - }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -2721,6 +3348,26 @@ "node": ">=4" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, "node_modules/emmet": { "version": "2.4.11", "resolved": "https://registry.npmjs.org/emmet/-/emmet-2.4.11.tgz", @@ -2742,8 +3389,18 @@ "version": "10.6.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, "license": "MIT" }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/entities": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", @@ -2756,16 +3413,46 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", "license": "MIT" }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", - "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "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": { @@ -2775,32 +3462,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.7", - "@esbuild/android-arm": "0.27.7", - "@esbuild/android-arm64": "0.27.7", - "@esbuild/android-x64": "0.27.7", - "@esbuild/darwin-arm64": "0.27.7", - "@esbuild/darwin-x64": "0.27.7", - "@esbuild/freebsd-arm64": "0.27.7", - "@esbuild/freebsd-x64": "0.27.7", - "@esbuild/linux-arm": "0.27.7", - "@esbuild/linux-arm64": "0.27.7", - "@esbuild/linux-ia32": "0.27.7", - "@esbuild/linux-loong64": "0.27.7", - "@esbuild/linux-mips64el": "0.27.7", - "@esbuild/linux-ppc64": "0.27.7", - "@esbuild/linux-riscv64": "0.27.7", - "@esbuild/linux-s390x": "0.27.7", - "@esbuild/linux-x64": "0.27.7", - "@esbuild/netbsd-arm64": "0.27.7", - "@esbuild/netbsd-x64": "0.27.7", - "@esbuild/openbsd-arm64": "0.27.7", - "@esbuild/openbsd-x64": "0.27.7", - "@esbuild/openharmony-arm64": "0.27.7", - "@esbuild/sunos-x64": "0.27.7", - "@esbuild/win32-arm64": "0.27.7", - "@esbuild/win32-ia32": "0.27.7", - "@esbuild/win32-x64": "0.27.7" + "@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/escalade": { @@ -2813,25 +3500,19 @@ "node": ">=6" } }, - "node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" + "node": ">= 0.6" } }, "node_modules/eventemitter3": { @@ -2840,6 +3521,77 @@ "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", "license": "MIT" }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.6.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.6.2.tgz", + "integrity": "sha512-YH4ru+eOJxQABscKFfRCy9R7x9QFGdezclVMwwgFFndzS2Xnm0uo6B0ABZsLhcpeptGv2qvuJVWlQr9gQZoC3A==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "ip-address": "^10.2.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -2853,10 +3605,25 @@ "dev": true, "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-uri": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz", - "integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", + "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", "dev": true, "funding": [ { @@ -2870,6 +3637,15 @@ ], "license": "BSD-3-Clause" }, + "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", @@ -2887,6 +3663,27 @@ } } }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/flattie": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", @@ -2917,6 +3714,24 @@ "node": ">=20" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2931,6 +3746,15 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -2945,6 +3769,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -2953,12 +3778,82 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gsap": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.15.0.tgz", + "integrity": "sha512-dMW4CWBTUK1AEEDeZc1g4xpPGIrSf9fJF960qbTZmN/QwZIWY5wgliS6JWl9/25fpTGJrMRtSjGtOmPnfjZB+A==", + "license": "Standard 'no charge' license: https://gsap.com/standard-license." + }, "node_modules/h3": { "version": "1.15.11", "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz", @@ -2976,6 +3871,30 @@ "uncrypto": "^0.1.3" } }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hast-util-from-html": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", @@ -3014,19 +3933,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-is-element": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", - "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/hast-util-parse-selector": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", @@ -3040,31 +3946,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-raw": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", - "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "@ungap/structured-clone": "^1.0.0", - "hast-util-from-parse5": "^8.0.0", - "hast-util-to-parse5": "^8.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "parse5": "^7.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/hast-util-to-html": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", @@ -3088,41 +3969,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-to-parse5": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", - "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-text": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", - "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "hast-util-is-element": "^3.0.0", - "unist-util-find-after": "^5.0.0" - }, - "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", @@ -3175,14 +4021,64 @@ "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", "license": "BSD-2-Clause" }, - "node_modules/import-meta-resolve": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", - "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.4.0.tgz", + "integrity": "sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" } }, "node_modules/iron-webcrypto": { @@ -3195,42 +4091,15 @@ } }, "node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "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": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "license": "MIT", - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -3248,25 +4117,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-wsl": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", - "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", - "license": "MIT", - "dependencies": { - "is-inside-container": "^1.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "funding": [ { "type": "github", @@ -3293,29 +4153,280 @@ "license": "MIT" }, "node_modules/jsonc-parser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", - "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==", - "dev": true, + "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/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "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": "github", - "url": "https://github.com/sponsors/wooorm" + "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" + ], + "libc": [ + "glibc" + ], + "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" + ], + "libc": [ + "musl" + ], + "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" + ], + "libc": [ + "glibc" + ], + "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" + ], + "libc": [ + "musl" + ], + "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": { @@ -3328,203 +4439,32 @@ } }, "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.1.0.tgz", + "integrity": "sha512-kS3VHe0nEPST2saQV4Rbkchcd3UBRkVTQHo1D3h/ZTwFDhai/mfKkmtPAtD129EOI7K3HlHIsFOt0WrI2/oU9g==", "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/magicast": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.3.tgz", - "integrity": "sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==", + "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.3", - "@babel/types": "^7.29.0", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", "source-map-js": "^1.2.1" } }, - "node_modules/markdown-table": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/mdast-util-definitions": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz", - "integrity": "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", - "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.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/mdast-util-from-markdown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", - "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", - "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">= 0.4" } }, "node_modules/mdast-util-to-hast": { @@ -3548,341 +4488,35 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.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": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", - "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/media-typer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", "license": "MIT", - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", - "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "license": "MIT", - "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" + "engines": { + "node": ">= 0.8" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://opencollective.com/express" } }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", - "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", - "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-tagfilter": { + "node_modules/merge-descriptors": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" + "engines": { + "node": ">=18" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", - "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", - "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-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "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-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/micromark-util-character": { @@ -3905,107 +4539,6 @@ "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", - "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" - } - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "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-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "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" - } - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", - "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^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", @@ -4022,60 +4555,6 @@ ], "license": "MIT" }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "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" - } - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, "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", @@ -4097,28 +4576,6 @@ "micromark-util-symbol": "^2.0.0" } }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^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", @@ -4151,6 +4608,31 @@ ], "license": "MIT" }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/mrmime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", @@ -4174,9 +4656,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.16", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", - "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "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", @@ -4191,10 +4673,19 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/neotraverse": { - "version": "0.6.18", - "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.18.tgz", - "integrity": "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==", + "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" @@ -4220,11 +4711,20 @@ "license": "MIT" }, "node_modules/node-mock-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.4.tgz", - "integrity": "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==", + "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/nodemailer": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-9.0.5.tgz", + "integrity": "sha512-wvjiKvjczmsN7U/8006JOdXubgBk2XFAbioDMbT+sM7cPs0QrhJTa6KBRX7P5REGGkDcLUz/EarWidb8G8C1jQ==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -4246,6 +4746,31 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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", @@ -4263,6 +4788,27 @@ "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", "license": "MIT" }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/oniguruma-parser": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz", @@ -4281,72 +4827,54 @@ } }, "node_modules/p-limit": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.2.0.tgz", - "integrity": "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.1.tgz", + "integrity": "sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q==", "license": "MIT", "dependencies": { - "yocto-queue": "^1.1.1" + "yocto-queue": "^1.2.1" }, "engines": { - "node": ">=18" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-queue": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.1.tgz", - "integrity": "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==", + "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.1", - "p-timeout": "^6.1.2" + "eventemitter3": "^5.0.4", + "p-timeout": "^7.0.0" }, "engines": { - "node": ">=18" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-timeout": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz", - "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==", + "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": ">=14.16" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/package-manager-detector": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.7.0.tgz", - "integrity": "sha512-xg1eHpwYL/D/HEdWw2goFZP6vV0FH7W+PZ5rFkGjdIDLtxq7EkzBUeT3m+lndYCt8wKbmofUu1MUdMCXkCk9ZQ==", + "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/parse-latin": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", - "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "@types/unist": "^3.0.0", - "nlcst-to-string": "^4.0.0", - "unist-util-modify-children": "^4.0.0", - "unist-util-visit-children": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/parse5": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", @@ -4359,6 +4887,15 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", @@ -4366,6 +4903,16 @@ "dev": true, "license": "MIT" }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/piccolore": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz", @@ -4391,9 +4938,9 @@ } }, "node_modules/postcss": { - "version": "8.5.19", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.19.tgz", - "integrity": "sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==", + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", "funding": [ { "type": "opencollective", @@ -4410,7 +4957,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -4419,9 +4966,9 @@ } }, "node_modules/prettier": { - "version": "3.9.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.5.tgz", - "integrity": "sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==", + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", "dev": true, "license": "MIT", "bin": { @@ -4443,17 +4990,13 @@ "node": ">=6" } }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "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", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, "engines": { - "node": ">= 6" + "node": ">=18.0.0" } }, "node_modules/property-information": { @@ -4466,19 +5009,77 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/radix3": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", "license": "MIT" }, - "node_modules/readdirp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", - "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "node_modules/range-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", "license": "MIT", "engines": { - "node": ">= 20.19.0" + "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" }, "funding": { "type": "individual", @@ -4509,148 +5110,6 @@ "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", "license": "MIT" }, - "node_modules/rehype": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.2.tgz", - "integrity": "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "rehype-parse": "^9.0.0", - "rehype-stringify": "^10.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-parse": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", - "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-from-html": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-raw": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", - "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-raw": "^9.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-stringify": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", - "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-to-html": "^9.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-gfm": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", - "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", - "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-smartypants": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.2.tgz", - "integrity": "sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==", - "license": "MIT", - "dependencies": { - "retext": "^9.0.0", - "retext-smartypants": "^6.0.0", - "unified": "^11.0.4", - "unist-util-visit": "^5.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/request-light": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.7.0.tgz", @@ -4658,16 +5117,6 @@ "dev": true, "license": "MIT" }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -4678,35 +5127,13 @@ "node": ">=0.10.0" } }, - "node_modules/retext": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", - "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==", + "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", - "dependencies": { - "@types/nlcst": "^2.0.0", - "retext-latin": "^4.0.0", - "retext-stringify": "^4.0.0", - "unified": "^11.0.0" - }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-latin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz", - "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "parse-latin": "^7.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, "node_modules/retext-smartypants": { @@ -4724,69 +5151,87 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/retext-stringify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz", - "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==", + "node_modules/rolldown": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.3.tgz", + "integrity": "sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A==", "license": "MIT", "dependencies": { - "@types/nlcst": "^2.0.0", - "nlcst-to-string": "^4.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rollup": { - "version": "4.62.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", - "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.9" + "@oxc-project/types": "=0.143.0", + "@rolldown/pluginutils": "^1.0.0" }, "bin": { - "rollup": "dist/bin/rollup" + "rolldown": "bin/cli.mjs" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.62.2", - "@rollup/rollup-android-arm64": "4.62.2", - "@rollup/rollup-darwin-arm64": "4.62.2", - "@rollup/rollup-darwin-x64": "4.62.2", - "@rollup/rollup-freebsd-arm64": "4.62.2", - "@rollup/rollup-freebsd-x64": "4.62.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", - "@rollup/rollup-linux-arm-musleabihf": "4.62.2", - "@rollup/rollup-linux-arm64-gnu": "4.62.2", - "@rollup/rollup-linux-arm64-musl": "4.62.2", - "@rollup/rollup-linux-loong64-gnu": "4.62.2", - "@rollup/rollup-linux-loong64-musl": "4.62.2", - "@rollup/rollup-linux-ppc64-gnu": "4.62.2", - "@rollup/rollup-linux-ppc64-musl": "4.62.2", - "@rollup/rollup-linux-riscv64-gnu": "4.62.2", - "@rollup/rollup-linux-riscv64-musl": "4.62.2", - "@rollup/rollup-linux-s390x-gnu": "4.62.2", - "@rollup/rollup-linux-x64-gnu": "4.62.2", - "@rollup/rollup-linux-x64-musl": "4.62.2", - "@rollup/rollup-openbsd-x64": "4.62.2", - "@rollup/rollup-openharmony-arm64": "4.62.2", - "@rollup/rollup-win32-arm64-msvc": "4.62.2", - "@rollup/rollup-win32-ia32-msvc": "4.62.2", - "@rollup/rollup-win32-x64-gnu": "4.62.2", - "@rollup/rollup-win32-x64-msvc": "4.62.2", - "fsevents": "~2.3.2" + "@rolldown/binding-android-arm64": "1.2.3", + "@rolldown/binding-darwin-arm64": "1.2.3", + "@rolldown/binding-darwin-x64": "1.2.3", + "@rolldown/binding-freebsd-x64": "1.2.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.3", + "@rolldown/binding-linux-arm64-gnu": "1.2.3", + "@rolldown/binding-linux-arm64-musl": "1.2.3", + "@rolldown/binding-linux-ppc64-gnu": "1.2.3", + "@rolldown/binding-linux-s390x-gnu": "1.2.3", + "@rolldown/binding-linux-x64-gnu": "1.2.3", + "@rolldown/binding-linux-x64-musl": "1.2.3", + "@rolldown/binding-openharmony-arm64": "1.2.3", + "@rolldown/binding-win32-arm64-msvc": "1.2.3", + "@rolldown/binding-win32-x64-msvc": "1.2.3" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/satteri": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/satteri/-/satteri-0.9.5.tgz", + "integrity": "sha512-ZuWVl+vnM64y+/TtX8Kosv2c00W+hLQiiwnEL6H0UKVVrxFqMw4D2CJHHQaouVd89OAhtBBfjWLqhKi3TVUV4w==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.5", + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "@types/unist": "^3.0.3" + }, + "optionalDependencies": { + "@bruits/satteri-darwin-arm64": "0.9.5", + "@bruits/satteri-darwin-x64": "0.9.5", + "@bruits/satteri-linux-arm64-gnu": "0.9.5", + "@bruits/satteri-linux-arm64-musl": "0.9.5", + "@bruits/satteri-linux-x64-gnu": "0.9.5", + "@bruits/satteri-linux-x64-musl": "0.9.5", + "@bruits/satteri-wasm32-wasi": "0.9.5", + "@bruits/satteri-win32-arm64-msvc": "0.9.5", + "@bruits/satteri-win32-x64-msvc": "0.9.5" } }, "node_modules/sax": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", - "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "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" @@ -4804,65 +5249,196 @@ "node": ">=10" } }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz", + "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==", "license": "Apache-2.0", "optional": true, "dependencies": { - "@img/colour": "^1.0.0", + "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", - "semver": "^7.7.3" + "semver": "^7.8.5" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" + "@img/sharp-darwin-arm64": "0.35.3", + "@img/sharp-darwin-x64": "0.35.3", + "@img/sharp-freebsd-wasm32": "0.35.3", + "@img/sharp-libvips-darwin-arm64": "1.3.2", + "@img/sharp-libvips-darwin-x64": "1.3.2", + "@img/sharp-libvips-linux-arm": "1.3.2", + "@img/sharp-libvips-linux-arm64": "1.3.2", + "@img/sharp-libvips-linux-ppc64": "1.3.2", + "@img/sharp-libvips-linux-riscv64": "1.3.2", + "@img/sharp-libvips-linux-s390x": "1.3.2", + "@img/sharp-libvips-linux-x64": "1.3.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", + "@img/sharp-libvips-linuxmusl-x64": "1.3.2", + "@img/sharp-linux-arm": "0.35.3", + "@img/sharp-linux-arm64": "0.35.3", + "@img/sharp-linux-ppc64": "0.35.3", + "@img/sharp-linux-riscv64": "0.35.3", + "@img/sharp-linux-s390x": "0.35.3", + "@img/sharp-linux-x64": "0.35.3", + "@img/sharp-linuxmusl-arm64": "0.35.3", + "@img/sharp-linuxmusl-x64": "0.35.3", + "@img/sharp-webcontainers-wasm32": "0.35.3", + "@img/sharp-win32-arm64": "0.35.3", + "@img/sharp-win32-ia32": "0.35.3", + "@img/sharp-win32-x64": "0.35.3" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, "node_modules/shiki": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.23.0.tgz", - "integrity": "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.4.2.tgz", + "integrity": "sha512-P8F/dFhRevaw2uSdeIYlq/5SXZNY85DPtmXQ947gD1Zj2JqO5AkNvVVBar0Me9JkFx3uzVud/qOtP5ek9NEQGA==", "license": "MIT", "dependencies": { - "@shikijs/core": "3.23.0", - "@shikijs/engine-javascript": "3.23.0", - "@shikijs/engine-oniguruma": "3.23.0", - "@shikijs/langs": "3.23.0", - "@shikijs/themes": "3.23.0", - "@shikijs/types": "3.23.0", + "@shikijs/core": "4.4.2", + "@shikijs/engine-javascript": "4.4.2", + "@shikijs/engine-oniguruma": "4.4.2", + "@shikijs/langs": "4.4.2", + "@shikijs/themes": "4.4.2", + "@shikijs/types": "4.4.2", "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/sisteransi": { @@ -4891,9 +5467,9 @@ } }, "node_modules/smol-toml": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.0.tgz", - "integrity": "sha512-aqVvWoyO21L23mb+drl4RmMXbf6N7FdHjAhTRA9ZBL7apWBgfWC16KjrASI+1p9GAroljyMHj6fK67i0UiTNvQ==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.1.tgz", + "integrity": "sha512-PPlsspAZ4jbMBu5DMFhfUGDQLu/vrL4SyBROVS37x8ynnVmFIs1VPBz1Co8Xks3TvpIaZXmU85y4DrQ+UyVFoQ==", "license": "BSD-3-Clause", "engines": { "node": ">= 18" @@ -4921,6 +5497,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/stream-replace-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz", @@ -4928,17 +5513,17 @@ "license": "MIT" }, "node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz", + "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==", + "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" }, "engines": { - "node": ">=18" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4962,6 +5547,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.2.2" @@ -5004,10 +5590,19 @@ "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", "license": "MIT" }, + "node_modules/tinyclip": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/tinyclip/-/tinyclip-0.1.15.tgz", + "integrity": "sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A==", + "license": "MIT", + "engines": { + "node": "^16.14.0 || >= 17.3.0" + } + }, "node_modules/tinyexec": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", - "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "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" @@ -5029,6 +5624,15 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", @@ -5049,44 +5653,41 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/tsconfck": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", - "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", - "deprecated": "unmaintained", - "license": "MIT", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, - "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "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 + "license": "0BSD" }, - "node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "license": "(MIT OR CC0-1.0)", + "node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, "engines": { - "node": ">=16" + "node": ">= 18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/typesafe-path": { @@ -5100,6 +5701,7 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -5173,20 +5775,6 @@ "ohash": "^2.0.11" } }, - "node_modules/unist-util-find-after": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", - "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/unist-util-is": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", @@ -5200,20 +5788,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-modify-children": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", - "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "array-iterate": "^2.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", @@ -5227,20 +5801,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-remove-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", - "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.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", @@ -5269,19 +5829,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-visit-children": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", - "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.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", @@ -5296,6 +5843,15 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/unstorage": { "version": "1.17.5", "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", @@ -5392,6 +5948,43 @@ } } }, + "node_modules/unstorage/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/unstorage/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/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", @@ -5435,23 +6028,22 @@ } }, "node_modules/vite": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", - "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz", + "integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==", "license": "MIT", "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.25", + "rolldown": "~1.2.1", + "tinyglobby": "^0.2.17" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -5460,14 +6052,15 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.4.0", + "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", + "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" @@ -5476,15 +6069,18 @@ "@types/node": { "optional": true }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, "jiti": { "optional": true }, "less": { "optional": true }, - "lightningcss": { - "optional": true - }, "sass": { "optional": true }, @@ -5508,463 +6104,6 @@ } } }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" - } - }, "node_modules/vitefu": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", @@ -6177,13 +6316,6 @@ "npm": ">=7.0.0" } }, - "node_modules/vscode-json-languageservice/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==", - "dev": true, - "license": "MIT" - }, "node_modules/vscode-jsonrpc": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.1.tgz", @@ -6284,34 +6416,11 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/which-pm-runs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", - "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/widest-line": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", - "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", - "license": "MIT", - "dependencies": { - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/wrap-ansi": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^6.2.1", @@ -6325,6 +6434,30 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, "node_modules/xxhash-wasm": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", @@ -6405,76 +6538,30 @@ } }, "node_modules/yargs": { - "version": "17.7.3", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", - "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.1.0.tgz", + "integrity": "sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==", "dev": true, "license": "MIT", "dependencies": { - "cliui": "^8.0.1", + "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", + "string-width": "^8.2.1", "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "yargs-parser": "^22.0.0" }, "engines": { - "node": ">=12" + "node": "^20.19.0 || ^22.12.0 || >=23" } }, "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "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": ">=12" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" + "node": "^20.19.0 || ^22.12.0 || >=23" } }, "node_modules/yocto-queue": { @@ -6489,33 +6576,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yocto-spinner": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/yocto-spinner/-/yocto-spinner-0.2.3.tgz", - "integrity": "sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==", - "license": "MIT", - "dependencies": { - "yoctocolors": "^2.1.1" - }, - "engines": { - "node": ">=18.19" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoctocolors": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", - "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/zod": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", @@ -6525,15 +6585,6 @@ "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/zod-to-json-schema": { - "version": "3.25.2", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", - "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.25.28 || ^4" - } - }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/package.json b/package.json index 246af64..3bbcca6 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,23 @@ "version": "1.0.0", "private": true, "type": "module", - "scripts": {"dev": "astro dev", "build": "astro check && astro build", "preview": "astro preview"}, - "dependencies": {"@astrojs/sitemap": "^3.2.1", "astro": "^5.10.0"}, - "devDependencies": {"@astrojs/check": "^0.9.4", "typescript": "^5.8.3"} + "scripts": { + "dev": "astro dev", + "build": "astro check && astro build", + "preview": "astro preview", + "start": "node server.js" + }, + "dependencies": { + "@aws-sdk/client-sesv2": "^3.883.0", + "@astrojs/sitemap": "^3.2.1", + "astro": "^7.1.1", + "express": "^5.1.0", + "express-rate-limit": "^8.2.1", + "gsap": "^3.15.0", + "nodemailer": "^9.0.3" + }, + "devDependencies": { + "@astrojs/check": "^0.9.4", + "typescript": "^5.8.3" + } } diff --git a/public/images/blog/2026-06-15-chatgpt-opens-ads-for-all-ad-placements.png b/public/images/blog/2026-06-15-chatgpt-opens-ads-for-all-ad-placements.png new file mode 100644 index 0000000..bbdaac0 Binary files /dev/null and b/public/images/blog/2026-06-15-chatgpt-opens-ads-for-all-ad-placements.png differ diff --git a/public/images/blog/2026-06-15-chatgpt-opens-ads-for-all-chatgpt-hero.png b/public/images/blog/2026-06-15-chatgpt-opens-ads-for-all-chatgpt-hero.png new file mode 100644 index 0000000..1c9ae6c Binary files /dev/null and b/public/images/blog/2026-06-15-chatgpt-opens-ads-for-all-chatgpt-hero.png differ diff --git a/public/images/blog/2026-06-15-chatgpt-opens-ads-for-all-comparison-table.png b/public/images/blog/2026-06-15-chatgpt-opens-ads-for-all-comparison-table.png new file mode 100644 index 0000000..9ccf30f Binary files /dev/null and b/public/images/blog/2026-06-15-chatgpt-opens-ads-for-all-comparison-table.png differ diff --git a/public/images/blog/2026-06-15-chatgpt-opens-ads-for-all-featured.png b/public/images/blog/2026-06-15-chatgpt-opens-ads-for-all-featured.png new file mode 100644 index 0000000..c30c3b1 Binary files /dev/null and b/public/images/blog/2026-06-15-chatgpt-opens-ads-for-all-featured.png differ diff --git a/public/images/blog/2026-06-15-chatgpt-opens-ads-for-all-numbers-chart.png b/public/images/blog/2026-06-15-chatgpt-opens-ads-for-all-numbers-chart.png new file mode 100644 index 0000000..c1b0ed6 Binary files /dev/null and b/public/images/blog/2026-06-15-chatgpt-opens-ads-for-all-numbers-chart.png differ diff --git a/public/images/blog/2026-06-15-chatgpt-opens-ads-for-all-sponsored-answers.png b/public/images/blog/2026-06-15-chatgpt-opens-ads-for-all-sponsored-answers.png new file mode 100644 index 0000000..cd7b3f8 Binary files /dev/null and b/public/images/blog/2026-06-15-chatgpt-opens-ads-for-all-sponsored-answers.png differ diff --git a/public/images/blog/2026-06-26-ai-visibility-operational-alignment-conways-law.png b/public/images/blog/2026-06-26-ai-visibility-operational-alignment-conways-law.png new file mode 100644 index 0000000..1100ad8 Binary files /dev/null and b/public/images/blog/2026-06-26-ai-visibility-operational-alignment-conways-law.png differ diff --git a/public/images/blog/2026-06-26-ai-visibility-operational-alignment-featured.png b/public/images/blog/2026-06-26-ai-visibility-operational-alignment-featured.png new file mode 100644 index 0000000..c9648ae Binary files /dev/null and b/public/images/blog/2026-06-26-ai-visibility-operational-alignment-featured.png differ diff --git a/public/images/blog/2026-06-26-ai-visibility-operational-alignment-framework-checklist.png b/public/images/blog/2026-06-26-ai-visibility-operational-alignment-framework-checklist.png new file mode 100644 index 0000000..c2f9aff Binary files /dev/null and b/public/images/blog/2026-06-26-ai-visibility-operational-alignment-framework-checklist.png differ diff --git a/public/images/blog/2026-06-26-ai-visibility-operational-alignment-risk-scenarios.png b/public/images/blog/2026-06-26-ai-visibility-operational-alignment-risk-scenarios.png new file mode 100644 index 0000000..35ac7ec Binary files /dev/null and b/public/images/blog/2026-06-26-ai-visibility-operational-alignment-risk-scenarios.png differ diff --git a/public/images/blog/2026-06-26-b2b-marketers-future-of-work-ai-importance-infographic.png b/public/images/blog/2026-06-26-b2b-marketers-future-of-work-ai-importance-infographic.png new file mode 100644 index 0000000..985d1d9 Binary files /dev/null and b/public/images/blog/2026-06-26-b2b-marketers-future-of-work-ai-importance-infographic.png differ diff --git a/public/images/blog/2026-06-26-b2b-marketers-future-of-work-featured.png b/public/images/blog/2026-06-26-b2b-marketers-future-of-work-featured.png new file mode 100644 index 0000000..43f7d38 Binary files /dev/null and b/public/images/blog/2026-06-26-b2b-marketers-future-of-work-featured.png differ diff --git a/public/images/blog/2026-06-26-b2b-marketers-future-of-work-future-of-work-job-displacement.png b/public/images/blog/2026-06-26-b2b-marketers-future-of-work-future-of-work-job-displacement.png new file mode 100644 index 0000000..6b8ecfa Binary files /dev/null and b/public/images/blog/2026-06-26-b2b-marketers-future-of-work-future-of-work-job-displacement.png differ diff --git a/public/images/blog/2026-06-26-b2b-marketers-future-of-work-training-gap.png b/public/images/blog/2026-06-26-b2b-marketers-future-of-work-training-gap.png new file mode 100644 index 0000000..6b8ecfa Binary files /dev/null and b/public/images/blog/2026-06-26-b2b-marketers-future-of-work-training-gap.png differ diff --git a/public/images/blog/2026-06-26-brand-reputation-precedes-you-with-ai-aicher-principle.png b/public/images/blog/2026-06-26-brand-reputation-precedes-you-with-ai-aicher-principle.png new file mode 100644 index 0000000..6d8dfe0 Binary files /dev/null and b/public/images/blog/2026-06-26-brand-reputation-precedes-you-with-ai-aicher-principle.png differ diff --git a/public/images/blog/2026-06-26-brand-reputation-precedes-you-with-ai-featured.png b/public/images/blog/2026-06-26-brand-reputation-precedes-you-with-ai-featured.png new file mode 100644 index 0000000..9bb3659 Binary files /dev/null and b/public/images/blog/2026-06-26-brand-reputation-precedes-you-with-ai-featured.png differ diff --git a/public/images/blog/2026-06-26-brand-reputation-precedes-you-with-ai-hero-brand-ai.png b/public/images/blog/2026-06-26-brand-reputation-precedes-you-with-ai-hero-brand-ai.png new file mode 100644 index 0000000..13b091f Binary files /dev/null and b/public/images/blog/2026-06-26-brand-reputation-precedes-you-with-ai-hero-brand-ai.png differ diff --git a/public/images/blog/2026-06-26-brand-reputation-precedes-you-with-ai-narrative-gravity.png b/public/images/blog/2026-06-26-brand-reputation-precedes-you-with-ai-narrative-gravity.png new file mode 100644 index 0000000..bdc80d5 Binary files /dev/null and b/public/images/blog/2026-06-26-brand-reputation-precedes-you-with-ai-narrative-gravity.png differ diff --git a/public/images/blog/2026-06-26-brand-reputation-precedes-you-with-ai-wikipedia-dominance.png b/public/images/blog/2026-06-26-brand-reputation-precedes-you-with-ai-wikipedia-dominance.png new file mode 100644 index 0000000..721280f Binary files /dev/null and b/public/images/blog/2026-06-26-brand-reputation-precedes-you-with-ai-wikipedia-dominance.png differ diff --git a/public/images/blog/2026-06-26-fintech-invisible-to-ai-agents-data-visualization.png b/public/images/blog/2026-06-26-fintech-invisible-to-ai-agents-data-visualization.png new file mode 100644 index 0000000..9e676f7 Binary files /dev/null and b/public/images/blog/2026-06-26-fintech-invisible-to-ai-agents-data-visualization.png differ diff --git a/public/images/blog/2026-06-26-fintech-invisible-to-ai-agents-devtools-screenshot.png b/public/images/blog/2026-06-26-fintech-invisible-to-ai-agents-devtools-screenshot.png new file mode 100644 index 0000000..34a7de5 Binary files /dev/null and b/public/images/blog/2026-06-26-fintech-invisible-to-ai-agents-devtools-screenshot.png differ diff --git a/public/images/blog/2026-06-26-fintech-invisible-to-ai-agents-featured.png b/public/images/blog/2026-06-26-fintech-invisible-to-ai-agents-featured.png new file mode 100644 index 0000000..31a7a83 Binary files /dev/null and b/public/images/blog/2026-06-26-fintech-invisible-to-ai-agents-featured.png differ diff --git a/public/images/blog/2026-06-26-fintech-invisible-to-ai-agents-solution-diagram.png b/public/images/blog/2026-06-26-fintech-invisible-to-ai-agents-solution-diagram.png new file mode 100644 index 0000000..cb30f2e Binary files /dev/null and b/public/images/blog/2026-06-26-fintech-invisible-to-ai-agents-solution-diagram.png differ diff --git a/public/images/blog/2026-06-26-google-seo-for-ai-agents-agentic-browsing.png b/public/images/blog/2026-06-26-google-seo-for-ai-agents-agentic-browsing.png new file mode 100644 index 0000000..f0e7f91 Binary files /dev/null and b/public/images/blog/2026-06-26-google-seo-for-ai-agents-agentic-browsing.png differ diff --git a/public/images/blog/2026-06-26-google-seo-for-ai-agents-featured.png b/public/images/blog/2026-06-26-google-seo-for-ai-agents-featured.png new file mode 100644 index 0000000..bb9187a Binary files /dev/null and b/public/images/blog/2026-06-26-google-seo-for-ai-agents-featured.png differ diff --git a/public/images/blog/2026-06-26-google-seo-for-ai-agents-quality-checklist.png b/public/images/blog/2026-06-26-google-seo-for-ai-agents-quality-checklist.png new file mode 100644 index 0000000..4f1c51f Binary files /dev/null and b/public/images/blog/2026-06-26-google-seo-for-ai-agents-quality-checklist.png differ diff --git a/public/images/blog/2026-06-26-google-seo-for-ai-agents-quality-signals.png b/public/images/blog/2026-06-26-google-seo-for-ai-agents-quality-signals.png new file mode 100644 index 0000000..8604ad7 Binary files /dev/null and b/public/images/blog/2026-06-26-google-seo-for-ai-agents-quality-signals.png differ diff --git a/public/images/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai-agent-playbook.png b/public/images/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai-agent-playbook.png new file mode 100644 index 0000000..91cd6a5 Binary files /dev/null and b/public/images/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai-agent-playbook.png differ diff --git a/public/images/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai-ai-adoption-stages-infographic.png b/public/images/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai-ai-adoption-stages-infographic.png new file mode 100644 index 0000000..63d6bf7 Binary files /dev/null and b/public/images/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai-ai-adoption-stages-infographic.png differ diff --git a/public/images/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai-b2b-marketer-ai-orchestration-hero.png b/public/images/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai-b2b-marketer-ai-orchestration-hero.png new file mode 100644 index 0000000..91cd6a5 Binary files /dev/null and b/public/images/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai-b2b-marketer-ai-orchestration-hero.png differ diff --git a/public/images/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai-featured.png b/public/images/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai-featured.png new file mode 100644 index 0000000..b903a38 Binary files /dev/null and b/public/images/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai-featured.png differ diff --git a/public/images/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai-production-vs-orchestration.png b/public/images/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai-production-vs-orchestration.png new file mode 100644 index 0000000..63d6bf7 Binary files /dev/null and b/public/images/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai-production-vs-orchestration.png differ diff --git a/public/images/blog/2026-06-27-ai-brand-reputation-167k-citations-featured.png b/public/images/blog/2026-06-27-ai-brand-reputation-167k-citations-featured.png new file mode 100644 index 0000000..291bcfa Binary files /dev/null and b/public/images/blog/2026-06-27-ai-brand-reputation-167k-citations-featured.png differ diff --git a/public/images/blog/2026-06-27-ai-brand-reputation-167k-citations-infographic_scope.png b/public/images/blog/2026-06-27-ai-brand-reputation-167k-citations-infographic_scope.png new file mode 100644 index 0000000..20297df Binary files /dev/null and b/public/images/blog/2026-06-27-ai-brand-reputation-167k-citations-infographic_scope.png differ diff --git a/public/images/blog/2026-06-27-ai-brand-reputation-167k-citations-multilingual_map.png b/public/images/blog/2026-06-27-ai-brand-reputation-167k-citations-multilingual_map.png new file mode 100644 index 0000000..494f87a Binary files /dev/null and b/public/images/blog/2026-06-27-ai-brand-reputation-167k-citations-multilingual_map.png differ diff --git a/public/images/blog/2026-06-27-ai-brand-reputation-167k-citations-pie_chart_citations.png b/public/images/blog/2026-06-27-ai-brand-reputation-167k-citations-pie_chart_citations.png new file mode 100644 index 0000000..a903638 Binary files /dev/null and b/public/images/blog/2026-06-27-ai-brand-reputation-167k-citations-pie_chart_citations.png differ diff --git a/public/images/blog/2026-06-27-ai-brand-reputation-167k-citations-wikipedia_dominance.png b/public/images/blog/2026-06-27-ai-brand-reputation-167k-citations-wikipedia_dominance.png new file mode 100644 index 0000000..721280f Binary files /dev/null and b/public/images/blog/2026-06-27-ai-brand-reputation-167k-citations-wikipedia_dominance.png differ diff --git a/public/images/blog/2026-06-27-ai-recommendation-multi-industry-map-ai-recommendation-concept.png b/public/images/blog/2026-06-27-ai-recommendation-multi-industry-map-ai-recommendation-concept.png new file mode 100644 index 0000000..de59641 Binary files /dev/null and b/public/images/blog/2026-06-27-ai-recommendation-multi-industry-map-ai-recommendation-concept.png differ diff --git a/public/images/blog/2026-06-27-ai-recommendation-multi-industry-map-featured.png b/public/images/blog/2026-06-27-ai-recommendation-multi-industry-map-featured.png new file mode 100644 index 0000000..4050311 Binary files /dev/null and b/public/images/blog/2026-06-27-ai-recommendation-multi-industry-map-featured.png differ diff --git a/public/images/blog/2026-06-27-ai-recommendation-multi-industry-map-gini-coefficient.png b/public/images/blog/2026-06-27-ai-recommendation-multi-industry-map-gini-coefficient.png new file mode 100644 index 0000000..6f8d939 Binary files /dev/null and b/public/images/blog/2026-06-27-ai-recommendation-multi-industry-map-gini-coefficient.png differ diff --git a/public/images/blog/2026-06-27-ai-recommendation-multi-industry-map-methodology-diagram.png b/public/images/blog/2026-06-27-ai-recommendation-multi-industry-map-methodology-diagram.png new file mode 100644 index 0000000..d6113fd Binary files /dev/null and b/public/images/blog/2026-06-27-ai-recommendation-multi-industry-map-methodology-diagram.png differ diff --git a/public/images/blog/2026-06-27-ai-recommendation-multi-industry-map-three-metrics-diagram-v2.png b/public/images/blog/2026-06-27-ai-recommendation-multi-industry-map-three-metrics-diagram-v2.png new file mode 100644 index 0000000..6c99faf Binary files /dev/null and b/public/images/blog/2026-06-27-ai-recommendation-multi-industry-map-three-metrics-diagram-v2.png differ diff --git a/public/images/blog/2026-06-27-ai-recommendation-multi-industry-map-three-metrics-diagram.png b/public/images/blog/2026-06-27-ai-recommendation-multi-industry-map-three-metrics-diagram.png new file mode 100644 index 0000000..4143c23 Binary files /dev/null and b/public/images/blog/2026-06-27-ai-recommendation-multi-industry-map-three-metrics-diagram.png differ diff --git a/public/images/blog/2026-06-27-google-open-knowledge-format-featured.png b/public/images/blog/2026-06-27-google-open-knowledge-format-featured.png new file mode 100644 index 0000000..ccb3bb3 Binary files /dev/null and b/public/images/blog/2026-06-27-google-open-knowledge-format-featured.png differ diff --git a/public/images/blog/2026-06-27-google-open-knowledge-format-google-okf-hero.png b/public/images/blog/2026-06-27-google-open-knowledge-format-google-okf-hero.png new file mode 100644 index 0000000..b97857a Binary files /dev/null and b/public/images/blog/2026-06-27-google-open-knowledge-format-google-okf-hero.png differ diff --git a/public/images/blog/2026-06-27-google-open-knowledge-format-okf-architecture.png b/public/images/blog/2026-06-27-google-open-knowledge-format-okf-architecture.png new file mode 100644 index 0000000..ddc8e5f Binary files /dev/null and b/public/images/blog/2026-06-27-google-open-knowledge-format-okf-architecture.png differ diff --git a/public/images/blog/2026-06-27-google-open-knowledge-format-okf-vs-llmstxt.png b/public/images/blog/2026-06-27-google-open-knowledge-format-okf-vs-llmstxt.png new file mode 100644 index 0000000..2622a51 Binary files /dev/null and b/public/images/blog/2026-06-27-google-open-knowledge-format-okf-vs-llmstxt.png differ diff --git a/public/images/blog/2026-06-27-opid-ai-agent-self-training-featured.png b/public/images/blog/2026-06-27-opid-ai-agent-self-training-featured.png new file mode 100644 index 0000000..88f7bbc Binary files /dev/null and b/public/images/blog/2026-06-27-opid-ai-agent-self-training-featured.png differ diff --git a/public/images/blog/2026-06-27-opid-ai-agent-self-training-hierarchical-skills.png b/public/images/blog/2026-06-27-opid-ai-agent-self-training-hierarchical-skills.png new file mode 100644 index 0000000..f28b278 Binary files /dev/null and b/public/images/blog/2026-06-27-opid-ai-agent-self-training-hierarchical-skills.png differ diff --git a/public/images/blog/2026-06-27-opid-ai-agent-self-training-opid-ai-agent-self-training-hero.png b/public/images/blog/2026-06-27-opid-ai-agent-self-training-opid-ai-agent-self-training-hero.png new file mode 100644 index 0000000..a0e6e54 Binary files /dev/null and b/public/images/blog/2026-06-27-opid-ai-agent-self-training-opid-ai-agent-self-training-hero.png differ diff --git a/public/images/blog/2026-06-27-opid-ai-agent-self-training-opid-on-policy-vs-off-policy.png b/public/images/blog/2026-06-27-opid-ai-agent-self-training-opid-on-policy-vs-off-policy.png new file mode 100644 index 0000000..64d259e Binary files /dev/null and b/public/images/blog/2026-06-27-opid-ai-agent-self-training-opid-on-policy-vs-off-policy.png differ diff --git a/public/images/blog/2026-06-27-opid-ai-agent-self-training-test-results.png b/public/images/blog/2026-06-27-opid-ai-agent-self-training-test-results.png new file mode 100644 index 0000000..a0e6e54 Binary files /dev/null and b/public/images/blog/2026-06-27-opid-ai-agent-self-training-test-results.png differ diff --git a/public/images/blog/2026-06-28-broken-form-cost-agency-leads-broken-form-leads-featured.png b/public/images/blog/2026-06-28-broken-form-cost-agency-leads-broken-form-leads-featured.png new file mode 100644 index 0000000..0d60f25 Binary files /dev/null and b/public/images/blog/2026-06-28-broken-form-cost-agency-leads-broken-form-leads-featured.png differ diff --git a/public/images/blog/2026-06-28-broken-form-cost-agency-leads-featured.png b/public/images/blog/2026-06-28-broken-form-cost-agency-leads-featured.png new file mode 100644 index 0000000..0d60f25 Binary files /dev/null and b/public/images/blog/2026-06-28-broken-form-cost-agency-leads-featured.png differ diff --git a/public/images/blog/2026-06-28-broken-form-cost-agency-leads-form-notification-diagram.png b/public/images/blog/2026-06-28-broken-form-cost-agency-leads-form-notification-diagram.png new file mode 100644 index 0000000..3ca1ef7 Binary files /dev/null and b/public/images/blog/2026-06-28-broken-form-cost-agency-leads-form-notification-diagram.png differ diff --git a/public/images/blog/2026-06-28-broken-form-cost-agency-leads-monitoring-dashboard.png b/public/images/blog/2026-06-28-broken-form-cost-agency-leads-monitoring-dashboard.png new file mode 100644 index 0000000..79c3e54 Binary files /dev/null and b/public/images/blog/2026-06-28-broken-form-cost-agency-leads-monitoring-dashboard.png differ diff --git a/public/images/blog/2026-06-28-broken-form-cost-agency-leads-qa-checklist.png b/public/images/blog/2026-06-28-broken-form-cost-agency-leads-qa-checklist.png new file mode 100644 index 0000000..41b750f Binary files /dev/null and b/public/images/blog/2026-06-28-broken-form-cost-agency-leads-qa-checklist.png differ diff --git a/public/images/blog/2026-06-28-openknowledge-ai-first-obsidian-diagram-ai-integration.png b/public/images/blog/2026-06-28-openknowledge-ai-first-obsidian-diagram-ai-integration.png new file mode 100644 index 0000000..4830b73 Binary files /dev/null and b/public/images/blog/2026-06-28-openknowledge-ai-first-obsidian-diagram-ai-integration.png differ diff --git a/public/images/blog/2026-06-28-openknowledge-ai-first-obsidian-featured.png b/public/images/blog/2026-06-28-openknowledge-ai-first-obsidian-featured.png new file mode 100644 index 0000000..1da5219 Binary files /dev/null and b/public/images/blog/2026-06-28-openknowledge-ai-first-obsidian-featured.png differ diff --git a/public/images/blog/2026-06-28-openknowledge-ai-first-obsidian-hero-openknowledge-ui.png b/public/images/blog/2026-06-28-openknowledge-ai-first-obsidian-hero-openknowledge-ui.png new file mode 100644 index 0000000..7f1a951 Binary files /dev/null and b/public/images/blog/2026-06-28-openknowledge-ai-first-obsidian-hero-openknowledge-ui.png differ diff --git a/public/images/blog/2026-06-28-openknowledge-ai-first-obsidian-screenshot-editor-interface.png b/public/images/blog/2026-06-28-openknowledge-ai-first-obsidian-screenshot-editor-interface.png new file mode 100644 index 0000000..42ce14c Binary files /dev/null and b/public/images/blog/2026-06-28-openknowledge-ai-first-obsidian-screenshot-editor-interface.png differ diff --git a/public/images/blog/2026-06-28-profound-vs-bluefish-aeo-aeo-overview-diagram.png b/public/images/blog/2026-06-28-profound-vs-bluefish-aeo-aeo-overview-diagram.png new file mode 100644 index 0000000..786adb5 Binary files /dev/null and b/public/images/blog/2026-06-28-profound-vs-bluefish-aeo-aeo-overview-diagram.png differ diff --git a/public/images/blog/2026-06-28-profound-vs-bluefish-aeo-comparison-table.png b/public/images/blog/2026-06-28-profound-vs-bluefish-aeo-comparison-table.png new file mode 100644 index 0000000..cd7b3f8 Binary files /dev/null and b/public/images/blog/2026-06-28-profound-vs-bluefish-aeo-comparison-table.png differ diff --git a/public/images/blog/2026-06-28-profound-vs-bluefish-aeo-featured-image.png b/public/images/blog/2026-06-28-profound-vs-bluefish-aeo-featured-image.png new file mode 100644 index 0000000..8423860 Binary files /dev/null and b/public/images/blog/2026-06-28-profound-vs-bluefish-aeo-featured-image.png differ diff --git a/public/images/blog/2026-06-28-profound-vs-bluefish-aeo-featured.png b/public/images/blog/2026-06-28-profound-vs-bluefish-aeo-featured.png new file mode 100644 index 0000000..590cb96 Binary files /dev/null and b/public/images/blog/2026-06-28-profound-vs-bluefish-aeo-featured.png differ diff --git a/public/images/blog/2026-06-28-profound-vs-bluefish-aeo-use-case-diagram.png b/public/images/blog/2026-06-28-profound-vs-bluefish-aeo-use-case-diagram.png new file mode 100644 index 0000000..c2f9aff Binary files /dev/null and b/public/images/blog/2026-06-28-profound-vs-bluefish-aeo-use-case-diagram.png differ diff --git a/public/images/blog/2026-07-01-chatgpt-reasoning-modes-overlap-featured.png b/public/images/blog/2026-07-01-chatgpt-reasoning-modes-overlap-featured.png new file mode 100644 index 0000000..0ca9f18 Binary files /dev/null and b/public/images/blog/2026-07-01-chatgpt-reasoning-modes-overlap-featured.png differ diff --git a/public/images/blog/2026-07-01-chatgpt-reasoning-modes-overlap-source-stacks-comparison.png b/public/images/blog/2026-07-01-chatgpt-reasoning-modes-overlap-source-stacks-comparison.png new file mode 100644 index 0000000..e3dfc97 Binary files /dev/null and b/public/images/blog/2026-07-01-chatgpt-reasoning-modes-overlap-source-stacks-comparison.png differ diff --git a/public/images/blog/2026-07-01-chatgpt-reasoning-modes-overlap-venn-overlap.png b/public/images/blog/2026-07-01-chatgpt-reasoning-modes-overlap-venn-overlap.png new file mode 100644 index 0000000..554a78d Binary files /dev/null and b/public/images/blog/2026-07-01-chatgpt-reasoning-modes-overlap-venn-overlap.png differ diff --git a/public/images/blog/2026-07-01-chatgpt-source-selection-fan-out-queries.png b/public/images/blog/2026-07-01-chatgpt-source-selection-fan-out-queries.png new file mode 100644 index 0000000..e68618e Binary files /dev/null and b/public/images/blog/2026-07-01-chatgpt-source-selection-fan-out-queries.png differ diff --git a/public/images/blog/2026-07-01-chatgpt-source-selection-featured.png b/public/images/blog/2026-07-01-chatgpt-source-selection-featured.png new file mode 100644 index 0000000..4c0aecf Binary files /dev/null and b/public/images/blog/2026-07-01-chatgpt-source-selection-featured.png differ diff --git a/public/images/blog/2026-07-01-chatgpt-source-selection-source-pipelines.png b/public/images/blog/2026-07-01-chatgpt-source-selection-source-pipelines.png new file mode 100644 index 0000000..663ec23 Binary files /dev/null and b/public/images/blog/2026-07-01-chatgpt-source-selection-source-pipelines.png differ diff --git a/public/images/blog/2026-07-01-deepseek-dspark-devin-fusion-featured.png b/public/images/blog/2026-07-01-deepseek-dspark-devin-fusion-featured.png new file mode 100644 index 0000000..061efbe Binary files /dev/null and b/public/images/blog/2026-07-01-deepseek-dspark-devin-fusion-featured.png differ diff --git a/public/images/blog/2026-07-01-deepseek-dspark-devin-fusion-speculative-decoding-flow.png b/public/images/blog/2026-07-01-deepseek-dspark-devin-fusion-speculative-decoding-flow.png new file mode 100644 index 0000000..5607ea9 Binary files /dev/null and b/public/images/blog/2026-07-01-deepseek-dspark-devin-fusion-speculative-decoding-flow.png differ diff --git a/public/images/blog/2026-07-01-deepseek-dspark-devin-fusion-two-agent-architecture.png b/public/images/blog/2026-07-01-deepseek-dspark-devin-fusion-two-agent-architecture.png new file mode 100644 index 0000000..5e7cb32 Binary files /dev/null and b/public/images/blog/2026-07-01-deepseek-dspark-devin-fusion-two-agent-architecture.png differ diff --git a/public/images/illustrations/ai-consult.jpg b/public/images/illustrations/ai-consult.jpg new file mode 100644 index 0000000..6df2b11 Binary files /dev/null and b/public/images/illustrations/ai-consult.jpg differ diff --git a/public/images/illustrations/automation-workflow.jpg b/public/images/illustrations/automation-workflow.jpg new file mode 100644 index 0000000..3d04e88 Binary files /dev/null and b/public/images/illustrations/automation-workflow.jpg differ diff --git a/public/images/illustrations/brand-direction.jpg b/public/images/illustrations/brand-direction.jpg new file mode 100644 index 0000000..9b2435f Binary files /dev/null and b/public/images/illustrations/brand-direction.jpg differ diff --git a/public/images/illustrations/marketing-consult.jpg b/public/images/illustrations/marketing-consult.jpg new file mode 100644 index 0000000..d7fbac0 Binary files /dev/null and b/public/images/illustrations/marketing-consult.jpg differ diff --git a/public/images/illustrations/website-development.jpg b/public/images/illustrations/website-development.jpg new file mode 100644 index 0000000..9430e48 Binary files /dev/null and b/public/images/illustrations/website-development.jpg differ diff --git a/public/images/logos/footer-logo-long-black.png b/public/images/logos/footer-logo-long-black.png new file mode 100644 index 0000000..d06225e Binary files /dev/null and b/public/images/logos/footer-logo-long-black.png differ diff --git a/public/images/logos/logo-long-black.png b/public/images/logos/logo-long-black.png new file mode 100644 index 0000000..4eb5c76 Binary files /dev/null and b/public/images/logos/logo-long-black.png differ diff --git a/public/images/portfolio/baofuling.png b/public/images/portfolio/baofuling.png new file mode 100644 index 0000000..51a9088 Binary files /dev/null and b/public/images/portfolio/baofuling.png differ diff --git a/public/images/portfolio/dataroot.png b/public/images/portfolio/dataroot.png new file mode 100644 index 0000000..49a9157 Binary files /dev/null and b/public/images/portfolio/dataroot.png differ diff --git a/public/images/portfolio/dealplustech.png b/public/images/portfolio/dealplustech.png new file mode 100644 index 0000000..81fae3d Binary files /dev/null and b/public/images/portfolio/dealplustech.png differ diff --git a/public/images/portfolio/jetindustries.png b/public/images/portfolio/jetindustries.png new file mode 100644 index 0000000..bb39d78 Binary files /dev/null and b/public/images/portfolio/jetindustries.png differ diff --git a/public/images/portfolio/lawyernoom.png b/public/images/portfolio/lawyernoom.png new file mode 100644 index 0000000..ab4db0d Binary files /dev/null and b/public/images/portfolio/lawyernoom.png differ diff --git a/public/images/portfolio/leudjorakhe.png b/public/images/portfolio/leudjorakhe.png new file mode 100644 index 0000000..a50eef0 Binary files /dev/null and b/public/images/portfolio/leudjorakhe.png differ diff --git a/public/images/portfolio/trainersunny.png b/public/images/portfolio/trainersunny.png new file mode 100644 index 0000000..0f82ea8 Binary files /dev/null and b/public/images/portfolio/trainersunny.png differ diff --git a/public/images/portfolio/tuanthong.png b/public/images/portfolio/tuanthong.png new file mode 100644 index 0000000..b771dc0 Binary files /dev/null and b/public/images/portfolio/tuanthong.png differ diff --git a/public/images/portfolio/underdog.png b/public/images/portfolio/underdog.png new file mode 100644 index 0000000..2b346df Binary files /dev/null and b/public/images/portfolio/underdog.png differ diff --git a/public/llms.txt b/public/llms.txt new file mode 100644 index 0000000..2ab371d --- /dev/null +++ b/public/llms.txt @@ -0,0 +1,21 @@ +# MoreminiMore + +MoreminiMore ให้คำปรึกษาและบริการด้านเว็บไซต์ การตลาดออนไลน์ ระบบอัตโนมัติ และ AI สำหรับ SME โดยเริ่มจากโจทย์ธุรกิจและข้อมูลจริง + +## ข้อมูลหลัก +- หน้าแรก: https://moreminimore.com/ +- เกี่ยวกับเรา: https://moreminimore.com/about/ +- บริการทั้งหมด: https://moreminimore.com/services/ +- Website Development: https://moreminimore.com/services/website-development/ +- Marketing Consult: https://moreminimore.com/services/marketing-consult/ +- Automation Workflow: https://moreminimore.com/services/automation-workflow/ +- AI Consult: https://moreminimore.com/services/ai-consult/ +- คำถามที่พบบ่อย: https://moreminimore.com/faq/ +- บทความ: https://moreminimore.com/blog/ +- นโยบายความเป็นส่วนตัว: https://moreminimore.com/privacy/ +- เงื่อนไขการใช้งาน: https://moreminimore.com/terms/ + +## ติดต่อ +- อีเมล: contact@moreminimore.com +- โทรศัพท์: +66 80 995 5945 +- LINE: https://line.me/R/ti/p/@moreminimore diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..81f6050 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,16 @@ +User-agent: * +Allow: / +Disallow: /api/ + +User-agent: GPTBot +User-agent: ChatGPT-User +User-agent: OAI-SearchBot +User-agent: ClaudeBot +User-agent: Claude-SearchBot +User-agent: PerplexityBot +User-agent: Google-Extended +User-agent: Applebot-Extended +Allow: / +Disallow: /api/ + +Sitemap: https://moreminimore.com/sitemap-index.xml diff --git a/server.js b/server.js new file mode 100644 index 0000000..6ebecb6 --- /dev/null +++ b/server.js @@ -0,0 +1,356 @@ +import express from 'express'; +import { rateLimit } from 'express-rate-limit'; +import nodemailer from 'nodemailer'; +import { SESv2Client, SendEmailCommand } from '@aws-sdk/client-sesv2'; +import crypto from 'node:crypto'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { dirname, join } from 'node:path'; + +const root = dirname(fileURLToPath(import.meta.url)); +const dist = join(root, 'dist'); +const port = Number.parseInt(process.env.PORT || '4321', 10); +const isProduction = process.env.NODE_ENV === 'production'; +const defaultContactEmail = 'kunthawat@moreminimore.com'; +const mailConfigured = ['SES_ACCESS_KEY_ID', 'SES_SECRET_ACCESS_KEY', 'SES_REGION'] + .every((key) => Boolean(process.env[key])); +const fromEmail = process.env.SES_FROM_EMAIL || defaultContactEmail; +const toEmail = process.env.CONTACT_TO_EMAIL || defaultContactEmail; + +const transporter = mailConfigured + ? nodemailer.createTransport({ + SES: { + sesClient: new SESv2Client({ + region: process.env.SES_REGION, + credentials: { + accessKeyId: process.env.SES_ACCESS_KEY_ID, + secretAccessKey: process.env.SES_SECRET_ACCESS_KEY, + }, + }), + SendEmailCommand, + }, + }) + : null; + +export const app = express(); +app.disable('x-powered-by'); +const trustProxySetting = (process.env.TRUST_PROXY || '0').trim().toLowerCase(); +if (!['0', 'false', ''].includes(trustProxySetting)) { + const trustedHops = Number.parseInt(trustProxySetting, 10); + if (!Number.isInteger(trustedHops) || trustedHops < 1 || trustedHops > 3 || String(trustedHops) !== trustProxySetting) { + throw new Error('TRUST_PROXY must be 0, false, or an explicit proxy-hop count from 1 to 3'); + } + app.set('trust proxy', trustedHops); +} +app.use((_, res, next) => { + res.setHeader('X-Content-Type-Options', 'nosniff'); + res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin'); + next(); +}); +app.use('/api', express.json({ limit: '12kb', strict: true })); + +const contactLimiter = rateLimit({ + windowMs: 10 * 60 * 1000, + limit: 6, + standardHeaders: 'draft-8', + legacyHeaders: false, + message: { ok: false, error: 'กรุณารอสักครู่แล้วลองใหม่' }, +}); + +const eventIds = new Map(); +const EVENT_TTL_MS = 15 * 60 * 1000; +const EVENT_MAX = 5000; +const UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; + +export function claimEventId(eventId, now = Date.now()) { + for (const [id, entry] of eventIds) { + if (entry.expiresAt <= now) eventIds.delete(id); + } + if (!UUID_V4.test(eventId)) return 'invalid'; + const existing = eventIds.get(eventId); + if (existing) return existing.status; + while (eventIds.size >= EVENT_MAX) eventIds.delete(eventIds.keys().next().value); + eventIds.set(eventId, { status: 'pending', expiresAt: now + EVENT_TTL_MS }); + return 'new'; +} + +function completeEventId(eventId, now = Date.now()) { + const existing = eventIds.get(eventId); + if (existing?.status === 'pending') { + eventIds.set(eventId, { status: 'completed', expiresAt: now + EVENT_TTL_MS }); + } +} + +function releaseEventId(eventId) { + eventIds.delete(eventId); +} + +function text(value, max) { + return typeof value === 'string' ? value.trim().slice(0, max) : ''; +} + +function escapeHtml(value) { + return value.replace(/[&<>"']/g, (character) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[character]); +} + +function validateContact(body) { + const name = text(body.name, 80); + const contact = text(body.contact, 160); + const problem = text(body.problem, 1500); + const source = /^[a-z0-9-]{1,60}$/i.test(body.source || '') ? body.source : 'unknown'; + const eventId = text(body.eventId, 64); + const fields = {}; + const email = contact.includes('@') ? contact : ''; + const phoneDigits = contact.replace(/\D/g, ''); + const tracking = body.tracking && typeof body.tracking === 'object' ? body.tracking : {}; + const consent = tracking.consent && typeof tracking.consent === 'object' ? tracking.consent : {}; + const analyticsConsent = consent.analytics === true; + const marketingConsent = consent.marketing === true; + + if (name.length < 2) fields.name = 'กรุณากรอกชื่ออย่างน้อย 2 ตัวอักษร'; + if (email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) fields.contact = 'กรุณากรอกอีเมลให้ถูกต้อง'; + if (!email && (phoneDigits.length < 8 || phoneDigits.length > 15)) fields.contact = 'กรุณากรอกเบอร์โทรให้ถูกต้อง'; + if (problem.length < 5) fields.problem = 'กรุณาเล่าปัญหาอย่างน้อย 5 ตัวอักษร'; + + const fbp = marketingConsent && /^fb\.\d\.\d{10,16}\.[A-Za-z0-9._-]{1,120}$/.test(tracking.fbp || '') ? tracking.fbp : ''; + const fbc = marketingConsent && /^fb\.\d\.\d{10,16}\.[A-Za-z0-9._-]{1,160}$/.test(tracking.fbc || '') ? tracking.fbc : ''; + const gaClientId = analyticsConsent && /^\d{1,20}\.\d{1,20}$/.test(tracking.gaClientId || '') ? tracking.gaClientId : ''; + const gaSessionId = analyticsConsent && /^\d{6,20}$/.test(tracking.gaSessionId || '') ? tracking.gaSessionId : ''; + + return { + fields, + validEventId: UUID_V4.test(eventId), + values: { + name, contact, problem, source, eventId, email, phoneDigits, + consent: { analytics: analyticsConsent, marketing: marketingConsent }, + fbp, fbc, gaClientId, gaSessionId, + }, + }; +} + +function sha256(value) { + return crypto.createHash('sha256').update(value).digest('hex'); +} + +function normalizedNameParts(name) { + const parts = name.normalize('NFKC').trim().toLowerCase().split(/\s+/).filter(Boolean); + return { first: parts[0] || '', last: parts.length > 1 ? parts[parts.length - 1] : '' }; +} + +export function normalizeThaiPhone(value) { + const digits = String(value || '').replace(/\D/g, ''); + if (/^0\d{8,9}$/.test(digits)) return `66${digits.slice(1)}`; + if (/^66\d{8,9}$/.test(digits)) return digits; + return ''; +} + +function sourceUrl(source) { + if (source === 'home') return 'https://moreminimore.com/'; + if (/^(website-development|marketing-consult|automation-workflow|ai-consult)$/.test(source)) { + return `https://moreminimore.com/services/${source}/`; + } + return 'https://moreminimore.com/'; +} + +async function providerPost(url, body, fetchImpl) { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 2200); + try { + const response = await fetchImpl(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + signal: controller.signal, + }); + if (!response.ok) throw new Error('provider_request_failed'); + } finally { + clearTimeout(timeout); + } +} + +export async function dispatchProviderConversions(values, requestContext, env = process.env, fetchImpl = globalThis.fetch) { + const tasks = []; + const eventTime = Math.floor(Date.now() / 1000); + + if (values.consent.marketing && env.META_ACCESS_TOKEN) { + const names = normalizedNameParts(values.name); + const normalizedPhone = normalizeThaiPhone(values.phoneDigits); + const userData = { + client_ip_address: requestContext.ip, + client_user_agent: requestContext.userAgent, + ...(values.email ? { em: [sha256(values.email.normalize('NFKC').trim().toLowerCase())] } : {}), + ...(!values.email && normalizedPhone ? { ph: [sha256(normalizedPhone)] } : {}), + ...(names.first ? { fn: [sha256(names.first)] } : {}), + ...(names.last ? { ln: [sha256(names.last)] } : {}), + ...(values.fbp ? { fbp: values.fbp } : {}), + ...(values.fbc ? { fbc: values.fbc } : {}), + }; + const metaPixelId = env.META_PIXEL_ID || '418349260078648'; + const metaUrl = `https://graph.facebook.com/v22.0/${encodeURIComponent(metaPixelId)}/events?access_token=${encodeURIComponent(env.META_ACCESS_TOKEN)}`; + tasks.push(providerPost(metaUrl, { + data: [{ + event_name: 'Lead', + event_time: eventTime, + event_source_url: sourceUrl(values.source), + action_source: 'website', + event_id: values.eventId, + user_data: userData, + custom_data: { form_location: values.source }, + }], + }, fetchImpl)); + } + + if (values.consent.analytics && env.GA4_API_SECRET && values.gaClientId) { + const measurementId = env.GA4_MEASUREMENT_ID || 'G-74BHREDLC3'; + const gaUrl = `https://www.google-analytics.com/mp/collect?measurement_id=${encodeURIComponent(measurementId)}&api_secret=${encodeURIComponent(env.GA4_API_SECRET)}`; + tasks.push(providerPost(gaUrl, { + client_id: values.gaClientId, + consent: { + ad_user_data: values.consent.marketing ? 'GRANTED' : 'DENIED', + ad_personalization: values.consent.marketing ? 'GRANTED' : 'DENIED', + }, + events: [{ + name: 'form_submitted', + params: { + ...(values.gaSessionId ? { session_id: values.gaSessionId } : {}), + engagement_time_msec: 100, + form_location: values.source, + event_id: values.eventId, + }, + }], + }, fetchImpl)); + } + + return Promise.allSettled(tasks); +} + +function isSameOriginRequest(req) { + if (req.get('sec-fetch-site') === 'cross-site') return false; + const origin = req.get('origin'); + if (!origin) return true; + try { + return new URL(origin).host === req.get('host'); + } catch { + return false; + } +} + +app.get('/api/health', (_, res) => { + res.setHeader('Cache-Control', 'no-store'); + res.json({ ok: true }); +}); + +const permanentRedirects = new Map([ + ['/portfolio', '/#portfolio'], ['/portfolio/', '/#portfolio'], + ['/contact', '/#contact'], ['/contact/', '/#contact'], + ['/sitemap.xml', '/sitemap-index.xml'], + ['/about', '/about/'], ['/services', '/services/'], ['/faq', '/faq/'], + ['/privacy', '/privacy/'], ['/terms', '/terms/'], ['/blog', '/blog/'], + ...['website-development', 'marketing-consult', 'automation-workflow', 'ai-consult'].map((slug) => [`/services/${slug}`, `/services/${slug}/`]), + ...[ + '2026-06-15-chatgpt-opens-ads-for-all', + '2026-06-26-ai-visibility-operational-alignment', + '2026-06-26-b2b-marketers-future-of-work', + '2026-06-26-brand-reputation-precedes-you-with-ai', + '2026-06-26-fintech-invisible-to-ai-agents', + '2026-06-26-google-seo-for-ai-agents', + '2026-06-26-two-things-b2b-marketers-should-do-with-ai', + '2026-06-27-ai-brand-reputation-167k-citations', + '2026-06-27-ai-recommendation-multi-industry-map', + '2026-06-27-google-open-knowledge-format', + '2026-06-27-opid-ai-agent-self-training', + '2026-06-28-broken-form-cost-agency-leads', + '2026-06-28-openknowledge-ai-first-obsidian', + '2026-06-28-profound-vs-bluefish-aeo', + '2026-07-01-chatgpt-reasoning-modes-overlap', + '2026-07-01-chatgpt-source-selection', + '2026-07-01-deepseek-dspark-devin-fusion', + ].map((slug) => [`/blog/${slug}`, `/blog/${slug}/`]), +]); + +app.use((req, res, next) => { + if (req.method !== 'GET' && req.method !== 'HEAD') return next(); + const destination = permanentRedirects.get(req.path); + if (!destination) return next(); + return res.redirect(301, destination); +}); + +app.post('/api/contact', contactLimiter, async (req, res) => { + res.setHeader('Cache-Control', 'no-store'); + if (!isSameOriginRequest(req)) return res.status(403).json({ ok: false, error: 'ไม่สามารถส่งข้อมูลได้' }); + if (!req.is('application/json')) return res.status(415).json({ ok: false, error: 'ไม่สามารถส่งข้อมูลได้' }); + if (text(req.body?.website, 200)) return res.json({ ok: true }); + + const { fields, validEventId, values } = validateContact(req.body || {}); + if (Object.keys(fields).length) return res.status(400).json({ ok: false, error: 'กรุณาตรวจสอบข้อมูล', fields }); + if (!validEventId) return res.status(400).json({ ok: false, error: 'ข้อมูลการส่งไม่ถูกต้อง' }); + const eventState = claimEventId(values.eventId); + if (eventState === 'completed') return res.json({ ok: true, duplicate: true }); + if (eventState === 'pending') return res.status(409).json({ ok: false, processing: true, error: 'ระบบกำลังดำเนินการคำขอนี้' }); + if (eventState !== 'new') return res.status(400).json({ ok: false, error: 'ข้อมูลการส่งไม่ถูกต้อง' }); + + if (!transporter) { + if (!isProduction) { + completeEventId(values.eventId); + console.info('[api/contact] Development submission accepted; email delivery is disabled'); + return res.json({ ok: true, devMode: true }); + } + releaseEventId(values.eventId); + console.error('[api/contact] Email delivery is not configured'); + return res.status(503).json({ ok: false, error: 'ระบบยังไม่พร้อม กรุณาลองใหม่ภายหลัง' }); + } + + try { + const subject = 'ข้อความติดต่อใหม่จากเว็บไซต์ MoreminiMore'; + const plain = [ + 'ข้อความติดต่อใหม่จากเว็บไซต์ MoreminiMore', '', + `ชื่อ: ${values.name}`, + `เบอร์โทรหรืออีเมล: ${values.contact}`, + `ตอนนี้ติดเรื่องอะไร: ${values.problem}`, + `หน้าที่ส่ง: ${values.source}`, + ].join('\n'); + const html = ` +

ข้อความติดต่อใหม่จากเว็บไซต์ MoreminiMore

+ + + + + +
ชื่อ${escapeHtml(values.name)}
เบอร์โทรหรืออีเมล${escapeHtml(values.contact)}
ตอนนี้ติดเรื่องอะไร${escapeHtml(values.problem).replace(/\n/g, '
')}
หน้าที่ส่ง${escapeHtml(values.source)}
`; + + await transporter.sendMail({ + from: `MoreminiMore <${fromEmail}>`, + to: toEmail, + replyTo: values.email || undefined, + subject, text: plain, html, + }); + + completeEventId(values.eventId); + await dispatchProviderConversions(values, { + ip: req.ip || '', + userAgent: text(req.get('user-agent'), 300), + }); + console.info('[api/contact] Message delivered'); + return res.json({ ok: true }); + } catch { + releaseEventId(values.eventId); + console.error('[api/contact] Email delivery failed'); + return res.status(500).json({ ok: false, error: 'ส่งไม่สำเร็จ กรุณาลองใหม่ภายหลัง' }); + } +}); + +app.use(express.static(dist, { + extensions: ['html'], + setHeaders(res, filePath) { + if (filePath.endsWith('.html')) res.setHeader('Cache-Control', 'no-cache'); + else res.setHeader('Cache-Control', 'public, max-age=604800, immutable'); + }, +})); + +app.use((_, res) => { + res.setHeader('Cache-Control', 'no-cache'); + res.status(404).sendFile(join(dist, '404.html')); +}); + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + app.listen(port, '0.0.0.0', () => console.info(`[server] Listening on port ${port}`)); +} diff --git a/src/components/BlogHeader.astro b/src/components/BlogHeader.astro new file mode 100644 index 0000000..30379ed --- /dev/null +++ b/src/components/BlogHeader.astro @@ -0,0 +1,24 @@ +--- +const { current = '' } = Astro.props; +--- + +
+ + +
+ เมนู + +
+ ลองคุยกัน 15 นาที ↗ +
diff --git a/src/components/ContactForm.astro b/src/components/ContactForm.astro new file mode 100644 index 0000000..a88e65a --- /dev/null +++ b/src/components/ContactForm.astro @@ -0,0 +1,202 @@ +--- +interface Props { + source?: string; +} + +const { source = 'home' } = Astro.props; +--- + +
+
+ + +

+
+
+ + +

+
+
+ + +

+
+ +
+ +

ใช้ข้อมูลเพื่อติดต่อกลับ และเมื่อยินยอมการตลาด อาจส่งชื่อ/ช่องทางติดต่อแบบแฮช พร้อม IP, เบราว์เซอร์ และรหัส Meta (_fbp/_fbc) เพื่อวัดผลโฆษณา

+
+

+
+ + diff --git a/src/components/ServiceLanding.astro b/src/components/ServiceLanding.astro new file mode 100644 index 0000000..cb2de4f --- /dev/null +++ b/src/components/ServiceLanding.astro @@ -0,0 +1,123 @@ +--- +import BaseLayout from '../layouts/BaseLayout.astro'; +import ContactForm from './ContactForm.astro'; +import SiteFooter from './SiteFooter.astro'; +import type { ServicePage } from '../data/servicePages'; +import { servicePages } from '../data/servicePages'; +import { breadcrumbSchema, organizationId } from '../data/schema'; +import '../styles/service-page.css'; + +interface Props { service: ServicePage } +const { service } = Astro.props; +const related = servicePages.filter((item) => item.slug !== service.slug); +const serviceUrl = `https://moreminimore.com/services/${service.slug}/`; +const structuredData = [ + { '@context': 'https://schema.org', '@type': 'Service', '@id': `${serviceUrl}#service`, name: service.name, description: service.lead, url: serviceUrl, serviceType: service.name, provider: { '@id': organizationId }, areaServed: { '@type': 'Country', name: 'Thailand' } }, + breadcrumbSchema([{ name: 'หน้าหลัก', path: '/' }, { name: 'บริการ', path: '/services/' }, { name: service.name, path: `/services/${service.slug}/` }]), +]; +--- + + +
+ + + ลองคุยกัน 15 นาที ↗ +
+ +
+
+
+

{service.label} / {service.name}

+

{service.title}

+

{service.lead}

+ ลองคุยกัน 15 นาที +
+
{`ภาพแนวคิดบริการ
+
+ + + +
+
+

คุณกำลังเจอแบบนี้อยู่หรือเปล่า?

+

ปัญหาที่เรา
เจอบ่อยที่สุด

+
+
    {service.pains.map((pain, index) =>
  1. 0{index + 1}

    {pain}

  2. )}
+
+ +
+

ทำไมต้องแก้ตอนนี้

+

{service.urgencyTitle}

+

{service.urgency}

+
+ +
+
+

เราเข้าไปช่วยอย่างไร

+

เริ่มจากธุรกิจ
ไม่ใช่เครื่องมือ

+
+
{service.approach.map((item, index) =>
0{index + 1}

{item}

)}
+
+ +
+
+

สิ่งที่คุณจะได้รับ

+

ผลลัพธ์ที่นำไป
ทำงานต่อได้จริง

+
+
    {service.outcomes.map((outcome) =>
  • {outcome}
  • )}
+
+ + {service.packages &&
+

รูปแบบเว็บไซต์

เลือกระบบให้พอดี
กับธุรกิจ

+
{service.packages.map((pkg) =>

{pkg.name}

{pkg.suit}

    {pkg.features.map((feature) =>
  • {feature}
  • )}
{pkg.price}{pkg.hosting}
)}
+

ราคาที่แสดงเป็นราคาเริ่มต้น และอาจเปลี่ยนตามจำนวนหน้า ฟีเจอร์ การเชื่อม API หรือการออกแบบเฉพาะของแต่ละธุรกิจ

+
} + + {service.caseStudy &&
+

กรณีศึกษา / Dataroot

ลดงบที่รั่ว
เพิ่มการเข้าถึงคนที่ใช่

เราพบว่าการตั้งค่ากลุ่มเป้าหมายกว้างเกินไป จึงปรับให้โฆษณาแสดงกับคนที่มีแนวโน้มเป็นลูกค้ามากขึ้น

+
หน้าเว็บไซต์ Dataroot
+
+373%Impression
+114.2%Click
−28.3%Ad spend
+
} + +
+

คำถามที่พบบ่อย

ตอบให้ชัด
ก่อนเริ่มงาน

+
{service.faqs.map((faq, index) =>
0{index + 1}{faq.question}

{faq.answer}

)}
+
+ + + +
+

เริ่มต้นง่าย ๆ

ลองคุยกัน 15 นาที

เล่าปัญหาที่อยากแก้แบบสั้น ๆ ทีมจะติดต่อกลับเพื่อช่วยดูว่าควรเริ่มจากจุดไหน โดยไม่แนะนำระบบที่เกินความจำเป็น

+ +
+
+ + + + +
diff --git a/src/components/SiteFooter.astro b/src/components/SiteFooter.astro new file mode 100644 index 0000000..be5e019 --- /dev/null +++ b/src/components/SiteFooter.astro @@ -0,0 +1,10 @@ + diff --git a/src/components/SiteHeader.astro b/src/components/SiteHeader.astro new file mode 100644 index 0000000..1c489f7 --- /dev/null +++ b/src/components/SiteHeader.astro @@ -0,0 +1,6 @@ +
+ + +
เมนู
+ ลองคุยกัน 15 นาที ↗ +
diff --git a/src/components/StaticPage.astro b/src/components/StaticPage.astro new file mode 100644 index 0000000..b4cbe6f --- /dev/null +++ b/src/components/StaticPage.astro @@ -0,0 +1,25 @@ +--- +import BaseLayout from '../layouts/BaseLayout.astro'; +import SiteHeader from './SiteHeader.astro'; +import SiteFooter from './SiteFooter.astro'; +import '../styles/static-pages.css'; + +interface Props { + title: string; + description: string; + eyebrow: string; + heading: string; + lead: string; + structuredData?: Record[]; +} +const { title, description, eyebrow, heading, lead, structuredData } = Astro.props; +--- + + + +
+

{eyebrow}

{heading}

{lead}

+ +
+ +
diff --git a/src/content.config.ts b/src/content.config.ts new file mode 100644 index 0000000..1174c8a --- /dev/null +++ b/src/content.config.ts @@ -0,0 +1,19 @@ +import { defineCollection } from 'astro:content'; +import { glob } from 'astro/loaders'; +import { z } from 'astro/zod'; + +const blog = defineCollection({ + loader: glob({ pattern: '**/*.md', base: './src/content/blog' }), + schema: z.object({ + title: z.string().min(1), + description: z.string().min(40), + pubDate: z.coerce.date(), + updatedDate: z.coerce.date().optional(), + category: z.string().min(1), + heroImage: z.string().startsWith('/images/blog/'), + tags: z.array(z.string()).default([]), + draft: z.boolean().default(false), + }), +}); + +export const collections = { blog }; diff --git a/src/content/blog/2026-06-15-chatgpt-opens-ads-for-all.md b/src/content/blog/2026-06-15-chatgpt-opens-ads-for-all.md new file mode 100644 index 0000000..d6de955 --- /dev/null +++ b/src/content/blog/2026-06-15-chatgpt-opens-ads-for-all.md @@ -0,0 +1,171 @@ +--- +title: "ChatGPT เปิดโฆษณาให้ทุกคน — โอกาสทองที่ไม่ควรมองข้าม" +description: "ChatGPT เปิดระบบโฆษณาแบบ self-serve ให้ทุกคน พร้อมโอกาสใหม่ที่นักการตลาดดิจิทัลและเจ้าของธุรกิจควรรู้" +pubDate: 2026-06-15 +updatedDate: 2026-06-30 +category: "AI & Marketing" +heroImage: "/images/blog/2026-06-15-chatgpt-opens-ads-for-all-featured.png" +tags: ['chatgpt', 'advertising', 'ai-ads', 'self-serve'] +draft: false +--- + +ถ้าคุณเป็นนักการตลาดดิจิทัล เจ้าของธุรกิจ หรือคนที่ยิงแอดอยู่ทุกวัน เรื่องนี้คุณต้องรู้: **ChatGPT เปิดระบบโฆษณาแบบ self-serve ให้ทุกคนแล้ว** ไม่ต้องมีขั้นต่ำ $50,000 อีกต่อไป พร้อมตัวเลขที่ทำให้ทุกคนอ้าปากค้าง — รายได้โฆษณา $100 ล้าน ในเวลาแค่ 6 สัปดาห์แรก + + +*ภาพหลัก: ChatGPT + โฆษณา — แนวคิดโฆษณาบนแพลตฟอร์ม AI* + +--- + +## TL;DR + +- ChatGPT ทำรายได้โฆษณา **$100M annualized** ภายใน 6 สัปดาห์แรก +- ผู้ใช้ **800 ล้านคน/สัปดาห์** สร้าง prompt **2.5 พันล้าน/วัน** +- Self-serve access เปิดแล้ว พ.ค. 2026 — **ไม่มีขั้นต่ำ $50K** +- ตอนนี้มีแค่ **<20% ของ eligible users** ที่เห็นโฆษณา — ยังมี room อีกเยอะ +- รูปแบบโฆษณา: Sponsored answers, Branded prompts, Text ads, Sponsored research +- ถ้าไม่เริ่มตอนนี้ จะเสีย first-mover advantage + +--- + +## สารบัญ + +1. [ตัวเลขที่น่าทึ่งของ ChatGPT Ads](#ตัวเลขที่น่าทึ่ง) +2. [Self-Serve Access เปิดแล้ว — ไม่มีขั้นต่ำ](#self-serve-access-เปิดแล้ว) +3. [ทำไม ChatGPT Ads ถึงต่างจากช่องอื่น?](#ทำไม-chatgpt-ads-ถึงต่างจากช่องอื่น) +4. [รูปแบบโฆษณาที่มีให้ใช้](#รูปแบบโฆษณาที่มีให้ใช้) +5. [คุณควรทำอะไรตอนนี้?](#คุณควรทำอะไรตอนนี้) +6. [FAQ](#faq) + +--- + +## ตัวเลขที่น่าทึ่ง + +ก่อนจะพูดถึงวิธีใช้ เรามาดูตัวเลขกันก่อน เพราะมันบ้ามากจริงๆ + +![ChatGPT advertising statistics — $100M revenue, 800M users](/images/blog/2026-06-15-chatgpt-opens-ads-for-all-numbers-chart.png) +*ภาพ: กราฟแสดงตัวเลขรายได้และผู้ใช้ ChatGPT Ads* + +- **$100 ล้าน** — รายได้โฆษณาแบบ annualized ภายใน 6 สัปดาห์แรก +- **800 ล้าน** — จำนวน weekly active users +- **2.5 พันล้าน** — จำนวน prompt ต่อวัน +- **85%** — สัดส่วนของ free/Go tier users ที่ eligible เห็นโฆษณา +- **<20%** — ของ eligible users ที่เห็นโฆษณาจริงในตอนนี้ + +ตัวเลขสุดท้ายนี่แหละที่สำคัญมาก — แปลว่าโฆษณายัง reach แค่ส่วนเล็กๆ ของฐานผู้ใช้ทั้งหมด นั่นหมายความว่า **โอกาสยังเปิดกว้างอยู่มาก** สำหรับคนที่เข้ามาตอนนี้ + +OpenAI ไม่ได้มาเล่นๆ แพลตฟอร์มนี้มี traffic มหาศาล และตอนนี้มันพร้อม monetize แล้ว + +--- + +## Self-Serve Access เปิดแล้ว + +เดิมที ChatGPT Ads เข้าถึงได้เฉพาะเอเจนซี่หรือแบรนด์ใหญ่ที่มีงบขั้นต่ำ **$50,000** เท่านั้น แต่ตอนนี้ OpenAI เปิดตัว **OpenAI Ads Manager** ในเดือนพฤษภาคม 2026 และเปิดให้ **ธุรกิจใดก็ได้ในสหรัฐฯ** สมัครใช้งานได้เอง + +### จุดสำคัญ: + +- **ไม่มีขั้นต่ำ** — ไม่ต้องมี $50K อีกต่อไป +- **Self-serve platform** — สร้างและจัดการแคมเปญได้เอง +- **CPC + CPM bidding** — เลือก bid แบบที่คุณคุ้นเคย +- **Conversion tracking** — มี pixel-based measurement พร้อมใช้ +- **ประเทศที่รองรับแล้ว:** สหรัฐฯ, แคนาดา, ออสเตรเลีย, นิวซีแลนด์, สหราชอาณาจักร, ญี่ปุ่น, เกาหลี, บราซิล, เม็กซิโก + +การเปิด self-serve หมายความว่า **ทุกคน** สามารถเริ่มต้นได้เลย ไม่ว่าจะเป็นธุรกิจเล็กหรือใหญ่ ถ้าคุณมีงบแค่ไม่กี่ร้อยเหรียญต่อเดือน ก็เริ่มได้แล้ว + +--- + +## ทำไม ChatGPT Ads ถึงต่างจากช่องอื่น? + +หลายคนอาจสงสัยว่า "ก็แค่โฆษณาอีกช่องนึง ต่างจาก Google Ads ยังไง?" คำตอบคือ: **ต่างมาก** + +![ChatGPT Ads vs Google Ads vs Meta Ads comparison](/images/blog/2026-06-15-chatgpt-opens-ads-for-all-comparison-table.png) +*ภาพ: ตารางเปรียบเทียบ ChatGPT Ads vs Google Ads vs Meta Ads* + +### 1. ผู้ใช้ผ่านการ research มาแล้ว + +คนที่ใช้ ChatGPT ไม่ได้แค่เสิร์จหาข้อมูลเฉยๆ พวกเขา **กำลังสนทนา** กับ AI เพื่อหาคำตอบเชิงลึก ผ่านการ research มาหลายขั้นตอนแล้ว ไม่เหมือน Google ที่คนพิมพ์ keyword สั้นๆ แล้วคลิกลิงก์แรก + +### 2. พร้อมตัดสินใจ + +เพราะผู้ใช้ ChatGPT อยู่ในช่วง "ใกล้ตัดสินใจ" มากกว่าช่องอื่น — พวกเขาถาม AI ว่า "ควรซื้อตัวไหนดี?" "บริการไหนเหมาะกับฉัน?" — โฆษณาที่ปรากฏในจังหวะนี้จึงมี conversion rate ที่สูงกว่า + +### 3. Conversational Platform + +ChatGPT เป็นแพลตฟอร์มแบบ conversational ไม่ใช่ search engine โฆษณาที่ดีจึงไม่ใช่ banner หรือ text ad ธรรมดา แต่เป็น **ส่วนหนึ่งของบทสนทนา** ที่ให้คุณค่ากับผู้ใช้จริงๆ + +### 4. ยังมี competition ต่ำ + +ตอนนี้มีผู้ลงโฆษณาน้อยมากเมื่อเทียบกับ Google หรือ Meta นั่นหมายความว่า **CPC ยังต่ำ** และคุณยังมี space ให้ทดลองได้เยอะ + +--- + +## รูปแบบโฆษณาที่มีให้ใช้ + +![ChatGPT ad placements — sponsored answers, branded prompts, text ads](/images/blog/2026-06-15-chatgpt-opens-ads-for-all-ad-placements.png) +*ภาพ: ไดอะแกรมแสดงตำแหน่งโฆษณาแต่ละรูปแบบบน ChatGPT* + +ChatGPT Ads มีรูปแบบหลักๆ ดังนี้: + +### Sponsored Answers +โฆษณาที่แทรกเข้าไปในคำตอบของ ChatGPT เมื่อผู้ใช้ถามคำถามที่เกี่ยวข้องกับสินค้าหรือบริการของคุณ คล้ายๆ Featured Snippet ของ Google แต่อยู่ในบริบทของบทสนทนา + +### Branded Prompts +แบรนด์สามารถนำเสนอ prompt suggestion ที่เชื่อมโยงกับสินค้าของตัวเอง ช่วยให้ผู้ใช้ค้นพบสิ่งที่แบรนด์นำเสนอผ่าน AI + +### Text Ads +โฆษณาแบบ text ที่ปรากฏในตำแหน่งที่เหมาะสมระหว่างบทสนทนา เป็นรูปแบบที่คุ้นเคยสำหรับคนที่เคยยิง Google Ads มาก่อน + +### Sponsored Research +โฆษณาที่ปรากฏเมื่อผู้ใช้ทำ research เชิงลึก เช่น ถาม AI ให้เปรียบเทียบสินค้าหลายๆ ตัว — แบรนด์ที่ซื้อโฆษณานี้จะถูกนำเสนอเป็นหนึ่งในตัวเลือก + +--- + +## คุณควรทำอะไรตอนนี้? + +ถ้าอ่านมาถึงตรงนี้แล้วรู้สึกว่า "โอเค น่าสนใจ แต่จะเริ่มยังไง?" — นี่คือ 3 สิ่งที่ควรทำทันที: + +### 1. สร้าง Creative ที่เฉพาะเจาะจง + +อย่า copy-paste โฆษณาจาก Google หรือ Facebook มาใช้ ChatGPT เป็นแพลตฟอร์มที่ต่างออกไป โฆษณาควรเป็น **answer-driven content** — เน้นให้ข้อมูล ให้คำตอบ ไม่ใช่แค่ขายของ + +### 2. ทดลองตั้งแต่ตอนนี้ + +First-mover advantage กำลังจะปิด เมื่อผู้ลงโฆษณามากขึ้น CPC ก็จะสูงขึ้น ตอนนี้เป็นช่วงที่ดีที่สุดในการทดลอง เรียนรู้ platform dynamics และสร้างความได้เปรียบก่อนคนอื่น + +### 3. เรียนรู้ Platform Dynamics + +ChatGPT Ads ยังใหม่มาก ยังไม่มี playbook ตายตัว คนที่เรียนรู้เร็วและปรับตัวได้ก่อนจะเป็นผู้ชนะ ติดตามข่าวสาร อ่าน case study และทดลองอย่างต่อเนื่อง + +### 4. ตั้ง Conversion Tracking ให้พร้อม + +OpenAI Ads Manager มี pixel-based measurement ตั้งค่าให้พร้อมตั้งแต่วันแรก เพื่อให้เก็บ data ได้มากที่สุด ยิ่งมี data เร็ว ยิ่ง optimize ได้เร็ว + +--- + +## สรุป + +ChatGPT Ads ไม่ใช่เรื่องของอนาคตอีกต่อไป — มันเกิดขึ้นแล้วตอนนี้ ด้วยตัวเลข $100M ใน 6 สัปดาห์ ผู้ใช้ 800 ล้านคน และ self-serve access ที่เปิดให้ทุกคน นี่คือโอกาสที่ไม่ได้มาบ่อยๆ + +คำถามไม่ใช่ "ควรลอง ChatGPT Ads ไหม?" แต่เป็น **"จะเริ่มเมื่อไหร่?"** ถ้าไม่ใช่ตอนนี้ ก็อาจสายเกินไป + +![ChatGPT advertising — conversational platform](/images/blog/2026-06-15-chatgpt-opens-ads-for-all-chatgpt-hero.png) +*ภาพ: Call to Action — เริ่มต้น ChatGPT Ads วันนี้* + +--- + +## FAQ + +### ChatGPT Ads ต่างจาก Google Ads ยังไง? + +ChatGPT Ads อยู่บนแพลตฟอร์ม conversational AI — ผู้ใช้ไม่ได้แค่เสิร์จ แต่กำลังสนทนาเพื่อหาคำตอบเชิงลึก โฆษณาจึงเข้าถึงคนที่อยู่ในช่วง "ใกล้ตัดสินใจ" มากกว่า Google ที่คนอาจแค่ browsing ทั่วไป นอกจากนี้ competition ยังต่ำกว่ามาก ทำให้ CPC ถูกกว่า + +### เริ่มต้น ChatGPT Ads อย่างไร? + +สมัครใช้งานผ่าน **OpenAI Ads Manager** — ตอนนี้เปิดให้ธุรกิจในสหรัฐฯ, แคนาดา, ออสเตรเลีย, นิวซีแลนด์, สหราชอาณาจักร, ญี่ปุ่น, เกาหลี, บราซิล และเม็กซิโก ไม่มีขั้นต่ำ เริ่มต้นด้วยงบเท่าไหร่ก็ได้ เลือก bid แบบ CPC หรือ CPM แล้วสร้างแคมเปญได้เลย + +### CPC ของ ChatGPT Ads เป็นยังไง? + +ตอนนี้ CPC ยังค่อนข้างต่ำเพราะ competition ยังน้อย อย่างไรก็ตาม ตัวเลขอาจเปลี่ยนแปลงได้เมื่อมีผู้ลงโฆษณามากขึ้น แนะนำให้เริ่มทดลองตอนนี้เพื่อเก็บ data และ optimize ก่อนที่ราคาจะสูงขึ้น + +--- + +*ข้อมูลอ้างอิง: [Neil Patel Blog — ChatGPT Ads Manager](https://neilpatel.com/blog/chatgpt-ads-manager/) (Chris Moreno, 15 มิ.ย. 2026)* \ No newline at end of file diff --git a/src/content/blog/2026-06-26-ai-visibility-operational-alignment.md b/src/content/blog/2026-06-26-ai-visibility-operational-alignment.md new file mode 100644 index 0000000..383ba1f --- /dev/null +++ b/src/content/blog/2026-06-26-ai-visibility-operational-alignment.md @@ -0,0 +1,202 @@ +--- +title: "AI Visibility ≠ SEO — ทำไมปัญหาอยู่ที่ \"การจัดการภายใน\" ไม่ใช่แค่ SEO" +description: "ทำ SEO ดีแล้วแต่ AI ยังตอบข้อมูลแบรนด์ผิด ปัญหาอาจอยู่ที่การจัดการข้อมูลและการทำงานร่วมกันภายในองค์กร" +pubDate: 2026-06-26 +updatedDate: 2026-06-30 +category: "SEO" +heroImage: "/images/blog/2026-06-26-ai-visibility-operational-alignment-featured.png" +tags: ['seo', 'ai-visibility', 'operations', 'marketing'] +draft: false +--- + +ถ้าคุณทำ SEO ดีแล้ว แต่ AI ยังตอบข้อมูลเกี่ยวกับแบรนด์คุณผิดๆ — ปัญหาอาจไม่ได้อยู่ที่ SEO เลย + +หลายคนยังเข้าใจว่า AI Visibility = SEO อีกแบบหนึ่ง แค่ optimize ให้ดีขึ้น ใส่ structured data ให้ครบ แล้ว AI จะตอบถูกเอง แต่ความจริงที่ Bill Hunt จาก Search Engine Journal ชี้ให้เห็นคือ: **ปัญหา AI Visibility ส่วนใหญ่มาจาก operational misalignment ภายในองค์กร** ไม่ใช่ technical SEO + +บทความนี้จะเล่าให้ฟังว่าทำไมมันเป็นแบบนั้น และเราจะตรวจสอบจุดเสี่ยงได้ยังไงบ้าง + +--- + +## TL;DR + +- **AI Visibility ≠ SEO** — ปัญหาหลักคือข้อมูลภายในองค์กรไม่สอดคล้องกัน ไม่ใช่แค่ technical optimization +- **Conway's Law (1967)** — องค์กรออกแบบระบบที่สะท้อนโครงสร้างการสื่อสารภายใน ถ้าทีมทำงานใน silos ข้อมูลที่ออกไปก็จะสะท้อนความสับสนนั้น +- **3 สถานการณ์เสี่ยง** — Product Launches, International Rollouts, Website Migrations ที่ operational friction ขยายผลรุนแรงขึ้นเมื่อ AI เข้ามาอ่านข้อมูล +- **Citation ≠ Value** — ถูกอ้างถึงมากขึ้นไม่ได้แปลว่าดีกว่า ถ้าข้อมูลไม่ accurate และไม่ aligned + +--- + +## สารบัญ + +1. [ข้อโต้แย้งหลัก: AI Visibility ≠ SEO](#ข้อโต้แย้งหลัก-ai-visibility--seo) +2. [Conway's Law: โครงสร้างองค์กรสะท้อนออกมาข้างนอก](#conways-law-โครงสร้างองค์กรสะท้อนออกมาข้างนอก) +3. [Generative AI ขยายแรงเสียดทานภายใน](#generative-ai-ขยายแรงเสียดทานภายใน) +4. [3 สถานการณ์เสี่ยงที่ควรระวัง](#3-สถานการณ์เสี่ยงที่ควรระวัง) +5. [Citation ≠ Value: มากกว่าไม่ได้แปลว่าดีกว่า](#citation--value-มากกว่าไม่ได้แปลว่าดีกว่า) +6. [Framework ตรวจสอบก่อน AI มองเห็นผิด](#framework-ตรวจสอบก่อน-ai-มองเห็นผิด) +7. [FAQ](#faq) + +--- + +## ข้อโต้แย้งหลัก: AI Visibility ≠ SEO + + + +พูดตรงๆ เลยนะ: หลายคนในวงการ marketing ยังคิดว่า AI Visibility เป็น "SEO อีกรูปแบบ" แค่ optimize content ให้ AI อ่านได้ดีขึ้น ใส่ schema markup ให้ครบ แล้วจบ + +แต่ Bill Hunt ชี้ให้เห็นว่าปัญหาที่แท้จริงอยู่ที่ **operational alignment** — ข้อมูลที่ทีมต่างๆ ในองค์กรปล่อยออกไปสอดคล้องกันไหม ไม่ใช่แค่ว่า technical SEO ถูกต้องหรือเปล่า + +ลองนึกภาพง่ายๆ: ถ้าทีม product marketing พูดอย่างหนึ่ง ทีม engineering พูดอีกอย่าง ทีม content แปลอีกแบบ — แล้ว AI จะสรุปยังไง? มันก็สรุปแบบสับสน เพราะ LLMs อ่าน patterns จากข้อมูลที่มีอยู่ ไม่ได้เข้าใจ brand intent ของคุณ + +ดังนั้นถ้าปัญหาอยู่ที่ "ข้อมูลภายในไม่ตรงกัน" — นั่นไม่ใช่ SEO problem มันคือ operations problem + +--- + +## Conway's Law: โครงสร้างองค์กรสะท้อนออกมาข้างนอก + +![Conway's Law — silos vs aligned teams](/images/blog/2026-06-26-ai-visibility-operational-alignment-conways-law.png) + +Melvin Conway เขียนไว้ตั้งแต่ปี 1967 — นานมากแล้ว แต่ยังใช้ได้ดีจนถึงวันนี้: + +> **"องค์กรออกแบบระบบที่สะท้อนโครงสร้างการสื่อสารภายในของตัวเอง"** + +แปลเป็นภาษามนุษย์ก็คือ: ถ้าทีม A กับทีม B ไม่คุยกัน ระบบ/ข้อมูลที่ทั้งสองทีมผลิตออกมาก็จะไม่สอดคล้องกัน + +ในบริบทของ AI Visibility นี่หมายความว่า: +- ถ้าทีม SEO กับทีม Product ไม่ sync → ข้อมูล product description อาจขัดแย้งกัน +- ถ้าทีม Global กับทีม Local ไม่คุยกัน → localized content อาจบอกคนละเรื่อง +- ถ้าทีม Dev กับทีม Content ไม่ coordinate → migration อาจทำให้ context หายไป + +AI ไม่ได้ดูแค่หน้าเว็บหน้าเดียว มันสังเคราะห์ข้อมูลจาก ecosystem ทั้งหมด ดังนั้นความไม่สอดคล้องแม้จุดเล็กๆ ก็ถูกขยายผลโดย LLMs + +--- + +## Generative AI ขยายแรงเสียดทานภายใน + +เรื่องนี้สำคัญมากที่ต้องเข้าใจ: **Generative AI ไม่ได้สร้างปัญหาใหม่ มันแค่ขยายปัญหาที่มีอยู่แล้วให้เห็นชัดขึ้น** + +ทีมทำงานใน silos เป็นเรื่องปกติมานานแล้ว แต่ก่อนหน้านี้ความไม่สอดคล้องของข้อมูลมันถูก "ซ่อน" อยู่ในแต่ละช่องทาง — เว็บนึงพูดอย่าง โบรชัวร์พูดอีกอย่าง คนไม่ค่อยสังเกต + +แต่พอ AI เข้ามา: +- LLMs สังเคราะห์ข้อมูลจาก ecosystem กว้างมาก — เว็บ, social, review, press release, structured data +- ถ้าข้อมูลขัดแย้งกัน AI จะ "อ่าน" patterns แล้วสรุปแบบที่มันเห็น — ไม่ใช่แบบที่คุณต้องการ +- ผลคือ AI อาจตอบข้อมูลเก่า, ข้อมูลผิด, หรือข้อมูลที่ขัดแย้งกันเอง + +> **LLMs อ่าน patterns ไม่ใช่ brand intent — มันแยกไม่ออกระหว่าง product description ที่ global team approve แล้ว กับ version เก่าเมื่อ 3 ปีที่แล้ว** + +--- + +## 3 สถานการณ์เสี่ยงที่ควรระวัง + +Bill Hunt ชี้ให้เห็น 3 สถานการณ์ที่ operational misalignment ขยายผลรุนแรงเป็นพิเศษ: + +### 1. Product Launches 🚀 + +![Product launch risk points infographic](/images/blog/2026-06-26-ai-visibility-operational-alignment-risk-scenarios.png) + +ช่วง launch เป็นช่วงที่ทีมทำงานเร่ง — product marketing, engineering, SEO, content, brand ทุกคนมี deadline ของตัวเอง + +**ปัญหา:** แต่ละทีมอาจมี assumptions ต่างกันเกี่ยวกับ positioning, features, pricing ถ้าไม่ sync ให้ดี → ข้อมูล conflicting ออกไปสู่สาธารณะ → AI สรุปแบบผิดๆ + +**ตัวอย่าง:** ทีม content เขียนว่า "รองรับ 5 ภาษา" แต่ engineering ยัง launch แค่ 3 ภาษา → AI ตอบว่ารองรับ 5 ภาษา → ลูกค้า expectations ผิด → ปัญหา + +### 2. International Rollouts 🌍 + +สินค้าตัวเดียวกัน แต่ description ต่างกันใน UK, US, Europe — แต่ละทีม localize ถูกต้องตาม context ของตลาดตัวเอง + +**ปัญหา:** แต่ละ version ถูกต้องในตัวเอง แต่เมื่อ AI สังเคราะห์รวมกัน → สับสนว่า product จริงๆ คืออะไรกันแน่ + +**ตัวอย่าง:** US version เน้น "enterprise-grade" UK version เน้น "cost-effective" → AI สรุปว่าเป็น "affordable enterprise solution" ซึ่งอาจไม่ตรงกับ positioning จริงของทั้งสองตลาด + +### 3. Website Migrations 🔄 + +Migration เป็นช่วงที่ context อ่อนแอลงมาก — URLs เปลี่ยน, content structure เปลี่ยน, internal links ขาด + +**ปัญหา:** ถ้า migration ไม่ดี → search engines และ AI systems สูญเสีย context ที่เคยใช้เข้าใจ brand → AI visibility ลดลงแบบที่ SEO checklist อย่างเดียวแก้ไม่ได้ + +**ตัวอย่าง:** ย้าย platform แล้วไม่ preserve structured data + ไม่ sync กับทีม content เรื่อง redirect mapping → AI ไม่รู้ว่า content เก่าหายไปไหน → ข้อมูลใน AI กลายเป็น outdated + +--- + +## Citation ≠ Value: มากกว่าไม่ได้แปลว่าดีกว่า + +หลายคนโฟกัสที่ "จำนวน citations" — ถูก AI อ้างถึงเยอะ = ดี + +แต่จริงๆ แล้ว: + +> **"A citation or a mention only adds value when the underlying information is accurate and aligned with the actual business."** +> — Bill Hunt, Search Engine Journal + +ถูกอ้างถึงมากขึ้นไม่ได้แปลว่าดีกว่า ถ้า: +- **Timing** ไม่ดี — ข้อมูลยังไม่อัพเดท +- **Accuracy** ไม่ดี — ข้อมูลผิด +- **Alignment** ไม่ดี — ข้อมูลขัดแย้งกับสิ่งที่ธุรกิจทำจริง + +**มากกว่า citations = amplify confusion มากขึ้น** ถ้าข้อมูลพื้นฐานไม่ถูกต้อง + +--- + +## Framework ตรวจสอบก่อน AI มองเห็นผิด + +ก่อน product launch, international rollout, หรือ migration — ลองถามตัวเอง 3 คำถามนี้: + +### ✅ ข้อ 1: ข้อมูลสอดคล้องกันทุกทีมไหม? + +- Product description ตรงกันทุกช่องทางไหม? +- ข้อมูล feature, pricing, availability ตรงกับ reality ไหม? +- ทีม Global กับทีม Local sync กันเรื่อง positioning ไหม? + +### ✅ ข้อ 2: Governance structure รองรับ AI Visibility หรือยัง? + +- มีคนรับผิดชอบเรื่อง data consistency ข้ามทีมไหม? +- มี process ตรวจสอบข้อมูลก่อน publish ไหม? +- มี approval workflow ที่รวม feedback จากทุก stakeholder ไหม? + +### ✅ ข้อ 3: Internal Communication สะท้อนออกมาเป็น External Signal ที่ Consistent ไหม? + +- ข้อมูลที่ทีมคุยกันภายในตรงกับสิ่งที่ออกไปข้างนอกไหม? +- ถ้ามี internal decision เปลี่ยน product positioning → external communication อัพเดทตามไหม? +- ทุกช่องทาง (web, social, PR, docs) พูดเรื่องเดียวกันไหม? + +> **"Your external AI presence is only as coherent as your internal workflows."** +> — Bill Hunt + +ถ้า workflow ภายในไม่ coherent AI ก็จะสะท้อนความไม่ coherent นั้นออกมา + +--- + +## FAQ + +### AI Visibility ต่างจาก SEO ยังไง? + +SEO focuses on technical optimization — structured data, crawlability, content quality, backlinks — เพื่อให้ search engines จัดอันดับได้ดี + +AI Visibility focuses on ว่า AI systems (LLMs, AI assistants, AI search) มองเห็นและเข้าใจข้อมูลเกี่ยวกับแบรนด์คุณถูกต้องไหม ซึ่งปัญหาหลักไม่ใช่ technical แต่เป็น **ข้อมูลภายในองค์กรสอดคล้องกันไหม** — นั่นคือ operational alignment + +SEO เป็นส่วนหนึ่งของ AI Visibility แต่ไม่ใช่ทั้งหมด ถ้าข้อมูลภายในขัดแย้งกัน technical SEO ดีแค่ไหนก็ช่วยไม่ได้ + +### ต้องตรวจสอบอะไรบ้างเพื่อให้ AI มองเห็นข้อมูลถูกต้อง? + +หลักๆ 3 เรื่อง: +1. **Data consistency** — ข้อมูล product, brand, positioning ตรงกันทุกทีมและทุกช่องทางไหม +2. **Governance** — มีคน/process รับผิดชอบดูแลให้ข้อมูล consistent ข้ามทีมไหม +3. **Communication alignment** — internal decisions สะท้อนออกมาเป็น external signals ที่ consistent ไหม + +โดยเฉพาะก่อน 3 สถานการณ์เสี่ยง: product launch, international rollout, website migration + +### Conway's Law คืออะไร? เกี่ยวอะไรกับ AI Visibility? + +Conway's Law (1967) โดย Melvin Conway บอกว่า: **องค์กรออกแบบระบบที่สะท้อนโครงสร้างการสื่อสารภายในของตัวเอง** + +ในบริบท AI Visibility หมายความว่า: ถ้าทีมในองค์กรทำงานใน silos ไม่คุยกัน → ข้อมูลที่ออกไปก็จะไม่สอดคล้องกัน → AI สังเคราะห์ข้อมูลนั้นแล้วสะท้อนความสับสนออกมา + +ปัญหา AI Visibility จึงไม่ได้แก้ด้วย SEO อย่างเดียว ต้องแก้ที่ operational alignment ภายในองค์กรด้วย + +--- + + +--- + +**แหล่งอ้างอิง:** +- Bill Hunt, "Why AI Visibility Does Not Only Depend On SEO", Search Engine Journal, 26 มิ.ย. 2026 +- Conway, Melvin E. (1967). "How Do Committees Invent?" \ No newline at end of file diff --git a/src/content/blog/2026-06-26-b2b-marketers-future-of-work.md b/src/content/blog/2026-06-26-b2b-marketers-future-of-work.md new file mode 100644 index 0000000..39a0a21 --- /dev/null +++ b/src/content/blog/2026-06-26-b2b-marketers-future-of-work.md @@ -0,0 +1,215 @@ +--- +title: "B2B Marketers กับ Future of Work: AI กำลังเปลี่ยนเกม แต่เราพร้อมแค่ไหน?" +description: "รายงาน State of AI for Business 2026 ชี้ว่า AI กำลังเปลี่ยนงาน B2B marketing และทักษะที่ทีมต้องเตรียมให้พร้อม" +pubDate: 2026-06-26 +category: "B2B" +heroImage: "/images/blog/2026-06-26-b2b-marketers-future-of-work-featured.png" +tags: ['b2b', 'future-of-work', 'ai', 'training'] +draft: false +--- + +ถ้าคุณเป็น B2B marketer ที่ยังรู้สึกว่า AI เป็นเรื่องของอนาคต ไม่ใช่เรื่องของวันนี้ — รายงานล่าสุดจาก Marketing AI Institute น่าจะทำให้คุณต้องหยุดคิดใหม่ + +รายงาน **2026 State of AI for Business Report** สำรวจ professionals กว่า 2,100 คน (84% อยู่ในสาย B2B) แล้วพบว่า: **71% คาดว่า AI จะทำให้ตำแหน่งงานหายไปมากกว่าที่สร้างภายใน 3 ปีข้างหน้า** เพิ่มขึ้นจาก 53% ในปีเดียว — กระโดดมา +18 percentage points เลยนะ + +ตัวเลขนี้ไม่ใช่แค่ตัวเลขในรายงาน มันสะท้อนความจริงที่ว่าคนในวงการเริ่มรู้สึกถึงแรงกดดันจาก AI มากขึ้นเรื่อยๆ และสำหรับ B2B marketers โดยเฉพาะ ผลกระทบจะยิ่งชัดเจนเพราะงานหลายอย่างที่เราทำอยู่ — content creation, data analysis, campaign optimization — กำลังถูก AI เข้ามา disrupt อย่างรวดเร็ว + +แต่เดี๋ยวก่อน ไม่ต้อง Panic เพราะบทความนี้จะสรุป 5 key takeaways ที่ B2B marketers ต้องรู้ พร้อมบอกว่าควรทำอะไรต่อจากนี้ + +## TL;DR + +- **71%** คาดว่า AI จะทำให้งานหายมากกว่าสร้างใน 3 ปี (+18pp จากปีก่อน) — ในกลุ่ม marketers ขึ้นจาก 53% เป็น 70% +- **74%** บอก AI สำคัญต่อความสำเร็จองค์กร — CEOs/founders พูดถึง 89% มองว่า AI เป็น business priority ไม่ใช่ nice-to-have +- **มากกว่า 50%** ยังไม่มี access to formal AI training ที่เหมาะกับตัวเอง — คนอยากเรียน workflow integration (58%) และ AI agents (51%) มากกว่า prompting basics +- มีแค่ **13%** ที่มี AI governance ครบทั้ง 4 ด้าน (roadmap, council, policies, ethics) — แต่ 32% ไม่มีเลยสักข้อ +- **AI Agents** คือ Trend #1 ที่ทุกคนจับตา — 51% อยากเรียนเรื่องนี้ Systems thinking กลายเป็น core competency ใหม่ + +## สารบัญ + +1. [71% คาดงานหาย — แต่แค่ 20% กังวลเรื่องตัวเอง](#1-71-คาดงานหาย--แต่แค่-20-กังวลเรื่องตัวเอง) +2. [74% บอก AI สำคัญ ไม่ใช่ nice-to-have อีกต่อไป](#2-74-บอก-ai-สำคัญ-ไม่ใช่-nice-to-have-อีกต่อไป) +3. [มากกว่า 50% ยังไม่มี AI Training ที่ใช่](#3-มากกว่า-50-ยังไม่มี-ai-training-ที่ใช่) +4. [แค่ 13% มี AI Governance ครบทุกด้าน](#4-แค่-13-มี-ai-governance-ครบทุกด้าน) +5. [AI Agents = Trend ที่จับตามากที่สุด](#5-ai-agents--trend-ที่จับตามากที่สุด) +6. [FAQ](#faq) + +--- + +## 1. 71% คาดงานหาย — แต่แค่ 20% กังวลเรื่องตัวเอง + +ตัวเลขนี้น่าตกใจมาก: **71% ของ professionals** ที่ตอบแบบสำรวจเชื่อว่า AI จะทำให้ตำแหน่งงานหายไปมากกว่าที่สร้างใน 3 ปีข้างหน้า เพิ่มขึ้นจาก 53% เมื่อปีที่แล้ว + +ในกลุ่ม marketers ตัวเลขก็พุ่งไม่แพ้กัน — จาก 53% ขึ้นไปเป็น **70%** ซึ่งหมายความว่า marketer เกือบ 3 ใน 4 คนเชื่อว่างานจะหายไปมากกว่าที่จะถูกสร้างขึ้นมาใหม่ + +![AI job displacement expectations — 71% overall, 70% among marketers](/images/blog/2026-06-26-b2b-marketers-future-of-work-future-of-work-job-displacement.png) + +แต่ Twist อยู่ตรงนี้: **มีแค่ 20% เท่านั้นที่กังวลเรื่องงานของตัวเอง** + +อ่านแล้วรู้สึกยังไง? มันคือ cognitive bias ชัดๆ — "คนอื่นจะโดนผลกระทบ แต่ฉันไม่เป็นไร" ความคิดแบบนี้อันตรายมาก เพราะมันทำให้เราชะล่าใจ ไม่เตรียมตัว แล้วก็ต้องมาตื่นตัวตอนที่สายเกินไป + +Cathy McPhillips จาก Marketing AI Institute สรุปได้ดีมาก: + +> "AI-forward professional is insulated. The one waiting to engage is not." + +ถ้าคุณเป็นคนที่กำลังเรียนรู้ AI อยู่แล้ว ลองใช้เครื่องมือใหม่ๆ หาวิธี integrate AI เข้ากับงาน คุณน่าจะปลอดภัย แต่ถ้าคุณยังรอให้องค์กรสอนก่อน หรือยังคิดว่า "เดี๋ยวค่อยว่า" — นั่นแหละคือคนที่เสี่ยงที่สุด + +### แล้ว B2B Marketers ควรทำยังไง? + +คำตอบง่ายมาก: **เริ่มลงมือทำตอนนี้** ไม่ต้องรอให้องค์กรมี training program ไม่ต้องรอให้ AI สมบูรณ์แบบ ลองใช้ AI ในงานที่ทำอยู่ทุกวัน เช่น content drafting, data analysis, campaign optimization แล้วดูว่ามันช่วยได้แค่ไหน สิ่งสำคัญคือต้องเปลี่ยน mindset จาก "AI จะมาแทนงานฉัน" เป็น "AI จะช่วยให้ฉันทำงานได้ดีขึ้น" — แล้วลงมือพิสูจน์ด้วยตัวเอง + +--- + +## 2. 74% บอก AI สำคัญ ไม่ใช่ nice-to-have อีกต่อไป + +**74% ของ respondents** บอกว่า AI สำคัญหรือสำคัญมากต่อความสำเร็จองค์กรใน 12 เดือนข้างหน้า + +ตัวเลขนี้ยิ่งชัดขึ้นในกลุ่ม **CEOs/founders ที่ 89%** — เกือบทุกคนเห็นตรงกันว่า AI ไม่ใช่เรื่องเล่นๆ แล้ว ผู้นำองค์กรมองว่า AI เป็น **business priority** ที่ต้องให้ความสำคัญเท่ากับเรื่องอื่นๆ ที่กระทบกับ bottom line + +![AI importance infographic — 74% overall, 89% among CEOs/founders](/images/blog/2026-06-26-b2b-marketers-future-of-work-ai-importance-infographic.png) + +ถ้าคุณยังขาย AI ในองค์กรเป็น "something to explore" หรือ "nice-to-have" ถึงเวลาปรับ mindset ใหม่ด่วนๆ เพราะผู้นำองค์กรมองว่า AI เป็น **business priority** ไม่ใช่โปรเจกต์ทดลองอีกต่อไป + +สำหรับ B2B marketers นี่แปลว่าอะไร? แปลว่า: + +- **AI literacy** กลายเป็นทักษะพื้นฐานที่ต้องมี (เหมือน Excel เมื่อ 10 ปีที่แล้ว หรือ social media marketing เมื่อ 5 ปีที่แล้ว) +- การนำเสนอ AI strategy ในทีมหรือในองค์กร จะกลายเป็นเรื่องปกติ — ไม่ใช่เรื่องแปลกอีกต่อไป +- คนที่เข้าใจ AI + marketing จะมี competitive advantage ชัดเจน ทั้งในเรื่อง career growth และ marketability + +สิ่งที่น่าสนใจคือ ตัวเลขนี้บ่งบอกว่า **AI ไม่ใช่แค่เทรนด์ แต่เป็น fundamental shift** ในวิธีที่ธุรกิจทำงาน องค์กรที่ยังไม่ปรับตัวจะเสียเปรียบคู่แข่งที่เดินหน้าไปแล้ว และ marketers ที่ไม่ upskill จะเสียเปรียบเพื่อนร่วมงานที่พร้อมรับการเปลี่ยนแปลง + +--- + +## 3. มากกว่า 50% ยังไม่มี AI Training ที่ใช่ + +นี่คือจุดที่น่ากังวลที่สุด: **มากกว่า 50% ของ professionals ยังไม่มี access to formal AI training** + +ตัวเลข training availability ดีขึ้นจาก 32% เป็น **46%** — แต่ก็ยังไม่ถึงครึ่ง และที่สำคัญกว่าคือ training ที่มีอยู่ก็อาจไม่ตรงกับสิ่งที่คนต้องการจริงๆ หลายคนบอกว่า training ที่ได้รับยังเป็นระดับเบื้องต้น ในขณะที่ตัวเองพร้อมจะไปไกลกว่านั้นแล้ว + +### คนอยากเรียนอะไร? + +| หัวข้อ | % ที่อยากเรียน | +|---|---| +| Workflow integration | 58% | +| AI Agents | 51% | +| No-code AI tools | 45% | + +### คนไม่อยากเรียนอะไร? + +| หัวข้อ | % ที่ไม่อยากเรียน | +|---|---| +| Prompting tips | 15% | + +เห็นภาพชัดเลย — **คนไม่ได้อยากเรียน prompting basics อีกต่อไปแล้ว** แต่อยากรู้ว่าจะ integrate AI เข้ากับ workflow ได้ยังไงต่างหาก นี่คือสัญญาณชัดเจนว่าคนพร้อมจะก้าวไปอีกขั้นแล้ว แต่องค์กรยังสอนอยู่แค่พื้นฐาน + +![Training gap — what people want to learn vs what's available](/images/blog/2026-06-26-b2b-marketers-future-of-work-training-gap.png) + +McPhillips สรุปไว้ดีมาก: + +> "If your org is still running intro-level AI workshops, your training is outdated." + +### แล้วองค์กรควรทำอะไร? + +1. **เลิกสอน prompting basics** — คนรู้แล้ว หรือหาเรียนเองได้ YouTube, LinkedIn Learning, มีหมด +2. **เน้น workflow integration** — สอนวิธีเอา AI ไปใช้ในงานจริง ไม่ใช่แค่ demo ให้ดูว่า "ว้าว ทำได้" แต่ให้ทำได้จริงในงานที่รับผิดชอบ +3. **เพิ่ม training เรื่อง AI Agents** — เพราะนี่คือเทรนด์ที่มาแรงที่สุด (ดูข้อ 5) +4. **เปิด access ให้ทุกคน** — ไม่ใช่แค่ทีม IT หรือ leadership แต่ทุกคนในองค์กรควรได้เรียน + +--- + +## 4. แค่ 13% มี AI Governance ครบทุกด้าน + +ถ้า training คือปัจเจก governance คือระดับองค์กร — และตัวเลขก็ไม่ค่อยดี + +มีแค่ **13% ของ organizations** ที่มี AI governance ครบทั้ง 4 ด้าน: + +1. ✅ AI roadmap — แผนชัดเจนว่าจะเอา AI ไปใช้ตรงไหน +2. ✅ AI council — ทีมที่ดูแลเรื่อง AI โดยเฉพาะ +3. ✅ Generative AI policies — กฎระเบียบการใช้ GenAI +4. ✅ AI ethics policy — หลักจริยธรรมในการใช้ AI + +และ **32% ไม่มีเลยสักข้อ** — ไม่มี roadmap, ไม่มี council, ไม่มี policies, ไม่มี ethics นั่นหมายความว่า 1 ใน 3 ของ organizations กำลังใช้ AI แบบไม่มีแนวทาง ไม่มีมาตรฐาน ไม่มีการควบคุม + +แต่! ข่าวดีก็คือ องค์กรที่มี governance แล้ว **50% บอกว่า AI momentum กำลัง accelerate** แปลว่า governance ไม่ใช่ตัวถ่วง แต่เป็นตัวเร่ง — มันช่วยให้องค์กร scale AI ได้เร็วขึ้น ไม่ใช่ช้าลง + +> "Governance isn't bureaucracy. It's the infrastructure that makes scale possible." + +### ทำไม B2B Marketers ต้องสน? + +เพราะถ้าคุณอยาก scale AI ในทีม marketing คุณต้องมี: + +- **Roadmap** — รู้ว่าจะเอา AI ไปใช้ตรงไหนก่อน-หลัง ไม่ใช่ random ใช้ไปเรื่อย +- **Policies** — รู้ว่าอะไรใช้ได้ อะไรใช้ไม่ได้ เช่น data privacy, brand guidelines, client confidentiality +- **Ethics** — รู้ว่า AI-generated content ต้อง disclose ไหม ต้อง fact-check ยังไง ใครรับผิดชอบถ้าผิดพลาด + +ไม่มี governance = ไม่มีมาตรฐาน = ไม่สามารถ scale ได้อย่างยั่งยืน และถ้าคุณเป็น B2B marketer ที่ทำงานกับ client data governance ยิ่งสำคัญเพราะคุณต้องมั่นใจว่า AI ที่ใช้ไม่ violate data privacy regulations + +--- + +## 5. AI Agents = Trend ที่จับตามากที่สุด + +ข้อสุดท้ายนี้น่าจะเป็นข้อที่สำคัญที่สุดสำหรับ B2B marketers: **AI Agents คือ trend อันดับ 1** ที่ทุกคนจับตา ไม่มี trend อื่นมาใกล้เลย + +**51% ของ respondents** ขอ training เรื่อง AI agents — สูงที่สุดในหัวข้อทั้งหมด นี่ไม่ใช่แค่กระแส แต่คือ signal ที่ชัดเจนว่าคนพร้อมจะก้าวไปอีกขั้นแล้ว + +AI Agents ไม่ใช่แค่ chatbot หรือ assistant ที่ตอบคำถาม แต่คือ **AI ที่สามารถ act autonomously** ทำงานแทนคุณได้หลายขั้นตอน โดยไม่ต้องมีคนจับมือทุก step เช่น: + +- วิเคราะห์ campaign data แล้วแนะนำ optimization โดยอัตโนมัติ +- จัดการ lead scoring + nurturing pipeline แบบ end-to-end +- สร้าง content brief → draft → review workflow โดยไม่ต้องมีคนดูแลทุกขั้นตอน +- Monitor social listening + generate response drafts แบบ real-time + +### Systems Thinking = ทักษะใหม่ที่ต้องมี + +การมาถึงของ AI Agents ทำให้ **systems thinking** กลายเป็น core competency ใหม่ของ B2B marketers + +ไม่ใช่แค่ "ฉันจะใช้ AI tool ตัวนี้" แต่ต้องคิดว่า: + +- **Workflow ทั้งหมด** เชื่อมกันยังไง? จุดไหนที่ AI เข้ามาช่วยได้? +- AI agent ตัวไหนรับช่วงต่อจากตัวไหน? ข้อมูลไหลจากจุด A ไปจุด B ยังไง? +- จุดไหนที่ต้องมี **human-in-the-loop** — จุดไหนที่ต้องมีคนตัดสินใจ ไม่ใช่ปล่อยให้ AI ทำเองทั้งหมด? +- ถ้า agent ทำงานผิดพลาด มี fallback plan ไหม? + +> "The next wave of AI that acts autonomously on your behalf is here." + +สำหรับ B2B marketers ที่อยากเตรียมตัว: เริ่มจากการ map workflow ปัจจุบันของคุณ แล้วดูว่าจุดไหนที่ AI agent สามารถเข้ามาช่วยได้ ไม่ต้องเริ่มจากจุดที่ซับซ้อนที่สุด แต่เริ่มจากจุดที่ง่ายและมี impact มากที่สุดก่อน + +--- + +## สรุป: Individual Adoption ไปเร็วกว่าองค์กร — ช่องว่างกำลังกว้าง + +สิ่งที่เห็นจากข้อมูลทั้งหมดคือ: **คนใช้ AI ไปไกลกว่าองค์กรที่พวกเขาทำงานอยู่** + +- คนเริ่มใช้ AI แล้ว แต่องค์กรยังไม่มี training ที่เหมาะกับระดับที่พวกเขาอยู่ +- คนเห็นว่า AI สำคัญ แต่องค์กรยังไม่มี governance ที่รองรับ +- คนอยากเรียน agents + workflow integration แต่องค์กรยังสอน prompting basics + +ช่องว่างตรงนี้คือทั้ง **โอกาส** และ **ความเสี่ยง** — ถ้าคุณเป็น B2B marketer ที่ proactive คุณจะเป็นคนที่นำเทรนด์ สร้างคุณค่าให้ตัวเองและองค์กร แต่ถ้าคุณรอ คุณจะถูกทิ้งไว้ข้างหลัง + +**สิ่งที่ควรทำตอนนี้:** + +1. 🚀 **เริ่ม integrate AI ใน workflow จริง** — ไม่ใช่แค่ทดลอง แต่ทำให้เป็นส่วนหนึ่งของงานประจำวัน +2. 📚 **เรียนรู้เรื่อง AI Agents** — นี่คือเทรนด์ที่มาแรงที่สุด และจะยิ่งสำคัญขึ้นเรื่อยๆ +3. 🏗️ **ผลักดัน AI governance ในองค์กร** — เริ่มจาก roadmap + policies ก็ได้ ไม่ต้องครบ 4 ด้านทันที +4. 🧠 **พัฒนา systems thinking** — คิดเป็นระบบ ไม่ใช่แค่ tools เดี่ยวๆ +5. 🤝 **สร้าง AI community ในทีม** — แชร์ความรู้ แชร์ use cases เรียนรู้ไปด้วยกัน + +--- + +## FAQ + +### ตำแหน่งงานไหนใน B2B Marketing ที่เสี่ยงที่สุด? + +จากข้อมูลในรายงาน ไม่ได้เจาะจงว่าตำแหน่งไหนเสี่ยงที่สุด แต่ pattern ที่เห็นชัดคือ: **ตำแหน่งที่ทำงานซ้ำๆ และไม่ต้องใช้ความคิดสร้างสรรค์มาก** จะถูก AI เข้ามาแทนที่เร็วที่สุด เช่น data entry, basic reporting, simple content generation แต่ตำแหน่งที่ต้องใช้ strategic thinking, relationship building, และ creative problem-solving ยังปลอดภัย — ตราบใดที่คุณเรียนรู้ที่จะทำงาน *ร่วมกับ* AI ไม่ใช่แข่งกับมัน สิ่งสำคัญคือต้องไม่ชะล่าใจ — มีแค่ 20% ที่กังวลเรื่องงานตัวเอง แต่ 71% เชื่อว่างานจะหายไป + +### ต้องเรียน AI อะไรก่อนเป็นอันดับแรก? + +จากข้อมูลในรายงาน สิ่งที่คนอยากเรียนมากที่สุดคือ **workflow integration** (58%) — ไม่ใช่ prompting basics อีกต่อไป แปลว่า: ถ้าคุณใช้ AI พื้นฐานเป็นแล้ว ขั้นตอนต่อไปคือเรียนวิธีเชื่อม AI เข้ากับเครื่องมือที่ใช้อยู่ เช่น CRM, marketing automation, analytics tools แล้วก็เรียนเรื่อง **AI Agents** (51%) เพราะนี่คือเทรนด์ที่มาแรงที่สุด ถ้ายังไม่เคยใช้ AI เลย เริ่มจาก tools ที่มีอยู่แล้วในเครื่องมือที่ใช้ประจำ เช่น AI features ใน HubSpot, Salesforce, หรือ Google Workspace + +### AI Governance สำคัญยังไงกับ B2B Marketing? + +Governance คือ infrastructure ที่ทำให้ scale AI ได้อย่างยั่งยืน ไม่ใช่ bureaucracy จากข้อมูลในรายงาน องค์กรที่มี governance ครบทั้ง 4 ด้าน (roadmap, council, policies, ethics) มี **50% ที่บอกว่า AI momentum กำลัง accelerate** สำหรับ B2B marketing team governance ช่วยเรื่อง: data privacy compliance (สำคัญมากถ้าทำงานกับ client data), brand consistency ใน AI-generated content, ethical AI use (disclosure, fact-checking), และการตัดสินใจว่าจะเอา AI ไปใช้ตรงไหนก่อน Governance ไม่ใช่ตัวถ่วง แต่เป็นตัวเร่ง — องค์กรที่มี governance กลับ scale AI ได้เร็วกว่า + +--- + +**อ้างอิง:** 2026 State of AI for Business Report — Marketing AI Institute (Cathy McPhillips, 18 มิ.ย. 2026) + diff --git a/src/content/blog/2026-06-26-brand-reputation-precedes-you-with-ai.md b/src/content/blog/2026-06-26-brand-reputation-precedes-you-with-ai.md new file mode 100644 index 0000000..d2bd89e --- /dev/null +++ b/src/content/blog/2026-06-26-brand-reputation-precedes-you-with-ai.md @@ -0,0 +1,147 @@ +--- +title: "ชื่อเสียงแบรนด์ของคุณ \"เดินทาง\" ไปถึง AI ก่อนตัวคุณแล้ว" +description: "AI สร้างเรื่องเล่าเกี่ยวกับแบรนด์จากข้อมูลที่เคยเรียนรู้ ซึ่งอาจไม่ตรงกับสิ่งที่แบรนด์ต้องการสื่อหรือข้อเท็จจริง" +pubDate: 2026-06-26 +updatedDate: 2026-06-30 +category: "AI & Marketing" +heroImage: "/images/blog/2026-06-26-brand-reputation-precedes-you-with-ai-featured.png" +tags: ['brand-reputation', 'ai', 'narrative-gravity'] +draft: false +--- + +เคยลองถาม ChatGPT ว่า "แบรนด์ XYZ ดีไหม?" แล้วได้คำตอบที่ทำให้เหวอไหม? ถ้าไม่เคย — ลองดูสักครั้ง แล้วคุณจะรู้ว่า AI ไม่ได้ "ค้นหา" ข้อมูลเกี่ยวกับแบรนด์คุณแบบ Google อีกต่อไปแล้ว แต่ AI กำลัง "เล่าเรื่อง" เกี่ยวกับแบรนด์คุณจากสิ่งที่มันเคยเรียนรู้มา — และเรื่องที่มันเล่า ไม่จำเป็นต้องเป็นเรื่องจริงเสมอไป + + + +นี่คือสิ่งที่ Cathy McPhillips จาก Marketing AI Institute ออกมาพูดไว้ได้ตรงประเด็นมาก: **ยุคของ AI นี้ ชื่อเสียงแบรนด์ของคุณเดินทางไปถึงผู้บริโภคก่อนตัวคุณเสมอ** มาดูกันว่ามันหมายความว่าอะไร แล้วเราต้องเตรียมตัวยังไง + +--- + +## TL;DR + +- AI ไม่ได้ retrieve ข้อเท็จจริงเกี่ยวกับแบรนด์คุณ แต่ **complete a narrative** จาก training data ของมัน +- การทดลองกับโอลิมปิก 2026 (2.7 ล้าน data points, 6 AI platforms) พบว่า AI ยังคงตอบข้อมูลผิดอย่างมั่นใจ — แม้ favorite จะแพ้ก็ยังบอกว่าชนะ +- Seer Interactive พบว่า LLMs เอา Glassdoor review เชิงลบมาตอบคำถามเกี่ยวกับบริษัท โดย "balance" ข้อมูลจนน้ำหนักเทไปทางลบ +- **The Aicher Principle**: อีเวนท์ใหญ่แค่ amplify สิ่งที่มีอยู่แล้ว — ไม่ได้สร้าง presence จากศูนย์ +- สิ่งที่ต้องทำตอนนี้: query AI ดูว่ามัน "รู้" อะไรเกี่ยวกับแบรนด์คุณ แล้วจัดการ narrative ให้ถูกต้อง + +--- + +## สารบัญ + +1. [AI "รู้" อะไรเกี่ยวกับแบรนด์คุณ?](#ai-รู้-อะไรเกี่ยวกับแบรนด์คุณ) +2. [การทดลอง: โอลิมปิก 2026 — 2.7 ล้าน data points](#การทดลองโอลิมปิก-2026) +3. [ตัวอย่างจริง: Seer Interactive](#ตัวอย่างจริง-seer-interactive) +4. [The Aicher Principle](#the-aicher-principle) +5. [ต้องทำยังไงต่อ?](#ต้องทำยังไงต่อ) +6. [FAQ](#faq) + +--- + +## AI "รู้" อะไรเกี่ยวกับแบรนด์คุณ? + +ก่อนอื่นต้องเข้าใจ concept ที่เรียกว่า **Narrative Gravity** ก่อน + +Narrative Gravity คือแนวโน้มที่ AI จะสร้าง "เรื่องเล่า" จาก training data ของมัน แล้วเล่าซ้ำๆ จนกลายเป็น narrative หลัก AI ไม่ได้ retrieve ข้อเท็จจริงแบบ search engine — มัน **complete a story** นึกภาพว่าคุณให้ AI เขียนนิยายสักเรื่อง ถ้าใน training data มีแต่ข้อมูลเชิงลบเกี่ยวกับแบรนด์ไหน แบรนด์นั้นก็จะถูกเล่าในแง่ลบเสมอ ไม่ว่าความจริงจะเป็นยังไง + +![Narrative Gravity — how AI forms brand stories](/images/blog/2026-06-26-brand-reputation-precedes-you-with-ai-narrative-gravity.png) + +นี่ไม่ใช่ bug — มันคือ how LLMs work เลย + +ปัญหาคือ: คนส่วนใหญ่เริ่มใช้ AI เป็นแหล่งข้อมูลอันดับแรกแล้ว ไม่ใช่แค่ Gen Z ที่ถาม ChatGPT ก่อน search Google แต่ B2B buyer ก็เหมือนกัน ซึ่งหมายความว่า **narrative ที่ AI เล่าเกี่ยวกับแบรนด์คุณ กลายเป็น first impression ของลูกค้าไปแล้ว** โดยที่คุณอาจไม่รู้ตัว + +--- + +## การทดลอง: โอลิมปิก 2026 + +อันนี้เป็น case study ที่น่าสนใจมาก + +ทีมวิจัยเก็บข้อมูลจาก **6 AI platforms** ได้แก่ ChatGPT, Gemini, AI Mode, AI Overviews, Perplexity และ Meta AI เป็นเวลา **9 สัปดาห์** ในช่วงโอลิมปิกฤดูหนาว 2026 รวมทั้งหมด **2.7 ล้าน data points** + +ผลลัพธ์ที่ได้ค่อนข้างช็อก: + +![Olympics 2026 data — 2.7M data points across 6 AI platforms](/images/blog/2026-06-26-brand-reputation-precedes-you-with-ai-wikipedia-dominance.png) + +- ตอนที่นักกีฬาตัวเต็งแพ้ AI ยังคงตอบว่า "ชนะ" อย่างมั่นใจ +- AI ไม่ได้รายงานผลการแข่งขันแบบ real-time — มัน "จำ" narrative ที่สร้างไว้ก่อนหน้าแล้วเล่าต่อ +- ยิ่งนักกีฬามี digital footprint มาก AI ก็ยิ่ง "รู้จัก" และพูดถึงมาก + +สิ่งที่น่ากลัวคือ: ถ้า AI ยังรายงานผลกีฬาผิดได้ขนาดนี้ แล้วมันจะรายงานข้อมูลเกี่ยวกับแบรนด์คุณถูกต้องแค่ไหน? + +--- + +## ตัวอย่างจริง: Seer Interactive + +อีก case ที่อยากให้ดูคือ Seer Interactive ซึ่งเป็นเอเจนซี่ดิจิทัลที่ค่อนข้างมีชื่อเสียงในวงการ + +พวกเขาพบว่าเมื่อลูกค้าหรือ candidate ถาม LLMs เกี่ยวกับบริษัท AI ดึงเอา **Glassdoor review เชิงลบ** มาตอบ — ไม่ใช่แค่ดึงมา แต่ AI พยายาม "balance" ข้อมูลระหว่างบวกกับลบ ซึ่งในทางปฏิบัติแล้ว weight ไปทางลบมากเกินไป + +นี่คือปัญหาของ Narrative Gravity เลย: ถ้า training data มี signal เชิงลบเยอะ (แม้จะเป็นแค่ review ไม่กี่อัน) AI จะให้น้ำหนักกับมันมากกว่าที่ควร + +> AI ไม่ได้ตั้งใจจะทำลายแบรนด์คุณ — มันแค่เล่าเรื่องตาม pattern ที่มันเรียนรู้มา + +--- + +## The Aicher Principle + +มี quote นึงที่ชอบมากจากงานวิจัยชิ้นนี้: + +> **"Events amplify what already exists. They do not create presence from nothing."** + +![The Aicher Principle — events amplify what already exists](/images/blog/2026-06-26-brand-reputation-precedes-you-with-ai-aicher-principle.png) + +หลักการนี้เรียกว่า **The Aicher Principle** — ตั้งชื่อตาม Otl Aicher ผู้ออกแบบ visual identity ให้โอลิมปิก 1972 + +ความหมายคือ: อีเวนท์ใหญ่ๆ ไม่ว่าจะเป็นโอลิมปิก งานเปิดตัวสินค้า หรือ crisis ทาง PR — มันไม่ได้สร้างชื่อเสียงแบรนด์จากศูนย์ มันแค่ **amplify สิ่งที่มีอยู่แล้ว** ถ้าแบรนด์คุณมี narrative เชิงบวกใน digital world อีเวนท์ก็จะขยายมัน แต่ถ้า narrative เป็นลบ อีเวนท์ก็จะทำให้มันหนักขึ้น + +ในบริบทของ AI: ถ้านักกีฬาไม่มี digital footprint เลย AI ก็จะไม่ mention เขาเลย ไม่ว่าเขาจะชนะเหรียญทองก็ตาม นี่เป็นหลักฐานชัดเจนว่า **การไม่มีตัวตนใน digital world = การไม่มีตัวตนในสายตา AI** + +--- + +## ต้องทำยังไงต่อ? + +พอเข้าใจปัญหาแล้ว มาดู action items กัน: + +### 1. Query AI ดูว่ามัน "รู้" อะไรเกี่ยวกับแบรนด์คุณ + +ลองถาม ChatGPT, Gemini, Perplexity และ Meta AI ด้วยคำถามง่ายๆ เช่น: +- "แบรนด์ [ชื่อ] ดีไหม?" +- "[ชื่อแบรนด์] มีข้อเสียอะไร?" +- "เปรียบเทียบ [ชื่อแบรนด์] กับ [คู่แข่ง]" + +เก็บคำตอบไว้ทั้งหมด แล้วดูว่า narrative ที่ AI เล่าตรงกับความจริงแค่ไหน + +### 2. วิเคราะห์ narrative หลัก + +ดูว่า AI เล่าเรื่องเกี่ยวกับแบรนด์คุณแบบไหน — บวก ลบ หรือ neutral? มีข้อมูลผิดอะไรบ้าง? signal จากแหล่งไหนมีน้ำหนักมากที่สุด? + +### 3. สร้าง counter-narrative content + +ถ้า narrative เป็นลบ คุณต้องสร้าง content ที่เป็นบวกขึ้นมา **เยอะพอ** ที่จะเปลี่ยน training data ของ AI ในระยะยาว ไม่ใช่แค่แก้ข่าว — ต้องสร้าง content ที่มี authority สูง เช่น บทความในสื่อที่เชื่อถือได้ งานวิจัย case study จริง + +### 4. Brand narrative management อย่างจริงจัง + +ยุคนี้ไม่ใช่แค่ทำ content marketing แล้วจบ — คุณต้อง **manage narrative** ที่ AI จะเรียนรู้และเล่าต่อ ทุกบทความ ทุกรีวิว ทุก PR ที่ออกไป ล้วนมีผลต่อสิ่งที่ AI จะ "รู้" เกี่ยวกับแบรนด์คุณในอนาคต + +--- + +## FAQ + +### Narrative Gravity คืออะไร? + +Narrative Gravity คือแนวโน้มของ LLMs ในการสร้าง "เรื่องเล่า" จาก training data แล้วเล่าซ้ำอย่างสม่ำเสมอ AI ไม่ได้ retrieve ข้อเท็จจริงแบบ database lookup — มัน complete a pattern ซึ่งหมายความว่าถ้า narrative ใน training data เป็นลบ AI ก็จะเล่าเรื่องเชิงลบเกี่ยวกับแบรนด์นั้นต่อไปเรื่อยๆ + +### ต้อง query AI ยังไง? + +เริ่มจากถามคำถามง่ายๆ เกี่ยวกับแบรนด์คุณบน ChatGPT, Gemini, Perplexity และ Meta AI ถามทั้งคำถามกลางๆ ("แบรนด์ X ดีไหม?") และคำถามเชิงลบ ("แบรนด์ X มีข้อเสียอะไร?") เพื่อดูว่า AI ตอบยังไงในทุกมุม เปรียบเทียบคำตอบจากหลาย platform แล้วจด narrative หลักที่ปรากฎ + +### Counter-narrative ทำอย่างไร? + +Counter-narrative ไม่ใช่แค่แก้ข่าวหรือออก PR denial — แต่เป็นการสร้าง content คุณภาพสูงจำนวนมากที่เล่าเรื่องราวที่ถูกต้องเกี่ยวกับแบรนด์คุณ เช่น case study จริง, รีวิวจากลูกค้าจริง, บทความจากสื่อที่เชื่อถือได้, และ thought leadership content เป้าหมายคือทำให้ training data ในอนาคตมี signal บวกมากพอที่ AI จะเล่า narrative ที่ถูกต้อง + +--- + +**แหล่งอ้างอิง:** [Marketing AI Institute — Cathy McPhillips (15 มิ.ย. 2026)](https://www.marketingaiinstitute.com/blog/brand-reputation-ai-b2b-marketers-summit) + +--- + diff --git a/src/content/blog/2026-06-26-fintech-invisible-to-ai-agents.md b/src/content/blog/2026-06-26-fintech-invisible-to-ai-agents.md new file mode 100644 index 0000000..01a07b8 --- /dev/null +++ b/src/content/blog/2026-06-26-fintech-invisible-to-ai-agents.md @@ -0,0 +1,147 @@ +--- +title: "Fintech มองไม่เห็น: เว็บไซต์กว่า 1 ใน 3 หายไปในสายตา AI Agents" +description: "ผลทดสอบเว็บไซต์ fintech 274 แห่งพบว่า AI agents มองไม่เห็นบางเว็บไซต์ แม้ประสบการณ์ใช้งานสำหรับคนจะดูสมบูรณ์" +pubDate: 2026-06-26 +updatedDate: 2026-06-30 +category: "SEO" +heroImage: "/images/blog/2026-06-26-fintech-invisible-to-ai-agents-featured.png" +tags: ['fintech', 'ai-agents', 'seo', 'visibility'] +draft: false +--- + +ลองนึกภาพว่าคุณทำ fintech startup มา 3 ปี ลงเงินไปหลายล้านกับ UX สวยๆ แล้ววันหนึ่งลูกค้าถาม AI agent ว่า "แนะนำ fintech ที่ดีที่สุด" — แต่ชื่อบริษัทคุณไม่ปรากฎเลย ไม่ใช่เพราะไม่ดีพอ แต่เพราะ **AI มองไม่เห็นเว็บคุณตั้งแต่แรก** ปัญหานี้เกิดขึ้นจริงแล้วกับ fintech กว่า 99 เว็บไซต์จาก 274 ที่ทดสอบ + + + +## TL;DR + +- **36% ของ fintech websites** คืนเนื้อหาให้ AI agents ได้ไม่ถึง 80% +- **17%** คืนแค่ HTML shell เปล่าๆ (0% content) +- สาเหตุหลัก: JavaScript rendering — AI crawlers ไม่รัน JS +- ทดสอบเว็บคุณได้ใน 30 วินาทีด้วย DevTools +- แก้ไขได้โดยไม่ต้อง rebuild ทั้ง app — เพิ่ม server-rendering layer ให้หน้าสำคัญ + +## สารบัญ + +1. [AI Agent มองไม่เห็นเว็บคุณได้ยังไง?](#ai-agent-มองไม่เห็นเว็บคุณได้ยังไง) +2. [ตัวเลขที่น่าตกใจ — 274 Fintech Homepages](#ตัวเลขที่น่าตกใจ--274-fintech-homepages) +3. [ทำไม Fintech ถึงเสี่ยงกว่าสายอื่น?](#ทำไม-fintech-ถึงเสี่ยงกว่าสายอื่น) +4. [ทดสอบเว็บคุณใน 30 วินาที](#ทดสอบเว็บคุณใน-30-วินาที) +5. [วิธีแก้ไขโดยไม่ต้อง Rebuild](#วิธีแก้ไขโดยไม่ต้อง-rebuild) +6. [บทเรียนสำหรับทุกธุรกิจ](#บทเรียนสำหรับทุกธุรกิจ) +7. [FAQ](#faq) + +--- + +## AI Agent มองไม่เห็นเว็บคุณได้ยังไง? + +ก่อนอื่นมาทำความเข้าใจก่อนว่า AI agents ทำงานยังไงเวลา "อ่าน" เว็บ + +เวลาที่เราเปิดเว็บด้วย Chrome หรือ Safari เบราว์เซอร์จะทำหลายอย่างมาก — โหลด HTML, รัน JavaScript, render components, fetch ข้อมูลจาก API, แล้วค่อยแสดงผลหน้าเว็บสวยๆ ให้เราดู ใช้เวลาทั้งหมดเป็นวินาที + +แต่ AI agents ไม่ได้ทำแบบนั้น AI crawlers ส่วนใหญ่ใช้ **raw HTTP fetch** — คือขอ HTML มาก็ได้ HTML กลับมาเลย ไม่มีการรัน JavaScript ไม่มีการ render components ไม่มีการรอ API calls + +ดังนั้นถ้าเว็บไซต์ของคุณใช้ JavaScript-heavy rendering เช่น React, Vue, Angular หรือ framework อื่นๆ ที่ render เนื้อหาฝั่ง client — AI agent จะเห็นแค่ **shell เปล่าๆ** ไม่มี content เลย + +![Browser rendering vs AI crawler — JavaScript disabled](/images/blog/2026-06-26-fintech-invisible-to-ai-agents-devtools-screenshot.png) + +มันเหมือนกับคุณส่งจดหมายหาเพื่อน แต่เพื่อนเปิดซองมาเจอแค่กระดาษเปล่า เพราะเนื้อหาจริงๆ ถูกเขียนด้วยหมึกที่ต้องใช้แสง UV ถึงจะเห็น — AI agent ไม่มี "แสง UV" ที่ว่า + +## ตัวเลขที่น่าตกใจ — 274 Fintech Homepages + +เมื่อเร็วๆ นี้มีการทดสอบ fintech homepages ทั้งหมด **274 เว็บไซต์** ว่า AI agents มองเห็นเนื้อหาได้มากน้อยแค่ไหน ผลลัพธ์ค่อนข้างช็อก: + +- **36% (99 เว็บ)** คืนเนื้อหาให้ AI ได้ไม่ถึง 80% — แปลว่า AI อ่านได้ไม่ครบ +- **17% (47 เว็บ)** คืนเนื้อหา **0%** — เหลือแค่ HTML shell เปล่าๆ ไม่มี content เลย +- **20%** คืนเนื้อหาได้แค่ไม่ถึง 30% +- **34 เว็บ (12%)** ไม่ถึง network idle ภายใน 30 วินาที — คือ render ไม่เสร็จด้วยซ้ำ + +![Fintech visibility data — 36% partially visible, 17% invisible](/images/blog/2026-06-26-fintech-invisible-to-ai-agents-data-visualization.png) + +ส่วนเว็บที่ทำได้ดีคืนเนื้อหาได้ **100%** มีทั้งหมด **101 เว็บ** ตัวอย่างเช่น Stripe, Plaid, Adyen, Marqeta, และ Starling Bank สังเกตว่าพวกนี้เป็น fintech ขนาดใหญ่ที่มีทีมวิศวกรและ SEO ที่แข็งแกร่ง + +ตัวเลขที่น่าสนใจอีกอย่าง: **median render time** ของเว็บที่มีปัญหาคือ **21x นานกว่า** raw HTTP fetch คือ AI agent ต้องรอนานกว่าปกติถึง 21 เท่า — และบางเว็บ AI ไม่รอเลย เพราะ timeout ก่อน + +สำหรับเว็บที่ render ได้เร็ว เช่น Fiserv (58ms), Acorns (76ms), Ledger (100ms) — พวกนี้มักจะมี server-side rendering อยู่แล้ว ทำให้ AI มองเห็นได้ทันที + +## ทำไม Fintech ถึงเสี่ยงกว่าสายอื่น? + +คุณอาจสงสัยว่า "ก็มันแค่ 36% ไม่ใช่เหรอ? ไม่เห็นจะน่าตกใจขนาดนั้น" + +แต่ปัญหาคือ **fintech ไม่เหมือนเว็บอื่น** เพราะ: + +### 1. Regulated Disclosures + +Fintech มีข้อมูลที่กฎหมายบังคับให้แสดง เช่น ใบอนุญาต, ข้อมูลการรับฝากเงิน, รายละเอียดด้านความปลอดภัย, อัตราดอกเบี้ย ถ้า AI agent มองไม่เห็นข้อมูลพวกนี้ แปลว่า fintech นั้นไม่สามารถผ่านการเปรียบเทียบได้เลย — ไม่ว่าจะเป็น comparison engine, AI advisor, หรือ chatbot ที่แนะนำผลิตภัณฑ์การเงิน + +### 2. ตกรอบตั้งแต่ยังไม่ได้แข่ง + +ถ้า AI agent แนะนำ "สินเชื่อดอกเบี้ยต่ำ 5 อันดับ" แล้วเว็บคุณมองไม่เห็น — คุณไม่ได้อยู่ในลิสต์เลย ไม่ใช่เพราะไม่ดีพอ แต่เพราะ AI ไม่รู้ว่าคุณมีอยู่จริง + +### 3. ความไว้วางใจ + +ผู้ใช้เริ่มพึ่งพา AI มากขึ้นเรื่อยๆ ในการตัดสินใจเรื่องเงิน ถ้า fintech ของคุณไม่ปรากฎในผลลัพธ์ของ AI คุณจะเสียความน่าเชื่อถือโดยปริยาย + +## ทดสอบเว็บคุณใน 30 วินาที + +มาดูวิธีทดสอบง่ายๆ ที่ใครก็ทำได้: + +1. เปิดเว็บไซต์ของคุณใน Chrome +2. กด **F12** หรือ **Cmd+Option+I** (Mac) เพื่อเปิด DevTools +3. กด **Cmd+Shift+P** (Mac) หรือ **Ctrl+Shift+P** (Windows) พิมพ์ "Disable JavaScript" แล้วกด Enter +4. **Reload** หน้าเว็บ (Cmd+R หรือ F5) +5. ดูผลลัพธ์ — ถ้าหน้าเว็บเปล่าหรือไม่มี content = AI agent เห็นแบบเดียวกัน + +![Server-side rendering solution for critical pages](/images/blog/2026-06-26-fintech-invisible-to-ai-agents-solution-diagram.png) + +ถ้าผลลัพธ์ออกมาเปล่า ไม่ต้องตกใจ — มีวิธีแก้ไขที่ไม่ต้อง rebuild ทั้งแอป + +## วิธีแก้ไขโดยไม่ต้อง Rebuild + +ข่าวดีคือคุณไม่ต้องรื้อทิ้งแล้วเริ่มใหม่ แค่เพิ่ม **server-rendering layer** สำหรับ routes สำคัญๆ: + +### หน้าที่ต้อง Server-Render ก่อนเลย + +- **Homepage** — หน้าแรกที่ AI มักจะ crawl เป็นอันดับแรก +- **Pricing page** — ข้อมูลราคาเป็นสิ่งที่ AI agents ต้องการมากที่สุด +- **Product pages** — รายละเอียดผลิตภัณฑ์และ features +- **Blog** — เนื้อหาให้ความรู้ที่ช่วยดึง traffic จาก AI + +### เทคโนโลยีที่ใช้ + +- **Next.js** — มี SSR/SSG built-in อยู่แล้ว +- **Nuxt.js** — สำหรับ Vue ecosystem +- **Astro** — ถ้าอยากได้ static-first approach +- **Edge rendering** — ใช้ Cloudflare Workers หรือ Vercel Edge Functions + +หลักการง่ายๆ: **ไม่ต้อง SSR ทุกหน้า** — เริ่มจากหน้าที่มี impact ต่อ AI visibility สูงสุดก่อน แล้วค่อยๆ ขยาย + +## บทเรียนสำหรับทุกธุรกิจ + +แม้ว่าบทความนี้จะโฟกัสที่ fintech แต่ปัญหานี้ไม่ได้จำกัดแค่สายการเงิน **ทุกเว็บที่ใช้ JavaScript-heavy rendering** มีความเสี่ยงเหมือนกัน + +บทเรียนสำคัญที่สุดคือ: **Architecture decision > Content quality** + +คุณจะเขียน content ดีแค่ไหน แต่ถ้า AI อ่านไม่ออก มันก็ไม่มีค่าในโลกที่ AI agents เป็นตัวกลางระหว่างผู้ใช้กับข้อมูล + +ยุคที่ SEO หมายถึง "ทำให้ Google เข้าใจ" กำลังเปลี่ยนเป็น "ทำให้ AI agents เข้าใจ" — และมันเริ่มขึ้นแล้ววันนี้ + +--- + +## FAQ + +### AI agents คืออะไร? + +AI agents คือซอฟต์แวร์ที่ทำหน้าที่แทนผู้ใช้ในการค้นหาและเปรียบเทียบข้อมูล ตัวอย่างเช่น ChatGPT, Perplexity, Google Gemini ที่สามารถ "อ่าน" เว็บไซต์และให้คำแนะนำได้โดยตรง แทนที่จะแสดงลิงก์ 10 อันดับเหมือน Google แบบเดิม + +### ทำไม JavaScript rendering ถึงเป็นปัญหา? + +AI crawlers ส่วนใหญ่ใช้ raw HTTP fetch — คือขอ HTML มาก็ได้กลับมาเลยโดยไม่รัน JavaScript ถ้าเว็บคุณ render เนื้อหาด้วย JavaScript (React, Vue, Angular) AI จะเห็นแค่ HTML shell เปล่าๆ เหมือนหนังสือที่มีแต่ปกไม่มีเนื้อหาข้างใน + +### วิธีแก้ไขทำอย่างไร? + +เพิ่ม server-side rendering (SSR) หรือ static site generation (SSG) ให้หน้าสำคัญๆ เช่น homepage, pricing, product pages ใช้ framework อย่าง Next.js, Nuxt.js หรือ Astro ที่มี SSR built-in ไม่ต้อง rebuild ทั้งแอป — แค่ render หน้าหลักๆ ฝั่ง server ก็ช่วยได้มากแล้ว + +--- + +*Source: [Search Engine Journal — A Third of Fintech Is Invisible to AI Agents](https://www.searchenginejournal.com/a-third-of-fintech-is-invisible-to-ai-agents/576193/) by Slobodan Sani Manić (26 มิ.ย. 2026)* \ No newline at end of file diff --git a/src/content/blog/2026-06-26-google-seo-for-ai-agents.md b/src/content/blog/2026-06-26-google-seo-for-ai-agents.md new file mode 100644 index 0000000..0fe66b7 --- /dev/null +++ b/src/content/blog/2026-06-26-google-seo-for-ai-agents.md @@ -0,0 +1,141 @@ +--- +title: "Google ตอบแล้ว: SEO สำหรับ AI Agents ต้องทำยังไง?" +description: "สรุปแนวทางจาก Google ว่าเว็บไซต์ควรเตรียม SEO และประสบการณ์ใช้งานอย่างไร เมื่อ AI agents เริ่มเข้ามาใช้งานเว็บจริง" +pubDate: 2026-06-26 +updatedDate: 2026-06-30 +category: "SEO" +heroImage: "/images/blog/2026-06-26-google-seo-for-ai-agents-featured.png" +tags: ['google', 'seo', 'ai-agents', 'gemini'] +draft: false +--- + +ถ้าคุณอยู่ในวงการ SEO มาสักพัก คุณน่าจะเคยได้ยินเรื่อง AI agents มาบ้างแล้ว — เจ้าพวกนี้ไม่ใช่แค่ crawler ธรรมดาที่มาเก็บข้อมูลเว็บเรา แต่มันเป็น AI ที่สามารถ "ใช้งานเว็บ" ได้จริง ๆ คลิกได้ กรอกฟอร์มได้ ซื้อของได้ แล้ว Google พูดถึงเรื่องนี้ยังไงบ้าง? เราต้องปรับ SEO ใหม่ทั้งหมดเลยไหม? บทความนี้จะสรุปให้ฟังแบบเข้าใจง่าย ๆ ครับ + + + +## TL;DR + +- **John Mueller จาก Google ย้ำชัด**: เว็บที่ดีสำหรับคน = ดีสำหรับ AI agents ไม่ต้องทำอะไรใหม่เป็นพิเศษ +- **Gemini 3.5 Flash มาพร้อม Computer Use** — AI ของ Google เริ่ม "ใช้งานเว็บ" ได้เหมือนคนแล้ว +- **ห้ามบล็อก agentic browsers แบบมั่วซั่ว** — ถ้าบล็อกผิดจังหวะ อาจเสียทราฟฟิกโดยไม่รู้ตัว +- **Technical accessibility** กลายเป็นปัจจัยใหม่ที่ต้องใส่ใจ + +--- + +## สารบัญ + +1. [คำถามที่ทุกคนอยากรู้](#คำถามที่ทุกคนอยากรู้) +2. [คำตอบจาก Google](#คำตอบจาก-google) +3. [สิ่งที่เปลี่ยน (และไม่เปลี่ยน)](#สิ่งที่เปลี่ยน-และไม่เปลี่ยน) +4. [นัยยะสำหรับนัก SEO](#นัยยะสำหรับนัก-seo) +5. [ต้องทำอะไรบ้าง?](#ต้องทำอะไรบ้าง) +6. [FAQ](#faq) + +--- + +## คำถามที่ทุกคนอยากรู้ + +ช่วงที่ผ่านมา Google ปล่อยข่าวใหญ่หลายอย่าง หนึ่งในนั้นคือ **Gemini 3.5 Flash ที่มาพร้อมฟีเจอร์ Computer Use** — หมายความว่า AI ของ Google ไม่ได้แค่อ่านข้อมูลบนเว็บอีกต่อไป แต่มันสามารถ "ใช้งาน" เว็บได้เหมือนมนุษย์เลยทีเดียว + +![Gemini Computer Use — agentic browsing diagram](/images/blog/2026-06-26-google-seo-for-ai-agents-agentic-browsing.png) + +พอข่าวนี้ออกมา คำถามแรกที่คนในวงการ SEO ถามกันก็คือ: + +> "แล้วเราจะต้องปรับ SEO สำหับ AI agents ยังไง? Quality standards จะเปลี่ยนไหม?" + +เป็นคำถามที่สมเหตุสมผลมาก เพราะถ้า AI เริ่มท่องเว็บแทนคน เราก็ต้องรู้ว่า Google มองเรื่องนี้ยังไง + +--- + +## คำตอบจาก Google + +**John Mueller** — Search Advocate ของ Google ออกมาตอบคำถามนี้ชัดเจนมากครับ เขาบอกว่า: + +> "A website that's useful for users, will generally also be useful for agentic browsers." + +แปลเป็นไทยง่าย ๆ ก็คือ: **เว็บที่ดีสำหรับคน ก็จะดีสำหรับ AI agents ด้วย** + +นี่คือข่าวดีสุด ๆ สำหรับคนที่ทำ SEO ถูกต้องมาตลอด ไม่ต้อง panic ไม่ต้องรื้อ strategy ใหม่ทั้งหมด Google ยืนยันว่า core quality standards ยังเหมือนเดิม — content ดี, site structure ดี, ได้รับความนิยม — สิ่งเหล่านี้ยังใช้ได้อยู่ + +![SEO quality checklist for AI agents](/images/blog/2026-06-26-google-seo-for-ai-agents-quality-signals.png) + +--- + +## สิ่งที่เปลี่ยน (และไม่เปลี่ยน) + +แม้ core principles จะไม่เปลี่ยน แต่มีบางอย่างที่เราต้องเริ่มให้ความสำคัญมากขึ้น: + +### ไม่เปลี่ยน + +- **Content quality** — เนื้อหายังต้องมีคุณภาพ ตอบคำถามคนอ่านได้จริง +- **Site structure** — เว็บยังต้องจัดระเบียบดี ใช้งานง่าย +- **Popularity signals** — backlinks, brand mention ยังมีผลเหมือนเดิม + +### เปลี่ยน (หรือเพิ่มเติม) + +1. **ห้ามบล็อก agentic browsers แบบมั่วซั่ว** — นี่คือจุดที่อันตรายที่สุด หลายเว็บไซต์ตั้ง robots.txt บล็อก bot ไว้เยอะมาก บางทีก็บล็อกแบบไม่ดูตาม้าตาเรือ ถ้าเผลอบล็อก agentic browser ของ Google เข้า ก็อาจจะเสียทราฟฟิกโดยไม่รู้ตัว + +2. **"New basics" จะเกิดขึ้น** — Google บอกว่าจะมี "พื้นฐานใหม่" เกิดขึ้นสำหรับการทำให้เว็บรองรับ AI agents ซึ่งยังไม่มีรายละเอียดชัดเจน 100% แต่แนวโน้มคือ technical accessibility จะสำคัญมากขึ้น + +3. **Technical accessibility เป็นปัจจัยใหม่** — ไม่ใช่แค่ "คนเข้าถึงได้" แต่ AI ก็ต้องเข้าถึงได้ด้วย เว็บที่ render ด้วย JavaScript ล้วน ๆ, มี CAPTCHA ซับซ้อน, หรือมี flow การใช้งานที่งง — อาจจะมีปัญหา + +--- + +## นัยยะสำหรับนัก SEO + +สรุปให้เห็นภาพชัด ๆ นะครับ: + +**Google ไม่ได้ rewrite quality standards** ใหม่ทั้งหมด ไม่ต้องทิ้งสิ่งที่ทำมา แต่ต้องเพิ่ม awareness เรื่อง technical accessibility เข้าไปอีกนิด + +- **Content quality** → ยังสำคัญอันดับ 1 +- **Site structure** → ยังสำคัญเหมือนเดิม +- **Popularity signals** → ยังมีผล +- **Technical accessibility** → ⚡ ปัจจัยใหม่ที่ต้องใส่ใจเพิ่ม + +สำหรับคนที่ทำ SEO แบบ white hat มาตลอด นี่คือข่าวดีเลย เพราะไม่ต้องเปลี่ยนอะไรมาก แค่ต้องมั่นใจว่าเว็บของเราไม่ได้บล็อก AI agents โดยไม่ตั้งใจ + +--- + +## ต้องทำอะไรบ้าง? + +ถ้าอ่านมาถึงตรงนี้แล้วอยากเริ่มลงมือ นี่คือ checklist ที่แนะนำ: + +### 1. อย่า block agentic browsers + +ตรวจสอบ robots.txt และ server-level rules ของคุณ ดูว่ามีการบล็อก bot ตัวไหนอยู่บ้าง ถ้าไม่แน่ใจว่าตัวไหนคือ agentic browser ของ Google อย่าบล็อกมั่ว ๆ เลยครับ + +### 2. ตรวจสอบ robots.txt + +เปิดไฟล์ `robots.txt` ของเว็บคุณแล้วดูดี ๆ ว่า: +- ไม่ได้ block user-agent ที่จำเป็น +- ไม่ได้ block path สำคัญ ๆ โดยไม่ตั้งใจ +- Allow directives ครอบคลุมหน้าที่ต้องการให้ index + +### 3. ตรวจสอบ Technical Accessibility + +ลองเช็คว่า: +- เว็บ render ได้ถูกต้องแม้ปิด JavaScript (หรืออย่างน้อย SSR ทำงานดี) +- ไม่มี CAPTCHA ขวางทาง bot ที่มาจาก Google +- หน้าเว็บโหลดเร็วและ structured data ถูกต้อง +- Internal linking ทำงานได้ดี ไม่มี broken links + +--- + +## FAQ + +### AI Agents คืออะไร? + +AI agents คือ ระบบ AI ที่ไม่ได้แค่ "ตอบคำถาม" แต่สามารถ "ลงมือทำ" ได้จริง เช่น ท่องเว็บ, กรอกฟอร์ม, สั่งซื้อสินค้า ต่างจาก chatbot ธรรมดาที่แค่ตอบข้อความ ตัวอย่างเช่น Gemini 3.5 Flash ของ Google ที่มาพร้อม Computer Use — ทำให้ AI สามารถควบคุมเบราว์เซอร์ได้เหมือนคนนั่งใช้เลย + +### ต้องเปลี่ยน SEO Strategy ไหม? + +**ไม่ต้องเปลี่ยนทั้งหมด** ถ้าคุณทำ SEO แบบเน้นคุณภาพมาตลอด ก็แค่เพิ่มเรื่อง technical accessibility เข้าไป สิ่งที่ต้องดูเพิ่มคือ robots.txt, server configuration, และมั่นใจว่า agentic browsers เข้าถึงเว็บได้สะดวก + +### ต้อง Block AI Crawlers ไหม? + +**ไม่แนะนำให้บล็อกแบบมั่วซั่ว** John Mueller จาก Google ย้ำว่าอย่าบล็อก agentic browsers แบบไม่คิด ถ้าบล็อกผิดตัว คุณอาจจะเสียโอกาสจาก AI-driven traffic ที่กำลังจะมีบทบาทมากขึ้นเรื่อย ๆ ควรตรวจสอบให้ดีก่อน block ทุกครั้ง + +--- + + +*อ้างอิง: [Search Engine Journal — Google Answers Question About SEO For AI Agents](https://www.searchenginejournal.com/google-seo-for-ai-agents/580589/) โดย Roger Montti (26 มิ.ย. 2026)* \ No newline at end of file diff --git a/src/content/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai.md b/src/content/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai.md new file mode 100644 index 0000000..5f6ad1e --- /dev/null +++ b/src/content/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai.md @@ -0,0 +1,177 @@ +--- +title: "Two Things B2B Marketer ควรทำกับ AI ตอนนี้ (ก่อนที่จะตามไม่ทัน)" +description: "สองเรื่องที่ B2B marketer ควรเริ่มทำ เมื่อ AI กำลังเปลี่ยนจากเครื่องมือผลิตคอนเทนต์ไปเป็นระบบขับเคลื่อนงานการตลาด" +pubDate: 2026-06-26 +updatedDate: 2026-06-30 +category: "B2B" +heroImage: "/images/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai-featured.png" +tags: ['b2b', 'ai-strategy', 'marketing', 'agents'] +draft: false +--- + +ถ้าคุณเป็น B2B Marketer ที่ยังใช้ AI แค่ "ช่วยเขียน copy" หรือ "generate รูป" — บอกเลยว่าคุณกำลังพลาดโอกาสใหญ่หลวง ปี 2026 นี้ AI ไม่ใช่แค่เครื่องมือช่วยผลิต content อีกต่อไปแล้ว แต่มันกำลังกลายเป็น "ระบบขับเคลื่อน" ทั้งหมดของการตลาด และทีมที่เข้าใจเรื่องนี้ก่อน จะทิ้งห่างคู่แข่งแบบตามไม่ทันใน 12 เดือนข้างหน้า + + + +## TL;DR + +**ข้อ 1:** เปลี่ยน mindset จาก "Production" (ใช้ AI แค่ช่วยผลิต content) เป็น "Orchestration" (ให้ AI ขับเคลื่อน content system ทั้งระบบ) ด้วย SPARK Flywheel Framework + +**ข้อ 2:** สร้าง AI Agent Playbooks — เอกสาร playbook ที่ออกแบบมาเฉพาะทีมคุณ ไม่ใช่แค่ทดลองใช้ AI ไปเรื่อยๆ + +ข้อมูลจาก 2026 State of AI Report พบว่า 53% ของธุรกิจอยู่ใน stage Integration/Transformation แต่มีแค่ 25% เท่านั้นที่ถึง Scaling phase — แปลว่าโอกาสยังเปิดกว้างมาก + +> *"The teams building this capability now will be the hardest to catch in 12 months."* — Marketing AI Institute + +--- + +## สารบัญ + +1. [สถานการณ์ปัจจุบัน: AI ใน B2B Marketing ตอนนี้เป็นยังไง?](#สถานการณ์ปัจจุบัน-ai-ใน-b2b-marketing-ตอนนี้เป็นยังไง) +2. [ข้อ 1: Production → Orchestration — เปลี่ยนวิธีคิด](#ข้อ-1-production--orchestration--เปลี่ยนวิธีคิด) +3. [ข้อ 2: สร้าง AI Agent Playbooks ก่อนคู่แข่ง](#ข้อ-2-สร้าง-ai-agent-playbooks-ก่อนคู่แข่ง) +4. [AI Operations Layer: กาวเชื่อมทุกอย่าง](#ai-operations-layer-กาวเชื่อมทุกอย่าง) +5. [บทเรียนสำหรับทีมของคุณ](#บทเรียนสำหรับทีมของคุณ) +6. [FAQ](#faq) + +--- + +## สถานการณ์ปัจจุบัน: AI ใน B2B Marketing ตอนนี้เป็นยังไง? + +ก่อนจะไปถึง "สองสิ่งที่ควรทำ" เรามาดูภาพรวมกันก่อน + +จาก **2026 State of AI for Business Report** ที่สำรวจจากผู้ตอบกว่า 2,100 คน: + +- **53%** ของธุรกิจอยู่ใน Integration/Transformation stage — หมายความว่าเกินครึ่งเริ่มเอา AI เข้ามาในระบบแล้ว แต่ยังไม่ได้ scale +- **แค่ 25%** เท่านั้นที่ถึง Scaling phase — คือใช้ AI ได้จริงจังในหลายแผนก +- **47%** ยังแค่ piloting — ทดลองใช้ ยังไม่มีระบบจริงจัง + +![AI adoption stages — 53% Integration, 25% Scaling, 47% Piloting](/images/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai-ai-adoption-stages-infographic.png) + +ตัวเลขนี้บอกอะไร? บอกว่า **โอกาสยังเปิดกว้างมาก** ถ้าคุณเริ่มสร้างระบบ AI ที่จริงจังตอนนี้ คุณจะอยู่ในกลุ่ม 25% ที่นำหน้า ในขณะที่คู่แข่งส่วนใหญ่ยังแค่ทดลอง + +--- + +## ข้อ 1: Production → Orchestration — เปลี่ยนวิธีคิด + +### Production คืออะไร? + +Production mindset คือการใช้ AI แบบ "ช่วยทำงาน" เช่น: +- ใช้ AI เขียนโพสต์ LinkedIn +- ใช้ AI สร้าง email draft +- ใช้ AI generate รูปภาพ + +ฟังดูดีใช่ไหม? แต่ปัญหาคือ — มันแค่ **เร็วขึ้น** ไม่ได้ **ฉลาดขึ้น** ทุกครั้งที่คุณใช้ AI เขียนโพสต์ มันเริ่มจากศูนย์ทุกครั้ง ไม่มีการเรียนรู้ ไม่มี feedback loop ไม่มี system + +### Orchestration คืออะไร? + +Orchestration mindset คือการให้ AI ขับเคลื่อน content system ทั้งหมด: +- AI วิเคราะห์ performance data เพื่อหา topic ที่ควรเขียน +- AI สร้าง content brief จากข้อมูลจริง +- AI กระจาย content ไปหลายช่องทางอัตโนมัติ +- AI เรียนรู้จากผลลัพธ์แต่ละรอบเพื่อปรับปรุงรอบถัดไป + +![Production vs Orchestration comparison](/images/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai-production-vs-orchestration.png) + +### SPARK Flywheel Framework + +เครื่องมือที่จะช่วยให้คุณเปลี่ยนจาก Production เป็น Orchestration คือ **SPARK Flywheel**: + +- **S — Strategy:** วางกลยุทธ์ AI ที่ชัดเจน ไม่ใช่แค่ "ลองใช้ AI ดู" +- **P — Process:** ออกแบบกระบวนการที่ AI กับคนทำงานร่วมกันได้ +- **A — Activate:** เริ่มใช้จริงใน workflow ที่มีผลกระทบสูง +- **R — Results:** วัดผลลัพธ์จริง ไม่ใช่แค่ "ใช้ AI แล้วรู้สึกดี" +- **K — Knowledge:** เก็บบทเรียนกลับเข้าระบบ ให้ AI เรียนรู้ต่อ + +จุดสำคัญคือ — Flywheel นี้หมุนต่อเนื่อง ยิ่งใช้ ยิ่งเรียนรู้ ยิ่งเก่งขึ้น ต่างจาก Production ที่ทุกงานเริ่มจากศูนย์ + +--- + +## ข้อ 2: สร้าง AI Agent Playbooks ก่อนคู่แข่ง + +### ทำไม Playbook ถึงสำคัญ? + +จากข้อมูลเดียวกัน: +- **40%** ของผู้ตอบบอกว่า AI agents คือ trend ที่สำคัญที่สุดที่กำลังจะมา +- **51%** ต้องการ training เรื่อง AI agents + +แต่ปัญหาคือ — ส่วนใหญ่แค่ "ลองใช้ AI agent ไปเรื่อย" ไม่มีแผนจริงจัง + +### AI Agent Playbook คืออะไร? + +Agent Playbook คือเอกสารที่ออกแบบมาเฉพาะทีมคุณ บอกว่า: +- **Agent ไหน** ทำอะไร (เช่น agent วิเคราะห์ competitor, agent สร้าง content brief) +- **Input** คืออะไร (ข้อมูลอะไรที่ agent ต้องการ) +- **Output** คืออะไร (ผลลัพธ์ที่คาดหวัง) +- **Human-AI handoff** ตรงไหน (จุดไหนที่คนต้อง review ก่อน) +- **Feedback loop** ยังไง (เอาผลลัพธ์กลับไปปรับปรุง agent) + +![AI Agent Playbook structure diagram](/images/blog/2026-06-26-two-things-b2b-marketers-should-do-with-ai-agent-playbook.png) + +### Designed vs Ad-Hoc + +ความแตกต่างระหว่างทีมที่มี playbook กับไม่มี: + +| ไม่มี Playbook (Ad-Hoc) | มี Playbook (Designed) | +|---|---| +| ลองใช้ AI ไปเรื่อย | ออกแบบ workflow ชัดเจน | +| ผลลัพธ์ไม่สม่ำเสมอ | ผลลัพธ์ predict ได้ | +| ไม่รู้ว่า AI ทำอะไรไปบ้าง | track ได้ทุก step | +| คนในทีมทำซ้ำกัน | knowledge แชร์กันได้ | + +ทีมที่สร้าง playbook ตอนนี้จะได้เปรียบอย่างมาก เพราะ playbook = ระบบเรียนรู้ ยิ่งใช้ ยิ่งเก่ง + +--- + +## AI Operations Layer: กาวเชื่อมทุกอย่าง + +สองข้อข้างบนจะไม่ work ถ้าไม่มี **AI Operations Layer** — คือชั้นปฏิบัติการที่เชื่อมคนกับ AI เข้าด้วยกัน + +สิ่งที่ AI Operations Layer ควรมี: + +1. **Defined Human-AI Handoffs** — จุดไหน AI ทำเองได้ จุดไหนคนต้อง review ต้องกำหนดให้ชัด ไม่ใช่ให้ AI ทำทุกอย่าง หรือไม่ให้ AI ทำอะไรเลย + +2. **Feedback Loops ที่ Compounding** — ทุก campaign ที่ทำ ต้องมีการเก็บ data กลับมา ให้ AI เรียนรู้ ยิ่งทำ ยิ่งแม่นขึ้น เหมือน compound interest ของการตลาด + +3. **Compress Campaign Cycles** — จากเดิมที่ campaign หนึ่งใช้เวลา 4-6 สัปดาห์ ด้วย AI orchestration ที่ดี อาจเหลือ 1-2 สัปดาห์ ไม่ใช่เพราะตัดขั้นตอน แต่เพราะ AI ช่วย parallel งานได้ + +--- + +## บทเรียนสำหรับทีมของคุณ + +### 1. ทีมที่สร้าง capability ตอนนี้ = ตามยากใน 12 เดือน + +ไม่ได้พูดเกินจริง — ถ้าคุณเริ่มสร้าง AI workflow, playbook, และ feedback loop ตอนนี้ ใน 12 เดือนข้างหน้าระบบนี้จะแข็งแกร่งมาก คู่แข่งที่เริ่มทีหลังจะต้องเริ่มจากศูนย์ ในขณะที่คุณมี data และ learnings สะสมมาแล้ว + +### 2. Workflow Documentation สำคัญมาก + +ทุกขั้นตอนที่ AI เข้ามาช่วย ต้องมีการ documented ไม่ใช่แค่ "รู้ในหัว" แต่ต้องเขียนเป็น playbook ให้คนอื่นในทีมทำตามได้ สิ่งนี้คือ competitive advantage ที่แท้จริง + +### 3. Systems Thinking = Core Competency + +B2B Marketer ที่จะอยู่รอดในยุค AI ต้องคิดแบบ "ระบบ" ไม่ใช่แค่ "งาน" ไม่ใช่แค่ "โพสต์นี้จะเขียนอะไร" แต่ "content system ทั้งหมดทำงานยังไง ปรับปรุงยังไง scale ยังไง" + +--- + +## FAQ + +### Orchestration ต่างจาก Production ยังไง? + +Production คือใช้ AI ช่วย "ผลิต" งานแต่ละชิ้น เช่น เขียน copy, สร้างรูป ทุกงานเริ่มจากศูนย์ Orchestration คือให้ AI ขับเคลื่อน "ระบบ" ทั้งหมด — ตั้งแต่วิเคราะห์ data, สร้าง content, กระจายช่องทาง, วัดผล, แล้วเอา feedback กลับมาปรับปรุง Production = เร็วขึ้น Orchestration = ฉลาดขึ้น + +### AI Agent Playbook คืออะไร? + +Agent Playbook คือเอกสารที่ออกแบบมาเฉพาะทีมคุณ บอกว่า AI agent แต่ละตัวทำอะไร, input/output คืออะไร, จุดไหนคนต้อง review, และ feedback loop ทำงานยังไง ต่างจากการ "ลองใช้ AI ไปเรื่อย" เพราะ playbook สร้างระบบเรียนรู้ที่ยิ่งใช้ยิ่งเก่ง + +### เริ่มต้นอย่างไรถ้าทีมยังไม่มี AI workflow เลย? + +เริ่มจาก 3 ขั้นตอนง่ายๆ: (1) เลือก workflow ที่ทำซ้ำบ่อยและมีผลกระทบสูง เช่น content creation หรือ lead nurturing (2) ออกแบบ human-AI handoff ให้ชัด — จุดไหน AI ทำ จุดไหนคน review (3) เริ่มเก็บ data จากวันแรก เพื่อสร้าง feedback loop ไม่ต้องสมบูรณ์แบบ แค่เริ่มและปรับปรุงไปเรื่อยๆ + +--- + + +--- + +**แหล่งอ้างอิง:** +- Marketing AI Institute — Cathy McPhillips (23 มิ.ย. 2026): [Two Things Every B2B Marketer Should Be Doing With AI Now](https://www.marketingaiinstitute.com/blog/b2b-marketers-summit-ai-orchestrator) +- 2026 State of AI for Business Report (2,100+ respondents) \ No newline at end of file diff --git a/src/content/blog/2026-06-27-ai-brand-reputation-167k-citations.md b/src/content/blog/2026-06-27-ai-brand-reputation-167k-citations.md new file mode 100644 index 0000000..e6c6139 --- /dev/null +++ b/src/content/blog/2026-06-27-ai-brand-reputation-167k-citations.md @@ -0,0 +1,188 @@ +--- +title: "AI สร้างชื่อเสียงให้แบรนด์จากไหน? — 167K Citations เผยคำตอบที่คุณอาจไม่ชอบ" +description: "งานวิจัย 167,551 citations เผยว่าแหล่งข้อมูลส่วนใหญ่ที่ AI ใช้เล่าเรื่องแบรนด์อยู่นอกการควบคุมของแบรนด์เอง" +pubDate: 2026-06-27 +updatedDate: 2026-06-30 +category: "AI & Marketing" +heroImage: "/images/blog/2026-06-27-ai-brand-reputation-167k-citations-featured.png" +tags: ['ai', 'brand-reputation', 'citations', 'wikipedia'] +draft: false +--- + +เคยสงสัยไหมว่า เวลาคนถาม ChatGPT, Claude, หรือ Gemini ว่า "แบรนด์นี้ดีไหม?" — AI มันไปเอาข้อมูลมาจากไหน? ไม่ใช่แค่สงสัยเล่นๆ นะ เพราะมีคนทำวิจัยจริงจังมาก วิเคราะห์ไป **167,551 citations** จาก AI ทั่วโลก ครอบคลุม 128 แบรนด์, 12 ตลาด, 13 ภาษา แล้วผลลัพธ์ก็ค่อนข้างช็อก: **แบรนด์ควบคุมแหล่งข้อมูลที่ AI ใช้แค่ 14.3% เท่านั้น** ที่เหลืออีก 85.7% มาจากแหล่งที่คุณไม่มีอำนาจควบคุมเลย + +บทความนี้จะสรุปงานวิจัยจาก Dmitrij Zatuchin (มิถุนายน 2026) ให้อ่านง่ายๆ พร้อม actionable insights สำหรับนักการตลาดไทย + +--- + +## TL;DR + +- AI ใช้ **167,551 URL-grounded citations** จาก 128 แบรนด์ ใน 12 ตลาดและ 13 ภาษา +- **85.7%** ของ citations มาจาก third-party sources (สื่อ, รีวิว, Wikipedia) — แบรนด์ควบคุมได้แค่ **14.3%** +- **Wikipedia** คือแหล่งข้อมูลอันดับ 1 ใน 11 จาก 12 ภาษาที่ศึกษา +- การกระจายตัวของแหล่งข้อมูลเป็นไปตาม **Zipf's Law** — แค่ ~18% ของ domains ให้ข้อมูลถึง 80% ของ citations +- ถ้า audit แค่ภาษาอังกฤษ คุณจะมองไม่เห็น visibility จริงของแบรนด์ในตลาดอื่น +- แบรนด์ไทยต้องเริ่มจาก: **Wikipedia page คุณภาพ + content บน third-party sites + multilingual strategy** + +> **แหล่งอ้างอิง:** Zatuchin, D. (2026). *How Large Language Models Source Brand Reputation Across Languages and Markets.* arXiv:2606.25787v1 — [arxiv.org/abs/2606.25787v1](https://arxiv.org/abs/2606.25787v1) + +--- + +## สารบัญ + +1. [AI ตอบคำถามเกี่ยวกับแบรนด์คุณจากไหน?](#ai-ตอบคำถามเกี่ยวกับแบรนด์คุณจากไหน) +2. [85% มาจากที่ที่คุณควบคุมไม่ได้](#85-มาจากที่ที่คุณควบคุมไม่ได้) +3. [Wikipedia คือ SEO ยุค AI](#wikipedia-คือ-seo-ยุค-ai) +4. [แต่ละตลาดใช้แหล่งข้อมูลต่างกัน](#แต่ละตลาดใช้แหล่งข้อมูลต่างกัน) +5. [ต้องทำยังไง?](#ต้องทำยังไง) +6. [FAQ](#faq) + +--- + +## AI ตอบคำถามเกี่ยวกับแบรนด์คุณจากไหน? + +ก่อนอื่นมาทำความเข้าใจ scope ของงานวิจัยนี้ก่อน Dmitrij Zatuchin วิเคราะห์ **167,551 URL-grounded citations** — หมายความว่าทุก citation ที่นับ มี URL จริงๆ กำกับ ไม่ใช่แค่ AI พูดลอยๆ ว่า "มีคนบอกว่า..." แต่ชี้ไปที่แหล่งข้อมูลจริงบนอินเทอร์เน็ต + +งานวิจัยครอบคลุม: +- **128 แบรนด์** จากหลากหลายอุตสาหกรรม +- **12 ตลาด** (ประเทศ/ภูมิภาค) +- **13 ภาษา** ที่แตกต่างกัน + +![Infographic showing scope of 167K citations study across 128 brands and 12 markets](/images/blog/2026-06-27-ai-brand-reputation-167k-citations-infographic_scope.png) +> *รูป: Infographic สรุป scope ของงานวิจัย — 167K citations, 128 brands, 12 markets, 13 languages* + +พูดง่ายๆ คือ นี่ไม่ใช่งานวิจัยเล็กๆ ที่ดูแค่ ChatGPT ภาษาอังกฤษแล้วสรุป มันเป็นการศึกษาที่ค่อนข้าง comprehensive ระดับหนึ่งเลย + +--- + +## 85% มาจากที่ที่คุณควบคุมไม่ได้ + +ตัวเลขนี้น่าจะเป็น insight ที่สำคัญที่สุดของทั้งงานวิจัย: + +| ประเภทแหล่งข้อมูล | สัดส่วน | +|---|---| +| **Third-party sources** (สื่อ, รีวิว, Wikipedia, forums) | **85.7%** | +| **Owned channels** (เว็บไซต์แบรนด์, official content) | **14.3%** | + +![Pie chart showing 85.7% citations from third-party sources vs 14.3% owned](/images/blog/2026-06-27-ai-brand-reputation-167k-citations-pie_chart_citations.png) +> *รูป: Pie chart แสดงสัดส่วน third-party (85.7%) vs owned (14.3%) citations* + +สิ่งที่น่าตกใจคือ ต่อให้แบรนด์ทำ content marketing ดีแค่ไหน ทำ SEO เทพแค่ไหน สร้าง content บน owned channels เยอะแค่ไหน — มันก็ยังเป็นแค่ **14.3%** ของสิ่งที่ AI เห็นเกี่ยวกับแบรนด์คุณ + +แล้ว 85.7% ที่เหลือคืออะไรบ้าง? + +- **Wikipedia** — แหล่งใหญ่สุด (จะพูดละเอียดในหัวข้อถัดไป) +- **สื่อข่าวและบทความ** — รีวิว, ข่าว, บทวิเคราะห์ +- **รีวิวจากผู้ใช้** — forums, review sites, social media +- **แหล่งข้อมูลอุตสาหกรรม** — รายงาน, เปรียบเทียบ, rankings + +พูดอีกอย่างคือ **ชื่อเสียงแบรนด์ในสายตา AI ไม่ได้วัดจากสิ่งที่แบรนด์พูดเกี่ยวกับตัวเอง แต่วัดจากสิ่งที่คนอื่นพูดเกี่ยวกับแบรนด์** มันเหมือน reputation ในโลกจริงเลย — คุณจะบอกว่าตัวเองดีแค่ไหนก็ได้ แต่คนจะเชื่อสิ่งที่คนอื่นพูดมากกว่า + +--- + +## Wikipedia คือ SEO ยุค AI + +ถ้าคุณเคยสงสัยว่า Wikipedia ยังสำคัญอยู่ไหมในยุคที่ SEO เปลี่ยนไป — คำตอบคือ **สำคัญมากขึ้นกว่าเดิมอีก** + +งานวิจัยพบว่า Wikipedia เป็นแหล่ง citations อันดับ 1 ใน **11 จาก 12 ภาษา** ที่ศึกษา ภาษาเดียวที่ Wikipedia ไม่ได้อันดับ 1 คือภาษาที่มีแหล่งข้อมูลท้องถิ่นแข็งแกร่งมากเป็นพิเศษ + +![Wikipedia dominance as top citation source across 11 of 12 languages](/images/blog/2026-06-27-ai-brand-reputation-167k-citations-wikipedia_dominance.png) +> *รูป: แผนภูมิแสดง Wikipedia dominance — #1 source ใน 11/12 ภาษา* + +แล้วยังมีเรื่องของ **Zipf's Law** ด้วย (α = 0.86, R² = 0.983) — ถ้าไม่คุ้นกับ Zipf's Law มันคือกฎที่บอกว่าการกระจายตัวไม่เท่ากัน ผลลัพธ์คือ: + +> **แค่ ~18% ของ domains ให้ข้อมูลถึง 80% ของ citations ทั้งหมด** + +อ่านอีกรอบนะ — 18% ของ domains ให้ข้อมูล 80% ของ citations นี่หมายความว่าไม่ต้องไปหวังจะมี presence ทุกที่บนอินเทอร์เน็ต แค่ให้ข้อมูลถูกต้องบน **domains หลักๆ ไม่กี่ตัว** (โดยเฉพาะ Wikipedia) ก็ครอบคลุม citations ส่วนใหญ่แล้ว + +ทำไม Wikipedia ถึงเป็นแหล่งข้อมูลหลักของ AI? + +1. **ข้อมูล structured** — มี format ชัดเจน AI อ่านง่าย +2. **มี citations ของตัวเอง** — Wikipedia มีแหล่งอ้างอิง ทำให้ข้อมูลน่าเชื่อถือ +3. **ครอบคลุมทุกภาษา** — มี Wikipedia เกือบทุกภาษา +4. **ไม่มี paywall** — AI เข้าถึงได้ตลอด +5. **Updated สม่ำเสมอ** — ข้อมูลไม่ค่อย outdated + +--- + +## แต่ละตลาดใช้แหล่งข้อมูลต่างกัน + +นี่คืออีก insight ที่นักการตลาดต้องให้ความสนใจ — ไม่ใช่ทุกตลาดจะเหมือนกัน + +งานวิจัยพบว่าแต่ละภาษามีแหล่งข้อมูลหลักที่ต่างกัน: + +- **โปแลนด์**: YouTube เป็นแหล่งข้อมูลหลักอันดับต้นๆ (ไม่ใช่ Wikipedia) +- **ลิทัวเนีย**: เว็บไซต์ข่าวธุรกิจ (business daily) มีบทบาทสูง +- **ภาษาอื่นๆ**: ส่วนใหญ่ Wikipedia ครองแชมป์ + +สิ่งที่น่าสนใจคือ **แบรนด์ท้องถิ่นเสียเปรียบอย่างมาก** เมื่อวัด visibility ด้วย English-only audit + +ตัวอย่างเช่น ถ้าคุณมีแบรนด์ที่แข็งแกร่งในตลาดโปแลนด์ มีข้อมูลบน YouTube, สื่อท้องถิ่น, เว็บข่าวธุรกิจเยอะมาก — แต่ถ้าคุณ audit เฉพาะภาษาอังกฤษ คุณจะไม่เห็น citations เหล่านี้เลย และจะสรุปว่าแบรนด์คุณ "ไม่มี visibility" ทั้งที่จริงๆ แล้วมีเยอะมาก + +![Multilingual brand visibility map across 12 markets and 13 languages](/images/blog/2026-06-27-ai-brand-reputation-167k-citations-multilingual_map.png) +> *รูป: แผนที่แสดงแหล่งข้อมูลหลักของแต่ละตลาด/ภาษา* + +**ข้อควรระวังสำหรับแบรนด์ไทย:** + +ถ้าคุณมีแบรนด์ที่แข็งแกร่งในตลาดไทย แต่ audit แค่ภาษาอังกฤษ — คุณจะมองไม่เห็นภาพจริง AI ที่ตอบคำถามเป็นภาษาไทยก็จะดึงข้อมูลจากแหล่งภาษาไทยด้วย ดังนั้นอย่า audit เฉพาะ English-only + +--- + +## ต้องทำยังไง? + +มาถึงส่วน practical กันบ้าง — แบรนด์ควรทำอะไรหลังจากอ่านข้อมูลนี้? + +### 1. สร้าง Wikipedia Page คุณภาพ + +ถ้าแบรนด์คุณยังไม่มี Wikipedia page — นี่คือ priority #1 ถ้ามีแล้ว — ตรวจสอบว่าข้อมูลถูกต้อง อัปเดต มีแหล่งอ้างอิงครบ + +เคล็ดลับ: +- อย่าเขียน Wikipedia page เหมือนโฆษณา — มันจะถูกลบ +- ใช้แหล่งอ้างอิงที่น่าเชื่อถือ (สื่อ, รายงานอุตสาหกรรม) +- แปลเป็นหลายภาษาถ้าเป็นไปได้ + +### 2. สร้าง Content บน Third-Party Sites + +จำตัวเลข 85.7% ได้ไหม? นั่นหมายความว่าคุณต้องมี presence บนแหล่งข้อมูลที่ไม่ใช่ของคุณ + +- **PR และสื่อ**: ให้ข้อมูลกับนักข่าว สร้าง press releases +- **รีวิว**: ให้ลูกค้ารีวิวบน platforms ที่หลากหลาย +- **Industry reports**: ให้ข้อมูลกับรายงานอุตสาหกรรม +- **Forums และ communities**: มี presence ในที่ที่คนพูดถึงแบรนด์ + +### 3. Multilingual Strategy + +อย่า audit แค่ภาษาเดียว ถ้าแบรนด์คุณทำตลาดหลายประเทศ ต้องดูแลข้อมูลในทุกภาษา + +- ดูว่าแหล่งข้อมูลหลักในแต่ละตลาดคืออะไร +- สร้าง content สำหรับแหล่งข้อมูลที่แตกต่างกันในแต่ละตลาด +- ตรวจสอบ visibility ในทุกภาษาที่เกี่ยวข้อง + +### 4. ตรวจสอบ AI Citations โดยตรง + +สุดท้าย — ถาม AI ตรงๆ เลยว่าแบรนด์คุณเป็นยังไง แล้วดูว่ามัน cite แหล่งไหน นี่คือ "AI reputation audit" แบบง่ายๆ ที่ทำได้เลยตอนนี้ + +--- + +## FAQ + +### AI ใช้อะไรตัดสินใจเล่าเรื่องแบรนด์? + +จากงานวิจัยนี้ AI ใช้ **URL-grounded citations** — หมายความว่า AI จะดึงข้อมูลจากแหล่งที่มีอยู่จริงบนอินเทอร์เน็ต ไม่ใช่ "สร้างเรื่อง" ขึ้นมาเอง แหล่งข้อมูลหลักคือ Wikipedia, สื่อข่าว, รีวิวจากผู้ใช้ และเว็บไซต์อุตสาหกรรม ดังนั้นสิ่งที่คนอื่นเขียนเกี่ยวกับแบรนด์คุณบนอินเทอร์เน็ต มีผลมากกว่าสิ่งที่แบรนด์เขียนเกี่ยวกับตัวเอง + +### ทำไม Wikipedia ถึงสำคัญมากสำหรับ AI? + +Wikipedia สำคัญเพราะเป็นแหล่งข้อมูลที่ **structured, มีแหล่งอ้างอิง, ไม่มี paywall, และครอบคลุมทุกภาษา** AI สามารถดึงข้อมูลจาก Wikipedia ได้ง่ายและเชื่อถือได้ งานวิจัยพบว่า Wikipedia เป็นแหล่ง citations อันดับ 1 ใน 11 จาก 12 ภาษาที่ศึกษา และแค่ ~18% ของ domains (รวม Wikipedia) ให้ข้อมูลถึง 80% ของ citations ทั้งหมด + +### แบรนด์ไทยควรทำอย่างไร? + +แบรนด์ไทยควรเริ่มจาก 3 อย่าง: **(1)** สร้างหรือปรับปรุง Wikipedia page ให้ข้อมูลถูกต้องและมีแหล่งอ้างอิงดี **(2)** สร้าง presence บน third-party sources — PR, รีวิว, สื่อ, forums **(3)** อย่า audit visibility เฉพาะภาษาอังกฤษ ต้องดูภาษาไทยด้วย เพราะ AI ที่ตอบคำถามเป็นภาษาไทยจะดึงข้อมูลจากแหล่งภาษาไทย และแต่ละภาษามีแหล่งข้อมูลหลักที่ต่างกัน + +--- + +## แหล่งอ้างอิง + +- Zatuchin, D. (2026). *How Large Language Models Source Brand Reputation Across Languages and Markets.* arXiv:2606.25787v1. [https://arxiv.org/abs/2606.25787v1](https://arxiv.org/abs/2606.25787v1) + +--- + diff --git a/src/content/blog/2026-06-27-ai-recommendation-multi-industry-map.md b/src/content/blog/2026-06-27-ai-recommendation-multi-industry-map.md new file mode 100644 index 0000000..e4b358c --- /dev/null +++ b/src/content/blog/2026-06-27-ai-recommendation-multi-industry-map.md @@ -0,0 +1,176 @@ +--- +title: "ใครเป็นเจ้าของ AI Recommendation? — Multi-Industry Map" +description: "สำรวจว่า AI เลือกแนะนำแบรนด์ในหลายอุตสาหกรรมอย่างไร และเหตุใดคำตอบจาก AI แต่ละแพลตฟอร์มจึงอาจไม่เหมือนกัน" +pubDate: 2026-06-27 +updatedDate: 2026-06-27 +category: "AI & Marketing" +heroImage: "/images/blog/2026-06-27-ai-recommendation-multi-industry-map-featured.png" +tags: ['ai', 'recommendation', 'brand', 'competitive-intelligence'] +draft: false +--- + +สมมติว่าคุณเปิด ChatGPT แล้วถามว่า "แนะนำร้านกาแฟให้หน่อย" — แบรนด์ที่ AI แนะนำขึ้นมา ไม่ใช่แบรนด์ที่ SEO ดีที่สุด ไม่ใช่แบรนด์ที่ยิง Ads เยอะที่สุด แต่เป็นแบรนด์ที่ AI "เลือก" จากข้อมูลที่มันเรียนรู้มา แล้วแบรนด์ของคุณล่ะ? AI แนะนำบ้างไหม? หรือเงียบหายไปเลย? แล้วถ้าคุณถาม AI อีกตัว ผลลัพธ์จะเหมือนกันไหม? + +นี่คือคำถามที่นักวิจัย Dmitrij Żatuchin ตั้งไว้ใน paper ล่าสุด *"Who Owns the AI Recommendation?"* (มิถุนายน 2026) ที่ทดสอบจริงกับ **3,750 responses** จาก 3 โมเดล AI ชั้นนำ — แล้วผลลัพธ์บอกว่า **เกมนี้ยังไม่มีแชมป์** และนี่อาจเป็นโอกาสที่ดีที่สุดสำหรับแบรนด์ที่ต้องการสร้างตัวในยุค AI + +![AI recommendation concept — brands from multiple industries competing in AI responses](/images/blog/2026-06-27-ai-recommendation-multi-industry-map-ai-recommendation-concept.png) + +## TL;DR + +- ทดสอบ 3,750 responses จาก GPT-5.2, Gemini 3 Flash, Perplexity Sonar Pro กับ 50 แบรนด์ ใน 5 อุตสาหกรรม +- **Mean Gini coefficient = 0.28** — ไม่ใช่ Winner-Takes-All, ยังมีการกระจายตัวดี +- **Competitive Vacuum** พบแค่ 8% ของ queries — AI แนะนำแบรนด์เกือบตลอด +- **Cross-model agreement = 41.6%** — แบรนด์ที่ GPT แนะนำ ≠ แบรนด์ที่ Gemini แนะนำ +- มี **3 Metrics ใหม่**: COI, CVI, DS สำหรับวัด Brand Visibility ใน AI +- **โอกาสเปิดกว้าง** — โดยเฉพาะสำหรับ SME ที่ยังไม่มีใครผูกขาด + +> ดู paper ฉบับเต็ม: [arxiv.org/abs/2606.23057v1](https://arxiv.org/abs/2606.23057v1) + +--- + +## สารบัญ + +1. [AI แนะนำแบรนด์อะไรบ้าง?](#ai-แนะนำแบรนด์อะไรบ้าง) +2. [ไม่ใช่ Winner-Takes-All!](#ไม่ใช่-winner-takes-all) +3. [3 Metrics ใหม่วัด Brand Visibility](#3-metrics-ใหม่วัด-brand-visibility) +4. [แต่ละอุตสาหกรรมต่างกัน](#แต่ละอุตสาหกรรมต่างกัน) +5. [โอกาสสำหรับทุกแบรนด์](#โอกาสสำหรับทุกแบรนด์) +6. [FAQ](#faq) + +--- + +## AI แนะนำแบรนด์อะไรบ้าง? + +ก่อนอื่นมาดูวิธีทดสอบกันก่อน — เพราะนี่ไม่ใช่แค่ถาม AI เล่นๆ แล้วเดา + +researcher ออกแบบ experiment มาอย่างดี ด้วย scope ที่ใหญ่พอจะเชื่อถือได้: + +- **3 โมเดล**: GPT-5.2 (OpenAI), Gemini 3 Flash (Google), Perplexity Sonar Pro — ครอบคลุมทั้ง Chatbot search และ AI-native search +- **5 อุตสาหกรรม**: ตั้งแต่ consulting, consumer goods, tech ไปจนถึงหมวดที่ niche กว่า +- **50 แบรนด์** ที่ถูกเลือกมาทดสอบ — ตั้งแต่แบรนด์ใหญ่ระดับโลกไปจนถึงแบรนด์ท้องถิ่น +- **250 queries** ที่หลากหลาย — จำลองคำถามจริงที่คนทั่วไปถาม AI +- **3,750 responses** รวมทั้งหมด — ทุก query ถูกถามเหมือนกันทุกโมเดล + +![Methodology: 3 AI models × 50 brands × 250 queries = 3,750 responses](/images/blog/2026-06-27-ai-recommendation-multi-industry-map-methodology-diagram.png) + +การออกแบบนี้มีจุดสำคัญคือ **controlled comparison** — ถามคำถามเดียวกัน ให้ทุกโมเดลตอบ เพื่อดูว่า AI ตัวไหน "เลือก" แบรนด์อะไร แล้วเอามาเปรียบเทียบกันอย่างเป็นระบบ + +สิ่งที่น่าสนใจคือ **ไม่มีโมเดลไหนที่แนะนำแบรนด์เดียวกันตลอดเวลา** — ทุกโมเดลมี "รสชาติ" ของตัวเอง ต่างจาก Google Search ที่ algorithm เดียวตัดสินทุกอย่าง นี่คือ paradigm shift ที่นักการตลาดต้องเข้าใจ + +--- + +## ไม่ใช่ Winner-Takes-All! + +นี่คือ insight ที่สำคัญที่สุดจาก paper เลย — **AI Recommendation ไม่ใช่เกม winner-takes-all** + +หลายคนอาจคิดว่า AI จะผูกขาดเหมือน Google ที่แบรนด์อันดับ 1 กินรวบ traffic ไป 90%+ แต่ผลวิจัยบอกว่า: + +### Gini coefficient = 0.28 + +Gini coefficient คือตัววัดความกระจุกตัว — 0 หมายถึงกระจายเท่ากันหมด, 1 หมายถึงผูกขาดคนเดียว + +ค่า 0.28 อยู่ในระดับกลาง — แปลว่า **มีการกระจายตัวที่ดี** แบรนด์ท็อปไม่ได้กินรวบ แบรนด์เล็กก็มีโอกาสแนะนำ นี่ต่างจาก Google Search ที่ Gini coefficient มักจะสูงกว่ามาก + +![Gini coefficient visualization — distribution of brand mentions in AI recommendations](/images/blog/2026-06-27-ai-recommendation-multi-industry-map-gini-coefficient.png) + +### Competitive Vacuum = 8% + +แค่ 8% ของ queries เท่านั้นที่ไม่มีแบรนด์ไหน "ยึด" ได้ชัดเจน — แปลว่า **92% ของ queries มีแบรนด์ถูกแนะนำเสมอ** AI ไม่ได้ตอบว่า "ไม่รู้" บ่อยขนาดนั้น + +ตัวเลขนี้บอกว่า AI มีความมั่นใจในการแนะนำค่อนข้างสูง แต่ก็ไม่ได้ผูกขาดกับแบรนด์เดียว — ยังมี "ที่ว่าง" สำหรับแบรนด์อื่นแทรกเข้ามาได้ + +### Cross-model agreement = 41.6% + +ตัวเลขนี้บอกว่าเมื่อถามเหมือนกัน GPT-5.2 กับ Gemini 3 Flash **แนะนำแบรนด์เดียวกันแค่ 41.6%** — เกือบ 60% ของเวลาที่เหลือ แต่ละโมเดลเลือกคนละแบรนด์ + +นี่คือสัญญาณว่า **เกมยังเปิด** — ไม่มีโมเดลไหนที่ตัดสินชี้ขาดเป็น "มาตรฐาน" ของวงการ แบรนด์ที่ optimize สำหรับ GPT อาจไม่ปรากฏใน Gemini และกลับกัน + +--- + +## 3 Metrics ใหม่วัด Brand Visibility + +paper นำเสนอ 3 metrics ใหม่ที่นักการตลาดควรรู้จัก — เพราะเมตริกเดิมๆ อย่าง SEO ranking ไม่พอแล้วในยุค AI นี่คือ framework ใหม่สำหรับวัดว่าแบรนด์ของคุณ "มีตัวตน" ในสายตา AI แค่ไหน + +### 1. Category Ownership Index (COI) + +วัดว่าแบรนด์ของคุณถูกกล่าวถึง (mention) ใน category เท่าไหร่เมื่อเทียบกับคู่แข่ง — ยิ่งสูง แปลว่ายิ่ง "เป็นเจ้าของ" หมวดนั้นในสายตา AI + +ถ้าแบรนด์คุณมี COI สูงในหมวดกาแฟ แปลว่าเมื่อไหร่ก็ตามที่คนถาม AI เรื่องกาแฟ แบรนด์คุณจะถูกแนะนำเป็นอันดับต้นๆ — เหมือนเป็น "default answer" ของ AI + +### 2. Competitive Vacuum Index (CVI) + +วัดว่า category ไหนที่ยังไม่มีผู้นำชัดเจน — นี่คือ **โอกาสทอง** สำหรับแบรนด์ที่ต้องการเข้ายึดพื้นที่ก่อนคนอื่น + +ถ้า CVI สูงในหมวดที่คุณทำอยู่ แปลว่า AI ยังไม่ได้ "เลือก" ใครเป็นเจ้าของ — คุณยังมีโอกาสสร้างตัวเป็น default recommendation ได้ + +### 3. Displacement Score (DS) + +วัดการ "แทนที่" ระหว่างคู่แบรนด์ — ถ้าคุณถาม AI แล้วแบรนด์ A ถูกแนะนำแทนแบรนด์ B บ่อยแค่ไหน? DS บอกว่าใครกำลังเสียตำแหน่งให้ใคร + +ถ้า DS สูง แปลว่ามีแบรนด์ที่กำลัง "กิน" ส่วนแบ่งของแบรนด์อื่น — นี่คือสัญญาณว่าต้องรีบ optimize ก่อนที่จะสาย + +![Three new metrics: COI, CVI, and DS for measuring brand visibility in AI](/images/blog/2026-06-27-ai-recommendation-multi-industry-map-three-metrics-diagram.png) + +เมตริกเหล่านี้ออกแบบมาเพื่อวัด **Brand Visibility ใน AI** โดยเฉพาะ — ไม่ใช่แค่ "AI พูดถึงแบรนด์เราไหม" แต่ "เราเป็นเจ้าของ category ในสายตา AI แค่ไหน" ซึ่งเป็นคนละมิติกับ SEO เลย + +--- + +## แต่ละอุตสาหกรรมต่างกัน + +หนึ่งใน findings ที่น่าสนใจคือ **Displacement Score ต่างกันมากตามอุตสาหกรรม** — ไม่ใช่ทุก industry จะมีเกมเดียวกัน + +- **Consulting**: DS = 0.4:1 — แทบไม่มีการ displacement แบรนด์ consulting ยังถูกแนะนำอย่างหลากหลาย ไม่มีแบรนด์ไหน "กินรวบ" หมวดนี้ +- **บางอุตสาหกรรม**: DS สูงถึง 4.3:1 — แปลว่าแบรนด์หนึ่งถูกแนะนำแทนอีกแบรนด์บ่อยมาก แบรนด์ที่มาก่อนกำลังได้เปรียบอย่างชัดเจน +- **Mean displacement**: 2.4:1 — โดยเฉลี่ยแล้วมี brand displacement แต่ไม่ extreme + +![Displacement Score comparison across industries — from 0.4:1 to 4.3:1](/images/blog/2026-06-27-ai-recommendation-multi-industry-map-three-metrics-diagram-v2.png) + +ตัวเลขนี้บอกว่า **แต่ละอุตสาหกรรมมีเกมที่ต่างกัน** — consulting ยังเปิดกว้าง แบรนด์เล็กยังแทรกเข้ามาได้ แต่อุตสาหกรรมที่ DS สูง แบรนด์ที่มาก่อนอาจกำลัง "กินรวบ" ในสายตา AI แล้ว + +นักการตลาดต้องดูว่า **อุตสาหกรรมตัวเองอยู่ตรงไหน** — ถ้า DS ต่ำ แปลว่ายังมีโอกาส ถ้า DS สูง ต้องรีบ optimize ก่อนที่จะสายเกินไป + +--- + +## โอกาสสำหรับทุกแบรนด์ + +ถ้าอ่านมาถึงตรงนี้ นี่คือ takeaway ที่สำคัญที่สุด — และมันเป็นข่าวดี: + +### ยังไม่มีใครผูกขาด + +Gini coefficient 0.28 บอกว่าเกมยังกระจาย — แบรนด์เล็กก็มีโอกาส ไม่เหมือน Google ที่อันดับ 1-3 กิน traffic ไปเกือบหมด นี่คือ window of opportunity ที่อาจไม่ได้อยู่ตลอดไป + +### ต้อง optimize สำหรับหลายโมเดล + +cross-model agreement แค่ 41.6% แปลว่า **ทำ SEO สำหรับ Google ตัวเดียวไม่พอแล้ว** — ต้อง optimize content ให้ AI หลายตัว "เห็น" แบรนด์ของคุณ ทั้ง GPT, Gemini, Perplexity และโมเดลอื่นๆ ที่กำลังจะออกมา + +### ต่างจาก Google Search + +Google Search เป็น winner-takes-all — แบรนด์ที่อยู่อันดับ 1 ได้ traffic มากกว่าอันดับ 2 หลายเท่า แต่ AI Recommendation ยังเป็น **competitive landscape** ที่เปิดกว้าง แบรนด์ที่เข้าใจเกมนี้ก่อนจะได้เปรียบ + +สำหรับ SME นี่คือโอกาส — คุณไม่จำเป็นต้องมี budget เท่าแบรนด์ใหญ่ แค่เข้าใจว่า AI คิดยังไง แล้ว optimize content ให้ AI "เข้าใจ" แบรนด์ของคุณ เกมยังเปิด — แต่จะไม่เปิดตลอดไป + +--- + +## FAQ + +### AI Recommendation ต่างจาก Google Search ยังไง? + +Google Search ใช้ algorithm จัดอันดับเว็บ — แบรนด์ที่ SEO ดี อยู่อันดับต้น ได้ traffic มากกว่า (winner-takes-all) แต่ AI Recommendation ใช้ LLM "เลือก" แบรนด์จากข้อมูลที่เรียนรู้มา — ไม่มี algorithm เดียวตัดสิน แต่ละโมเดลอาจแนะนำคนละแบรนด์ ดังนั้นเกมยังเปิดกว้างกว่า ที่สำคัญคือ AI recommendation ไม่ได้ดูแค่ backlink หรือ keyword density แต่ดู "ความเข้าใจ" ที่ AI มีต่อแบรนด์ + +### SME มีโอกาสแค่ไหน? + +มีโอกาสเยอะ! เพราะ Gini coefficient = 0.28 แปลว่าแบรนด์เล็กไม่ได้ถูกบดขยี้เหมือนใน Google — AI กระจายการแนะนำ แบรนด์เล็กที่มี content ดี สามารถถูกแนะนำได้เท่ากับแบรนด์ใหญ่ สิ่งสำคัญคือ **optimize content ให้ AI เข้าใจ** — ไม่ใช่แค่ SEO แบบเดิม แต่ต้องคิดว่า "AI จะเข้าใจแบรนด์เราอย่างไร" + +### ต้อง optimize สำหรับโมเดลไหนบ้าง? + +cross-model agreement = 41.6% บอกว่าแต่ละโมเดลแนะนำคนละแบรนด์ — ดังนั้น **optimize สำหรับหลายโมเดล** ดีกว่า focus แค่ตัวเดียว ตอนนี้โมเดลหลักๆ คือ GPT-5.2, Gemini 3 Flash, Perplexity Sonar Pro แต่ landscape เปลี่ยนเร็ว ต้อง monitor ตลอด สิ่งสำคัญคือไม่ต้อง optimize แยกสำหรับทุกโมเดล — แต่ต้องเข้าใจว่า AI แต่ละตัว "มองเห็น" แบรนด์คุณต่างกันอย่างไร แล้วสร้าง content ที่ AI ทุกตัวเข้าใจได้ + +--- + +## แหล่งอ้างอิง + +- Żatuchin, D. (2026). *Who Owns the AI Recommendation? A Multi-Industry Empirical Map of Brand Category Ownership Across Large Language Models*. arXiv:2606.23057v1. [https://arxiv.org/abs/2606.23057v1](https://arxiv.org/abs/2606.23057v1) +- ข้อมูลเพิ่มเติม: [https://doi.org/10.5281/zenodo.20788142](https://doi.org/10.5281/zenodo.20788142) + +--- + diff --git a/src/content/blog/2026-06-27-google-open-knowledge-format.md b/src/content/blog/2026-06-27-google-open-knowledge-format.md new file mode 100644 index 0000000..d6b85bf --- /dev/null +++ b/src/content/blog/2026-06-27-google-open-knowledge-format.md @@ -0,0 +1,186 @@ +--- +title: "Google OKF คืออะไร? มาตรฐานใหม่ที่จะเปลี่ยนวิธีที่ AI เข้าถึงความรู้ขององค์กร" +description: "รู้จัก Open Knowledge Format ของ Google รูปแบบที่ช่วยส่งมอบ knowledge ให้ AI agents อ่านและนำไปใช้งานได้โดยตรง" +pubDate: 2026-06-27 +updatedDate: 2026-06-27 +category: "SEO" +heroImage: "/images/blog/2026-06-27-google-open-knowledge-format-featured.png" +tags: ['google', 'okf', 'ai-search', 'seo'] +draft: false +--- + +ถ้าคุณอยู่ในวงการ SEO หรือ content marketing มาสักพัก คุณน่าจะเคยได้ยินเรื่อง `llms.txt` — ไฟล์ที่ชี้ทางให้ AI crawler หา content ที่มีประโยชน์บนเว็บเรา แต่ตอนนี้ Google ขยับไปอีกขั้นแล้ว เปิดตัว **Open Knowledge Format (OKF)** ที่ไม่ใช่แค่ "ชี้ทาง" แต่ "ยื่น knowledge ให้ AI agent อ่านเลย" ตรง ๆ ไม่ต้อง crawl + +![Google Open Knowledge Format — AI agents reading structured knowledge](/images/blog/2026-06-27-google-open-knowledge-format-google-okf-hero.png) + +--- + +## TL;DR + +- **OKF (Open Knowledge Format) v0.1** คือมาตรฐานเปิดจาก Google ที่ให้องค์กรแพ็ก knowledge เป็นไฟล์ markdown + YAML frontmatter ให้ AI agent อ่านได้โดยตรง +- ใช้ 3 หลักการง่าย ๆ: Just markdown, Just files, Just YAML frontmatter — ไม่มี SDK ใหม่ ไม่มี runtime +- ต่างจาก `llms.txt` (ชี้ทางให้ crawler) และ XML Sitemap (บอกว่ามีหน้าไหน) — OKF ส่ง knowledge ให้ agent เลย +- เปิดเผยบน GitHub ภายใต้ **Apache 2.0** — vendor-neutral ใครก็ใช้ได้ +- **ยังไม่ต้องทำอะไรวันนี้** แต่ foundation ที่ทำตอนนี้ (clean content, structured data) จะคุ้มค่าในอนาคต + +--- + +## สารบัญ + +1. [OKF คืออะไร?](#okf-คืออะไร) +2. [3 หลักการของ OKF](#3-หลักการของ-okf) +3. [OKF vs llms.txt vs Sitemaps — ต่างกันยังไง?](#okf-vs-llmstxt-vs-sitemaps--ต่างกันยังไง) +4. [ทำไม OKF ถึงสำคัญ?](#ทำไม-okf-ถึงสำคัญ) +5. [ตัวอย่างการใช้งานจริง](#ตัวอย่างการใช้งานจริง) +6. [ต้องทำอะไรตอนนี้?](#ต้องทำอะไรตอนนี้) +7. [FAQ](#faq) + +--- + +## OKF คืออะไร? + +พูดง่าย ๆ **OKF (Open Knowledge Format)** คือวิธีแพ็กข้อมูลความรู้ขององค์กรให้ AI agent อ่านออก ไม่ต้องผ่าน API ซับซ้อน ไม่ต้องตั้ง server เพิ่ม — แค่จัดไฟล์ markdown ให้เป็นระเบียบแล้วใส่ YAML frontmatter กำกับ + +OKF เปิดตัวเป็น **v0.1** บน GitHub ภายใต้ลิขสิทธิ์ **Apache 2.0** ซึ่งหมายความว่า: +- ใครก็ใช้ได้ — ไม่ผูกกับ Google เจ้าเดียว +- ไม่ต้องจ่ายเงิน +- องค์กรไหนก็ adopt ได้เลย + +![OKF Architecture — Markdown files + YAML frontmatter → AI Agent](/images/blog/2026-06-27-google-open-knowledge-format-okf-architecture.png) + +Cecilia Meis จาก Semrush สรุปไว้ดีมาก: + +> "OKF goes a step further and hands over the knowledge itself, packaged as files an agent reads directly." + +ถ้า `llms.txt` คือ "ป้ายบอกทาง" ที่ชี้ให้ AI crawler ไปหา content ที่ถูกต้อง — OKF คือ "ส่งเอกสารให้มือ" เลย ไม่ต้อง crawl ไม่ต้องค้นหา แค่อ่าน + +--- + +## 3 หลักการของ OKF + +สิ่งที่ทำให้ OKF น่าสนใจคือความเรียบง่าย มันไม่ได้สร้างเทคโนโลยีใหม่ แต่จัดระเบียบสิ่งที่มีอยู่แล้วให้ใช้งานง่ายขึ้น ด้วย 3 หลักการ: + +### 1. Just Markdown + +ใช้ markdown เป็น format หลัก — ไม่ต้องเรียนรู้ syntax ใหม่ ไม่ต้อง convert ไฟล์ ใครเขียนเอกสารเป็น markdown อยู่แล้วก็พร้อมใช้เลย + +### 2. Just Files + +knowledge ถูกแพ็กเป็น "directory ของไฟล์" — ไม่มี database ไม่มี runtime ไม่มี compression พิเศษ แค่จัดไฟล์ให้เป็นระเบียบในโฟลเดอร์ + +### 3. Just YAML Frontmatter + +ใช้ YAML frontmatter กำกับ metadata ของแต่ละไฟล์ — เหมือนที่นักพัฒนาหลายคนคุ้นเคยจาก static site generators อย่าง Jekyll, Hugo, หรือ Astro อยู่แล้ว + +![OKF vs llms.txt — Comparison table](/images/blog/2026-06-27-google-open-knowledge-format-okf-vs-llmstxt.png) + +--- + +## OKF vs llms.txt vs Sitemaps — ต่างกันยังไง? + +นี่คือจุดที่หลายคนสับสน เพราะมี "มาตรฐาน" เกี่ยวกับ AI search ออกมาหลายตัว ลองมาเทียบกันชัด ๆ: + +| Format | ทิศทาง | ใช้ทำอะไร | +|--------|--------|-----------| +| **XML Sitemap** | Outward → crawlers | บอก Google ว่ามีหน้าไหนบนเว็บ ช่วยให้ crawl ได้ครบ | +| **llms.txt** | Outward → AI crawlers | ชี้ AI crawlers ไปยัง content ที่มีประโยชน์ที่สุด | +| **OKF** | Inward → org's own agents | ส่ง knowledge โดยตรงให้ AI agent ขององค์กรอ่านเลย | + +สังเกตว่า **ทิศทางต่างกันชัดเจน**: +- Sitemap กับ llms.txt เป็นแบบ **"Outward"** — ส่งข้อมูลออกไปให้ bot/crawler ข้างนอก +- OKF เป็นแบบ **"Inward"** — ส่ง knowledge ให้ agent ภายในองค์กร + +ไม่ได้แข่งกัน แต่ทำหน้าที่ต่างกัน Sitemap ยังจำเป็นสำหรับ SEO แบบเดิม `llms.txt` ช่วยให้ AI search เจอ content เรา แต่ OKF เหมาะกับกรณีที่องค์กรอยากให้ AI agent ภายใน (เช่น chatbot ภายใน, AI assistant) เข้าถึง knowledge base ได้โดยตรง + +ลองนึกภาพง่าย ๆ — Sitemap เหมือนสารบัญหนังสือที่วางไว้หน้าห้องสมุด `llms.txt` เหมือนป้าย "แนะนำหนังสือเด่น" แต่ OKF เหมือนการยื่นหนังสือให้คนอ่านในมือเลย ไม่ต้องเดินหาชั้น + +--- + +## ทำไม OKF ถึงสำคัญ? + +คุณอาจจะคิดว่า "ก็แค่ markdown files — ทำไมต้องตื่นเต้น?" ลองดูเหตุผลเหล่านี้: + +### มาตรฐานจาก Google เอง + +ไม่เหมือน `llms.txt` ที่เป็น community initiative — OKF มาจาก Google ซึ่งมีอิทธิพลต่อ ecosystem ของ web มาตรฐานจาก Google มักจะถูก adopt ได้เร็วกว่าและกว้างกว่า + +### มีโอกาส widespread กว่า llms.txt + +ด้วย backing จาก Google และความเรียบง่ายของ format (แค่ markdown + YAML) OKF มีโอกาสกลายเป็นมาตรฐานที่องค์กรทั่วไป adopt ได้ง่าย ไม่ต้องเปลี่ยน workflow มาก + +### AI Agents อ่าน Knowledge ได้โดยตรง + +นี่คือจุดเปลี่ยนจริง ๆ แทนที่จะให้ AI ต้อง crawl, index, แล้วค่อย retrieve — OKF ให้องค์กรแพ็ก knowledge ส่งให้ agent อ่านตรง ๆ ลดความซับซ้อน เพิ่มความแม่นยำ + +### ไม่ใช่ Search Ranking Signal + +ข้อนี้สำคัญ — OKF **ไม่มีผลต่อ Google Search rankings** นะครับ มันเป็น internal knowledge format สำหรับ AI agents ไม่ใช่ SEO trick ใหม่ อย่าสับสน + +หลายคนอาจจะเห็นชื่อ Google แล้วรีบไป implement ทันที — ใจเย็นก่อน OKF ออกแบบมาเพื่อให้องค์กรจัดการ knowledge ของตัวเองให้ AI agent เข้าถึงได้ ไม่ใช่ hack SEO ตัวใหม่ + +--- + +## ตัวอย่างการใช้งานจริง + +แม้ OKF จะยังอยู่ในช่วง v0.1 (early stage) ลองจินตนาการว่ามันใช้งานได้เต็มที่แล้ว: + +### Enterprise Knowledge Management + +บริษัทขนาดใหญ่มีเอกสาร internal เยอะมาก — policy, process, product docs แทนที่จะเก็บใน wiki ที่ AI เข้าถึงยาก ก็แพ็กเป็น OKF ให้ internal AI assistant อ่านได้เลย + +### AI Agent Integration + +ทีมพัฒนาที่สร้าง AI agent สำหรับ customer support สามารถใช้ OKF เป็น knowledge source — agent อ่าน markdown files ได้โดยตรง ไม่ต้อง build RAG pipeline ซับซ้อน + +### Content Delivery + +องค์กรที่อยากแชร์ knowledge กับ partner หรือ developer community ก็แค่ส่ง directory ของ OKF files — ไม่ต้องสร้าง API ไม่ต้องเปิด access ให้ database + +### เปรียบเทียบให้เห็นภาพ + +| Use Case | วิธีเดิม | วิธี OKF | +|----------|---------|----------| +| ให้ AI ตอบคำถาม internal | สร้าง RAG pipeline + vector DB | แพ็ก markdown files ให้ agent อ่านตรง | +| Share knowledge กับ partner | สร้าง API + documentation | ส่ง directory ของ OKF files | +| Onboard AI agent ใหม่ | Connect API + data pipeline | Point ไปที่ OKF directory | + +--- + +## ต้องทำอะไรตอนนี้? + +คำตอบสั้น ๆ: **ยังไม่ต้องทำอะไรวันนี้** + +OKF ยังอยู่ในช่วง v0.1 ยังไม่มี adoption ที่กว้าง enough ที่จะส่งผลต่อ SEO หรือ AI search visibility ของคุณในตอนนี้ + +**แต่** — ถ้าคุณเริ่มทำ foundation เหล่านี้ตั้งแต่วันนี้ จะคุ้มค่ามากเมื่อมาตรฐานแบบ agent-facing มาถึงจุดที่ sitemaps และ schema markup ไปถึง: + +1. **Clean content** — เขียน content ให้มีโครงสร้างชัดเจน ไม่เยิ่นเย้อ +2. **Structured data** — ใช้ schema markup, YAML frontmatter, metadata ให้เป็นนิสัย +3. **AI crawler access** — ปลดบล็อก AI crawlers ใน robots.txt (ถ้ายัง block อยู่) + +ถ้า content ของคุณพร้อมอยู่แล้วในแง่ structure — ไม่ว่า OKF หรือมาตรฐานใหม่ตัวไหนจะมา ก็ปรับตัวได้เร็ว + +สิ่งที่แนะนำให้เริ่มทำตั้งแต่วันนี้ (ไม่ต้องรอ OKF): +- **จัดเอกสาร internal ให้เป็น markdown** — ถ้าเอกสารในองค์กรยังอยู่ใน Google Docs หรือ Notion ลอง export เป็น markdown ดูบ้าง +- **ใส่ frontmatter ให้เป็นนิสัย** — ทุกไฟล์ควรมี metadata กำกับ (title, description, tags, date) +- **จัด directory structure ให้เป็นระเบียบ** — โฟลเดอร์ไหนเก็บเรื่องอะไร ตั้งชื่อให้ชัดเจน + +--- + +## FAQ + +### OKF ต่างจาก llms.txt ยังไง? + +`llms.txt` เป็นไฟล์เดี่ยวที่ชี้ทางให้ AI crawler ไปหา content ที่มีประโยชน์ — คล้าย sitemap แต่สำหรับ AI OKF ต่างกันตรงที่มันไม่ได้ชี้ทาง แต่ส่ง knowledge ให้ agent อ่านเลย เป็น directory ของ markdown files ที่มี YAML frontmatter กำกับ metadata อีกที + +### ต้อง implement OKF เมื่อไหร่? + +ยังไม่ต้องรีบ OKF ยังอยู่ในช่วง v0.1 และ adoption ยังไม่กว้างพอที่จะส่งผลต่อ SEO หรือ AI search visibility แต่ควรจับตาดู เพราะมาตรฐานจาก Google มักจะมาเร็วกว่าที่คิด ระหว่างนี้ focus ที่ clean content และ structured data ก่อน + +### OKF มีผลต่อ SEO ไหม? + +**ไม่มี** OKF ไม่ใช่ search ranking signal มันเป็น internal knowledge format สำหรับ AI agents ขององค์กร ไม่เกี่ยวกับ Google Search rankings การทำ OKF ไม่ได้ช่วยให้อันดับ SEO ดีขึ้นโดยตรง แต่เป็นการเตรียมพร้อมสำหรับอนาคตที่ AI agents จะเข้าถึงข้อมูลมากขึ้น + +--- + +*Source: [Semrush Blog — Google Launches Open Knowledge Format for AI Agents](https://www.semrush.com/blog/google-launches-open-knowledge-format-for-ai-agents/) by Cecilia Meis* \ No newline at end of file diff --git a/src/content/blog/2026-06-27-opid-ai-agent-self-training.md b/src/content/blog/2026-06-27-opid-ai-agent-self-training.md new file mode 100644 index 0000000..b4ecfd5 --- /dev/null +++ b/src/content/blog/2026-06-27-opid-ai-agent-self-training.md @@ -0,0 +1,150 @@ +--- +title: "OPID — AI Agent ที่ฝึกตัวเองได้ ไม่ต้องพึ่งครู ไม่ต้องมีสกิลเมโมรี" +description: "รู้จัก OPID ระบบที่ช่วยให้ AI Agent เรียนรู้ทักษะจากประสบการณ์ของตัวเอง และนำสิ่งที่เรียนรู้ไปใช้ต่อได้" +pubDate: 2026-06-27 +updatedDate: 2026-06-30 +category: "Tools" +heroImage: "/images/blog/2026-06-27-opid-ai-agent-self-training-featured.png" +tags: ['ai', 'reinforcement-learning', 'agents', 'research'] +draft: false +--- + +ถ้าบอกว่า AI Agent สามารถเรียนรู้จาก "ประสบการณ์ของตัวเอง" โดยไม่ต้องมีคนสอน ไม่ต้องสร้างฐานข้อมูลสกิลแยก — ฟังดูเหมือน sci-fi เนอะ แต่ตอนนี้มันเริ่มเป็นจริงแล้ว OPID (On-Policy Skill Distillation for Agentic RL) คือระบบที่ทำให้ AI Agent ฝึกตัวเองจาก trajectory ที่มันเพิ่งเล่นจบไปหมาดๆ และดึงสกิลออกมาใช้ต่อได้เลย ไม่ต้องรอใครมาป้อนข้อมูล + + + +## TL;DR + +- **OPID** คือวิธีฝึก AI Agent แบบ on-policy ที่ดึง skill supervision จาก trajectory ของตัวเอง +- ใช้ **hierarchical skills** สองระดับ: episode-level (แผนภาพรวม) + step-level (แต่ละขั้นตอน) +- **ไม่ต้องใช้ external skill memory** — ทุกอย่างอยู่ในตัว agent เลย +- ทดสอบบน ALFWorld, WebShop, Search-based QA แล้วเห็นผลชัดเรื่อง performance, sample efficiency, และ robustness +- ได้ 38 upvotes บน HuggingFace Daily Papers ⭐ +- Paper: [arxiv.org/abs/2606.26790](https://arxiv.org/abs/2606.26790) + +## สารบัญ + +1. [OPID คืออะไร?](#opid-คืออะไร) +2. [ปัญหาเดิมของ Reinforcement Learning](#ปัญหาเดิมของ-reinforcement-learning) +3. [OPID แก้ปัญหายังไง?](#opid-แก้ปัญหายังไง) +4. [Hierarchical Skills — สกิลสองชั้น](#hierarchical-skills--สกิลสองชั้น) +5. [ผลลัพธ์จากการทดสอบ](#ผลลัพธ์จากการทดสอบ) +6. [ทำไม OPID ถึงสำคัญ?](#ทำไม-opid-ถึงสำคัญ) +7. [FAQ](#faq) + +## OPID คืออะไร? + +OPID ย่อมาจาก **On-Policy Skill Distillation for Agentic RL** เป็นผลงานวิจัยโดย Shuo Yang และคณะ ที่เสนอวิธีฝึก AI Agent ให้เรียนรู้จาก trajectory ของตัวเองแบบ on-policy + +![RL diagram showing on-policy vs off-policy learning flow](/images/blog/2026-06-27-opid-ai-agent-self-training-opid-on-policy-vs-off-policy.png) + +คำว่า "on-policy" ตรงนี้สำคัญมาก แปลว่า agent เรียนรู้จากสิ่งที่มัน "เพิ่งทำไป" จริงๆ ไม่ใช่จากข้อมูลเก่าที่สะสมไว้ มันเหมือนนักกีฬาที่ดูเทปตัวเองหลังแข่งจบแล้วปรับปรุงทันที ไม่ใช่ดูเทปของคนอื่น + +สิ่งที่ทำให้ OPID น่าสนใจจนได้ 38 upvotes บน HuggingFace Daily Papers คือแนวคิดที่ว่า: เราไม่จำเป็นต้องสร้าง external skill memory แยกออกมาเลย ทุกอย่างที่ agent ต้องเรียนรู้ มันดึงออกมาจาก trajectory ของตัวเองได้หมด + +## ปัญหาเดิมของ Reinforcement Learning + +ก่อนจะเข้าใจว่า OPID เจ๋งยังไง เรามาดูปัญหาเดิมๆ ของ Reinforcement Learning (RL) กันก่อน + +### Sparse Reward — รางวัลน้อย เรียนรู้ช้า + +ปัญหาคลาสสิกของ RL คือ **sparse reward** คือ agent ต้องทำหลายขั้นตอนมากๆ กว่าจะได้รางวัลสักที สมมติ agent ต้องทำ 20 ขั้นตอนเพื่อสำเร็จภารกิจ แต่ละขั้นตอนไม่มี feedback อะไรเลย มันก็เหมือนคนเดินในห้องมืด ไม่รู้ว่าตัวเองเดินถูกทางหรือเปล่า + +### ต้องใช้ External Skill Memory + +วิธีแก้ปัญหาก่อนหน้านี้คือการสร้าง **external skill memory** — ฐานข้อมูลสกิลที่แยกออกมาต่างหาก แล้วค่อยดึงมาใช้ตอนฝึก แต่ปัญหาคือ: +- ต้องสร้างและ maintain เพิ่มเติม +- Deployment ซับซ้อนขึ้น +- ข้อมูลใน skill memory อาจไม่ตรงกับสถานการณ์จริงของ agent + +### Deployment ยุ่งยาก + +ระบบที่ต้องพึ่ง external components เยอะๆ จะ deploy ยากกว่า ต้องดูแลหลายส่วน ไหนจะเรื่อง latency ในการดึงข้อมูล ไหนจะเรื่อง synchronization — มันไม่ใช่แค่เขียนโมเดลแล้วจบ + +## OPID แก้ปัญหายังไง? + +OPID เสนอแนวทางที่เรียบง่ายแต่ทรงพลัง: **ดึง skill supervision จาก completed trajectories ของตัวเอง** + +![Hierarchical skills — episode-level and step-level decisions](/images/blog/2026-06-27-opid-ai-agent-self-training-hierarchical-skills.png) + +ขั้นตอนหลักๆ มีดังนี้: + +### 1. ทำภารกิจให้จบก่อน (Outcome-based RL) + +Agent ทำภารกิจจนจบหนึ่งรอบ (episode) ไม่ว่าจะสำเร็จหรือล้มเหลว แล้วใช้ผลลัพธ์สุดท้าย (outcome) เป็นตัวกำกับการเรียนรู้ วิธีนี้เรียกว่า **outcome-based RL** — ไม่ต้องให้รางวัลทุกขั้นตอน แค่ดูว่าตอนจบได้ผลยังไง + +### 2. ดึงสกิลจาก Trajectory + +จาก trajectory ที่เพิ่งเล่นจบ OPID จะ distill สกิลออกมา 2 ระดับ: +- **Episode-level skills**: ภาพรวมว่า "ทำภารกิจนี้ต้องวางแผนยังไง" +- **Step-level skills**: รายละเอียดว่า "แต่ละขั้นตอนต้องทำอะไร" + +### 3. Self-Distillation ระดับ Token + +ขั้นตอนสุดท้ายคือ **token-level self-distillation** — agent ใช้สกิลที่ดึงออกมาได้มาฝึกตัวเองในระดับ token (หน่วยย่อยที่สุดของข้อความ) ทำให้การเรียนรู้แม่นยำขึ้น ไม่ใช่แค่จำภาพรวม แต่จำได้ถึงรายละเอียดปลีกย่อย + +## Hierarchical Skills — สกิลสองชั้น + +จุดเด่นที่สุดของ OPID คือระบบ **hierarchical skills** ที่แบ่งสกิลออกเป็นสองชั้น: + +### Episode-Level Skills + +เป็นแผนภาพรวมของภารกิจ คล้ายๆ กับ "สูตรอาหาร" — บอกว่าต้องเตรียมอะไร ลำดับขั้นตอนใหญ่ๆ เป็นยังไง เช่น "เดินไปห้องครัว → เปิดตู้เย็น → หยิบแอปเปิ้ล → วางบนโต๊ะ" + +### Step-Level Skills + +เป็นรายละเอียดของแต่ละขั้นตอน คล้ายๆ กับ "เทคนิคการทำอาหาร" — จับมีดยังไง หั่นแบบไหน เวลาเท่าไหร่ เช่น "หันซ้าย 30 องศา → เดินตรง 5 ก้าว → ยื่นมือขวา → หยิบวัตถุ" + +การแบ่งสองชั้นแบบนี้ทำให้ agent ทั้งเข้าใจภาพรวมและทำรายละเอียดได้ถูกต้อง เหมือนคนที่ทั้งรู้แผนและทำเป็น + +## ผลลัพธ์จากการทดสอบ + +OPID ถูกทดสอบบน 3 benchmarks หลัก: + +| Benchmark | ลักษณะงาน | ผลลัพธ์ | +|-----------|-----------|---------| +| **ALFWorld** | ทำงานบ้านในบ้านจำลอง | ปรับปรุง performance ชัดเจน | +| **WebShop** | ช้อปปิงออนไลน์อัตโนมัติ | sample efficiency ดีขึ้น | +| **Search-based QA** | ค้นหาข้อมูลตอบคำถาม | robustness สูงขึ้น | + +![OPID test results across benchmarks — ALFWorld, WebShop, SearchQA](/images/blog/2026-06-27-opid-ai-agent-self-training-test-results.png) + +จุดที่น่าสนใจคือ OPID ไม่ได้แค่ทำให้ agent เก่งขึ้นในเรื่อง performance เท่านั้น แต่ยังทำให้เรียนรู้ได้เร็วขึ้น (**sample efficiency**) และทำงานได้คงที่กว่าเดิม (**robustness**) ด้วย ทั้งหมดนี้โดยไม่ต้องเพิ่ม external component ใดๆ + +## ทำไม OPID ถึงสำคัญ? + +### ไม่ต้องสร้าง External Skill Database + +นี่คือจุดเปลี่ยนสำคัญเลย ระบบก่อนหน้านี้ต้องสร้าง skill database แยก ต้อง maintain ต้อง sync กับ agent — OPID ตัดขั้นตอนนี้ทิ้งหมด agent เรียนรู้จากตัวเอง ดึงสกิลจากตัวเอง ใช้จากตัวเอง + +### Deployment ง่ายขึ้น + +เมื่อไม่มี external component ที่ต้องดูแล deployment ก็ง่ายขึ้นเยอะ ไม่ต้องกังวลเรื่อง latency ของ skill retrieval ไม่ต้องกังวลเรื่อง data pipeline แยกต่างหาก + +### AI Agent เก่งขึ้นเรื่อยๆ + +เพราะ agent เรียนรู้จาก trajectory ของตัวเองแบบ on-policy ทุกครั้งที่มันทำภารกิจใหม่ มันก็ได้ข้อมูลใหม่มาฝึกตัวเอง ยิ่งทำมาก ยิ่งเก่งขึ้น — เหมือนคนที่เรียนรู้จากประสบการณ์จริง + +### ต่อยอดได้ง่าย + +แนวคิด hierarchical skills + on-policy distillation สามารถนำไปประยุกต์กับ agent ในหลายๆ โดเมนได้ ไม่จำกัดแค่การทำงานบ้านหรือช้อปปิงออนไลน์ + +--- + +## FAQ + +### On-Policy ต่างจาก Off-Policy ยังไง? + +**On-policy** คือการเรียนรู้จากนโยบายปัจจุบันของ agent เอง — คือสิ่งที่มัน "เพิ่งทำไป" เปรียบเทียบกับ **off-policy** ที่เรียนรู้จากข้อมูลเก่าที่สะสมไว้ หรือจากนโยบายอื่น ข้อดีของ on-policy คือข้อมูลตรงกับสถานการณ์จริงที่ agent กำลังเผชิญ ทำให้เรียนรู้ได้แม่นยำกว่า แต่ก็แลกมากับต้องเก็บข้อมูลใหม่เรื่อยๆ + +### OPID ใช้ทำอะไรได้บ้าง? + +OPID ถูกทดสอบกับงานหลายแบบ: การทำงานบ้านจำลอง (ALFWorld), การช้อปปิงออนไลน์อัตโนมัติ (WebShop), และการค้นหาข้อมูลตอบคำถาม (Search-based QA) แต่แนวคิดหลักของ OPID — คือการ distill สกิลจาก trajectory ของตัวเอง — สามารถนำไปประยุกต์กับ agent ในโดเมนอื่นๆ ได้ด้วย ไม่ว่าจะเป็น coding agent, data analysis agent หรือ agent ในสายงานอื่นๆ + +### มีโค้ดให้ลองไหม? + +มี! โค้ดของ OPID เปิดให้ใช้งานบน GitHub ที่ [github.com/jinyangwu/OPID](https://github.com/jinyangwu/OPID) ส่วน paper ฉบับเต็มอ่านได้ที่ [arxiv.org/abs/2606.26790](https://arxiv.org/abs/2606.26790) + +--- + +*Source: [OPID: On-Policy Skill Distillation for Agentic RL](https://arxiv.org/abs/2606.26790) — Shuo Yang et al.* \ No newline at end of file diff --git a/src/content/blog/2026-06-28-broken-form-cost-agency-leads.md b/src/content/blog/2026-06-28-broken-form-cost-agency-leads.md new file mode 100644 index 0000000..a9093eb --- /dev/null +++ b/src/content/blog/2026-06-28-broken-form-cost-agency-leads.md @@ -0,0 +1,165 @@ +--- +title: "ฟอร์มพังจุดเดียว... เสีย leads ไปเป็นเดือน! บทเรียนราคาแพงจาก Agency ตัวจริง" +description: "กรณีศึกษาฟอร์มที่ยังบันทึกข้อมูลได้ แต่ระบบแจ้งเตือนหยุดทำงานจน leads ถูกทิ้งไว้นานนับเดือน พร้อมบทเรียนเรื่อง QA และ monitoring" +pubDate: 2026-06-28 +updatedDate: 2026-06-30 +category: "AI & Marketing" +heroImage: "/images/blog/2026-06-28-broken-form-cost-agency-leads-featured.png" +tags: ['conversion', 'lead-generation', 'qa', 'marketing'] +draft: false +--- + +![Featured Image: ฟอร์มที่พังกับ leads ที่หายไป](/images/blog/2026-06-28-broken-form-cost-agency-leads-broken-form-leads-featured.png) + +## TL;DR + +> Danny Gavin จาก Optidge เจอฝันร้าย — ระบบ email notification ของฟอร์มลูกค้า (ศูนย์บำบัดออทิสติก) หยุดทำงานโดยไม่มีใครรู้ leads ถูกเก็บใน database ปกติ แต่ไม่มี email แจ้งเตือนไปถึงลูกค้า ผลคือเสีย leads ไปหลายสิบราย เป็นเวลากว่า 1-2 เดือนกว่าจะรู้ตัว บทเรียนสำคัญ: QA และ monitoring สำคัญพอๆ กับ performance optimization เลยทีเดียว + +--- + +## สารบัญ + +1. [กรณีศึกษา: ฟอร์มพังที่ไม่มีใครสังเกต](#กรณีศึกษาฟอร์มพังที่ไม่มีใครสังเกต) +2. [ทำไมถึงเกิดเรื่องแบบนี้ได้?](#ทำไมถึงเกิดเรื่องแบบนี้ได้) +3. [บทเรียนที่ได้](#บทเรียนที่ได้) +4. [วิธีป้องกันไม่ให้เกิดซ้ำ](#วิธีป้องกันไม่ให้เกิดซ้ำ) +5. [สรุป](#สรุป) +6. [FAQ](#faq) + +--- + +## กรณีศึกษา: ฟอร์มพังที่ไม่มีใครสังเกต + +เรื่องนี้มาจาก Search Engine Land เขียนโดย Anu Adegbola (26 มิถุนายน 2026) — เป็นเรื่องจริงของ Danny Gavin ผู้ก่อตั้ง Optidge agency ที่ต้องเจอบทเรียนราคาแพงแบบไม่ทันตั้งตัว + +ลูกค้าของเขาคือ **ศูนย์บำบัดสำหรับเด็กออทิสติก** — ธุรกิจที่ทุก lead มีค่ามาก เพราะหมายถึงเด็กคนหนึ่งที่จะได้รับความช่วยเหลือ แคมเปญ Google Ads ก็ทำงานได้ดี traffic ไหลเข้ามา ผู้คนกรอกฟอร์มสมัครใช้บริการ... แต่แล้วเกิดอะไรขึ้น? + +**ระบบ email notification หยุดทำงาน** + +ใช่ครับ แค่นั้นเลย ฟอร์มยังใช้งานได้ปกติ คนกรอกข้อมูลเสร็จก็ได้รับ confirmation ว่าส่งสำเร็จแล้ว ข้อมูลก็ถูกเก็บเข้า database เรียบร้อย... แต่ **ไม่มี email แจ้งเตือนไปถึงลูกค้า** ว่ามีคนกรอกฟอร์มมา + +ผลคือ leads ที่ควรจะได้รับการติดต่อกลับภายในไม่กี่ชั่วโมง กลับถูกทิ้งไว้ใน database เฉยๆ เป็นสัปดาห์ เป็นเดือน... จนในที่สุดก็กลายเป็น leads ที่เย็นจนแทบไม่มีค่าแล้ว + +> "Are you actually receiving the leads?" +> — คำถามง่ายๆ ที่ไม่มีใครถาม + +![Image: แผนภาพแสดงระบบฟอร์มและ email notification](/images/blog/2026-06-28-broken-form-cost-agency-leads-form-notification-diagram.png) + +กว่าจะรู้ตัวว่ามีปัญหา ก็ผ่านไป **1-2 เดือน** leads หลายสิบรายหายไปในอากาศ Danny บอกว่ารู้สึกผิดมาก เหมือนเอาเงินลูกค้าไปแล้วไม่ได้ส่งมอบคุณค่ากลับไป — ความรู้สึก guilt, regret, helplessness ถาโถมเข้ามาเต็มๆ + +--- + +## ทำไมถึงเกิดเรื่องแบบนี้ได้? + +หลายคนอาจสงสัยว่า agency ระดับนี้ทำไมถึงพลาดง่ายๆ ได้? คำตอบคือมันไม่ได้เกิดจากความผิดพลาดจุดเดียว แต่เป็น **ช่องโหว่หลายจุดที่รวมกัน** + +### 1. ขาด QA Process + +Agency ส่วนใหญ่โฟกัสที่ **performance optimization** — CTR สูงไหม, CPC ถูกพอไหม, Conversion Rate เป็นยังไง... แต่ไม่ค่อยมีใครทำ QA กับ **ระบบหลังบ้าน** ว่ายังทำงานได้ปกติหรือเปล่า + +ฟอร์มพัง, email ไม่ส่ง, link เสีย — สิ่งเหล่านี้มักถูกมองข้ามเพราะมันไม่ได้แสดงใน dashboard ของ Google Ads + +### 2. Communication Gap + +ระหว่างทีม PPC กับลูกค้า มักมี assumption ว่า "ถ้ามี leads เข้ามา ลูกค้าก็คงบอก" แต่ความจริงคือลูกค้าอาจไม่รู้ว่าควรได้ leads เท่าไหร่ หรืออาจคิดว่า "ช่วงนี้เงียบๆ ก็คงปกติ" + +### 3. Monitoring ไม่พอ + +การ monitor แค่ ad performance ไม่เพียงพอ เราต้อง monitor **ทั้ง funnel** — ตั้งแต่คนคลิกโฆษณา จนถึงลูกค้าได้รับข้อมูลจริงๆ + +![Image: Dashboard สำหรับ monitoring leads](/images/blog/2026-06-28-broken-form-cost-agency-leads-monitoring-dashboard.png) + +--- + +## บทเรียนที่ได้ + +จากกรณีของ Danny Gavin มีบทเรียนสำคัญหลายข้อที่ทุก agency ควรเรียนรู้: + +### 📌 QA สำคัญพอๆ กับ Performance + +เราใช้เวลาปรับ bid, เขียน ad copy, ทำ A/B testing... แต่เคยใช้เวลาตรวจสอบว่า **ระบบยังทำงานได้ปกติ** บ้างไหม? QA ไม่ใช่ optional — มันคือ core part ของงานเรา + +### 📌 ต้องมี Monitoring System + +ไม่ใช่แค่ monitor ว่า ads ทำงานได้ แต่ต้อง monitor ว่า leads ถูกส่งถึงมือลูกค้าจริงๆ ต้องมี system ที่แจ้งเตือนทันทีเมื่อจำนวน leads ผิดปกติ + +### 📌 Communication ต้องชัดเจน + +ต้องคุยกับลูกค้าอย่างสม่ำเสมอว่า leads ที่ได้รับมีจำนวนเท่าไหร่ คุณภาพเป็นยังไง ไม่ใช่แค่รายงาน performance ของ ads แล้วจบ + +--- + +## วิธีป้องกันไม่ให้เกิดซ้ำ + +หลังจากเจอเหตุการณ์นี้ Danny Gavin ได้วางระบบป้องกันใหม่ ซึ่งทุก agency สามารถนำไปใช้ได้: + +### ✅ 1. CC Agency ในทุก Lead Notification + +ตั้งค่าให้ email notification ส่งถึงทั้งลูกค้า **และ** ทีม agency ด้วย วิธีนี้ถ้าลูกค้าไม่ได้รับ email เราก็ยังเห็นว่ามี leads เข้ามา + +### ✅ 2. Auto-log Leads ลง Google Sheet + +ใช้ automation ส่ง leads ทุกรายลง Google Sheet หรือ CRM อื่นๆ อัตโนมัติ ทำให้มี backup record ที่ตรวจสอบได้ตลอดเวลา + +### ✅ 3. Regular Form Testing + +**ทุกสัปดาห์** ควรทดสอบฟอร์มด้วยการกรอกข้อมูลจริง แล้วตรวจสอบว่า: +- ฟอร์มส่งสำเร็จหรือไม่ +- Email notification ถูกส่งหรือไม่ +- ข้อมูลถูกเก็บใน database ถูกต้องไหม + +![Image: QA Checklist สำหรับตรวจสอบฟอร์ม](/images/blog/2026-06-28-broken-form-cost-agency-leads-qa-checklist.png) + +### ✅ 4. Routinely Check กับลูกค้า + +ทุกครั้งที่คุยกับลูกค้า ควรถามคำถามง่ายๆ ว่า: + +> "ได้รับ leads จากเราปกติไหมครับ/คะ?" + +คำถามแค่นี้ อาจช่วยป้องกันไม่ให้เสีย leads ไปเป็นเดือนๆ + +--- + +## สรุป + +เรื่องของ Danny Gavin และ Optidge เป็นบทเรียนที่ดีมากว่า **technical issues เล็กๆ น้อยๆ อาจสร้างความเสียหายใหญ่หลวงได้** ฟอร์มพังจุดเดียว ระบบ email notification หยุดทำงานแค่จุดเดียว — แต่ผลกระทบคือ leads หลายสิบรายที่หายไปเป็นเดือนๆ + +สิ่งที่ต้องจำคือ: +- **Performance optimization อย่างเดียวไม่พอ** — ต้องมี QA ด้วย +- **Monitoring ต้องครอบคลุมทั้ง funnel** ไม่ใช่แค่ ads +- **Communication กับลูกค้าต้องสม่ำเสมอ** — แค่ถามว่า "ได้รับ leads ไหม" ก็อาจช่วยได้มาก + +อย่าปล่อยให้ฟอร์มพังเงียบๆ แล้วเสีย leads ไปโดยไม่รู้ตัวนะครับ 🙏 + +--- + +*ข้อมูลอ้างอิง: [Search Engine Land — How one broken form cost an agency months of leads](https://searchengineland.com/how-one-broken-form-cost-an-agency-months-of-leads-ft-danny-gavin-481247) โดย Anu Adegbola (26 มิถุนายน 2026)* + +--- + +## FAQ + +### ต้องตรวจสอบฟอร์มบ่อยแค่ไหน? + +แนะนำให้ทดสอบอย่างน้อย **สัปดาห์ละครั้ง** ด้วยการกรอกข้อมูลจริงผ่านฟอร์ม แล้วตรวจสอบว่า email notification ส่งถึงทั้งลูกค้าและทีม agency นอกจากนี้ควรมี automated monitoring ที่แจ้งเตือนทันทีเมื่อจำนวน leads ผิดปกติ + +### เครื่องมือ monitoring อะไรที่แนะนำ? + +- **Google Sheets + Zapier/Make** — auto-log leads ทุกรายลง spreadsheet +- **UptimeRobot หรือ Pingdom** — monitor ว่าหน้าฟอร์มยังโหลดได้ปกติ +- **Google Tag Manager** — track form submissions แล้วส่ง alert เมื่อจำนวน drop +- **CRM ที่มี built-in monitoring** เช่น HubSpot หรือ Salesforce + +### ป้องกันปัญหาแบบนี้ยังไงดีที่สุด? + +วิธีที่ดีที่สุดคือ **ทำหลายชั้น (redundancy)**: +1. CC agency ในทุก email notification +2. Auto-log leads ลง database/spreadsheet สำรอง +3. ตั้ง automated alert เมื่อจำนวน leads ผิดปกติ +4. ทดสอบฟอร์มเป็นประจำทุกสัปดาห์ +5. คุยกับลูกค้าเรื่อง leads เป็นประจำ + +ทำทั้ง 5 ข้อนี้ โอกาสที่จะพลาดแบบ Danny Gavin ก็แทบจะเป็นศูนย์แล้วครับ + +--- + diff --git a/src/content/blog/2026-06-28-openknowledge-ai-first-obsidian.md b/src/content/blog/2026-06-28-openknowledge-ai-first-obsidian.md new file mode 100644 index 0000000..ef87257 --- /dev/null +++ b/src/content/blog/2026-06-28-openknowledge-ai-first-obsidian.md @@ -0,0 +1,223 @@ +--- +title: "OpenKnowledge — ทางเลือก Open Source สำหรับคนที่อยากได้ Obsidian + AI แบบจัดเต็ม" +description: "OpenKnowledge คือเครื่องมือจดโน้ตแบบ open source และ local-first ที่ออกแบบให้ AI, MCP, Skills และ RAG เป็นส่วนหนึ่งของระบบตั้งแต่ต้น" +pubDate: 2026-06-28 +updatedDate: 2026-06-30 +category: "Tools" +heroImage: "/images/blog/2026-06-28-openknowledge-ai-first-obsidian-featured.png" +tags: ['openknowledge', 'ai-tools', 'obsidian', 'open-source'] +draft: false +--- + +ถ้าคุณเป็นคนที่ใช้ Obsidian หรือ Notion จด note มาตลอด แต่รู้สึกว่า "มันขาดอะไรบางอย่าง" — ลองนึกภาพว่า editor ของคุณมันไม่ใช่แค่ที่จด แต่มันเป็น **AI second brain** ที่คุณคุยด้วยได้ ค้นหาข้อมูลข้าม note ได้ และสั่งแก้ไขเนื้อหาด้วยภาษาธรรมชาติได้เลย นั่นแหละคือสิ่งที่ **OpenKnowledge** กำลังจะทำ + +![OpenKnowledge UI with AI interaction](/images/blog/2026-06-28-openknowledge-ai-first-obsidian-hero-openknowledge-ui.png) + +## TL;DR + +- **OpenKnowledge** คือ open source markdown editor + knowledge management tool ที่ออกแบบมาให้ AI เป็น first-class citizen ตั้งแต่วันแรก +- รองรับ **Claude, Codex, Cursor** เข้ามาช่วยแก้ไขและค้นหาข้อมูลใน note ของคุณ +- มี **MCP, Skills, และ RAG** built-in — ไม่ต้องต่อ external tool เอง +- เทียบกับ Obsidian/Notion: open source (GPL-3.0), local-first, AI-first (ไม่ใช่แค่ AI-added) +- GitHub 1.6k stars, ขึ้น Hacker News front page (372 points) +- ใช้ฟรีทั้งหมด — ดาวน์โหลด macOS app หรือ `npm install` ใช้บน Linux/Windows ก็ได้ + +--- + +## สารบัญ + +1. [OpenKnowledge คืออะไร?](#openknowledge-คืออะไร) +2. [ฟีเจอร์หลักที่น่าสนใจ](#ฟีเจอร์หลักที่น่าสนใจ) +3. [ตัวอย่างการใช้งานจริง](#ตัวอย่างการใช้งานจริง) +4. [เปรียบเทียบกับ Obsidian และ Notion](#เปรียบเทียบกับ-obsidian-และ-notion) +5. [วิธีติดตั้งและเริ่มใช้งาน](#วิธีติดตั้งและเริ่มใช้งาน) +6. [เหมาะกับใคร?](#เหมาะกับใคร) +7. [FAQ](#faq) + +--- + +## OpenKnowledge คืออะไร? + +พูดสั้นๆ: OpenKnowledge คือ **open source markdown editor ที่สร้างขึ้นมาเพื่อ AI ตั้งแต่ต้น** + +ไม่ใช่แค่ editor ที่มี AI เสริมเข้ามาทีหลังแบบ "อ๋อ เดี๋ยวเพิ่ม sidebar ให้ถาม ChatGPT ได้นะ" — แต่เป็นเครื่องมือที่ออกแบบ architecture ให้ AI เข้าใจและทำงานกับ knowledge base ของคุณได้อย่างเต็มที่ + +![OpenKnowledge WYSIWYG Editor Interface](/images/blog/2026-06-28-openknowledge-ai-first-obsidian-screenshot-editor-interface.png) + +ตัว editor เป็นแบบ **WYSIWYG** (เห็นอะไรตอนพิมพ์ ก็ออกมาแบบนั้น) คล้าย Google Docs หรือ Notion แต่เบื้องหลังเก็บเป็น **Markdown** ทั้งหมด ซึ่งแปลว่า: + +- ย้ายไป editor อื่นได้ตลอด — ไม่ถูกล็อก +- Git-friendly — commit ได้, diff ได้ +- AI อ่านและแก้ไขได้ง่ายเพราะเป็น plain text + +ทีมที่สร้างคือ **Inkeep** (เดิมทำ AI search/assistant สำหรับ developer docs) โดย open source ภายใต้ **GPL-3.0** และได้รับความสนใจอย่างรวดเร็ว — ขึ้น Hacker News front page ด้วย 372 points และ GitHub ทะลุ 1.6k stars ภายในไม่กี่วัน + +ทำไมถึงน่าจับตา? เพราะมันไม่ใช่แค่ editor ใหม่ — มันคือ **paradigm shift** ของการจัดการความรู้ จากเดิมที่เราจด note แล้วก็ปล่อยทิ้งไว้ใน folder ให้ฝุ่นจับ กลายเป็นว่า note ทุกอันกลายเป็น **ข้อมูลที่ AI เข้าถึงได้** ค้นหาได้ และทำงานร่วมกับคุณได้ + +**Website:** [openknowledge.ai](https://openknowledge.ai) +**GitHub:** [github.com/inkeep/open-knowledge](https://github.com/inkeep/open-knowledge) + +--- + +## ฟีเจอร์หลักที่น่าสนใจ + +### 1. Editor ระดับ production + +ไม่ใช่แค่ textarea ธรรมดา — OpenKnowledge ใช้ **Tiptap + ProseMirror** สำหรับ rich text editing และ **CodeMirror** สำหรับ code block ซึ่งเป็น open source editor engine ระดับเดียวกับที่ใช้ใน Notion และ Linear + +มีทั้ง **macOS desktop app** (Electron) และ **Web UI** พร้อม file navigator, tabs, search, แม้แต่ graph view สำหรับดู wiki links (สาย Obsidian น่าจะคุ้นเคย) นอกจากนี้ยังมี **built-in TUI** (Terminal UI) สำหรับคนที่ชอบ command line และต้องการความเร็ว + +สิ่งที่ชอบคือ editor มัน **ลื่นมาก** — พิมพ์ markdown syntax ปุ๊บมัน render ให้เห็นทันที ไม่ต้อง preview mode แยก แถม support rich components ฝัง HTML ได้ด้วย ซึ่งมีประโยชน์มากสำหรับเขียน spec, report, หรือ documentation ที่ต้องมี diagram หรือ interactive element + +### 2. AI Integration แบบ built-in + +![AI Integration Architecture — Claude, Codex, Cursor](/images/blog/2026-06-28-openknowledge-ai-first-obsidian-diagram-ai-integration.png) + +จุดที่ทำให้ OpenKnowledge ต่างจาก editor อื่นคือ **AI ไม่ใช่ plugin แต่เป็น core feature**: + +- **Claude Integration** — ให้ Claude อ่านทั้ง knowledge base แล้วตอบคำถาม, สรุปเนื้อหา, หรือแก้ไข note ให้ +- **Codex Integration** — สั่ง generate/edit code ใน note ได้เลย +- **Cursor Desktop Integration** — สำหรับคนที่ใช้ Cursor เป็น AI coding assistant อยู่แล้ว ก็เชื่อมต่อได้ทันที +- **Built-in MCP (Model Context Protocol)** — protocol สำหรับให้ AI models เข้าถึงข้อมูลใน knowledge base ของคุณอย่างมีโครงสร้าง +- **Skills System** — สั่งให้ AI ทำงานซับซ้อนๆ ได้ เช่น "สรุป note ทั้งหมดที่เกี่ยวกับ project X" หรือ "ย้าย note เหล่านี้ไปโฟลเดอร์ใหม่" +- **RAG (Retrieval-Augmented Generation)** — AI ค้นหาข้อมูลจาก note ของคุณก่อนตอบ ไม่ใช่แค่เดาจาก memory ของ model + +ลองนึกภาพว่าคุณจด note เกี่ยวกับ project ไว้ 50 อัน — แทนที่จะมานั่งหาเองว่า "note ที่เคยจดเรื่อง database schema อยู่ไหน" แค่ถาม AI ว่า "สรุป architecture ของ project X ให้หน่อย" แล้ว AI จะค้น note ที่เกี่ยวข้องทั้งหมด, อ่าน, และสรุปให้คุณเลย นี่คือพลังของ RAG ที่ built-in มาตั้งแต่ต้น + +### 3. Collaboration ผ่าน Git + CRDT + +สำหรับทีม OpenKnowledge รองรับ **CRDT (Conflict-free Replicated Data Type)** ผ่าน **yjs** สำหรับ real-time collaboration และ sync ผ่าน **Git/GitHub** สำหรับ async workflow — ไม่ต้องพึ่ง cloud server ของใคร + +### 4. Starter Packs + +มี template สำเร็จรูปให้เริ่มใช้งานได้ทันที เช่น **LLM Wiki template** สำหรับสร้าง wiki ที่ AI ค้นหาได้ตั้งแต่ต้น + +--- + +## ตัวอย่างการใช้งานจริง + +### 🧑‍💻 Developer: จัดการ Project Documentation + +ถ้าคุณเป็น developer ที่มีหลาย project — แต่ละ project มี README, architecture doc, API spec, meeting note ฯลฯ — OpenKnowledge ช่วยให้คุณจัดการทุกอย่างในที่เดียว แล้วให้ AI ช่วยเชื่อมโยงข้อมูลข้าม project ได้ เช่น "project A กับ project B ใช้ database ตัวเดียวกันไหม?" — AI ตอบได้จาก note ที่คุณจดไว้ + +### 📝 Content Creator: สร้าง Content จาก Knowledge Base + +สำหรับนักเขียนหรือ content creator ที่สะสม reference material เยอะๆ — OpenKnowledge ช่วยให้ AI ค้นหาข้อมูลจาก note ทั้งหมดแล้วช่วยร่าง article หรือสรุปข้อมูลให้ ไม่ต้องมานั่ง copy-paste ข้าม note เอง + +### 🏢 ทีม: Internal Wiki ที่ AI ค้นหาได้ + +ด้วย LLM Wiki template ทีมสามารถสร้าง internal wiki ที่ไม่ใช่แค่จดข้อมูล แต่ AI ช่วยตอบคำถามจากข้อมูลใน wiki ได้เลย — เหมือนมี junior ที่อ่าน docs ทุกอันแล้วตอบได้ตลอด 24 ชั่วโมง + +--- + +## เปรียบเทียบกับ Obsidian และ Notion + +| หัวข้อ | OpenKnowledge | Obsidian | Notion | +|---|---|---|---| +| **Open Source** | ✅ GPL-3.0 | ❌ Proprietary | ❌ Proprietary | +| **AI-first** | ✅ Built-in ตั้งแต่ต้น | ⚠️ เพิ่มทีหลัง (plugin) | ⚠️ เพิ่มทีหลัง | +| **Local-first** | ✅ ข้อมูลอยู่บนเครื่องคุณ | ✅ Local files | ❌ Cloud-based | +| **Markdown** | ✅ | ✅ | ⚠️ ส่งออกได้ แต่ไม่ native | +| **MCP/RAG** | ✅ Built-in | ❌ ต้องต่อเอง | ❌ ไม่รองรับ | +| **Free** | ✅ ฟรีทั้งหมด | ⚠️ ฟรีส่วนตัว, Sync เสียเงิน | ⚠️ Free tier จำกัด | + +### OpenKnowledge vs Obsidian + +Obsidian เป็นเครื่องมือที่ดีมากสำหรับ markdown note-taking — มี community plugin เยอะ, graph view สวย, และคนใช้เยอะมาก แต่ AI integration ของ Obsidian ต้องพึ่ง plugin ซึ่งไม่ได้ถูกออกแบบมาให้ AI เข้าถึง knowledge base ทั้งหมดได้อย่างมีประสิทธิภาพ คุณอาจจะต้องต่อ API เอง, config เอง, และเจอ limitation เรื่อง context window ที่ส่งข้อมูลได้ไม่หมด + +OpenKnowledge สร้าง architecture มาเพื่อรองรับ AI ตั้งแต่วันแรก — RAG, MCP, skills ทุกอย่างเป็น core feature ไม่ใช่ add-on ซึ่งแปลว่าคุณไม่ต้อง config อะไรมาก มันพร้อมใช้ตั้งแต่วันแรก + +อีกจุดคือ **license**: Obsidian เป็น proprietary ซึ่งหมายความว่าถ้าทีมพัฒนาเปลี่ยนทิศทางหรือปิดตัว คุณก็ทำอะไรไม่ได้มาก OpenKnowledge ใช้ GPL-3.0 ซึ่งแปลว่า code เปิดตลอด — fork ได้, ดัดแปลงได้, ไม่ต้องกลัววันที่มันหายไป สำหรับบางคนเรื่องนี้อาจจะไม่สำคัญ แต่ถ้าคุณกำลัง build knowledge base ที่จะใช้ไปอีกหลายปี license model สำคัญกว่าที่คิด + +### OpenKnowledge vs Notion + +Notion เหมาะกับทีมที่ต้องการ UI สวยและ workflow ง่าย — ปฏิเสธไม่ได้ว่า Notion ทำ onboarding ได้ดีมาก ใครก็เริ่มใช้ได้ใน 5 นาที แต่ข้อมูลอยู่บน cloud ทั้งหมด — ถ้าเน็ตหลุดก็ใช้ไม่ได้, ถ้า Notion ปิดตัวข้อมูลก็หาย (แม้จะ export ได้ แต่ไม่ใช่ทุกอย่าง) + +OpenKnowledge เก็บข้อมูล local เป็น markdown — คุณเป็นเจ้าของข้อมูลของตัวเอง 100% แถมยังมี AI built-in ที่ทำงานกับข้อมูล local ได้ทันที ไม่ต้องรอ Notion AI ที่เพิ่งออกมาและยังมี limitation เยอะ + +--- + +## วิธีติดตั้งและเริ่มใช้งาน + +### macOS (ง่ายสุด) + +1. ไปที่ [openknowledge.ai](https://openknowledge.ai) +2. ดาวน์โหลด DMG file +3. ติดตั้งและเปิดใช้งาน — เสร็จ! + +### Linux / Windows / Intel Mac + +ใช้ผ่าน command line: + +```bash +npm install -g @inkeep/open-knowledge +ok init +ok start --open +``` + +`ok init` จะสร้าง knowledge base folder ให้ และ `ok start --open` จะเปิด Web UI ใน browser + +### เริ่มต้นใช้งาน + +หลังจากติดตั้งเสร็จ แนะนำให้ลอง: + +1. **Import note ที่มีอยู่แล้ว** — ถ้าคุณมี Obsidian vault อยู่แล้ว สามารถ copy markdown files เข้าไปได้เลย +2. **เปิด AI integration** — config API key สำหรับ Claude หรือ Codex ที่คุณมี +3. **ลองใช้ Starter Pack** — LLM Wiki template ช่วยให้เห็นภาพว่า AI-first workflow เป็นยังไง + +### Tech Stack เบื้องหลัง + +- **Editor:** Tiptap + ProseMirror + CodeMirror +- **Real-time sync:** yjs (CRDT) +- **Desktop app:** Electron +- **Search engine:** Orama (embedded, local search) +- **AI integration:** MCP protocol, built-in RAG + +ทั้งหมดเป็น open source stack — ไม่มี proprietary dependency ใดๆ + +--- + +## เหมาะกับใคร? + +### ✅ คนที่ต้องการ AI Second Brain + +ถ้าคุณจด note เยอะๆ แล้วอยากให้ AI ช่วยค้นหา, เชื่อมโยง, และสรุปข้อมูลให้ — OpenKnowledge ออกแบบมาเพื่อ use case นี้โดยเฉพาะ แทนที่ note จะเป็นแค่ข้อความที่จดทิ้งไว้ มันจะกลายเป็น **knowledge base ที่มีชีวิต** ที่ AI ช่วยดูแลและเชื่อมโยงให้ + +### ✅ คนที่เชื่อใน Open Source + +ไม่อยากถูกล็อกกับ proprietary tool? OpenKnowledge เป็น GPL-3.0 ทั้ง stack — fork ได้, audit ได้, มั่นใจว่ามันจะไม่หายไปไหน + +### ✅ คนที่ต้องการ Local-first + +ข้อมูลทุกอย่างอยู่บนเครื่องคุณ ไม่ต้อง upload ไป cloud ของใคร ใช้ offline ได้ sync ผ่าน git เมื่อต้องการ + +### ✅ Developer ที่ต้องการ AI ช่วยจัดการ docs + +ถ้าคุณมี codebase ที่ต้อง maintain docs, wiki, หรือ spec — OpenKnowledge รองรับ embedding HTML, rich components, และ export ได้หลายรูปแบบ + +### ✅ คนที่เบื่อ vendor lock-in + +เคยรู้สึกไหมว่าข้อมูลทั้งหมดอยู่ใน platform ที่เราควบคุมไม่ได้? OpenKnowledge ใช้ markdown + git ซึ่งเป็น open standard — ถ้าวันหนึ่งไม่พอใจ ย้ายออกได้ทันที ไม่ต้อง export ไม่ต้องแปลง format + +--- + +## FAQ + +### ต่างจาก Obsidian ยังไง? + +หลักๆ คือ **AI-first vs AI-added** — Obsidian เป็น markdown editor ที่ดีมาก แต่ AI เป็น plugin ที่เพิ่มเข้ามาทีหลัง OpenKnowledge ออกแบบ architecture ให้ AI เข้าถึง knowledge base ได้ทั้งหมดตั้งแต่วันแรก (RAG, MCP, skills) อีกจุดคือ OpenKnowledge เป็น open source ภายใต้ GPL-3.0 ในขณะที่ Obsidian เป็น proprietary + +### ต้องใช้ AI อะไรบ้าง? + +รองรับ **Claude** (Anthropic), **Codex** (OpenAI), และ **Cursor** desktop app คุณเลือกใช้ตัวใดตัวหนึ่งหรือหลายตัวพร้อมกันก็ได้ ไม่จำเป็นต้องใช้ทุกตัว — เลือกตามที่คุณสะดวก + +### ข้อมูลปลอดภัยไหม? ต้องส่งไป cloud ของ AI? + +ข้อมูล note ทั้งหมดเก็บ **local บนเครื่องคุณ** ส่วน AI integration จะส่งเฉพาะข้อมูลที่จำเป็นไปยัง AI model ตามปกติของ LLM API (เหมือนคุณ copy-paste ข้อความไปถาม ChatGPT) — ไม่มี knowledge base ทั้งหมดถูก upload ไปไหน ถ้าต้องการ privacy สุดๆ ก็ใช้ local LLM ผ่าน MCP protocol ได้ + +--- + + +**แหล่งข้อมูล:** +- [GitHub — inkeep/open-knowledge](https://github.com/inkeep/open-knowledge) +- [OpenKnowledge Official Website](https://openknowledge.ai) \ No newline at end of file diff --git a/src/content/blog/2026-06-28-profound-vs-bluefish-aeo.md b/src/content/blog/2026-06-28-profound-vs-bluefish-aeo.md new file mode 100644 index 0000000..2d3618b --- /dev/null +++ b/src/content/blog/2026-06-28-profound-vs-bluefish-aeo.md @@ -0,0 +1,158 @@ +--- +title: "เจาะลึก AEO: เปรียบเทียบ Profound vs Bluefish AI — ใครเด่นกว่าในยุค AI Search?" +description: "เปรียบเทียบ Profound และ Bluefish AI สำหรับติดตามและพัฒนา visibility ของแบรนด์ในคำตอบจาก AI และ Answer Engine" +pubDate: 2026-06-28 +updatedDate: 2026-06-30 +category: "Tools" +heroImage: "/images/blog/2026-06-28-profound-vs-bluefish-aeo-featured.png" +tags: ['aeo', 'seo', 'ai-tools', 'answer-engine'] +draft: false +--- + +![AEO Tools Comparison Concept](/images/blog/2026-06-28-profound-vs-bluefish-aeo-featured-image.png) + +ทุกวันนี้ ถ้าคุณยังโฟัสแค่ SEO แบบเดิม บอกเลยว่ากำลังพลาดเกมใหญ่ไปแล้ว เพราะ McKinsey เพิ่งรายงานว่า **ผู้บริโภค 50% หันมาใช้ Answer Engine** ในการค้นหาข้อมูล และ **70% พึ่งพา AI-generated answers** เพื่อถามคำถามและหาข้อมูลต่างๆ นั่นแปลว่า ถ้าแบรนด์ของคุณไม่ปรากฏในคำตอบที่ AI สร้างขึ้น คุณก็เหมือนไม่มีตัวตนบนโลกออนไลน์ + +เครื่องมือ AEO (Answer Engine Optimization) จึงกลายเป็นสิ่งจำเป็นสำหรับนักการตลาดยุคใหม่ บทความนี้จะพาไปเจาะลึกเปรียบเทียบสองแพลตฟอร์มมาแรงอย่าง **Profound** และ **Bluefish AI** ว่าตัวไหนตอบโจทย์มากกว่ากัน + +--- + +## TL;DR + +- **AEO** คือการปรับแต่งเนื้อหาให้ปรากฏใน AI-generated answers จาก ChatGPT, Perplexity, Google AI Overviews ฯลฯ +- **Profound** — เน้น monitoring และวัดผล visibility ใน AI search แบบครบวงจร พร้อมเครื่องมือปรับปรุงแบรนด์ +- **Bluefish AI** — แพลตฟอร์ม AEO ที่เน้นการ monitor visibility ใน AI-generated answers เช่นกัน +- **ทั้งสองตัว** ตอบโจทย์นักการตลาดที่อยาก track แบรนด์ใน AI search แต่ต่างกันในรายละเอียดฟีเจอร์และราคา +- **ฟรี!** ลองเช็ค visibility ของแบรนด์คุณด้วย [HubSpot AEO Grader](https://hubspot.com/aeo-grader) ก่อนตัดสินใจ + +--- + +## สารบัญ + +1. [AEO คืออะไร?](#aeo-คืออะไร) +2. [Profound — AI Visibility Platform](#profound--ai-visibility-platform) +3. [Bluefish AI — AEO Monitoring](#bluefish-ai--aeo-monitoring) +4. [เปรียบเทียบ Profound vs Bluefish AI](#เปรียบเทียบ-profound-vs-bluefish-ai) +5. [ต้องใช้ AEO Tools ไหม?](#ต้องใช้-aeo-tools-ไหม) +6. [FAQ](#faq) + +--- + +## AEO คืออะไร? + +AEO ย่อมาจาก **Answer Engine Optimization** คือการปรับแต่งเนื้อหาและกลยุทธ์เพื่อให้แบรนด์ของคุณปรากฏในคำตอบที่ AI สร้างขึ้น + +ต่างจาก SEO แบบดิ่งตรงที่ SEO เน้นจัดอันดับในหน้าผลการค้นหา (SERPs) แต่ AEO เน้นให้แบรนด์ถูกอ้างอิงใน **AI-generated answers** ไม่ว่าจะเป็น ChatGPT, Perplexity, Google AI Overviews หรือ Gemini + +![How AEO Works with AI Answer Engines](/images/blog/2026-06-28-profound-vs-bluefish-aeo-aeo-overview-diagram.png) + +ทำไมถึงสำคัญ? เพราะทุกวันนี้ผู้ใช้ไม่ได้คลิกเข้าเว็บไซต์เหมือนเดิมอีกแล้ว — พวกเขาถาม AI แล้วได้คำตอบเลย ถ้าแบรนด์คุณไม่ถูก AI mention คุณก็สูญเสียทราฟิกไปแบบไม่รู้ตัว + +--- + +## Profound — AI Visibility Platform + +Profound เป็นแพลตฟอร์มที่ออกแบบมาเพื่อ **monitor, measure, และ improve** การปรากฏตัวของแบรนด์ใน AI-driven search โดยเฉพาะ + +### ฟีเจอร์หลัก + +- **AI Visibility Monitoring** — ติดตามว่าแบรนด์ถูก mention ใน AI answers บ่อยแค่ไหน +- **Brand Positioning Analysis** — วิเคราะห์ว่า AI พูดถึงแบรนด์คุณในแง่บวก ลบ หรือเป็นกลาง +- **Competitive Intelligence** — เปรียบเทียบ visibility กับคู่แข่ง +- **Recommendations** — ให้คำแนะนำในการปรับปรุงแบรนด์ให้ AI หยิบไปอ้างอิงมากขึ้น + +### จุดแข็ง + +- ครอบคลุม AI engines หลายตัว +- มีเครื่องมือวิเคราะห์เชิงลึก +- เหมาะกับทีมการตลาดที่ต้องการ data-driven insights + +### ราคา + +เป็น enterprise-level pricing — เหมาะกับแบรนด์ขนาดกลางถึงใหญ่ที่มีงบประมาณด้าน marketing technology + +--- + +## Bluefish AI — AEO Monitoring + +Bluefish AI เป็นอีกหนึ่งแพลตฟอร์ม AEO ที่เน้นการ **monitor visibility ใน AI-generated answers** เหมือนกัน แต่มีแนวทางและฟีเจอร์ที่ต่างออกไป + +### ฟีเจอร์หลัก + +- **AI Answer Tracking** — ติดตามว่า AI ตอบคำถามเกี่ยวกับแบรนด์คุณอย่างไร +- **Visibility Scoring** — ให้คะแนนความโดดเด่นของแบรนด์ใน AI search +- **Content Gap Analysis** — หาช่องว่างที่เนื้อหายังไม่ครอบคลุม +- **Reporting** — รายงานผลแบบเข้าใจง่าย + +### จุดแข็ง + +- อินเทอร์เฟซใช้งานง่าย +- เน้น actionable insights +- เข้าถึงง่ายสำหรับทีมเล็กๆ + +### ราคา + +มีราคาที่ย่อมเยากว่า Profound — เหมาะกับธุรกิจขนาดเล็กถึงกลางที่อยากเริ่มต้นกับ AEO + +--- + +## เปรียบเทียบ Profound vs Bluefish AI + +![Feature Comparison — Profound vs Bluefish AI](/images/blog/2026-06-28-profound-vs-bluefish-aeo-comparison-table.png) + +| หัวข้อ | Profound | Bluefish AI | +|---|---|---| +| **จุดเด่น** | ครบวงจร + วิเคราะห์เชิงลึก | ใช้ง่าย + เข้าถึงง่าย | +| **AI Engines ที่ครอบคลุม** | หลายตัว | หลายตัว | +| **Competitive Analysis** | ✅ มี | ✅ มี | +| **Brand Sentiment** | ✅ วิเคราะห์เชิงลึก | ⚠️ พื้นฐาน | +| **ราคา** | ระดับ Enterprise | ย่อมเยากว่า | +| **เหมาะกับ** | แบรนด์กลาง-ใหญ่ | ธุรกิจเล็ก-กลาง | +| **Learning Curve** | ปานกลาง | ต่ำ | + +### เลือกตัวไหนดี? + +- **เลือก Profound** ถ้าคุณเป็นแบรนด์ขนาดใหญ่ ต้องการ data เชิงลึก มีงบประมาณ และต้องการ competitive edge แบบจริงจัง +- **เลือก Bluefish AI** ถ้าคุณเป็นธุรกิจขนาดเล็ก-กลาง อยากเริ่มต้นกับ AEO แบบไม่ต้องลงทุนมาก แต่ยังได้ insights ที่ใช้ได้จริง + +![Decision Flowchart — Which Tool for Which Scenario](/images/blog/2026-06-28-profound-vs-bluefish-aeo-use-case-diagram.png) + +--- + +## ต้องใช้ AEO Tools ไหม? + +### ทำไมถึงสำคัญ? + +ตัวเลขจาก McKinsey ไม่โกหก — **50% ของผู้บริโภคใช้ Answer Engine** และ **70% พึ่งพา AI-generated answers** ในการหาข้อมูล นี่ไม่ใช่เทรนด์ที่จะมาในอนาคต แต่เป็นปัจจุบันแล้ว + +ถ้าคุณไม่ track ว่า AI พูดถึงแบรนด์คุณอย่างไร คุณก็ไม่มีทางรู้ว่ากำันเสียโอกาสทางการตลาดไปเท่าไหร่ + +### เริ่มต้นอย่างไร? + +1. **เช็ค visibility ก่อน** — ลองใช้ [HubSpot AEO Grader](https://hubspot.com/aeo-grader) (ฟรี!) เพื่อดูว่าแบรนด์คุณปรากฏใน AI answers แค่ไหน +2. **วิเคราะห์ผล** — ดูว่า AI พูดถึงแบรนด์คุณถูกต้องไหม มีข้อมูลผิดพลาดหรือเปล่า +3. **เลือกเครื่องมือ** — ประเมินว่า Profound หรือ Bluefish AI เหมาะกับงบประมาณและขนาดธุรกิจของคุณ +4. **ปรับปรุงเนื้อหา** — ใช้ insights จากเครื่องมือ AEO มาปรับแต่งเนื้อหาให้ AI หยิบไปอ้างอิงมากขึ้น + +--- + +## FAQ + +### AEO ต่างจาก SEO ยังไง? + +SEO (Search Engine Optimization) เน้นจัดอันดับเว็บไซต์ในผลการค้นหาของ Google แบบดั้งเดิม ส่วน AEO (Answer Engine Optimization) เน้นให้แบรนด์ปรากฏใน **คำตอบที่ AI สร้างขึ้น** เช่น ChatGPT, Perplexity, Google AI Overviews ทั้งสองตัวยังทำงานคู่กันได้ แต่ AEO เป็นสิ่งที่ต้องเพิ่มเข้ามาในกลยุทธ์ยุค AI + +### เครื่องมือ AEO ไหนดีกว่ากัน? + +ไม่มีตัวไหนดีกว่าแบบ absolute — ขึ้นอยู่กับความต้องการ Profound เหมาะกับแบรนด์ใหญ่ที่ต้องการ insights เชิงลึก ส่วน Bluefish AI เหมาะกับธุรกิจเล็ก-กลางที่อยากเริ่มต้นแบบประหยัด แนะนำให้ลองทั้งสองตัว (ถ้ามี free trial) แล้วเปรียบเทียบว่าตัวไหนใช้งานได้จริงกับทีมคุณ + +### เริ่มต้นกับ AEO ยังไงดี? + +เริ่มง่ายๆ ด้วย [HubSpot AEO Grader](https://hubspot.com/aeo-grader) — เป็นเครื่องมือฟรีที่ช่วยเช็คว่าแบรนด์คุณมี visibility ใน AI search แค่ไหน จากนั้นค่อยประเมินว่าต้องการเครื่องมือ paid อย่าง Profound หรือ Bluefish AI หรือไม่ + +--- + + +**Sources:** +- HubSpot Blog — Zoe Ashbridge (25 มิ.ย. 2026): [Profound vs Bluefish AI](https://blog.hubspot.com/marketing/profound-vs-bluefish) +- McKinsey Consumer Research (2026) \ No newline at end of file diff --git a/src/content/blog/2026-07-01-chatgpt-reasoning-modes-overlap.md b/src/content/blog/2026-07-01-chatgpt-reasoning-modes-overlap.md new file mode 100644 index 0000000..33ac162 --- /dev/null +++ b/src/content/blog/2026-07-01-chatgpt-reasoning-modes-overlap.md @@ -0,0 +1,167 @@ +--- +title: "ChatGPT Instant vs Thinking Mode: 75% ของ Sources ที่ใช้ ไม่เหมือนกัน" +description: "Semrush study เผย ChatGPT Instant mode กับ Thinking mode cite sources ต่างกัน 75% — 99 domains โผล่เฉพาะใน Thinking mode ที่ไม่เคยมีใน Instant mode" +pubDate: 2026-07-01 +category: "SEO" +heroImage: "/images/blog/2026-07-01-chatgpt-reasoning-modes-overlap-featured.png" +tags: ["ChatGPT", "Reasoning-Modes", "Semrush-Study", "AI-Visibility", "GEO"] +draft: false +--- + +> ถ้าคุณ optimize content ให้ติดแค่ mode เดียว — คุณหายไป 75% แบบไม่รู้ตัว + +ถ้าคุณคิดว่า ChatGPT คือ search engine ตัวเดียวที่ทำงานเหมือนเดิมทุกครั้ง — คุณคิดผิด + +Kevin Indig (Growth Memo) และทีม Semrush ร่วมกันวิเคราะห์ข้อมูลจาก AI Visibility Toolkit ผ่าน 100 prompts ใน 4 หมวดหมู่ธุรกิจ (B2B SaaS, Finance, Consumer Tech, Health & Lifestyle) — แบ่งเป็น 20 buyer journeys 5 ขั้นตอน (Problem → Exploration → Comparison → Validation → Selection) + +และพวกเขาพบว่า **ChatGPT Instant mode กับ Thinking mode cite sources ต่างกัน 75%** + +## 📚 สารบัญ +- Only 25.6% ซ้ำ — สอง mode = สอง search engine +- Citation rate + citations ต่อ response เพิ่มขึ้นเท่าตัว +- 4.6x fan-out queries ใน Thinking mode +- Source types: Reddit หาย, Government กับ Academic มา +- Finance เปลี่ยนมากที่สุด, Consumer Tech เปลี่ยนน้อย +- Full-funnel persistence — 4 ใน 20 +- Actionable Takeaways สำหรับ Content Strategists + +--- + +## Only 25.6% ซ้ำ — นี่คือ "Different Search Engine" + +![Venn diagram showing only 25% overlap between Instant and Thinking modes](/images/blog/2026-07-01-chatgpt-reasoning-modes-overlap-venn-overlap.png) + +ตัวเลขที่ช็อคที่สุดในงานวิจัยนี้: +- **Only 25.6%** ของ cited domains ซ้ำกันระหว่าง Instant mode กับ Thinking mode +- **เกือบ 3 ใน 4** ของ sources ที่ cited ต่างกันโดยสิ้นเชิง +- Thinking mode ดึงข้อมูลจาก **173 unique domains** vs Instant mode **127 domains** +- **99 domains** ปรากฏเฉพาะใน Thinking mode — ไม่เคยมีใน Instant mode เลย + +นี่ไม่ใช่แค่ "ปรับเพิ่มอีกนิด" — มันคือ **คนละ machine** + +| Metric | Instant Mode | Thinking Mode | +|:---|:---:|:---:| +| Unique domains cited | 127 | 173 | +| Overlap | — | 25.6% | +| Domains exclusive to this mode | 74 | 99 | + +--- + +## Citation Rate + Citations ต่อ Response เพิ่มขึ้นเท่าตัว + +![Source stacks comparison: Instant Mode (2.6 citations) vs Thinking Mode (4.5 citations)](/images/blog/2026-07-01-chatgpt-reasoning-modes-overlap-source-stacks-comparison.png) + +เมื่อเปิด Thinking mode: +- **Citation rate**: 50% → 68% (+18 pp) — อัตราการตอบที่มี external sources เพิ่มขึ้น +- **Average citations**: 2.6 → 4.5 — เกือบเท่าตัว +- **Fan-out queries**: 4.6x — ยิง sub-queries เพื่อค้นหามากกว่าเดิม 4.6 เท่า + +แปลว่า Thinking mode **ค้นหาจริงจังกว่า** — มันไม่ shortcut ตอบจากความจำ แต่ออกไปเสิร์ช หา sources เพิ่ม อ่านเยอะขึ้น และ cite มากขึ้น + +### ภาพรวมของ Query Pattern + +ใน Instant mode — ChatGPT จะยิง search query 1-2 ครั้ง รวบรัดได้ใจความ ใน Thinking mode — 1 คำถามของคุณกลายเป็น 15-40 การค้นหาย่อย ตามที่ Suganthan พบในงานวิจัยของ SEJ + +--- + +## Source Types Shift: Reddit หาย, Government มา + +เมื่อเปิด reasoning — ลักษณะของ sources ที่ cited เปลี่ยนไปอย่างมีนัยยะ: + +| Source Type | Instant Mode | Thinking Mode | การเปลี่ยนแปลง | +|:---|:---:|:---:|:---:| +| **Reddit & UGC** | สัดส่วนสูง (cited มากที่สุด) | **ลดลง ~50%** | เสีย share ครึ่งนึง | +| **Government (.gov)** | น้อย | เพิ่มขึ้นมาก | ได้ credibility boost | +| **Academic (.edu, .org)** | ปานกลาง | เพิ่มขึ้นมาก | กลายเป็น primary sources | +| **Official documentation** | ปานกลาง | เพิ่มขึ้นมาก | API docs, product docs | + +เหตุผล logical มาก: เมื่อ ChatGPT อยู่ในโหมด "คิดหนัก" — มันอยากได้ข้อมูลที่ **เชื่อถือได้** มากกว่า Reddit opinion ดังนั้นมันจึง shift ไปหา government sites, academic papers, และ official documentation แทน + +Reddit ยังคงถูก cited อยู่ — แต่สัดส่วนลดลง ขณะที่ Wikipedia ยังคงเป็น domain ที่ถูก cited มากที่สุดใน 11 จาก 12 ภาษาตามงานวิจัยของ arXiv + +--- + +## Finance เปลี่ยนมากที่สุด, Consumer Tech แทบไม่เปลี่ยน + +Industry matters — แต่ละอุตสาหกรรมได้รับผลกระทบจาก reasoning mode ไม่เท่ากัน: + +| Industry | Instant Citation Rate | Thinking Citation Rate | Change | +|:---|:---:|:---:|:---:| +| **Finance** | ~45% | ~73% | **+28 pp** ⬆️ มากที่สุด | +| B2B SaaS | ~52% | ~70% | +18 pp | +| Health & Lifestyle | ~48% | ~65% | +17 pp | +| Consumer Tech | ~55% | ~62% | +7 pp (น้อยที่สุด) | + +**ทำไม Finance ถึงเปลี่ยนมาก?** เพราะคำถามการเงินต้องการข้อมูลปัจจุบัน อัตราดอกเบี้ย, ราคาหุ้น, กฎหมายภาษี — Instant mode ที่ตอบจาก training data ไม่พอ ต้อง search จริงใน Thinking mode + +**ทำไม Consumer Tech แทบไม่เปลี่ยน?** เพราะคนถาม "iPhone รุ่นไหนดี" — ความคิดเห็นจาก Reddit และ review sites ก็พอแล้ว ไม่ต้องไปหา government sources + +--- + +## Full-Funnel Persistence — 4 ใน 20 + +นี่คือสิ่งที่อาจเปลี่ยนวิธีคิดเรื่อง content marketing ของคุณ: + +ใน **4 จาก 20 buyer journeys** (20%) — เมื่อใช้ Thinking mode — **แบรนด์เดียวกันปรากฏตั้งแต่ Problem → Exploration → Comparison → Validation → Selection** + +Instant mode แทบไม่เกิด pattern นี้ + +**นัยยะ:** ถ้าคุณทำ content ที่ติดอันดับ Top-of-Funnel (ToFu) ใน Thinking mode — มีโอกาสที่แบรนด์คุณจะถูก cite ต่อเนื่องไปจนถึง Bottom-of-Funnel (BoFu) โดยที่คุณไม่ต้องทำ content BoFu ด้วยซ้ำ + +แต่นี่ใช้ได้กับ Thinking mode เท่านั้น — Instant mode ไม่มี persistence pattern นี้ + +--- + +## Actionable Takeaways สำหรับ Content Strategists + +### 1. ต้อง optimize ทั้งสอง mode + +ไม่ใช่แค่เขียน content ดีๆ — แต่ต้องเข้าใจว่า **Instant mode กับ Thinking mode เลือก sources ต่างกัน** + +| Content Type | Instant Mode | Thinking Mode | +|:---|:---|:---| +| Reddit / UGC | 🟢 มีโอกาส cited สูง | 🟡 โอกาสลดลง ~50% | +| Brand-owned / Blog | 🟡 ต้อง text-based | 🟡 ต้อง text-based | +| Official docs / Whitepapers | 🔴 โอกาสต่ำ | 🟢 โอกาสสูงมาก | +| Government / Academic | 🔴 โอกาสต่ำ | 🟢 โอกาสสูงมาก | + +### 2. สร้าง multi-mode citation strategy + +คำถามที่คุณควรถามตัวเอง: +- **คำถามในอุตสาหกรรมคุณถูกถามใน mode ไหนบ่อย?** — Finance → Thinking mode แทบ guarantee +- **เนื้อหาแบบไหนที่แต่ละ mode ชอบ?** — Instant → Reddit, listicles | Thinking → Academic, official sources +- **คุณติดทั้งสอง mode หรือแค่ mode เดียว?** — ใช้เครื่องมืออย่าง Semrush AI Visibility Toolkit เช็ค + +### 3. ลงทุนกับ Top-of-Funnel content + +**— โดยเฉพาะถ้าคุณอยู่ในอุตสาหกรรมที่ Thinking mode มีบทบาทมาก (Finance, B2B)** — เพราะ 20% ของ journeys ใน Thinking mode แสดง persistence จาก ToFu → BoFu + +### 4. เปิดใช้งาน Instant และ Thinking mode ในการ test + +อย่า test แค่ default mode — รัน prompt เดียวกันทั้งสอง mode แล้วดูว่า: +- sources ที่ cited ต่างกันยังไง? +- แบรนด์คุณโผล่ใน mode ไหน? หายไปจาก mode ไหน? +- competitors ใครที่โผล่ใน mode ที่คุณไม่ติด? + +### 5. Official documentation ≠ เนื้อหาการตลาด + +สำหรับ Thinking mode — การมี API documentation ที่ดี, technical whitepapers, และ product specs ที่อ่านได้ (plain HTML) — มีค่าเทียบเท่าการมี blog post ที่ viral + +--- + +## สรุปภาพใหญ่ + +**ChatGPT ไม่ใช่ระบบเดียว** — มันคือสอง search engines ในร่างเดียว: +- **Instant mode**: เร็ว, ใช้ UGC/Reddit, citation rate 50% +- **Thinking mode**: ช้า, ลึก, ชอบ official sources, citation rate 68%, 4.6x fan-out queries + +แค่ 25.6% ของ sources ซ้ำกัน — ถ้าคุณ optimize แค่ mode เดียว คุณกำลังหายไป 75% + +และนี่คือ GPT-5.2 — รุ่นถัดไปอาจแตกต่างกว่าเดิมอีก + +--- + +**ที่มา:** +- Margarita Loktionova, "Only 25% of cited sources overlap between ChatGPT's different reasoning modes [Study]" — Semrush Blog, June 30, 2026 +- Kevin Indig, Growth Memo — data partnership with Semrush AI Visibility Toolkit +- Suganthan Mohanadasan, "How ChatGPT Actually Picks Sources" — SEJ, June 30, 2026 diff --git a/src/content/blog/2026-07-01-chatgpt-source-selection.md b/src/content/blog/2026-07-01-chatgpt-source-selection.md new file mode 100644 index 0000000..697e259 --- /dev/null +++ b/src/content/blog/2026-07-01-chatgpt-source-selection.md @@ -0,0 +1,184 @@ +--- +title: "ChatGPT เลือกแหล่งข้อมูลยังไง? เปิด Network Traffic ดูของจริง" +description: "เบื้องหลังการทำงานของ ChatGPT ในการเลือก sources — จาก network traffic จริง พบ result_source 4 แบบ, turn_use_case 6 ประเภท, และ 75% ของ citations ไม่ได้มาจากเว็บที่คุณคิด" +pubDate: 2026-07-01 +category: "SEO" +heroImage: "/images/blog/2026-07-01-chatgpt-source-selection-featured.png" +tags: ["ChatGPT", "GEO", "AI-Search", "Source-Selection"] +draft: false +--- + +> การตลาดยุคใหม่ต้องเข้าใจว่า AI "เห็น" เรายังไง — ไม่ใช่แค่ Google Rank เท่านั้น + +ถ้าคุณเป็นคนทำ SEO หรือ Content Marketing คุณคงเคยได้ยินคำถามนี้: + +**"ทำยังไงให้แบรนด์เราโผล่ใน ChatGPT?"** + +คำตอบที่เราได้ยินซ้ำๆ คือ "เขียนคอนเทนต์ดีๆ", "ทำลิสต์icle", "ไปเม้น Reddit" — แต่ไม่มีใครเคยบอกว่า **เราไปอ่าน network traffic ของ ChatGPT จริงๆ ดูไหมว่า mechanism มันทำงานยังไง?** + +Suganthan Mohanadasan (Co-founder Snippet Digital) ทำแบบนั้นแหละ เขานั่งอ่าน JSON ที่ ChatGPT ส่งไปมายังบราวเซอร์ของเขา — ไม่ใช่แค่อ่าน output ที่เราเห็น แต่ดู layer ลึกลงไปว่า ChatGPT **ตัดสินใจดึงข้อมูลจากที่ไหน** และใช้เกณฑ์อะไร + +นี่คือสิ่งที่เขาค้นพบ + +## 📚 สารบัญ +- 4 ชั้นของผลลัพธ์ที่ ChatGPT ใช้ +- 6 ประเภทคำถาม — บางประเภทไม่ Search เลย +- Fan-out Queries: 1 คำถาม → 40 การค้นหา +- Fetched ≠ Cited ≠ Mentioned +- สิ่งที่ ChatGPT มองไม่เห็น +- ทำยังไงให้แบรนด์คุณโดนเลือก? +- สรุป Actionable Takeaways + +--- + +## 4 ชั้นของแหล่งข้อมูลใน ChatGPT + +![ChatGPT source pipelines: SERP, Labrador, Bright Data, Oxylabs](/images/blog/2026-07-01-chatgpt-source-selection-source-pipelines.png) + +ใน network traffic ของ ChatGPT มี field หนึ่งชื่อ `result_source` — มันถูกติดไว้กับทุก web result ที่ ChatGPT ดึงมา โดยคุณไม่มีทางเห็น field นี้ใน answer ปกติ + +`result_source` มี 4 ค่า: + +| result_source | มันคืออะไร | ตัวอย่าง | +|:---|:---|:---| +| **serp** | Open web baseline — ส่วนใหญ่เป็นข่าว | Yahoo, StreetInsider | +| **labrador** | Allowlist ของ publishers ที่มีดีลกับ OpenAI | Reuters, WSJ, FT, Wikipedia, arXiv | +| **bright** | Bright Data — commercial web scraper | Reddit, Forbes, rtings | +| **oxylabs** | Oxylabs — คู่แข่ง Bright Data | Gulf News, Khaleej Times, regional press | + +**labrador** คือ licensed tier — ถ้าคุณไม่ได้เป็นเจ้าของหนังสือพิมพ์ระดับประเทศ ก็เข้า tier นี้ไม่ได้ + +**bright** กับ **oxylabs** คือคู่หูที่ ChatGPT จ้างมา crawl เว็บให้ โดย bright ทำหน้าที่หลักใน commercial, shopping, finance, weather ส่วน oxylabs เก็บบริเวณ regional และ local press + +ใน query เดียว — เช่นถามสภาพอากาศ — ChatGPT ดึงข้อมูลจากหลาย pipeline พร้อมกัน: `metoffice.gov.uk` ผ่าน **bright**, `khaleejtimes.com` ผ่าน **oxylabs** + +### 📌 GEO Takeaway + +คุณแข่งขันใน **scraped tier** (bright + oxylabs) ดังนั้น: +- **ทำให้เว็บคุณ cleanly scrapable** — ข้อมูล ตัวเลข ราคา ต้องอยู่ใน plain HTML text +- อย่าซ่อนไว้ใน script, PDF, หรือ image — scrapers อ่านไม่ได้ +- สร้าง third-party coverage, PR, และ brand mentions — นี่คือทางเข้า scraper ที่จะเจอคุณ + +--- + +## 6 ประเภทคำถาม — บางประเภทไม่ Search เลย + +ChatGPT มี field `turn_use_case` ที่จัดประเภทคำถามของคุณก่อนว่าจะ search หรือไม่: + +1. **instant search** — ค้นหาทั่วไป +2. **shopping** — ค้นหาสินค้า +3. **text** — **ไม่ search เลย**, ตอบจาก training data +4. **local** — ค้นหารอบตัว +5. **thinking** — reasoning mode, ค้นหาเชิงลึก +6. **image generation** — สร้างภาพ + +**ประเภทที่ต้องสนใจมากที่สุดคือ `text`** + +เมื่อ ChatGPT จัดคำถามคุณเป็น `text` — มันจะไม่ search เลย มันตอบจากความจำใน training data คำถามประเภท how-to, definition, translation — แม้แต่ "latest treatment guidelines for type 2 diabetes" ก็ถูกจัดเป็น `text`! + +ใน 10 คำถามปัจจุบันที่เขาทดสอบ — 3 คำถามถูกจัดเป็น `text` และตอบจาก training data โดยไม่ search เลย + +**การตัดสินใจขึ้นอยู่กับ wording ไม่ใช่ topic** — "best coffee near me" → local pipeline, "best 4K TVs to buy" → shopping แต่ "best 4K TVs with reviews" → normal search + +### 📌 GEO Takeaway + +**ก่อนลงทุนทำหน้า page หนึ่ง — เช็คก่อนว่าคำถามนี้ ChatGPT search จริงๆ หรือตอบจาก training data** + +ถ้ามันถูกจัดเป็น `text` — ไม่มี page ไหนในโลกเข้าไปได้ เพราะ ChatGPT ไม่ได้ search เลย ต้องใช้เวลาสร้าง authority และรอให้แบรนด์คุณถูกรวมใน training data รอบหน้า + +--- + +## Fan-out Queries: 1 คำถาม → 40 การค้นหา + +![Fan-out queries: 1 question splitting into 15-40 sub-queries](/images/blog/2026-07-01-chatgpt-source-selection-fan-out-queries.png) + +ChatGPT เปิดเผย search queries ที่มันรันผ่าน API — และใน Thinking mode **1 คำถามกลายเป็นการค้นหาย่อย 15-40 ครั้ง** + +ตัวอย่างของ query เปรียบเทียบ pricing เครื่องมือ AI visibility: + +``` +"Profound AI search visibility pricing AI engines tracked 2026" +"AthenaHQ pricing AI search visibility tool" +"site:peec.ai/pricing Peec AI Starter Pro Advanced 50 prompts 150 prompts" +"Peec AI pricing $95 $245 $495 official" ← เดาราคาแล้ว search ยืนยัน +"Scrunch AI pricing" ← ไม่ได้อยู่ใน prompt แต่เจอระหว่างค้น +...รวม ~40 queries +``` + +สามสิ่งที่โดดเด่น: +1. ChatGPT ยิง `site:` probes ตรงไปที่ vendor pricing pages +2. มัน **เดาราคา แล้ว search เพื่อยืนยัน** +3. มัน widen ไปเรื่อยๆ — เจอ tool ที่คุณไม่ได้ถามถึง ก็ chase pricing ต่อ + +### 📌 GEO Takeaway + +- **ตัวเลขสำคัญต้องอยู่ใน plain HTML text** — โดยเฉพาะ pricing เพราะ ChatGPT grep หา `$` และ `€` +- JavaScript-based toggle และ dynamic data loading = ตาย เพราะ ChatGPT อ่านไม่เจอ +- **เขียนให้ตรงกับ query ที่ ChatGPT รัน จริง — ไม่ใช่คำค้นของมนุษย์** +- ถ้า pricing ของคุณซ่อนใน JS — ChatGPT จะยอมแพ้แล้วไป quote G2 แทน + +--- + +## Fetched ≠ Cited ≠ Mentioned + +นี่คือความแตกต่างที่คนสับสนมากที่สุด — สามสิ่งนี้ไม่เหมือนกัน: + +1. **Fetched** — ChatGPT ดึงหน้าคุณเข้ามาใน context (user ไม่เห็น) +2. **Cited** — ใส่ URL ของคุณเป็น source ข้างหลัง specific sentence +3. **Mentioned** — ชื่อแบรนด์คุณโผล่ใน answer (อาจเป็น chip ลิงก์เว็บคุณ) + +**คุณสามารถชนะหรือแพ้แต่ละอย่างได้โดยอิสระ** + +ในการทดสอบของ Suganthan — Reddit ถูก fetch 278 ครั้ง แต่ cited แค่ 11 ครั้ง ส่วน YouTube ถูก fetch 201 ครั้ง แต่ **ไม่เคยถูก cited เลยสักครั้ง** + +สาเหตุ: citation ต้อง bind กับ text ที่ model อ่านได้จริง — YouTube page ให้แค่ metadata (title, description) ไม่ใช่ transcript ส่วน Reddit มี text ให้ quote เต็มๆ + +### 📌 GEO Takeaway + +- **คุณ cite ตัวเองไม่ได้** — claim เกี่ยวกับคุณ ต้องมี third-party coverage +- เน้น content ที่เป็น text (Reddit, blog, review sites) มากกว่า video +- **1 หน้าที่แข็งแรง ดีกว่า 20 หน้าที่บาง** — ChatGPT dedup by domain +- ราคาและ spec ของคุณควรอ่านได้จากหน้าของคุณเอง ไม่งั้นมือไป G2 + +--- + +## สิ่งที่ ChatGPT มองไม่เห็น — และทำไม G2 ถึงได้ cited + +ChatGPT เก็บ **chain of thought** ไว้ใน conversation — และมันอธิบายการเลือก sources ด้วยคำพูดของมันเอง: + +> "pricing page seems more current, so I should cite that" + +> "the pricing isn't showing up directly in the search result, possibly because it's loaded with JavaScript" + +> "I can quote third-party sources since the official page is hard to parse and doesn't show prices" + +> "use citations from G2 where appropriate" + +นี่คือเกมทั้งเกมใน trace เดียว — ChatGPT **อยาก cite official page** แต่ถ้า official page อ่านไม่ได้ (JS, image-based pricing) — มันจะ fallback ไปที่ G2 และ third-party sites + +**ข้อควรรู้เพิ่มเติม:** +- ไม่มี ranking score หรือ domain authority ใน traffic ที่ browser เห็น — ใครขาย "ChatGPT ranking factors" คือขายน้ำมันงู +- **Personalization มีจริง** — ChatGPT ดึง conversation history คุณมาใช้ใน answer (field: `personal_sources: ["convo_search", "gmail", "files"]`) +- **Local results ถูก capped ที่ 2** — ถ้าถามร้านกาแฟใกล้ฉัน ได้แค่ 2 ที่ ไม่ใช่ top 10 + +--- + +## สรุป Actionable Takeaways + +| สิ่งที่ต้องทำ | เหตุผล | +|:---|:---| +| ✅ **ทำให้เว็บ cleanly scrapable** | ข้อมูล ราคา spec ต้องอยู่ใน plain HTML — scrapers อ่าน JS ไม่ได้ | +| ✅ **สร้าง third-party coverage** | 85% ของ citations มาจาก third-party sources — PR, Reddit, review sites | +| ✅ **เขียนเพื่อ query ที่ ChatGPT รันจริง** | ChatGPT ไม่ได้ใช้คำค้นมนุษย์ — มัน rephrase และยิง site: probes | +| ✅ **เน้น text-based content** | YouTube ถูก cited = 0 — Reddit ถูก cited ตลอด | +| ✅ **1 หน้าที่แข็งแรง ดีกว่า 20 หน้าบาง** | ChatGPT dedup by domain | +| ❌ **อย่าซ่อน pricing ไว้ใน JS** | ChatGPT จะยอมแพ้และไป cite G2 แทน | +| ❌ **อย่าลงทุนกับคำถามที่ ChatGPT ไม่ search** | เช็คก่อนว่าคำถามของคุณถูกจัดเป็น `text` หรือไม่ | + +**ทิ้งท้าย:** ChatGPT ไม่ใช่ search engine ดังนั้นหยุด optimize เหมือนทำ SEO บน Google มันอ่านหน้า **คุณเพื่อหาข้อเท็จจริง** และอ่าน **third-party เพื่อหาความคิดเห็น** — และมันจะทำอย่างนั้นก็ต่อเมื่อคำถามนั้นคุ้มค่าที่จะ search + +--- + +**ที่มา:** +- Suganthan Mohanadasan, "How ChatGPT Actually Picks Sources (I Read The Network Traffic, Not The Outputs)" — Search Engine Journal, June 30, 2026 +- arXiv 2606.25787 — "How Large Language Models Source Brand Reputation Across Languages and Markets" diff --git a/src/content/blog/2026-07-01-deepseek-dspark-devin-fusion.md b/src/content/blog/2026-07-01-deepseek-dspark-devin-fusion.md new file mode 100644 index 0000000..5ed74a3 --- /dev/null +++ b/src/content/blog/2026-07-01-deepseek-dspark-devin-fusion.md @@ -0,0 +1,214 @@ +--- +title: "DeepSeek DSpark + Devin Fusion: ถึงเวลา Multi-Model Architecture ที่ลดต้นทุน AI 85%" +description: "สองเทรนด์ที่เปลี่ยนวิธีคิดเรื่อง AI inference: DeepSeek DSpark เร่งความเร็ว 85% ด้วย speculative decoding ส่วน Devin Fusion ลดต้นทุน 35% ด้วย multi-model harness" +pubDate: 2026-07-01 +category: "AI & Marketing" +heroImage: "/images/blog/2026-07-01-deepseek-dspark-devin-fusion-featured.png" +tags: ["DeepSeek", "DSpark", "Devin", "Fusion", "Speculative-Decoding", "Multi-Model"] +draft: false +--- + +> คุณไม่ขับ Lamborghini ไปซื้อของหน้าปากซอย — แล้วทำไมใช้ GPT-5.6 เพื่อตรวจ grammar? + +สองข่าวใหญ่ในสัปดาห์นี้ชี้ทางเดียวกัน: **ยุคของ single-model-for-everything กำลังจะจบลง** + +1. **DeepSeek เปิดซอร์ส DSpark** — เร่งความเร็ว LLM inference ได้ 60-85% โดยใช้ "scout model" เดา path ล่วงหน้า (MIT License) +2. **Cognition เปิด Devin Fusion** — multi-model harness ลดต้นทุน 35% โดยใช้ two-agent architecture (main + sidekick) + +ทั้งสอง approach มีแนวคิดเดียวกัน: **ไม่ต้องใช้ flagship model สำหรับทุกงาน** — แค่ใช้มันในจังหวะที่จำเป็นเท่านั้น + +## 📚 สารบัญ +- DSpark: AI Scout ที่เดาทางก่อน +- Devin Fusion: Two-Agent Architecture +- เปรียบเทียบ: กลไกเดียวกัน, scale ต่างกัน +- Sidekick Pattern: ทำไม Fable 5 ถึง performance ดีกว่าใน multi-model setup +- Dynamic Mid-Session Routing +- ผลกระทบต่อธุรกิจ +- สรุป: อนาคตของ AI Cost Architecture + +--- + +## DSpark: AI Scout ที่เดาทางก่อน + +![Speculative decoding: scout model generates tokens ahead, main model verifies](/images/blog/2026-07-01-deepseek-dspark-devin-fusion-speculative-decoding-flow.png) + +DeepSeek เปิดซอร์ส DSpark ภายใต้ MIT License — พร้อม technical paper, model checkpoints, และ DeepSpec (codebase สำหรับ speculative decoding) + +### หลักการทำงาน + +LLM เขียนข้อความทีละ token — แต่ละ token ต้องรอให้ token ก่อนหน้าเสร็จก่อน เหมือนคนข้ามแม่น้ำทีละก้อนหิน + +DSpark เพิ่ม **scout model** ที่วิ่งล่วงหน้า 2-3 ก้าว: +1. **Scout** เดา path ที่น่าจะเป็น +2. **Main model** ตรวจสอบความถูกต้อง +3. ถ้าเดาถูก — ข้ามไปได้เลย (เร็วกว่า) +4. ถ้าเดาผิด — เสียเวลาแค่ตรวจสอบ path สั้นๆ + +| เปรียบเทียบ | Before (MTP-1) | After (DSpark) | +|:---|:---:|:---:| +| V4-Flash per-user speed | baseline | +60% ถึง **85%** 🚀 | +| V4-Pro per-user speed | baseline | +57% ถึง **78%** | +| V4-Flash throughput (80 tokens/s/user) | baseline | +51% | +| V4-Pro throughput (35 tokens/s/user) | baseline | +52% | +| V4-Flash ที่ 120 tokens/s/user | near collapse | +661% throughput | +| V4-Pro ที่ 50 tokens/s/user | near collapse | +406% throughput | + +**ตัวเลข 661% และ 406%** ฟังดูเวอร์ แต่ต้องเข้าใจบริบท: มันคือ **increased throughput under strict speed targets** — ถ้า baseline รับ concurrent users ได้ 10 คนที่ speed 120 t/s, DSpark รับได้ ~70 คน — ไม่ใช่ "token generation เร็วขึ้น 6 เท่า" + +ที่สำคัญ — DSpark **ไม่จำกัดแค่ DeepSeek-V4** — มันรองรับ Qwen และ Gemma ด้วย (มี checkpoints ให้) + +### Speculative Decoding คืออะไร? + +ปัญหาพื้นฐานของ LLM: มันสร้าง output ได้แค่ **1 token ต่อครั้ง** — เทียบกับมนุษย์ที่คิดทั้งประโยคก่อนพูด + +Speculative decoding = ให้ "draft model" (เล็กกว่า เร็วกว่า) เดา output ล่วงหน้า แล้วให้ "target model" (ใหญ่กว่า) ตรวจสอบและแก้ไข + +ถ้า draft model เดาถูก — target model verify เป็น batch (แทนทีละ token) → เร็วกว่า **โดยไม่เปลี่ยน output** + +--- + +## Devin Fusion: Two-Agent Architecture + +![Two-agent architecture: frontier model delegates to sidekick model](/images/blog/2026-07-01-deepseek-dspark-devin-fusion-two-agent-architecture.png) + +Cognition ประกาศ Devin Fusion — ไม่ใช่แค่ "โมเดลผสม" แต่เป็น **multi-model harness ที่ซับซ้อนกว่านั้นมาก** + +### หลักการทำงาน + +Devin Fusion ใช้ **2 agents ที่ทำงานพร้อมกัน**: +1. **Main Agent** (frontier model) — วางแผน, ตัดสินใจ, ตรวจสอบ +2. **Sidekick Agent** (cost-effective model) — execute งานย่อย, รันเทส, เขียน boilerplate + +**ทั้งสอง maintain context cache ของตัวเอง** — ต่างจาก architecture อื่นที่ต้อง serialize context เมื่อเปลี่ยน model + +ผลลัพธ์บน FrontierCode benchmark: + +| Config | Score | Cost/Task | +|:---|:---:|:---:| +| Fusion + Fable 5 | **57.6** | **$3.00** | +| Fable 5 (medium) alone | 57.0 | $5.12 | +| Fusion alone | 47.9 | $2.38 | +| Opus 4.8 (high) | 48.8 | $3.24 | +| GPT-5.5 (high) | 44.8 | $2.70 | + +**น่าสนใจ:** Fusion + Fable 5 = 57.6 (คะแนนสูงสุด) + $3.00 (ต้นทุนถูกกว่า Fable 5 alone 41%) + +Fusion ที่ไม่มี Fable 5 = 47.9 (คะแนนต่ำกว่า Opus 4.8 เล็กน้อย แต่ต้นทุนต่ำกว่า 26%) + +### Sidekick in Action + +Cognition ยกตัวอย่างจริง: + +1. **Modernize search.js → ES6** — เขียน diff เสร็จ ส่ง sidekick รัน test suite ที่ช้า → cost -62%, score +2 (98→100) ✅ +2. **Rip out OpenTracing integration** — mechanical removal หลายไฟล์ → cost -32%, score -1 (98→97) ✅ +3. **Add team selector with React/Redux** — hard feature, judgment-driven → cost -28% **แต่ score -27 (54→27)** ❌ + +**บทเรียน:** เมื่องานต้องการ judgment — การ delegate coding ให้ sidekick ทำให้คุณภาพตก Hard tasks จะแพ้ทาง sidekick + +4. **Integrate LangChain4j WebSocket MCP** — hard task แต่กลไกเป็น mechanical reuse → cost -25%, **score +12 (69→81)** 🏆 + +--- + +## เปรียบเทียบ: DSpark vs Devin Fusion + +| มิติ | DSpark | Devin Fusion | +|:---|:---|:---| +| ปัญหาที่แก้ | Inference speed | Cost-quality tradeoff | +| กลไก | Speculative decoding (draft + target) | Multi-agent routing (main + sidekick) | +| Scale | Model-level (token generation) | Task-level (user request) | +| จุดเด่น | 85% per-user speedup | 35-41% cost reduction | +| Implementation | ควบคุม weights + serving stack | API-level, ไม่ต้องควบคุม weights | +| Licensing | MIT License | Proprietary | +| ใครใช้ได้ | Enterprise ที่ deploy own models | ผู้ใช้ Devin (cloud service) | + +**ทั้งสอง approach มาจากรากเดียวกัน:** Don't use the big model for everything. ใช้ big model เฉพาะจุดที่ต้องใช้ real intelligence ส่วนงานที่ routine / predictable — ให้ sidekick (DSpark: draft model → Devin Fusion: cost-effective model) จัดการ + +--- + +## Sidekick Pattern: ทำไมยิ่งใช้ ยิ่งได้ผล + +Cognition พบ pattern สำคัญ: **Sidekick scales better as models get smarter** + +Fable 5 (Anthropic's latest frontier) ทำงานใน Devin Fusion ได้ performance ดีกว่า Opus 4.8 หรือ GPT-5.5 ใน role ของ main agent — เพราะ Fable 5: +- **Delegate งานได้ฉลาดกว่า** — รู้ว่างานไหนควรส่ง sidekick +- **Request context ได้มีประสิทธิภาพกว่า** — ไม่ขอข้อมูลซ้ำซ้อน +- **Plan ได้แม่นยำกว่า** — ลดความจำเป็นต้องเปลี่ยนแผนระหว่างทาง + +| Model | Fusion Cost Reduction | +|:---|:---:| +| Fable 5 | **41%** 🏆 | +| Opus 4.8 | ~35% | +| GPT-5.5 | ~35% | + +> "The sidekick pattern is one that will become more useful as base models get better." — Cognition Team + +--- + +## Dynamic Mid-Session Routing + +หนึ่งในความท้าทายของการใช้ multi-model: **เลือก model ผิดตั้งแต่ต้น? หรือ task ยากกว่าที่คิด?** + +Devin Fusion แก้ด้วย **Dynamic Mid-Session Routing**: +- ระหว่างทำงาน จะมี lightweight classifiers ส่งสัญญาณว่า "ถึงเวลาต้องเปลี่ยน model" +- การเปลี่ยน model เกิดขึ้นตอน **context compaction** — ตอนที่ architecture ต้องเคลียร์ cache อยู่แล้ว → ไม่มี extra cost +- Route ได้ทั้งจาก sidekick → main, หรือ main → sidekick + +**นั่นหมายความว่า:** แม้ sidekick จะเจองานที่ยากเกินไป — system สามารถ escalate กลับไปหา main agent โดยไม่เสีย cache penalty + +--- + +## ผลกระทบต่อธุรกิจ + +### 1. AI Cost Reduction ถึง 85% + +รวมผลจากสองเทรนด์: +- DSpark: 85% per-user speedup → **ลด compute cost ต่อ request** +- Devin Fusion: 35-41% cost reduction → **ลด API cost ต่อ task** +- รวม: enterprise ที่ deploy own models + ใช้ multi-model routing = **ลดต้นทุน AI ได้มากถึง 50-70%** + +### 2. ยุคของ Agentic Architecture + +AI ไม่ใช่แค่ chatbot อีกต่อไป — แต่มันคือ **system of agents** ที่ทำงานร่วมกัน: +- Planner agent (main, frontier) +- Executor agent (sidekick, cost-effective) +- Reviewer agent (verify quality) +- Router agent (decide which model for which task) + +### 3. Open Source vs Proprietary + +DSpark (MIT) = enterprise ที่ deploy own weights สามารถใช้ได้ทันที Devin Fusion (proprietary) = ต้องใช้ผ่าน Devin cloud — สำหรับทีมที่ไม่ต้องการจัดการ infrastructure เอง + +### 4. Fable 5 กับ Geopolitics + +Fable 5 ถูก US government สั่งระงับ access วันที่ 12 มิถุนายน 2026 — Cognition จึงรายงานผล Fable 5 จาก measurement ก่อน suspension + +นี่เพิ่มความไม่แน่นอนให้ enterprise ที่พึ่งพา frontier model เจียวเดียว — ข้อดีของ multi-model architecture คือ resilience ต่อ supply shock + +--- + +## สรุป: อนาคตของ AI Cost Architecture + +``` +ก่อน: 1 Model → 1 Task (expensive for everything) + GPT-5.6 → เขียน email, คิดโค้ด, ตรวจ grammar, ทุกอย่าง + +หลัง: Multi-Model → Smart Routing + Frontier Model → เฉพาะ complex reasoning + judgment + Cost-effective Model → routine tasks, boilerplate, test + Open-source Speculative Decoding → faster token gen +``` + +**DeepSeek DSpark** แก้ที่ layer ของ **token generation** — ทำให้ inference ถูกและเร็วขึ้นโดยไม่เปลี่ยน output + +**Devin Fusion** แก้ที่ layer ของ **task orchestration** — ทำให้ task ถูก execute ด้วย model ที่เหมาะสมที่สุด + +ทั้งสอง = อนาคตที่ AI deployment ไม่ bottleneck ที่ cost อีกต่อไป + +> "The age of using one model for all of your work is coming to an end." — Cognition Team + +--- + +**ที่มา:** +- Carl Franzen, "DeepSeek open sources DSpark" — VentureBeat, June 29, 2026 +- Cognition Team, "Devin Fusion: Frontier Performance at 35% Lower Cost" — cognition.com, June 29, 2026 +- DeepSeek, "DSpark: Confidence-Scheduled Speculative Decoding" — GitHub/deepseek-ai/DeepSpec diff --git a/src/data/schema.ts b/src/data/schema.ts new file mode 100644 index 0000000..4b6d3cc --- /dev/null +++ b/src/data/schema.ts @@ -0,0 +1,29 @@ +export const organizationId = 'https://moreminimore.com/#organization'; + +export function webPageSchema(path: string, name: string, description: string) { + const url = new URL(path, 'https://moreminimore.com').toString(); + return { + '@context': 'https://schema.org', + '@type': 'WebPage', + '@id': `${url}#webpage`, + url, + name, + description, + isPartOf: { '@id': 'https://moreminimore.com/#website' }, + about: { '@id': organizationId }, + inLanguage: 'th-TH', + }; +} + +export function breadcrumbSchema(items: { name: string; path: string }[]) { + return { + '@context': 'https://schema.org', + '@type': 'BreadcrumbList', + itemListElement: items.map((item, index) => ({ + '@type': 'ListItem', + position: index + 1, + name: item.name, + item: new URL(item.path, 'https://moreminimore.com').toString(), + })), + }; +} diff --git a/src/data/servicePages.ts b/src/data/servicePages.ts new file mode 100644 index 0000000..ff4f2c0 --- /dev/null +++ b/src/data/servicePages.ts @@ -0,0 +1,129 @@ +export type ServicePage = { + slug: string; + label: string; + name: string; + title: string; + lead: string; + pains: string[]; + urgencyTitle: string; + urgency: string; + approach: string[]; + outcomes: string[]; + faqs: { question: string; answer: string }[]; + packages?: { + name: string; + suit: string; + features: string[]; + price: string; + hosting: string; + }[]; + caseStudy?: boolean; +}; + +export const servicePages: ServicePage[] = [ + { + slug: 'website-development', + label: 'พัฒนาเว็บไซต์', + name: 'Website Development', + title: 'เว็บไซต์ที่ช่วยหาลีด และทำให้คนจำแบรนด์ได้', + lead: 'สำหรับธุรกิจที่มีเว็บแล้วแต่ลูกค้ายังไม่ค่อยติดต่อ หรืออยากเริ่มเว็บใหม่ที่วัดผลได้ตั้งแต่วันแรก พร้อมวางพื้นฐาน SEO และ GEO ให้เติบโตต่อได้', + pains: [ + 'มีเว็บไซต์ แต่คนเข้าเว็บน้อยหรือแทบไม่มี', + 'มีคนเข้าเว็บ แต่ยังไม่มีคนติดต่อหรือขอใบเสนอราคา', + 'เว็บไซต์ไม่มีระบบเก็บสถิติ จึงไม่รู้ว่าลูกค้าสนใจอะไร', + ], + urgencyTitle: 'ทุกวันที่เว็บยังไม่พร้อม คือโอกาสที่ส่งต่อให้คู่แข่ง', + urgency: 'เว็บที่หาไม่เจอ สื่อสารไม่ชัด หรือวัดผลไม่ได้ ทำให้คุณเสียทั้งคนที่กำลังค้นหาและข้อมูลสำคัญสำหรับตัดสินใจ เราจึงออกแบบเว็บจากเป้าหมายธุรกิจ ไม่ได้เริ่มจากความสวยเพียงอย่างเดียว', + approach: [ + 'ศึกษาสินค้า กลุ่มเป้าหมาย และเหตุผลที่ลูกค้าตัดสินใจก่อนเริ่มออกแบบ', + 'ติดตั้งระบบเก็บสถิติ โดยข้อมูลยังเป็นของธุรกิจคุณ', + 'วางโครงสร้างให้ทีมแก้ไขเนื้อหาได้ง่าย ไม่ต้องพึ่งนักพัฒนาทุกครั้ง', + 'แนะนำ Content Strategy เบื้องต้น เพื่อให้เว็บมีบทบาทต่อยอดขายหลังเปิดใช้งาน', + ], + outcomes: [ + 'เว็บไซต์ที่มีเป้าหมายชัด ทั้งการหาลีดและสร้างการจดจำแบรนด์', + 'Responsive พร้อมพื้นฐาน SEO และ GEO', + 'ระบบหลังบ้านหรือวิธีแก้เนื้อหาที่เหมาะกับทีมของคุณ', + 'แนวทางการตลาดระยะสั้นสำหรับทำต่อหลังเว็บไซต์เปิดใช้งาน', + ], + packages: [ + { + name: 'Astro Website', + suit: 'เร็ว โหลดไว เหมาะกับเว็บแนะนำธุรกิจ Landing Page และเว็บแสดงข้อมูล', + features: ['Static Site ประสิทธิภาพสูง', 'วางพื้นฐาน SEO ตั้งแต่โครงสร้าง', 'แก้เนื้อหาผ่าน Chatbot ได้', 'เหมาะกับเว็บที่ไม่ซับซ้อน'], + price: 'เว็บไซต์เริ่มต้น 0 บาท', + hosting: 'Hosting 5,000 บาท/ปี', + }, + { + name: 'WordPress Website', + suit: 'ยืดหยุ่นและเชื่อมต่อระบบได้ เหมาะกับ E-commerce ระบบสมาชิก และ ERP', + features: ['มีระบบหลังบ้าน', 'รองรับผู้ใช้หลายระดับ', 'ต่อยอดด้วย Plugin และ API', 'เชื่อม Automation และ AI ได้'], + price: 'เว็บไซต์เริ่มต้น 30,000 บาท', + hosting: 'Hosting เริ่มต้น 10,000 บาท/ปี — ฟรีปีแรก', + }, + ], + faqs: [ + { question: 'Astro กับ WordPress เลือกอะไรดี?', answer: 'ถ้าเป็นเว็บแนะนำธุรกิจหรือ Landing Page ที่เน้นความเร็ว Astro มักเหมาะกว่า ส่วน WordPress เหมาะกับเว็บที่ต้องมี E-commerce ระบบสมาชิก หรือเชื่อมระบบหลังบ้าน เราจะช่วยเลือกจากโจทย์จริง ไม่ยัดระบบที่เกินความจำเป็น' }, + { question: 'ทำเว็บใช้เวลากี่วัน?', answer: 'Astro โดยทั่วไปใช้เวลา 14–30 วัน ส่วน WordPress ประมาณ 30–60 วันหรือมากกว่า ขึ้นอยู่กับจำนวนหน้า ฟีเจอร์ และความพร้อมของข้อมูล ระยะเวลาจริงจะระบุใน Proposal ก่อนเริ่มงาน' }, + { question: 'เว็บที่ทำจะติดหน้าแรก Google ไหม?', answer: 'เราวาง SEO พื้นฐานให้ครบทั้งโครงสร้าง Meta Tags ความเร็ว และ Responsive แต่ตำแหน่งบน Google ยังขึ้นกับการแข่งขันของ Keyword และคุณภาพเนื้อหา จึงไม่ควรรับประกันอันดับแบบตายตัว' }, + { question: 'มีเว็บเดิมอยู่แล้ว ย้ายมาได้ไหม?', answer: 'ได้ เราจะวิเคราะห์สิ่งที่เว็บเดิมทำได้ดี เก็บส่วนที่มีคุณค่า และวาง Redirect เพื่อรักษาคนเข้าเว็บและอันดับเดิมให้มากที่สุด' }, + ], + }, + { + slug: 'marketing-consult', + label: 'ที่ปรึกษาการตลาดออนไลน์', + name: 'Marketing Consult', + title: 'วางกลยุทธ์การตลาดจากข้อมูล ไม่ใช่ความรู้สึก', + lead: 'สำหรับธุรกิจที่ยิงโฆษณาอยู่ แต่ยอดขายยังไม่คุ้ม มีคนติดต่อเข้ามาแต่ไม่ใช่ลูกค้าที่ใช่ หรือยังตอบไม่ได้ว่าเงินที่ลงทุนไปสร้างผลลัพธ์จริงแค่ไหน', + pains: ['ยิงโฆษณาแล้วมีคนคลิก แต่ยอดขายยังไม่คุ้ม', 'มีคนติดต่อเข้ามา แต่ทีมขายปิดการขายไม่ได้', 'เพิ่มงบโฆษณาแล้ว แต่ยอดขายไม่โตตาม'], + urgencyTitle: 'ถ้ายังวัดผลไม่ชัด งบโฆษณาจะรั่วต่อไปเรื่อย ๆ', + urgency: 'ปัญหาอาจไม่ได้อยู่ที่ตัวโฆษณา แต่อยู่ที่การเก็บข้อมูล กลุ่มเป้าหมาย หรือการตีความตัวเลข เราช่วยเชื่อมข้อมูลตั้งแต่การมองเห็น การคลิก ไปจนถึงยอดขาย เพื่อหาจุดที่ควรแก้ก่อนเพิ่มงบ', + approach: ['วางระบบเก็บข้อมูลจากเว็บไซต์ โฆษณา และยอดขาย', 'ใช้ข้อมูลสถิติเป็นฐานในการตั้งสมมติฐานและปรับกลยุทธ์', 'แยกกลยุทธ์ตามช่องทางและกลุ่มลูกค้า แทนการใช้สูตรเดียวกับทุกคน'], + outcomes: ['เห็นชัดขึ้นว่างบส่วนใดสร้างยอดขาย', 'ได้คลิกและลีดที่ตรงกลุ่มเป้าหมายมากขึ้น', 'ลดต้นทุนต่อยอดขายจากการตัดงบที่ไม่คุ้ม'], + caseStudy: true, + faqs: [ + { question: 'ต้องมีข้อมูลอะไรบ้างก่อนเริ่ม?', answer: 'ถ้ามี Google Analytics, Ads Dashboard หรือข้อมูลยอดขายอยู่แล้วจะช่วยให้เริ่มวิเคราะห์ได้เร็วขึ้น แต่ถ้ายังไม่มี เราช่วยวางระบบเก็บข้อมูลให้ตั้งแต่ต้นได้' }, + { question: 'ใช้เวลากี่เดือนถึงเห็นผล?', answer: 'โดยทั่วไป 1–3 เดือนจะเริ่มเห็นแนวโน้มชัดขึ้น ทั้งนี้ขึ้นกับปริมาณข้อมูลและความซับซ้อนของตลาด เราจะติดตามและอัปเดตความคืบหน้าเป็นระยะ' }, + { question: 'ถ้างบน้อย ควรเริ่มจากตรงไหน?', answer: 'เริ่มจากตรวจข้อมูลที่มี แล้วเลือกจุดที่คาดว่าจะสร้างผลกระทบมากที่สุดโดยใช้งบน้อยที่สุดก่อน' }, + { question: 'ต้องเซ็นสัญญารายปีไหม?', answer: 'ไม่ต้อง คุณสามารถเริ่มจาก 1 เดือนได้ เพื่อให้ประเมินวิธีทำงานและผลลัพธ์ก่อนตัดสินใจทำต่อ' }, + ], + }, + { + slug: 'automation-workflow', + label: 'วางระบบอัตโนมัติ', + name: 'Automation Workflow', + title: 'ลดงานซ้ำ ลดความผิดพลาด คืนเวลาให้ทีม', + lead: 'สำหรับธุรกิจที่ยังใช้แรงงานคนกับงานเดิม ๆ ต้องค้นข้อมูลจากหลายที่ หรือเสียเวลาแก้ข้อผิดพลาดจากการคีย์ข้อมูลด้วยมือ', + pains: ['พนักงานสร้างเอกสารหรือคัดลอกข้อมูลแบบเดิมซ้ำทุกวัน', 'ข้อมูลกระจัดกระจายหลายโฟลเดอร์ ทำให้ค้นหานาน', 'รายงานผิดพลาดเพราะต้องคีย์และรวมข้อมูลด้วยมือ'], + urgencyTitle: 'งานซ้ำที่ปล่อยไว้นาน กลายเป็นต้นทุนที่มองไม่เห็น', + urgency: 'ทุกนาทีที่ทีมใช้กับงานซ้ำ คือเวลาที่หายไปจากงานซึ่งสร้างมูลค่า ระบบที่เหมาะสมจะช่วยลดเวลาทำงาน ลด OT และลดข้อผิดพลาด โดยไม่บังคับให้ทีมเปลี่ยนวิธีทำงานทั้งหมดในวันเดียว', + approach: ['แทนงานที่ทำซ้ำด้วยระบบอัตโนมัติเท่าที่เหมาะสม', 'เลือกเครื่องมือจากความต้องการและพฤติกรรมขององค์กร', 'เชื่อมระบบเข้ากับวิธีทำงานเดิม เพื่อให้ทีมเริ่มใช้ได้ง่าย'], + outcomes: ['ใช้เวลาทำงานน้อยลงและได้ผลลัพธ์เร็วขึ้น', 'ทีมมีเวลาไปทำงานที่ต้องใช้ความคิดและการตัดสินใจ', 'ลดต้นทุนระยะยาว เพราะพนักงานหนึ่งคนดูแลงานได้มากขึ้น'], + faqs: [ + { question: 'Automation จะมาแทนที่คนไหม?', answer: 'เราเน้นแทนเฉพาะงานซ้ำและงานที่เสี่ยงผิดพลาด เพื่อให้คนมีเวลาไปทำงานที่ต้องใช้ความคิดสร้างสรรค์ การสื่อสาร และการตัดสินใจ' }, + { question: 'ถ้ากระบวนการทำงานยังไม่ชัด จะเริ่มได้ไหม?', answer: 'ได้ เราจะคุยและสังเกตการทำงานจริง แล้วช่วยจัดลำดับขั้นตอนก่อนเลือกเทคโนโลยี' }, + { question: 'ใช้เครื่องมืออะไร และแพงไหม?', answer: 'ขึ้นกับงาน อาจใช้ n8n, Make, Google Apps Script หรือระบบที่พัฒนาเฉพาะ หากเครื่องมือ Open Source ตอบโจทย์ ค่าใช้จ่ายเพิ่มเติมมักต่ำ' }, + { question: 'ประโยชน์จะเห็นชัดแบบไหน?', answer: 'ตัวอย่างเช่น งานรายงานที่เคยใช้ 30 นาทีอาจเหลือเพียงไม่กี่นาที เวลาที่ได้คืนช่วยลด OT และให้ทีมไปทำงานที่สร้างรายได้มากกว่าเดิม' }, + ], + }, + { + slug: 'ai-consult', + label: 'ที่ปรึกษาด้าน AI', + name: 'AI Consult', + title: 'เปลี่ยนความรู้ในทีม ให้กลายเป็นระบบขององค์กร', + lead: 'สำหรับธุรกิจที่อยากใช้ AI ให้เกิดประโยชน์จริง ลดการพึ่งพาความรู้ของพนักงานคนใดคนหนึ่ง และช่วยให้ทีมเข้าถึงข้อมูลได้เร็วขึ้น', + pains: ['พนักงานลาออกแล้วข้อมูลลูกค้าหรือความรู้สำคัญหายตามไป', 'ข้อมูลในรายงานตรวจสอบย้อนกลับได้ยาก', 'ทีมทำงานช้า เพราะต้องค้นหาและรวบรวมข้อมูลใหม่ทุกครั้ง'], + urgencyTitle: 'ถ้าความรู้อยู่กับคนเพียงอย่างเดียว ธุรกิจต้องเริ่มใหม่ทุกครั้งที่ทีมเปลี่ยน', + urgency: 'AI ที่เชื่อมกับฐานความรู้ขององค์กรช่วยให้ทีมค้นข้อมูล ตรวจสอบข้อเท็จจริง และทำงานได้เร็วขึ้น โดยออกแบบให้เข้ากับเครื่องมือที่พนักงานใช้อยู่แล้ว', + approach: ['วาง AI ให้เป็นส่วนหนึ่งของขั้นตอนทำงาน ไม่ใช่เครื่องมือแยกเดี่ยว', 'ออกแบบให้ทีมเริ่มใช้ได้โดยไม่ต้องเปลี่ยนพฤติกรรมมาก', 'เก็บและย้ายข้อมูลได้ง่าย พร้อมเลือกแนวทางความปลอดภัยตามระดับความลับ'], + outcomes: ['เก็บความรู้ของพนักงานเป็นองค์ความรู้ขององค์กร', 'ทีมทำงานเร็วขึ้นและผิดพลาดน้อยลง', 'ระบบเรียนรู้จากการใช้งานและตอบโจทย์แต่ละบทบาทมากขึ้น', 'ตรวจสอบข้อมูลได้ง่ายขึ้น ลดโอกาสที่รายงานจะคลาดเคลื่อน'], + faqs: [ + { question: 'AI จะมาแทนที่พนักงานไหม?', answer: 'เป้าหมายคือให้ AI เป็นผู้ช่วย ช่วยลดงานซ้ำ ค้นข้อมูล และตรวจสอบข้อผิดพลาด เพื่อให้พนักงานทำงานที่ต้องใช้มนุษย์ได้ดีขึ้น' }, + { question: 'ข้อมูลบริษัทจะปลอดภัยไหม?', answer: 'เราจะเลือกผู้ให้บริการที่มีนโยบายไม่ใช้ข้อมูลของคุณฝึกโมเดล และสามารถเก็บองค์ความรู้ไว้บนระบบของลูกค้าได้ หากข้อมูลมีความลับสูง อาจพิจารณา Local LLM ซึ่งต้องลงทุนโครงสร้างพื้นฐานเพิ่ม' }, + { question: 'ธุรกิจเล็กใช้ AI คุ้มไหม?', answer: 'คุ้มเมื่อเริ่มจากปัญหาที่ชัดและวัดผลได้ เช่น ลดเวลาทำเอกสารหรือค้นข้อมูล เราจะไม่แนะนำระบบใหญ่เกินความจำเป็น' }, + { question: 'ต้องมีความรู้ AI มาก่อนไหม?', answer: 'ไม่จำเป็น เราสามารถออกแบบให้ทีมใช้ผ่านเครื่องมือที่คุ้นเคย เช่น LINE โดยประสบการณ์ใช้งานใกล้เคียงกับการคุยกับผู้ช่วยคนหนึ่ง' }, + ], + }, +]; + +export const serviceBySlug = Object.fromEntries(servicePages.map((service) => [service.slug, service])); diff --git a/src/data/staticPages.ts b/src/data/staticPages.ts new file mode 100644 index 0000000..708a038 --- /dev/null +++ b/src/data/staticPages.ts @@ -0,0 +1,32 @@ +export const workProcess = [ + ['เข้าใจธุรกิจ', 'ทำความรู้จักสินค้า กลุ่มเป้าหมาย พฤติกรรมองค์กรของลูกค้า และปัญหาที่เจอจริง'], + ['ดูข้อมูล', 'รวบรวมข้อมูลทางสถิติ ถ้าลูกค้ามีข้อมูลน้อย หรือ ยังไม่มีจะช่วยวางระบบเก็บข้อมูลให้ด้วย'], + ['เลือกทางที่คุ้ม', 'สรุปกลยุทธ์สำหรับลูกค้าทั้งในระยะสั้น และระยะยาว พร้อมทั้งแนวทางการปรับกลยุทธ์เบื้องต้น'], + ['ลงมือและวัดผล', 'การติดตามผลหลังจากปฏิบัติตามกลยุทธ์ และปรับกลยุทธ์ตามสถานการณ์ที่เปลี่ยนไป'], +] as const; + +export const hubServices = [ + { name: 'Website Development', slug: 'website-development', badge: 'พัฒนาเว็บไซต์', objective: 'ทำเว็บให้ขายได้', headline: 'เว็บไชต์ที่ช่วยหาลีดหรือช่วยให้จำแบรนด์', detail: 'เริ่มจากถามว่าเว็บนี้ทำเพื่ออะไร ขายของ เก็บ lead หรือสร้างความน่าเชื่อถือ แล้วออกแบบหน้าและข้อความให้ทำหน้าที่นั้นได้จริง' }, + { name: 'Marketing Consult', slug: 'marketing-consult', badge: 'ที่ปรึกษาการตลาดออนไลน์', objective: 'เพิ่มยอดขาย', headline: 'วางกลยุทธ์การตลาดจากสถิติ', detail: 'ดูสถิติคนเข้าเว็บ คนคลิกโฆษณา ยอดขาย และกลุ่มลูกค้าก่อน แล้วค่อยเลือกช่องทาง ข้อความ และงบประมาณที่ควรใช้' }, + { name: 'Automation Workflow', slug: 'automation-workflow', badge: 'วางระบบอัตโนมัติ', objective: 'ลดต้นทุนและเวลา', headline: 'วางระบบงานให้มีความรวดเร็วและลดความผิดพลาด', detail: 'เริ่มจากดู workflow ปัจจุบันว่าอะไรซ้ำ ทำมือ หรือเสียเวลา แล้วเลือกว่าจะใช้ n8n ระบบเฉพาะทาง หรือ AI ช่วยในจุดไหน' }, + { name: 'AI Consult', slug: 'ai-consult', badge: 'ที่ปรึกษาด้าน AI', objective: 'รักษาความรู้ขององค์กร', headline: 'ระบบงานที่ผสาน AI เป็นส่วนหนึ่งในระบบ', detail: 'เลือกโมเดลและวิธีใช้ให้เหมาะกับงานจริง โดยเฉพาะงานที่ต้องรักษาความรู้และความลับขององค์กร' }, +] as const; + +export const generalFaqs = [ + ['บริการ', 'MoreminiMore ทำอะไรบ้าง?', 'เราช่วยเรื่องเว็บไซต์ การตลาดออนไลน์ ระบบอัตโนมัติ และ AI สำหรับ SME โดยเริ่มจากดูโจทย์และข้อมูลจริงก่อนเลือกวิธีทำ'], + ['บริการ', 'ถ้ามีงบน้อย ควรเริ่มจากอะไร?', 'ควรเริ่มจากปัญหาที่กระทบยอดขายหรือต้นทุนมากที่สุด ถ้ายังไม่มีเว็บให้เริ่มจากฐานข้อมูลและหน้าเว็บ ถ้ามีเว็บแล้วให้ดูคุณภาพ lead และข้อมูลแอดก่อน'], + ['ราคา', 'ราคาเริ่มต้นเท่าไหร่?', 'เว็บ Astro เริ่ม 5,000 บาท, WordPress เริ่ม 30,000 บาท, งานที่ปรึกษาเริ่มตามขอบเขตจริง เราจะบอกช่วงราคาหลังเข้าใจโจทย์มากพอ'], + ['ระยะเวลา', 'ทำเว็บใช้เวลานานแค่ไหน?', 'เว็บ Astro ส่วนใหญ่ใช้เวลาประมาณ 14-30 วัน ส่วน WordPress หรือระบบที่ซับซ้อนกว่านั้นใช้เวลามากขึ้นตามขอบเขตงาน'], + ['วิธีทำงาน', 'ต้องรู้ก่อนไหมว่าจะใช้บริการไหน?', 'ไม่จำเป็น เล่าโจทย์หรืออาการที่เจอก่อนก็ได้ แล้วเราจะช่วยดูว่าควรเริ่มจากบริการไหน'], + ['หลังส่งมอบ', 'มีประกันงานไหม?', 'มี — ถ้าเว็บมีปัญหาจากการพัฒนาของเรา จะแก้ไขให้ฟรี 30-90 วันหลังส่งมอบ (ขึ้นอยู่กับประเภทงาน) ถ้าเกิดจากการแก้ไขของลูกค้าเอง จะคิดค่าแก้ตามจริง'], + ['บริการ', 'เหมาะกับธุรกิจแบบไหน?', 'SME ที่ต้องการคนช่วยดูเรื่อง Online Marketing และระบบ IT ลูกค้าที่ไม่มีความรู้ IT มากแต่จำเป็นต้องใช้ระบบ IT ในการทำงาน ไม่เหมาะกับ: องค์กรขนาดใหญ่ที่มีการเมืองเยอะ คนที่อยากได้ของฟรีหรือจ่ายน้อยแต่คาดหวังงานระดับแสน'], + ['ราคา', 'มีแพ็คเกจไหม?', 'ไม่มีแพ็คเกจตายตัว เพราะงานแต่ละชิ้นต่างกัน เราจะส่ง Proposal เป็น PDF พร้อมรายละเอียดงาน ราคา ระยะเวลา ให้คุณอ่านก่อนเซ็น แก้ไข scope ได้'], + ['ราคา', 'จ่ายยังไง?', 'เว็บไซต์: มัดจำ 50% ตอนเซ็น ที่เหลือจ่ายตอนส่งมอบ · ที่ปรึกษารายเดือน: จ่ายต้นเดือน เริ่มจ่ายเดือนแรกตอนเซ็นสัญญา ออกใบกำกับภาษีได้'], + ['ราคา', 'มีค่าใช้จ่ายแอบแฝงไหม?', 'ไม่มี — เราจะบอกทุกค่าใช้จ่ายใน Proposal ตั้งแต่ต้น ไม่มี "อันนี้เพิ่มเงินนะ" ตอนใกล้ส่งมอบ ถ้ามีงานเพิ่มจริง ๆ จะคุยและตกลงราคาก่อนทำ'], + ['ระยะเวลา', 'SEO ใช้เวลาเห็นผลกี่เดือน?', 'SEO ปกติใช้เวลา 3-6 เดือนถึงจะเห็นผลชัด ขึ้นอยู่กับคีย์เวิร์ดและคู่แข่ง เราเคยมีเคสติดหน้า 1 Google ใน 4 เดือน แต่ก็มีเคสที่ใช้เวลานานกว่านั้น — จะบอกตรง ๆ ตั้งแต่แรก'], + ['ระยะเวลา', 'งานด่วน ทำได้ไหม?', 'ทำได้ แต่จะคิดราคาเร่งด่วนเพิ่ม เพราะกระทบกับงานอื่นที่มีอยู่ เราจะบอกชัดว่าค่าเร่งเท่าไหร่ก่อนรับงาน'], + ['AI', 'AI ใช้ของแพงหรือของถูก?', 'เราเลือกตามงาน ไม่ใช่เลือกของแพงสุด งาน 80% ใช้ Model ราคาถูก (GPT-4o-mini, Haiku, Local LLM) ก็ได้ผล ส่วนงานที่ซับซ้อนมากค่อยใช้ของแพง วิธีนี้ช่วยลูกค้าประหยัดค่าใช้จ่ายได้มากกว่าครึ่ง'], + ['AI', 'ข้อมูลปลอดภัยไหม (PDPA)?', 'ปลอดภัย — สำหรับงานที่ต้องการความลับ เราใช้ Local LLM ที่รันในเครื่องของลูกค้า ข้อมูลไม่ออกไปไหน ส่วนงานทั่วไปใช้ API ของผู้ให้บริการที่เชื่อถือได้ พร้อมทำ NDA ได้'], + ['หลังส่งมอบ', 'ติดต่อช่องทางไหน?', 'LINE OA: @moreminimore (ตอบเร็วที่สุด) · โทร: 080-995-5945 · Email: contact@moreminimore.com คนที่ตอบคือคนที่ทำงานให้คุณ ไม่ใช่ Bot'], + ['หลังส่งมอบ', 'มีแพ็คเกจดูแลรายเดือนไหม?', 'มี เริ่ม 2,000 บาท/เดือน รวมอัปเดตเนื้อหา ปรับ SEO แก้บั๊ก ตอบคำถามผ่าน LINE ถ้าไม่เอาแพ็คเกจ ก็จ่ายเป็นงาน ๆ ไป แล้วแต่ความเหมาะ'], +] as const; diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index f549b4a..32dfc06 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -1,20 +1,175 @@ --- import '../styles/global.css'; -interface Props { title?: string; description?: string; } -const { title = 'MoreminiMore | เพิ่มกำไรให้ลูกค้า', description = 'พาร์ทเนอร์สำหรับ SME ที่อยากตัดสินใจให้คุ้มขึ้น ด้วยเว็บไซต์ การตลาด AI และระบบอัตโนมัติที่ยึดข้อมูลจริง' } = Astro.props; +import '../styles/refresh.css'; +import '../styles/site-footer.css'; +interface Props { + title?: string; + description?: string; + ogType?: 'website' | 'article'; + image?: string; + imageAlt?: string; + publishedTime?: string; + updatedTime?: string; + structuredData?: Record | Record[]; + robots?: string; +} +const { + title = 'MoreminiMore | เพิ่มกำไรให้ลูกค้า', + description = 'พาร์ทเนอร์สำหรับ SME ที่อยากตัดสินใจให้คุ้มขึ้น ด้วยเว็บไซต์ การตลาด AI และระบบอัตโนมัติที่ยึดข้อมูลจริง', + ogType = 'website', + image = '/og.png', + imageAlt = 'MoreminiMore — เป้าหมายของเราคือเพิ่มกำไรให้ลูกค้า', + publishedTime, + updatedTime, + structuredData, + robots = 'index, follow, max-image-preview:large', +} = Astro.props; const canonicalURL = new URL(Astro.url.pathname, 'https://moreminimore.com'); -const socialImageURL = new URL('/og.png', 'https://moreminimore.com'); -const organization = { '@context': 'https://schema.org', '@type': 'ProfessionalService', name: 'MoreminiMore', url: 'https://moreminimore.com', description, telephone: '+66809955945', email: 'contact@moreminimore.com', sameAs: ['https://line.me/R/ti/p/@moreminimore'], areaServed: { '@type': 'Country', name: 'Thailand' }, serviceType: ['Website Development', 'Marketing Consult', 'Automation Workflow', 'AI Consult'] }; +const socialImageURL = new URL(image, 'https://moreminimore.com'); +const organization = { '@context': 'https://schema.org', '@type': 'Organization', '@id': 'https://moreminimore.com/#organization', name: 'MoreminiMore', url: 'https://moreminimore.com/', description: 'ที่ปรึกษาด้านเว็บไซต์ การตลาดออนไลน์ AI และระบบอัตโนมัติสำหรับ SME', telephone: '+66809955945', email: 'contact@moreminimore.com', sameAs: ['https://line.me/R/ti/p/@moreminimore'] }; +const website = { '@context': 'https://schema.org', '@type': 'WebSite', '@id': 'https://moreminimore.com/#website', url: 'https://moreminimore.com/', name: 'MoreminiMore', publisher: { '@id': 'https://moreminimore.com/#organization' }, inLanguage: 'th-TH' }; +const isHome = Astro.url.pathname === '/'; +const enableTracking = import.meta.env.PROD; --- - + - - {title} + + {publishedTime && }{updatedTime && } + + {title}{isHome && }{structuredData && } + {enableTracking && + + + + + + } - + + + diff --git a/src/pages/404.astro b/src/pages/404.astro new file mode 100644 index 0000000..406c0bd --- /dev/null +++ b/src/pages/404.astro @@ -0,0 +1,11 @@ +--- +import BaseLayout from '../layouts/BaseLayout.astro'; +import SiteHeader from '../components/SiteHeader.astro'; +import SiteFooter from '../components/SiteFooter.astro'; +import '../styles/static-pages.css'; +--- + + +

404

ไม่พบหน้านี้

หน้าที่คุณค้นหาอาจถูกย้าย เปลี่ยนชื่อ หรือไม่มีอยู่ กรุณาตรวจสอบ URL หรือกลับไปหน้าแรก

ไม่แน่ใจว่าควรเริ่มตรงไหน กลับไปหน้าแรกเพื่อดูข้อมูลเพิ่มเติม

กลับหน้าแรก
+ +
diff --git a/src/pages/about.astro b/src/pages/about.astro new file mode 100644 index 0000000..da1f72f --- /dev/null +++ b/src/pages/about.astro @@ -0,0 +1,14 @@ +--- +import StaticPage from '../components/StaticPage.astro'; +import { workProcess } from '../data/staticPages'; +import { breadcrumbSchema, webPageSchema } from '../data/schema'; + +const description = 'รู้จัก MoreminiMore ที่ปรึกษาเว็บไซต์ การตลาด และ AI สำหรับ SME ที่เริ่มจากข้อมูลจริงก่อนเลือกสิ่งที่ควรทำ'; +const schema = [webPageSchema('/about/', 'เกี่ยวกับเรา | MoreminiMore', description), breadcrumbSchema([{ name: 'หน้าหลัก', path: '/' }, { name: 'เกี่ยวกับเรา', path: '/about/' }])]; +--- + +
ทำไมถึงมีเรา

ธุรกิจไม่ควรเสียต้นทุนกับการ “ตาม” คนอื่นจน “ลืม” ตัวเอง

เราเห็น SME หลายรายจ่ายเงินกับเว็บที่สวยแต่ไม่มีคนทัก โฆษณาที่มีคนคลิกแต่ไม่เกิดยอดขาย หรือเครื่องมือ AI ที่ดูน่าตื่นเต้นแต่ไม่เข้ากับงานจริง สุดท้ายคือเสียเงินเปล่าเพราะ “ตาม” สิ่งที่คนอื่นขาย โดย “ลืม” ว่าธุรกิจตัวเองต้องการอะไรกันแน่

นโยบาย

เป้าหมายของเราคือการเพิ่มกำไรให้ลูกค้า

ถ้าลูกค้ามีกำไรมากขึ้น ลูกค้าก็อยู่ต่อได้ และเราก็ทำงานต่อได้ด้วย ทุกงานจึงต้องตอบให้ได้ว่าช่วยเพิ่มยอดขาย ลดต้นทุน ประหยัดเวลา หรือช่วยตัดสินใจได้ดีขึ้นอย่างไร

+

วิธีการทำงาน

กลยุทธ์และระบบงานของเรา สำหรับลูกค้าแต่ละรายจะไม่เหมือนกัน

    {workProcess.map(([title, copy], index) =>
  1. 0{index + 1}

    {title}

    {copy}

  2. )}
+

เทคโนโลยีและแอป

เราเลือกเทคโนโลยีและแอปให้เหมาะสมกับงาน

01

Data Collection & Analytics

รวบรวมและวิเคราะห์ข้อมูลจากทุกช่องทาง — Google Analytics, Search Console, Meta Ads, LINE OA และอื่น ๆ เพื่อให้ทุกการตัดสินใจมีตัวเลขรองรับ ไม่ใช่แค่ความรู้สึก

02

Development Stack

Astro, WordPress, Python, PHP, Node.js — เราเลือก stack จากโจทย์จริง ไม่ใช่จากเทรนด์ ทุกเทคโนโลยีต้องตอบโจทย์ธุรกิจคุณ

03

Automation & AI Integration

n8n, Make, OpenAI API, Local LLM — เชื่อมต่อระบบ ลดงานซ้ำ และใช้ AI ในจุดที่สร้างผลลัพธ์จริง ไม่ใช่แค่เพิ่มเครื่องมือ

+

ส่งโจทย์ของคุณมาให้เราดู ไม่ต้องเตรียม brief สมบูรณ์ แค่เล่าว่าตอนนี้ธุรกิจติดตรงไหน

ลองคุยกัน 15 นาที ↗
+
diff --git a/src/pages/blog/[slug].astro b/src/pages/blog/[slug].astro new file mode 100644 index 0000000..342d54d --- /dev/null +++ b/src/pages/blog/[slug].astro @@ -0,0 +1,70 @@ +--- +import { getCollection, render, type CollectionEntry } from 'astro:content'; +import BaseLayout from '../../layouts/BaseLayout.astro'; +import BlogHeader from '../../components/BlogHeader.astro'; +import SiteFooter from '../../components/SiteFooter.astro'; +import '../../styles/blog.css'; + +export async function getStaticPaths() { + const posts = await getCollection('blog', ({ data }) => !data.draft); + return posts.map((post) => ({ params: { slug: post.id }, props: { post } })); +} + +interface Props { post: CollectionEntry<'blog'> } +const { post } = Astro.props; +const { Content } = await render(post); +const canonical = new URL(`/blog/${post.id}/`, 'https://moreminimore.com').toString(); +const image = new URL(post.data.heroImage, 'https://moreminimore.com').toString(); +const formatDate = (date: Date) => new Intl.DateTimeFormat('th-TH', { day: 'numeric', month: 'long', year: 'numeric' }).format(date); +const structuredData = [ + { + '@context': 'https://schema.org', + '@type': 'BlogPosting', + '@id': `${canonical}#blogposting`, + headline: post.data.title, + description: post.data.description, + image, + datePublished: post.data.pubDate.toISOString(), + ...(post.data.updatedDate ? { dateModified: post.data.updatedDate.toISOString() } : {}), + mainEntityOfPage: canonical, + publisher: { '@id': 'https://moreminimore.com/#organization' }, + }, + { + '@context': 'https://schema.org', + '@type': 'BreadcrumbList', + '@id': `${canonical}#breadcrumb`, + itemListElement: [ + { '@type': 'ListItem', position: 1, name: 'หน้าหลัก', item: 'https://moreminimore.com/' }, + { '@type': 'ListItem', position: 2, name: 'บทความ', item: 'https://moreminimore.com/blog/' }, + { '@type': 'ListItem', position: 3, name: post.data.title, item: canonical }, + ], + }, +]; +--- + + + +
+ +
+ +
diff --git a/src/pages/blog/index.astro b/src/pages/blog/index.astro new file mode 100644 index 0000000..147d0cd --- /dev/null +++ b/src/pages/blog/index.astro @@ -0,0 +1,34 @@ +--- +import { getCollection } from 'astro:content'; +import BaseLayout from '../../layouts/BaseLayout.astro'; +import BlogHeader from '../../components/BlogHeader.astro'; +import SiteFooter from '../../components/SiteFooter.astro'; +import '../../styles/blog.css'; + +const posts = (await getCollection('blog', ({ data }) => !data.draft)) + .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()); +const formatDate = (date: Date) => new Intl.DateTimeFormat('th-TH', { day: 'numeric', month: 'long', year: 'numeric' }).format(date); +--- + + + +
+
+

INSIGHTS / BLOG

+

บทความสำหรับคนที่อยาก
ตัดสินใจให้ทันโลก

+

สรุปเรื่อง AI, SEO, การตลาด และระบบดิจิทัลให้เห็นทั้งโอกาส ความเสี่ยง และสิ่งที่นำไปทำต่อได้จริง

+
+
+ {posts.map((post) => )} +
+
+ +
diff --git a/src/pages/faq.astro b/src/pages/faq.astro new file mode 100644 index 0000000..dc6a589 --- /dev/null +++ b/src/pages/faq.astro @@ -0,0 +1,12 @@ +--- +import StaticPage from '../components/StaticPage.astro'; +import { generalFaqs } from '../data/staticPages'; +import { breadcrumbSchema, webPageSchema } from '../data/schema'; + +const description = 'คำถามที่พบบ่อยเกี่ยวกับบริการ เว็บไซต์ การตลาด ระบบอัตโนมัติ AI ราคา และวิธีทำงานของ MoreminiMore'; +const schema = [webPageSchema('/faq/', 'คำถามที่พบบ่อย | MoreminiMore', description), breadcrumbSchema([{ name: 'หน้าหลัก', path: '/' }, { name: 'คำถามที่พบบ่อย', path: '/faq/' }])]; +--- + +
{generalFaqs.map(([category, question, answer]) =>
{category}{question}

{answer}

)}
+

ถ้าคำถามของคุณไม่อยู่ในนี้ ส่งโจทย์มาให้เราดูได้

ส่งโจทย์ให้เราดู ↗
+
diff --git a/src/pages/index.astro b/src/pages/index.astro index b458e79..128fe80 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,5 +1,7 @@ --- import BaseLayout from '../layouts/BaseLayout.astro'; +import ContactForm from '../components/ContactForm.astro'; +import SiteFooter from '../components/SiteFooter.astro'; const services = [ ['01', 'Website Development', 'เว็บไซต์ที่ช่วยหาลีดหรือช่วยให้จำแบรนด์', 'เหมาะกับธุรกิจที่มีเว็บแล้วแต่ลูกค้ายังไม่ค่อยติดต่อ หรืออยากเริ่มเว็บใหม่ให้วัดผลได้ตั้งแต่วันแรก รวมถึงธุรกิจที่มีปัญหาเรื่อง SEO และ GEO', ['ศึกษาสินค้าและกลุ่มเป้าหมายก่อนเริ่มงาน เพื่อให้เว็บไซต์ตรงกับรูปแบบที่กลุ่มเป้าหมายต้องการมากที่สุด', 'มีระบบเก็บสถิติให้ โดยลูกค้าเป็นเจ้าของข้อมูลนั้นเอง', 'คำนึงถึงความง่ายในการปรับแต่ง ให้ลูกค้าสามารถแก้ไขเว็บไซต์ได้ด้วยตัวเองเป็นหลัก', 'ให้คำแนะนำแนวทาง content strategy เบื้องต้น เพราะเราคาดหวังให้ลูกค้าได้กำไรจากผลงาน']], ['02', 'Marketing Consult', 'วางกลยุทธ์การตลาดจากสถิติ', 'เหมาะกับธุรกิจที่ยิงโฆษณาอยู่ แต่ยอดขายไม่คุ้ม หรือมีคนทักมาแล้วไม่ใช่ลูกค้าที่ควรได้ รวมถึงธุรกิจที่ยิงโฆษณา โดยไม่รู้ว่าโฆษณาที่ลงทุนไปนั้นคุ้มค่าหรือยัง?', ['วางระบบเก็บข้อมูลทางสถิติสำหรับเว็บไซต์ โฆษณา และยอดขาย', 'การวางกลยุทธ์จะใช้ข้อมูลสถิติเป็นพื้นฐาน', 'แต่ละช่องทางและแต่ละกลุ่มลูกค้าจะมีกลยุทธ์แตกต่างกัน']], @@ -7,18 +9,55 @@ const services = [ ['04', 'AI Consult', 'ระบบงานที่ผสาน AI เป็นส่วนหนึ่งในระบบ', 'เหมาะกับธุรกิจที่อยากใช้ AI ให้เกิดประโยชน์สูงสุด และไม่ต้องการให้ความรู้อยู่กับพนักงานเท่านั้น', ['วางระบบ AI ให้เป็นส่วนหนึ่งของการทำงาน', 'พนักงานจะไม่ต้องปรับตัวมาก และกระตุ้นให้ใช้งานระบบที่สร้างขึ้นจนเป็นนิสัย', 'ข้อมูลจะถูกเก็บอย่างเป็นความลับ และง่ายต่อการเข้าถึงและโยกย้าย']], ] as const; const process = [['01', 'เข้าใจธุรกิจ', 'ทำความรู้จักสินค้า กลุ่มเป้าหมาย พฤติกรรมองค์กรของลูกค้า และปัญหาที่เจอจริง'], ['02', 'ดูข้อมูล', 'รวบรวมข้อมูลทางสถิติ ถ้าลูกค้ามีข้อมูลน้อย หรือยังไม่มีจะช่วยวางระบบเก็บข้อมูลให้ด้วย'], ['03', 'เลือกทางที่คุ้ม', 'สรุปกลยุทธ์สำหรับลูกค้าทั้งในระยะสั้น และระยะยาว พร้อมทั้งแนวทางการปรับกลยุทธ์เบื้องต้น'], ['04', 'ลงมือและวัดผล', 'การติดตามผลหลังจากปฏิบัติตามกลยุทธ์ และปรับกลยุทธ์ตามสถานการณ์ที่เปลี่ยนไป']]; -const portfolio = [['Marketing Consult', 'Dataroot', 'บริษัทให้บริการ ERP Software และการปรับแต่ง ERP Software สำหรับองค์กรขนาดใหญ่', 'https://erp.dataroot.asia/'], ['Website Development', 'Jet Industries', 'เว็บไซต์องค์กรผู้ผลิตพลาสติกฉีดขึ้นรูป ประสบการณ์กว่า 40 ปี', 'https://jetindustries.co.th/'], ['Marketing Consult · Website Development', 'Dealplustech', 'เว็บไซต์จัดจำหน่ายระบบน้ำและสุขภัณฑ์คุณภาพสูงสำหรับโรงงานและบ้านเรือน', 'https://www.dealplustech.co.th/'], ['Website Development', 'เลือดจระเข้วานิไทย', 'เว็บไซต์ผลิตภัณฑ์แคปซูลเลือดจระเข้เพื่อสุขภาพ', 'https://xn--22cl9aoj9cdnw9cvb0a1a6a8jh7e5d.com/'], ['Website Development', 'เทรนเนอร์ซันนี่', 'เว็บไซต์ personal brand และหลักสูตรอบรมสำหรับเทรนเนอร์ฟิตเนส', 'https://trainersunny.com/'], ['Website Development', 'Underdog Marketing', 'เว็บไซต์ของอาจารย์บุ้ง ดีดติ่งหู ที่ปรึกษาการตลาดและการขาย', 'https://underdog.run/'], ['Website Development', 'ทวนทอง 99', 'เว็บไซต์จำหน่ายสินค้าอุปโภคบริโภคและสมุนไพรไทย', 'https://tuanthong99.com/'], ['Website Development', 'สำนักงานกฎหมาย ตถาตา', 'เว็บไซต์สำนักงานกฎหมายที่เน้นความน่าเชื่อถือและการเข้าถึงง่าย', 'https://lawyernoom.com/'], ['Website Development', 'Baofuling Shop', 'เว็บไซต์ร้านค้าออนไลน์จำหน่ายผลิตภัณฑ์อาหารเสริมและสุขภาพ', 'https://baofulingshop.com/']]; +const serviceSlugs = ['website-development', 'marketing-consult', 'automation-workflow', 'ai-consult']; +const portfolio = [ + ['Marketing Consult', 'Dataroot', 'บริษัทให้บริการ ERP Software และการปรับแต่ง ERP Software สำหรับองค์กรขนาดใหญ่', 'https://erp.dataroot.asia/', 'dataroot.png'], + ['Website Development', 'Jet Industries', 'เว็บไซต์องค์กรผู้ผลิตพลาสติกฉีดขึ้นรูป ประสบการณ์กว่า 40 ปี', 'https://jetindustries.co.th/', 'jetindustries.png'], + ['Marketing Consult · Website Development', 'Dealplustech', 'เว็บไซต์จัดจำหน่ายระบบน้ำและสุขภัณฑ์คุณภาพสูงสำหรับโรงงานและบ้านเรือน', 'https://www.dealplustech.co.th/', 'dealplustech.png'], + ['Website Development', 'เลือดจระเข้วานิไทย', 'เว็บไซต์ผลิตภัณฑ์แคปซูลเลือดจระเข้เพื่อสุขภาพ', 'https://xn--22cl9aoj9cdnw9cvb0a1a6a8jh7e5d.com/', 'leudjorakhe.png'], + ['Website Development', 'เทรนเนอร์ซันนี่', 'เว็บไซต์ personal brand และหลักสูตรอบรมสำหรับเทรนเนอร์ฟิตเนส', 'https://trainersunny.com/', 'trainersunny.png'], + ['Website Development', 'Underdog Marketing', 'เว็บไซต์ของอาจารย์บุ้ง ดีดติ่งหู ที่ปรึกษาการตลาดและการขาย', 'https://underdog.run/', 'underdog.png'], + ['Website Development', 'ทวนทอง 99', 'เว็บไซต์จำหน่ายสินค้าอุปโภคบริโภคและสมุนไพรไทย', 'https://tuanthong99.com/', 'tuanthong.png'], + ['Website Development', 'สำนักงานกฎหมาย ตถาตา', 'เว็บไซต์สำนักงานกฎหมายที่เน้นความน่าเชื่อถือและการเข้าถึงง่าย', 'https://lawyernoom.com/', 'lawyernoom.png'], + ['Website Development', 'Baofuling Shop', 'เว็บไซต์ร้านค้าออนไลน์จำหน่ายผลิตภัณฑ์อาหารเสริมและสุขภาพ', 'https://baofulingshop.com/', 'baofuling.png'], +]; --- - +
-

Partner for practical growth

พาร์ทเนอร์สำหรับ SME ที่อยากตัดสินใจให้คุ้มขึ้น

เป้าหมายของเราคือ
เพิ่มกำไรให้ลูกค้า

เราช่วย SME ดูข้อมูลจริงก่อนตัดสินใจทำเว็บ การตลาด AI หรือระบบอัตโนมัติ เพื่อเลือกสิ่งที่ควรทำอย่างมีประสิทธิภาพ และเหมาะกับธุรกิจของคุณ

VALUE MAP / 01SME FOCUS
รายได้ความเสี่ยงกำไรเวลาข้อมูล
Marketing เพิ่มรายได้ AI ลดต้นทุนและเวลา Business Knowledge ลดความเสี่ยง
+

Partner for practical growth

พาร์ทเนอร์สำหรับ SME ที่อยากตัดสินใจให้คุ้มขึ้น

เป้าหมายของเราคือ
เพิ่มกำไรให้ลูกค้า

เราช่วย SME ดูข้อมูลจริงก่อนตัดสินใจทำเว็บ การตลาด AI หรือระบบอัตโนมัติ เพื่อเลือกสิ่งที่ควรทำอย่างมีประสิทธิภาพ และเหมาะกับธุรกิจของคุณ

เข็มทิศธุรกิจชี้ไปสู่ทิศทางการเติบโต
+

START WITH THE PROBLEM

คุณมีปัญหาเหล่านี้ไหม?

01 เว็บมีอยู่แล้ว แต่ไม่ค่อยมีลูกค้าทัก

02 ยิงแอดอยู่ แต่ยอดขายไม่คุ้ม

03 ทีมงานทำงานช้า หรือผิดพลาดบ่อย

04 พนักงานลาออก พร้อมกับความรู้เกี่ยวกับลูกค้า

ถ้าคุณมี 1 ใน 4 ปัญหานี้ คุณคือคนที่เราอยากช่วย

-

SERVICES / WHAT WE DO

บริการเพื่อตอบโจทย์
ปัญหาของคุณ

ทุกบริการเริ่มจากบริบทธุรกิจและข้อมูลที่มี ไม่ใช่จากคำตอบสำเร็จรูป

{services.map(([number,name,title,text,points]) =>
{number}

{name}

{title}

{text}

    {points.map((point) =>
  • {point}
  • )}
)}
-

CASE STUDY / DATAROOT

ปรับให้ทุกบาท
ไปถึงคนที่ใช่

บริษัทให้บริการ ERP Software และการปรับแต่ง ERP Software สำหรับองค์กรขนาดใหญ่

เยี่ยมชม Dataroot
CAMPAIGN RESULTMARKETING CONSULT
01

ปัญหา

ยิงโฆษณาและทำการตลาดอยู่แล้ว แต่ต้องใช้งบประมาณสูงมาก โดยลูกค้าที่ได้มาไม่คุ้มค่ากับงบประมาณที่จ่ายไป

02

สิ่งที่พบ

ตั้งค่าโฆษณากว้างมากเกินไป ทำให้เสียงบประมาณไปกับกลุ่มคนที่ไม่ใช่เป้าหมาย

03

สิ่งที่ปรับ

ปรับการตั้งค่าโดยโฟกัสกลุ่มเป้าหมาย เพื่อให้โฆษณาไปแสดงเฉพาะกลุ่มคนที่น่าจะเป็นลูกค้า

+373%

impression

+114.2%

click

−28.3%

ad spend

+

SERVICES / WHAT WE DO

บริการเพื่อตอบโจทย์
ปัญหาของคุณ

ทุกบริการเริ่มจากบริบทธุรกิจและข้อมูลที่มี ไม่ใช่จากคำตอบสำเร็จรูป

{services.map(([number,name,title,text,points], index) => )}
+

CASE STUDY / DATAROOT

ปรับให้ทุกบาท
ไปถึงคนที่ใช่

บริษัทให้บริการ ERP Software และการปรับแต่ง ERP Software สำหรับองค์กรขนาดใหญ่

เยี่ยมชม Dataroot
หน้าเว็บไซต์ Dataroot
CAMPAIGN RESULTMARKETING CONSULT
01

ปัญหา

ยิงโฆษณาและทำการตลาดอยู่แล้ว แต่ต้องใช้งบประมาณสูงมาก โดยลูกค้าที่ได้มาไม่คุ้มค่ากับงบประมาณที่จ่ายไป

02

สิ่งที่พบ

ตั้งค่าโฆษณากว้างมากเกินไป ทำให้เสียงบประมาณไปกับกลุ่มคนที่ไม่ใช่เป้าหมาย

03

สิ่งที่ปรับ

ปรับการตั้งค่าโดยโฟกัสกลุ่มเป้าหมาย เพื่อให้โฆษณาไปแสดงเฉพาะกลุ่มคนที่น่าจะเป็นลูกค้า

+373%

impression

+114.2%

click

−28.3%

ad spend

HOW WE WORK

กลยุทธ์และระบบงานของเรา
สำหรับลูกค้าแต่ละรายไม่เหมือนกัน

    {process.map(([number,title,text]) =>
  1. {number}

    {title}

    {text}

  2. )}
-

SELECTED WORK

ผลงานจริง
ของเรา

ผลงานที่แสดงเป็นส่วนที่เปิดเผยได้ — บางโปรเจกต์มีข้อมูลลับของลูกค้า จึงไม่สามารถนำมาโชว์ได้

เราออกแบบตามวัตถุประสงค์ของลูกค้า ไม่ใช่แค่ความสวยหรือเทคโนโลยี — เพราะกลุ่มเป้าหมายต่างกัน ประสบการณ์ที่ดีสำคัญกว่าเทคนิคที่หวือหวา

{portfolio.map(([type,name,description,url],index) => {type}0{index+1}

{name}

{description}

)}
-

LET'S START SIMPLE

ส่งโจทย์มาให้เรา
ดูก่อนได้

เริ่มจากปัญหาที่คุณอยากแก้ แล้วเราจะช่วยดูทางเลือกที่เหมาะกับธุรกิจของคุณ

+

SELECTED WORK

ผลงานจริง
ของเรา

ผลงานที่แสดงเป็นส่วนที่เปิดเผยได้ — บางโปรเจกต์มีข้อมูลลับของลูกค้า จึงไม่สามารถนำมาโชว์ได้

เราออกแบบตามวัตถุประสงค์ของลูกค้า ไม่ใช่แค่ความสวยหรือเทคโนโลยี — เพราะกลุ่มเป้าหมายต่างกัน ประสบการณ์ที่ดีสำคัญกว่าเทคนิคที่หวือหวา

{portfolio.map(([type,name,description,url,image],index) => {type}0{index+1}
{`ผลงานเว็บไซต์

{name}

{description}

)}
+

LET'S START SIMPLE

ลองคุยกัน
15 นาที

เล่าปัญหาที่อยากแก้แบบสั้น ๆ ทีมจะติดต่อกลับเพื่อช่วยดูว่าควรเริ่มจากจุดไหน

- + +
diff --git a/src/pages/privacy.astro b/src/pages/privacy.astro new file mode 100644 index 0000000..b7a4113 --- /dev/null +++ b/src/pages/privacy.astro @@ -0,0 +1,20 @@ +--- +import StaticPage from '../components/StaticPage.astro'; +import { breadcrumbSchema, webPageSchema } from '../data/schema'; +const description = 'นโยบายความเป็นส่วนตัวของ MoreminiMore — อธิบายข้อมูลที่เราเก็บ วิธีใช้ และสิทธิของท่านตามพระราชบัญญัติคุ้มครองข้อมูลส่วนบุคคล พ.ศ. 2562'; +const schema = [webPageSchema('/privacy/', 'นโยบายความเป็นส่วนตัว | MoreminiMore', description), breadcrumbSchema([{ name: 'หน้าหลัก', path: '/' }, { name: 'นโยบายความเป็นส่วนตัว', path: '/privacy/' }])]; +--- + +
+
diff --git a/src/pages/services/[slug].astro b/src/pages/services/[slug].astro new file mode 100644 index 0000000..445c8a9 --- /dev/null +++ b/src/pages/services/[slug].astro @@ -0,0 +1,13 @@ +--- +import ServiceLanding from '../../components/ServiceLanding.astro'; +import { servicePages, type ServicePage } from '../../data/servicePages'; + +export function getStaticPaths() { + return servicePages.map((service) => ({ params: { slug: service.slug }, props: { service } })); +} + +interface Props { service: ServicePage } +const { service } = Astro.props; +--- + + diff --git a/src/pages/services/index.astro b/src/pages/services/index.astro new file mode 100644 index 0000000..1bc11dd --- /dev/null +++ b/src/pages/services/index.astro @@ -0,0 +1,14 @@ +--- +import StaticPage from '../../components/StaticPage.astro'; +import { hubServices, workProcess } from '../../data/staticPages'; +import { breadcrumbSchema, organizationId, webPageSchema } from '../../data/schema'; + +const description = 'บริการของ MoreminiMore สำหรับ SME ที่ต้องการทำเว็บ การตลาด ระบบอัตโนมัติ และ AI โดยเริ่มจากข้อมูลจริงก่อนเลือกวิธีทำ'; +const itemList = { '@context': 'https://schema.org', '@type': 'ItemList', name: 'บริการของ MoreminiMore', itemListElement: hubServices.map((service, index) => ({ '@type': 'ListItem', position: index + 1, item: { '@type': 'Service', name: service.name, url: `https://moreminimore.com/services/${service.slug}/`, provider: { '@id': organizationId } } })) }; +const schema = [webPageSchema('/services/', 'บริการ | MoreminiMore', description), breadcrumbSchema([{ name: 'หน้าหลัก', path: '/' }, { name: 'บริการ', path: '/services/' }]), itemList]; +--- + +
{hubServices.map((service, index) => )}
+

วิธีการทำงาน

ทำให้ง่ายต่อการตัดสินใจ ไม่ใช่ทำให้ดูเยอะ

    {workProcess.map(([title, copy], index) =>
  1. 0{index + 1}

    {title}

    {copy}

  2. )}
+

ไม่แน่ใจว่าควรเริ่มบริการไหน ส่งโจทย์มาให้เราดูก่อนได้

ส่งโจทย์ให้เราดู ↗
+
diff --git a/src/pages/terms.astro b/src/pages/terms.astro new file mode 100644 index 0000000..ea58d60 --- /dev/null +++ b/src/pages/terms.astro @@ -0,0 +1,22 @@ +--- +import StaticPage from '../components/StaticPage.astro'; +import { breadcrumbSchema, webPageSchema } from '../data/schema'; +const description = 'เงื่อนไขการใช้งานเว็บไซต์ MoreminiMore — การยอมรับเงื่อนไข ทรัพย์สินทางปัญญา ข้อห้าม และข้อจำกัดความรับผิด'; +const schema = [webPageSchema('/terms/', 'เงื่อนไขการใช้งาน | MoreminiMore', description), breadcrumbSchema([{ name: 'หน้าหลัก', path: '/' }, { name: 'เงื่อนไขการใช้งาน', path: '/terms/' }])]; +--- + +
+
diff --git a/src/styles/blog.css b/src/styles/blog.css new file mode 100644 index 0000000..96346bb --- /dev/null +++ b/src/styles/blog.css @@ -0,0 +1,107 @@ +.blog-header { + position: sticky; + z-index: 20; + top: 0; + height: 78px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 2rem; + padding: 0 clamp(1.25rem, 5vw, 6rem); + border-bottom: 1px solid rgba(32, 31, 27, .2); + background: rgba(248, 244, 233, .92); + backdrop-filter: blur(12px); +} + +.blog-logo { display: inline-flex; } +.blog-logo img { display: block; width: clamp(145px, 14vw, 195px); height: auto; } +.blog-header nav { display: flex; gap: 1.7rem; font-size: .88rem; font-weight: 700; } +.blog-header nav a { border-bottom: 1px solid transparent; } +.blog-header nav a:hover, .blog-header nav a.active, .blog-header nav a[aria-current="page"] { color: var(--brand-green); border-bottom-color: currentColor; } +.blog-contact { font-size: .86rem; font-weight: 700; border-bottom: 1px solid var(--ink); } +.blog-mobile-nav { display: none; } + +.blog-index { padding: clamp(4.5rem, 9vw, 8rem) clamp(1.25rem, 7vw, 8rem) clamp(6rem, 10vw, 9rem); } +.blog-index-hero { display: grid; grid-template-columns: 1.3fr .7fr; gap: 2rem 5rem; align-items: end; margin-bottom: clamp(3rem, 7vw, 6rem); } +.blog-eyebrow { grid-column: 1 / -1; margin: 0; color: var(--muted); font: 700 .7rem/1.2 Manrope, sans-serif; letter-spacing: .12em; } +.blog-index-hero h1 { margin: 0; font-size: clamp(2.8rem, 6vw, 6rem); line-height: .98; letter-spacing: -.065em; } +.blog-index-hero h1 em { color: var(--brand-green); font-style: normal; } +.blog-index-hero > p:last-child { max-width: 31rem; margin: 0 0 .4rem; color: var(--muted); line-height: 1.75; } + +.blog-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; gap: clamp(1.5rem, 4vw, 3.5rem) 1.5rem; } +.blog-card { min-width: 0; border: 1px solid var(--ink); background: var(--paper); box-shadow: 7px 7px 0 rgba(32, 31, 27, .12); } +.blog-card-image { display: block; aspect-ratio: 1200 / 630; overflow: hidden; border-bottom: 1px solid var(--ink); background: #e4decf; } +.blog-card-image img { width: 100%; height: 100%; display: block; object-fit: cover; transition: transform .45s ease; } +.blog-card:hover .blog-card-image img { transform: scale(1.025); } +.blog-card-copy { padding: clamp(1.15rem, 3vw, 1.7rem); } +.blog-card-meta, .article-meta { display: flex; flex-wrap: wrap; gap: .5rem 1rem; color: var(--muted); font: 700 .67rem/1.4 Manrope, sans-serif; letter-spacing: .06em; text-transform: uppercase; } +.blog-card-meta span:first-child, .article-meta span:first-child { color: var(--brand-green); } +.blog-card h2 { margin: 1rem 0 .7rem; font-size: clamp(1.35rem, 2.4vw, 2rem); line-height: 1.18; letter-spacing: -.035em; } +.blog-card h2 a:hover { color: var(--brand-green); } +.blog-card-copy > p { margin: 0; color: var(--muted); line-height: 1.7; } +.blog-read { display: inline-flex; gap: .7rem; margin-top: 1.25rem; padding-bottom: .15rem; border-bottom: 1px solid var(--ink); font-size: .85rem; font-weight: 700; } + +.article-page { padding: clamp(3.5rem, 7vw, 6rem) clamp(1.25rem, 6vw, 6rem) clamp(6rem, 10vw, 9rem); } +.article-page > article { width: min(100%, 1120px); margin: 0 auto; } +.article-header { max-width: 920px; margin: 0 auto clamp(2rem, 5vw, 4rem); } +.article-back { display: inline-block; margin-bottom: 2.2rem; color: var(--brand-green); font-size: .86rem; font-weight: 700; } +.article-header h1 { margin: 1rem 0 1.35rem; font-size: clamp(2.5rem, 5.5vw, 5.5rem); line-height: 1.03; letter-spacing: -.06em; } +.article-header > p { max-width: 760px; margin: 0; color: var(--muted); font-size: clamp(1rem, 1.7vw, 1.2rem); line-height: 1.75; } +.article-hero { margin: 0 0 clamp(3rem, 7vw, 6rem); border: 1px solid var(--ink); background: #e4decf; box-shadow: 12px 12px 0 var(--yellow); } +.article-hero img { display: block; width: 100%; height: auto; aspect-ratio: 1200 / 630; object-fit: cover; } + +.article-prose { width: min(100%, 760px); margin: 0 auto; color: #2d2b25; font-size: clamp(1rem, 1.3vw, 1.08rem); line-height: 1.86; overflow-wrap: anywhere; } +.article-prose > :first-child { margin-top: 0; } +.article-prose h2, .article-prose h3, .article-prose h4 { color: var(--ink); line-height: 1.25; letter-spacing: -.025em; scroll-margin-top: 100px; } +.article-prose h2 { margin: 3.5rem 0 1rem; padding-top: .7rem; border-top: 1px solid var(--line); font-size: clamp(1.7rem, 3vw, 2.35rem); } +.article-prose h3 { margin: 2.4rem 0 .8rem; font-size: clamp(1.3rem, 2vw, 1.65rem); } +.article-prose h4 { margin: 2rem 0 .6rem; font-size: 1.16rem; } +.article-prose p, .article-prose ul, .article-prose ol { margin: 0 0 1.35rem; } +.article-prose ul, .article-prose ol { padding-left: 1.35rem; } +.article-prose li { margin-bottom: .45rem; } +.article-prose a { color: var(--brand-green); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; } +.article-prose a:hover { color: #173b2c; } +.article-prose img { display: block; max-width: 100%; height: auto; margin: 2.2rem auto; border: 1px solid var(--line); } +.article-prose blockquote { margin: 2rem 0; padding: 1.25rem 1.4rem; border-left: 5px solid var(--yellow); background: #fff5d4; color: var(--ink); } +.article-prose blockquote > :last-child { margin-bottom: 0; } +.article-prose hr { margin: 3rem 0; border: 0; border-top: 1px solid var(--line); } +.article-prose code { border-radius: 3px; background: #e8e2d5; padding: .12rem .32rem; font: .9em/1.6 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; } +.article-prose pre { max-width: 100%; margin: 2rem 0; overflow-x: auto; padding: 1.2rem; border: 1px solid #3e3b34; background: #201f1b; color: #fffdf7; } +.article-prose pre code { background: transparent; padding: 0; color: inherit; } +.article-prose table { display: block; width: 100%; max-width: 100%; margin: 2rem 0; overflow-x: auto; border-collapse: collapse; font-size: .9rem; } +.article-prose th, .article-prose td { min-width: 9rem; padding: .75rem; border: 1px solid var(--line); text-align: left; vertical-align: top; } +.article-prose th { background: #ece5d5; color: var(--ink); } +.article-prose strong { color: var(--ink); } + +.article-end { width: min(100%, 760px); margin: clamp(4rem, 8vw, 7rem) auto 0; padding: 1.5rem; border: 1px solid var(--ink); background: var(--yellow); display: flex; justify-content: space-between; align-items: center; gap: 1rem; } +.article-end p { margin: 0; font-weight: 700; } +.article-end a { flex: 0 0 auto; border-bottom: 1px solid currentColor; font-weight: 700; } + +.blog-header a:focus-visible, .blog-card a:focus-visible, .article-page a:focus-visible { outline: 3px solid var(--yellow); outline-offset: 4px; } + +@media (max-width: 800px) { + .blog-header { height: 68px; gap: 1rem; } + .blog-desktop-nav, .blog-contact { display: none !important; } + .blog-mobile-nav { display: block; } + .blog-mobile-nav summary { list-style: none; cursor: pointer; border: 1px solid var(--ink); padding: .42rem .6rem; font: 700 .78rem Manrope, sans-serif; } + .blog-mobile-nav summary::-webkit-details-marker { display: none; } + .blog-mobile-nav[open] summary span { display: inline-block; transform: rotate(45deg); } + .blog-mobile-nav nav { position: absolute; right: 1.25rem; top: 58px; width: min(18rem, calc(100vw - 2.5rem)); display: flex; flex-direction: column; gap: 0; padding: .75rem; border: 1px solid var(--ink); background: var(--paper); box-shadow: 8px 8px 0 var(--yellow); } + .blog-mobile-nav nav a { padding: .8rem .35rem; border-bottom: 1px solid var(--line); } + .blog-mobile-nav nav a:last-child { border-bottom: 0; } + .blog-index-hero { grid-template-columns: 1fr; } + .blog-eyebrow { grid-column: auto; } + .blog-grid { grid-template-columns: 1fr; } + .article-page { padding-top: 2.7rem; } + .article-hero { box-shadow: 7px 7px 0 var(--yellow); } +} + +@media (max-width: 560px) { + .article-header h1 { font-size: 2.45rem; } + .article-end { align-items: flex-start; flex-direction: column; } +} + +@media (prefers-reduced-motion: reduce) { + .blog-card-image img { transition: none; } + .blog-card:hover .blog-card-image img { transform: none; } +} diff --git a/src/styles/global.css b/src/styles/global.css index d6f84d8..b06fe03 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -1 +1 @@ -:root{--ink:#101828;--warm:#f7f6f2;--green:#32d583;--cobalt:#155eef;--muted:#667085;--line:#d9dcd5;--paper:#fffefa;font-family:'IBM Plex Sans Thai',sans-serif;color:var(--ink);background:var(--warm);font-synthesis:none}*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;line-height:1.55}a{color:inherit;text-decoration:none}.skip-link{position:fixed;left:1rem;top:-4rem;background:var(--ink);color:#fff;padding:.7rem 1rem;z-index:100}.skip-link:focus{top:1rem}.site-header{height:84px;display:flex;align-items:center;justify-content:space-between;padding:0 clamp(1.25rem,4vw,4.75rem);border-bottom:1px solid var(--line);background:rgba(247,246,242,.92);backdrop-filter:blur(12px);position:sticky;top:0;z-index:10}.wordmark{font:800 1rem/1 Manrope,sans-serif;letter-spacing:-.07em}.wordmark span{color:var(--green)}.wordmark .wordmark-dot{color:var(--cobalt)}nav{display:flex;gap:1.7rem;font-size:.88rem;font-weight:600}nav a:hover,.text-link:hover{color:var(--cobalt)}.mobile-nav{display:none}.header-cta{font-size:.88rem;font-weight:700;border-bottom:1px solid var(--ink);padding-bottom:3px}.header-cta span{color:var(--cobalt);margin-left:.25rem}.hero{display:grid;grid-template-columns:1.13fr .87fr;min-height:calc(100vh - 84px);padding:clamp(4.5rem,10vw,9rem) clamp(1.25rem,7vw,8rem) clamp(3rem,6vw,6rem);gap:4rem;align-items:center}.eyebrow{font:700 .71rem/1.2 Manrope,sans-serif;letter-spacing:.1em;color:var(--muted);margin:0 0 1.3rem}.live-dot{display:inline-block;background:var(--green);width:8px;height:8px;border-radius:50%;margin-right:.45rem}.hero-kicker{font-size:1rem;font-weight:600;margin:0 0 1rem;color:#344054}.hero h1,.section-heading h2,.case-intro h2,.contact h2{font-size:clamp(3rem,6.15vw,6.4rem);letter-spacing:-.075em;line-height:.98;margin:0;font-weight:700}.hero h1 em,.section-heading h2 em,.case-intro h2 em{font-style:normal;color:var(--cobalt)}.hero-lede{font-size:1.08rem;line-height:1.75;max-width:39rem;margin:2.2rem 0;color:#344054}.hero-actions{display:flex;align-items:center;gap:1.75rem}.button{display:inline-flex;align-items:center;gap:2rem;padding:1rem 1.3rem;font-weight:700;font-size:.95rem}.button-primary{color:var(--ink);background:var(--green);transition:transform .2s,background .2s}.button-primary:hover{background:#66df9e;transform:translateY(-2px)}.button span{font:1.25rem Manrope}.text-link{font-size:.93rem;font-weight:700;border-bottom:1px solid var(--ink);padding:.2rem 0}.signal-panel{background:var(--ink);color:#fff;padding:1.45rem;min-height:390px;display:flex;flex-direction:column;justify-content:space-between;position:relative;overflow:hidden}.signal-panel:before,.signal-panel:after{content:'';position:absolute;border:1px solid rgba(255,255,255,.2);border-radius:50%;width:420px;height:420px;left:50%;top:50%;transform:translate(-50%,-43%)}.signal-panel:after{width:260px;height:260px;border-color:rgba(50,213,131,.45)}.panel-topline,.data-topline{display:flex;justify-content:space-between;font:600 .64rem Manrope,sans-serif;letter-spacing:.11em;position:relative;z-index:1;color:#b6c0d0}.profit-orbit{width:min(100%,275px);height:auto;aspect-ratio:1;margin:1rem auto;position:relative;z-index:1;display:grid;place-items:center}.profit-orbit strong{width:105px;height:105px;border-radius:50%;background:var(--green);color:var(--ink);display:grid;place-items:center;font-size:1.65rem;letter-spacing:-.06em}.orbit-label{font-size:.8rem;position:absolute;color:#e6e9ed}.orbit-label.top{top:9px}.orbit-label.bottom{bottom:9px}.orbit-label.left{left:-13px;top:49%}.orbit-label.right{right:-6px;top:49%}.signal-legend{position:relative;z-index:1;display:flex;flex-wrap:wrap;gap:.6rem 1rem;font-size:.68rem;color:#d0d5dd}.signal-legend i{width:7px;height:7px;display:inline-block;border-radius:50%;margin-right:.3rem}.green{background:var(--green)}.blue{background:var(--cobalt)}.ink{background:#d0d5dd}.problem-strip{padding:5.5rem clamp(1.25rem,7vw,8rem);background:#eaece5}.problem-strip h2{font-size:clamp(2rem,4vw,4.1rem);letter-spacing:-.06em;line-height:1.05;margin:0 0 3rem}.problem-list{border-top:1px solid #b7bcb1}.problem-list p{margin:0;padding:1.1rem 0;border-bottom:1px solid #b7bcb1;font-size:clamp(1rem,1.5vw,1.3rem);font-weight:500}.problem-list b{font:600 .75rem Manrope,sans-serif;color:var(--cobalt);display:inline-block;width:4rem}.problem-close{font-weight:600;margin:2rem 0 0}.problem-close span{color:var(--cobalt)}.section{padding:clamp(5rem,10vw,9rem) clamp(1.25rem,7vw,8rem)}.section-heading{display:grid;grid-template-columns:1.1fr .65fr;gap:3rem;align-items:end;margin-bottom:4rem}.section-heading .eyebrow{grid-column:1/-1;margin:0}.section-heading h2{font-size:clamp(2.5rem,4.8vw,5rem)}.section-heading>p:not(.eyebrow){font-size:1rem;color:#475467;max-width:23rem;margin:0 0 .5rem}.service-list{border-top:1px solid var(--line)}.service-card{display:grid;grid-template-columns:80px minmax(0,1fr) 32px;gap:1rem;padding:2rem 0;border-bottom:1px solid var(--line)}.service-index{font:600 .75rem Manrope,sans-serif;color:var(--cobalt);padding-top:.25rem}.service-name{font:700 .72rem Manrope,sans-serif;color:#667085;letter-spacing:.09em;margin:0 0 .55rem}.service-card h3{font-size:clamp(1.45rem,2.5vw,2.4rem);letter-spacing:-.045em;line-height:1.1;margin:0}.service-text{max-width:48rem;margin:1.15rem 0;color:#475467}.service-card ul{padding:0;list-style:none;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.5rem 1.5rem;max-width:65rem}.service-card li{font-size:.88rem;color:#475467;display:flex;gap:.5rem}.service-card li:before{content:'•';color:var(--green);font-size:1.1rem;line-height:1.2}.service-arrow{font-size:1.3rem;color:var(--cobalt)}.case-study{background:var(--ink);color:#fff;display:grid;grid-template-columns:.85fr 1.15fr;padding:clamp(5rem,10vw,9rem) clamp(1.25rem,7vw,8rem);gap:5rem}.light{color:#a7b3c6}.case-intro h2{font-size:clamp(3rem,5.5vw,5.5rem)}.case-intro>p:not(.eyebrow){max-width:27rem;color:#b6c0d0;margin:2rem 0}.case-link{color:var(--green);font-weight:700;border-bottom:1px solid var(--green);padding-bottom:.25rem}.case-data{padding-top:.25rem}.data-story{border-top:1px solid #445169;margin-top:2rem}.data-story>div{display:grid;grid-template-columns:45px 115px 1fr;gap:1rem;padding:1.25rem 0;border-bottom:1px solid #445169}.data-story b{font:600 .7rem Manrope,sans-serif;color:var(--green)}.data-story h3{margin:0;font-size:1rem}.data-story p{margin:0;color:#c0cad8;font-size:.91rem}.metrics{margin-top:3rem;display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:#445169}.metrics>div{background:var(--ink);padding:1.2rem}.metrics strong{font:700 clamp(1.9rem,3.8vw,3.7rem)/1 Manrope,sans-serif;letter-spacing:-.08em;color:var(--green)}.metrics strong span{font-size:.53em}.metrics p{color:#a7b3c6;margin:.45rem 0 0;font:600 .65rem Manrope,sans-serif;letter-spacing:.08em;text-transform:uppercase}.process{background:var(--paper)}.process-grid{list-style:none;margin:0;padding:0;border-top:1px solid var(--line);display:grid;grid-template-columns:repeat(4,1fr)}.process-grid li{padding:1.5rem 1.5rem 1rem 0;border-right:1px solid var(--line);min-height:230px}.process-grid li:not(:first-child){padding-left:1.5rem}.process-grid li:last-child{border-right:0}.process-grid span{font:600 .7rem Manrope,sans-serif;color:var(--cobalt)}.process-grid h3{font-size:1.3rem;letter-spacing:-.04em;margin:2.5rem 0 .7rem}.process-grid p{font-size:.88rem;color:#475467;margin:0}.portfolio-heading{grid-template-columns:1.1fr .65fr}.portfolio-heading .eyebrow{grid-column:auto}.portfolio-heading p:not(.eyebrow){align-self:end}.portfolio-note{max-width:55rem;margin:-1.8rem 0 3rem;color:#475467}.portfolio-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--line)}.portfolio-card{background:var(--paper);min-height:315px;padding:1.25rem;position:relative;display:flex;flex-direction:column;justify-content:flex-end;overflow:hidden;transition:background .2s}.portfolio-card:hover{background:#eff2ec}.project-type,.project-number{position:absolute;top:1.2rem;font:600 .61rem Manrope,sans-serif;letter-spacing:.07em;color:#667085;max-width:65%}.project-number{right:1.2rem;color:var(--cobalt)}.project-mark{height:125px;display:grid;place-items:center;position:absolute;left:1.25rem;right:1.25rem;top:3.5rem;background:var(--ink);color:white;overflow:hidden}.project-mark:before{content:'';position:absolute;width:220px;height:220px;border:22px solid var(--green);border-radius:50%;transform:translate(34%,20%)}.project-mark span{font:800 5.3rem/1 Manrope,sans-serif;z-index:1;letter-spacing:-.13em}.portfolio-2 .project-mark{background:var(--cobalt)}.portfolio-2 .project-mark:before{border-color:#fff;transform:translate(-35%,-20%);border-radius:0}.portfolio-3 .project-mark{background:#d8a25e}.portfolio-3 .project-mark:before{border-color:var(--ink);transform:translate(22%,-27%)}.portfolio-4 .project-mark{background:#7d261d}.portfolio-5 .project-mark{background:#e4e5e0;color:var(--ink)}.portfolio-5 .project-mark:before{border-color:var(--cobalt)}.portfolio-6 .project-mark{background:#b9e6dd;color:var(--ink)}.portfolio-6 .project-mark:before{border-color:var(--ink);transform:translate(-33%,18%)}.portfolio-7 .project-mark{background:#e5c438;color:var(--ink)}.portfolio-7 .project-mark:before{border-color:#c83932}.portfolio-8 .project-mark{background:#e0e2dd;color:var(--ink)}.portfolio-8 .project-mark:before{border-color:var(--ink);transform:translate(35%,-22%)}.portfolio-9 .project-mark{background:#ce715e}.portfolio-9 .project-mark:before{border-color:#f7d86d}.portfolio-card h3{font-size:1.18rem;letter-spacing:-.04em;margin:0 0 .28rem;max-width:85%}.portfolio-card p{font-size:.77rem;line-height:1.5;margin:0;color:#475467;max-width:92%}.project-arrow{position:absolute;right:1.25rem;bottom:1.25rem;color:var(--cobalt)}.contact{background:var(--cobalt);color:white;padding:clamp(5rem,10vw,9rem) clamp(1.25rem,7vw,8rem);display:grid;grid-template-columns:1fr 1fr;gap:3rem}.contact h2{font-size:clamp(2.8rem,5.2vw,5.4rem)}.contact>div>p:not(.eyebrow){max-width:29rem;color:#e3ebff;margin:1.5rem 0 0}.contact-links{align-self:end}.contact-links a{border-top:1px solid rgba(255,255,255,.5);display:flex;gap:1rem;align-items:center;padding:1.05rem 0;font-size:clamp(1rem,1.65vw,1.35rem);font-weight:600}.contact-links a:last-child{border-bottom:1px solid rgba(255,255,255,.5)}.contact-links span{font:600 .62rem Manrope,sans-serif;letter-spacing:.1em;color:#d9e5ff;width:3.5rem}.contact-links b{margin-left:auto;color:var(--green)}footer{background:var(--ink);color:white;padding:1.6rem clamp(1.25rem,7vw,8rem);display:flex;align-items:center;justify-content:space-between;font-size:.77rem;color:#a7b3c6}footer .wordmark{color:white}footer a:last-child{color:white}@media(max-width:800px){.site-header{height:68px}.desktop-nav{display:none}.mobile-nav{display:block;order:3}.mobile-nav summary{cursor:pointer;list-style:none;font:700 .78rem Manrope,sans-serif;border:1px solid var(--ink);padding:.45rem .6rem}.mobile-nav summary::-webkit-details-marker{display:none}.mobile-nav[open] summary span{display:inline-block;transform:rotate(45deg)}.mobile-nav nav{display:flex;position:absolute;right:1.25rem;top:58px;width:min(18rem,calc(100vw - 2.5rem));padding:.75rem;background:var(--paper);border:1px solid var(--ink);box-shadow:8px 8px 0 var(--green);flex-direction:column;gap:0}.mobile-nav nav a{padding:.8rem .35rem;border-bottom:1px solid var(--line);font-size:.92rem}.mobile-nav nav a:last-child{border:0;color:var(--cobalt)}.header-cta{display:none}.hero{grid-template-columns:1fr;padding-top:4.5rem;gap:3.5rem;min-height:auto}.signal-panel{min-height:340px}.section-heading,.portfolio-heading,.case-study,.contact{grid-template-columns:1fr}.section-heading{gap:1.3rem;margin-bottom:2.7rem}.section-heading .eyebrow{grid-column:auto}.service-card{grid-template-columns:42px minmax(0,1fr) 20px}.service-card ul{grid-template-columns:1fr}.case-study{gap:3rem}.data-story>div{grid-template-columns:30px 1fr;gap:.65rem}.data-story h3,.data-story p{grid-column:2}.process-grid{grid-template-columns:repeat(2,1fr)}.process-grid li:nth-child(2){border-right:0}.process-grid li:nth-child(n+3){border-top:1px solid var(--line)}.process-grid li:nth-child(3){padding-left:0}.portfolio-grid{grid-template-columns:repeat(2,1fr)}.contact-links{margin-top:1rem}}@media(max-width:480px){.hero h1{font-size:3.1rem}.hero-actions{align-items:flex-start;flex-direction:column;gap:1.1rem}.signal-panel{padding:1rem}.profit-orbit{width:min(100%,250px);margin:.75rem auto}.orbit-label.left{left:0}.orbit-label.right{right:0}.metrics{grid-template-columns:1fr}.metrics>div{padding:.9rem}.portfolio-grid{grid-template-columns:1fr}.portfolio-card{min-height:290px}.problem-list b{width:2.5rem}.contact-links a{font-size:.98rem}.contact-links span{width:2.5rem}footer{gap:1rem;flex-wrap:wrap}.process-grid{grid-template-columns:1fr}.process-grid li{border-right:0!important;border-top:1px solid var(--line);padding:1.4rem 0!important}.process-grid li:first-child{border-top:0}} +:root{--ink:#201F1B;--warm:#F8F4E9;--green:#FEB400;--cobalt:#2D5A47;--muted:#625F55;--line:#D9D3C4;--paper:#FFFDF7;font-family:'IBM Plex Sans Thai',sans-serif;color:var(--ink);background:var(--warm);font-synthesis:none}*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;line-height:1.55}a{color:inherit;text-decoration:none}.skip-link{position:fixed;left:1rem;top:-4rem;background:var(--ink);color:#fff;padding:.7rem 1rem;z-index:100}.skip-link:focus{top:1rem}.site-header{height:84px;display:flex;align-items:center;justify-content:space-between;padding:0 clamp(1.25rem,4vw,4.75rem);border-bottom:1px solid var(--line);background:rgba(247,246,242,.92);backdrop-filter:blur(12px);position:sticky;top:0;z-index:10}.wordmark{font:800 1rem/1 Manrope,sans-serif;letter-spacing:-.07em}.wordmark span{color:var(--green)}.wordmark .wordmark-dot{color:var(--cobalt)}nav{display:flex;gap:1.7rem;font-size:.88rem;font-weight:600}nav a:hover,.text-link:hover{color:var(--cobalt)}.mobile-nav{display:none}.header-cta{font-size:.88rem;font-weight:700;border-bottom:1px solid var(--ink);padding-bottom:3px}.header-cta span{color:var(--cobalt);margin-left:.25rem}.hero{display:grid;grid-template-columns:1.13fr .87fr;min-height:calc(100vh - 84px);padding:clamp(4.5rem,10vw,9rem) clamp(1.25rem,7vw,8rem) clamp(3rem,6vw,6rem);gap:4rem;align-items:center}.eyebrow{font:700 .71rem/1.2 Manrope,sans-serif;letter-spacing:.1em;color:var(--muted);margin:0 0 1.3rem}.live-dot{display:inline-block;background:var(--green);width:8px;height:8px;border-radius:50%;margin-right:.45rem}.hero-kicker{font-size:1rem;font-weight:600;margin:0 0 1rem;color:#625F55}.hero h1,.section-heading h2,.case-intro h2,.contact h2{font-size:clamp(3rem,6.15vw,6.4rem);letter-spacing:-.075em;line-height:.98;margin:0;font-weight:700}.hero h1 em,.section-heading h2 em,.case-intro h2 em{font-style:normal;color:var(--cobalt)}.hero-lede{font-size:1.08rem;line-height:1.75;max-width:39rem;margin:2.2rem 0;color:#625F55}.hero-actions{display:flex;align-items:center;gap:1.75rem}.button{display:inline-flex;align-items:center;gap:2rem;padding:1rem 1.3rem;font-weight:700;font-size:.95rem}.button-primary{color:var(--ink);background:var(--green);transition:transform .2s,background .2s}.button-primary:hover{background:#66df9e;transform:translateY(-2px)}.button span{font:1.25rem Manrope}.text-link{font-size:.93rem;font-weight:700;border-bottom:1px solid var(--ink);padding:.2rem 0}.signal-panel{background:var(--ink);color:#fff;padding:1.45rem;min-height:390px;display:flex;flex-direction:column;justify-content:space-between;position:relative;overflow:hidden}.signal-panel:before,.signal-panel:after{content:'';position:absolute;border:1px solid rgba(255,255,255,.2);border-radius:50%;width:420px;height:420px;left:50%;top:50%;transform:translate(-50%,-43%)}.signal-panel:after{width:260px;height:260px;border-color:rgba(50,213,131,.45)}.panel-topline,.data-topline{display:flex;justify-content:space-between;font:600 .64rem Manrope,sans-serif;letter-spacing:.11em;position:relative;z-index:1;color:#b6c0d0}.profit-orbit{width:min(100%,275px);height:auto;aspect-ratio:1;margin:1rem auto;position:relative;z-index:1;display:grid;place-items:center}.profit-orbit strong{width:105px;height:105px;border-radius:50%;background:var(--green);color:var(--ink);display:grid;place-items:center;font-size:1.65rem;letter-spacing:-.06em}.orbit-label{font-size:.8rem;position:absolute;color:#e6e9ed}.orbit-label.top{top:9px}.orbit-label.bottom{bottom:9px}.orbit-label.left{left:-13px;top:49%}.orbit-label.right{right:-6px;top:49%}.signal-legend{position:relative;z-index:1;display:flex;flex-wrap:wrap;gap:.6rem 1rem;font-size:.68rem;color:#d0d5dd}.signal-legend i{width:7px;height:7px;display:inline-block;border-radius:50%;margin-right:.3rem}.green{background:var(--green)}.blue{background:var(--cobalt)}.ink{background:#d0d5dd}.problem-strip{padding:5.5rem clamp(1.25rem,7vw,8rem);background:#eaece5}.problem-strip h2{font-size:clamp(2rem,4vw,4.1rem);letter-spacing:-.06em;line-height:1.05;margin:0 0 3rem}.problem-list{border-top:1px solid #b7bcb1}.problem-list p{margin:0;padding:1.1rem 0;border-bottom:1px solid #b7bcb1;font-size:clamp(1rem,1.5vw,1.3rem);font-weight:500}.problem-list b{font:600 .75rem Manrope,sans-serif;color:var(--cobalt);display:inline-block;width:4rem}.problem-close{font-weight:600;margin:2rem 0 0}.problem-close span{color:var(--cobalt)}.section{padding:clamp(5rem,10vw,9rem) clamp(1.25rem,7vw,8rem)}.section-heading{display:grid;grid-template-columns:1.1fr .65fr;gap:3rem;align-items:end;margin-bottom:4rem}.section-heading .eyebrow{grid-column:1/-1;margin:0}.section-heading h2{font-size:clamp(2.5rem,4.8vw,5rem)}.section-heading>p:not(.eyebrow){font-size:1rem;color:#625F55;max-width:23rem;margin:0 0 .5rem}.service-list{border-top:1px solid var(--line)}.service-card{display:grid;grid-template-columns:80px minmax(0,1fr) 32px;gap:1rem;padding:2rem 0;border-bottom:1px solid var(--line)}.service-index{font:600 .75rem Manrope,sans-serif;color:var(--cobalt);padding-top:.25rem}.service-name{font:700 .72rem Manrope,sans-serif;color:#625F55;letter-spacing:.09em;margin:0 0 .55rem}.service-card h3{font-size:clamp(1.45rem,2.5vw,2.4rem);letter-spacing:-.045em;line-height:1.1;margin:0}.service-text{max-width:48rem;margin:1.15rem 0;color:#625F55}.service-card ul{padding:0;list-style:none;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.5rem 1.5rem;max-width:65rem}.service-card li{font-size:.88rem;color:#625F55;display:flex;gap:.5rem}.service-card li:before{content:'•';color:var(--green);font-size:1.1rem;line-height:1.2}.service-arrow{font-size:1.3rem;color:var(--cobalt)}.case-study{background:var(--ink);color:#fff;display:grid;grid-template-columns:.85fr 1.15fr;padding:clamp(5rem,10vw,9rem) clamp(1.25rem,7vw,8rem);gap:5rem}.light{color:#a7b3c6}.case-intro h2{font-size:clamp(3rem,5.5vw,5.5rem)}.case-intro>p:not(.eyebrow){max-width:27rem;color:#b6c0d0;margin:2rem 0}.case-link{color:var(--green);font-weight:700;border-bottom:1px solid var(--green);padding-bottom:.25rem}.case-data{padding-top:.25rem}.data-story{border-top:1px solid #445169;margin-top:2rem}.data-story>div{display:grid;grid-template-columns:45px 115px 1fr;gap:1rem;padding:1.25rem 0;border-bottom:1px solid #445169}.data-story b{font:600 .7rem Manrope,sans-serif;color:var(--green)}.data-story h3{margin:0;font-size:1rem}.data-story p{margin:0;color:#c0cad8;font-size:.91rem}.metrics{margin-top:3rem;display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:#445169}.metrics>div{background:var(--ink);padding:1.2rem}.metrics strong{font:700 clamp(1.9rem,3.8vw,3.7rem)/1 Manrope,sans-serif;letter-spacing:-.08em;color:var(--green)}.metrics strong span{font-size:.53em}.metrics p{color:#a7b3c6;margin:.45rem 0 0;font:600 .65rem Manrope,sans-serif;letter-spacing:.08em;text-transform:uppercase}.process{background:var(--paper)}.process-grid{list-style:none;margin:0;padding:0;border-top:1px solid var(--line);display:grid;grid-template-columns:repeat(4,1fr)}.process-grid li{padding:1.5rem 1.5rem 1rem 0;border-right:1px solid var(--line);min-height:230px}.process-grid li:not(:first-child){padding-left:1.5rem}.process-grid li:last-child{border-right:0}.process-grid span{font:600 .7rem Manrope,sans-serif;color:var(--cobalt)}.process-grid h3{font-size:1.3rem;letter-spacing:-.04em;margin:2.5rem 0 .7rem}.process-grid p{font-size:.88rem;color:#625F55;margin:0}.portfolio-heading{grid-template-columns:1.1fr .65fr}.portfolio-heading .eyebrow{grid-column:auto}.portfolio-heading p:not(.eyebrow){align-self:end}.portfolio-note{max-width:55rem;margin:-1.8rem 0 3rem;color:#625F55}.portfolio-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--line);align-items:stretch}.portfolio-card{background:var(--paper);padding:1.25rem;position:relative;display:flex;flex-direction:column;justify-content:flex-end;overflow:hidden;transition:background .2s}.portfolio-card:hover{background:#eff2ec}.project-type,.project-number{position:absolute;top:1.2rem;font:600 .61rem Manrope,sans-serif;letter-spacing:.07em;color:#625F55;max-width:65%}.project-number{right:1.2rem;color:var(--cobalt)}.project-mark{height:125px;display:grid;place-items:center;position:absolute;left:1.25rem;right:1.25rem;top:3.5rem;background:var(--ink);color:white;overflow:hidden}.project-mark:before{content:'';position:absolute;width:220px;height:220px;border:22px solid var(--green);border-radius:50%;transform:translate(34%,20%)}.project-mark span{font:800 5.3rem/1 Manrope,sans-serif;z-index:1;letter-spacing:-.13em}.portfolio-2 .project-mark{background:var(--cobalt)}.portfolio-2 .project-mark:before{border-color:#fff;transform:translate(-35%,-20%);border-radius:0}.portfolio-3 .project-mark{background:#d8a25e}.portfolio-3 .project-mark:before{border-color:var(--ink);transform:translate(22%,-27%)}.portfolio-4 .project-mark{background:#7d261d}.portfolio-5 .project-mark{background:#e4e5e0;color:var(--ink)}.portfolio-5 .project-mark:before{border-color:var(--cobalt)}.portfolio-6 .project-mark{background:#b9e6dd;color:var(--ink)}.portfolio-6 .project-mark:before{border-color:var(--ink);transform:translate(-33%,18%)}.portfolio-7 .project-mark{background:#e5c438;color:var(--ink)}.portfolio-7 .project-mark:before{border-color:#c83932}.portfolio-8 .project-mark{background:#e0e2dd;color:var(--ink)}.portfolio-8 .project-mark:before{border-color:var(--ink);transform:translate(35%,-22%)}.portfolio-9 .project-mark{background:#ce715e}.portfolio-9 .project-mark:before{border-color:#f7d86d}.portfolio-card h3{font-size:1.18rem;letter-spacing:-.04em;margin:0 0 .28rem;max-width:85%}.portfolio-card p{font-size:.77rem;line-height:1.5;margin:0;color:#625F55;max-width:92%}.project-arrow{position:absolute;right:1.25rem;bottom:1.25rem;color:var(--cobalt)}.contact{background:var(--cobalt);color:white;padding:clamp(5rem,10vw,9rem) clamp(1.25rem,7vw,8rem);display:grid;grid-template-columns:1fr 1fr;gap:3rem}.contact h2{font-size:clamp(2.8rem,5.2vw,5.4rem)}.contact>div>p:not(.eyebrow){max-width:29rem;color:#e3ebff;margin:1.5rem 0 0}.contact-links{align-self:end}.contact-links a{border-top:1px solid rgba(255,255,255,.5);display:flex;gap:1rem;align-items:center;padding:1.05rem 0;font-size:clamp(1rem,1.65vw,1.35rem);font-weight:600}.contact-links a:last-child{border-bottom:1px solid rgba(255,255,255,.5)}.contact-links span{font:600 .62rem Manrope,sans-serif;letter-spacing:.1em;color:#d9e5ff;width:3.5rem}.contact-links b{margin-left:auto;color:var(--green)}@media(max-width:800px){.site-header{height:68px}.desktop-nav{display:none}.mobile-nav{display:block;order:3}.mobile-nav summary{cursor:pointer;list-style:none;font:700 .78rem Manrope,sans-serif;border:1px solid var(--ink);padding:.45rem .6rem}.mobile-nav summary::-webkit-details-marker{display:none}.mobile-nav[open] summary span{display:inline-block;transform:rotate(45deg)}.mobile-nav nav{display:flex;position:absolute;right:1.25rem;top:58px;width:min(18rem,calc(100vw - 2.5rem));padding:.75rem;background:var(--paper);border:1px solid var(--ink);box-shadow:8px 8px 0 var(--green);flex-direction:column;gap:0}.mobile-nav nav a{padding:.8rem .35rem;border-bottom:1px solid var(--line);font-size:.92rem}.mobile-nav nav a:last-child{border:0;color:var(--cobalt)}.header-cta{display:none}.hero{grid-template-columns:1fr;padding-top:4.5rem;gap:3.5rem;min-height:auto}.signal-panel{min-height:340px}.section-heading,.portfolio-heading,.case-study,.contact{grid-template-columns:1fr}.section-heading{gap:1.3rem;margin-bottom:2.7rem}.section-heading .eyebrow{grid-column:auto}.service-card{grid-template-columns:42px minmax(0,1fr) 20px}.service-card ul{grid-template-columns:1fr}.case-study{gap:3rem}.data-story>div{grid-template-columns:30px 1fr;gap:.65rem}.data-story h3,.data-story p{grid-column:2}.process-grid{grid-template-columns:repeat(2,1fr)}.process-grid li:nth-child(2){border-right:0}.process-grid li:nth-child(n+3){border-top:1px solid var(--line)}.process-grid li:nth-child(3){padding-left:0}.portfolio-grid{grid-template-columns:repeat(2,1fr)}.contact-links{margin-top:1rem}}@media(max-width:480px){.hero h1{font-size:3.1rem}.hero-actions{align-items:flex-start;flex-direction:column;gap:1.1rem}.signal-panel{padding:1rem}.profit-orbit{width:min(100%,250px);margin:.75rem auto}.orbit-label.left{left:0}.orbit-label.right{right:0}.metrics{grid-template-columns:1fr}.metrics>div{padding:.9rem}.portfolio-grid{grid-template-columns:1fr}.problem-list b{width:2.5rem}.contact-links a{font-size:.98rem}.contact-links span{width:2.5rem}.process-grid{grid-template-columns:1fr}.process-grid li{border-right:0!important;border-top:1px solid var(--line);padding:1.4rem 0!important}.process-grid li:first-child{border-top:0}} diff --git a/src/styles/refresh.css b/src/styles/refresh.css new file mode 100644 index 0000000..333c141 --- /dev/null +++ b/src/styles/refresh.css @@ -0,0 +1,280 @@ +:root { + --ink: #201F1B; + --warm: #F8F4E9; + --green: #FEB400; + --cobalt: #2D5A47; + --muted: #625F55; + --line: #D9D3C4; + --paper: #FFFDF7; + --brand-green: #2D5A47; + --yellow: #FEB400; +} + +body { + background: + linear-gradient(90deg, rgba(32, 31, 27, 0.035) 1px, transparent 1px) 0 0 / 56px 56px, + var(--warm); +} + +::selection { background: var(--green); color: var(--ink); } + +.site-header { + background: rgba(248, 244, 233, 0.9); + border-bottom-color: rgba(32, 31, 27, 0.18); +} + +.brand-logo { display: inline-flex; align-items: center; flex: 0 0 auto; } +.brand-logo img { display: block; width: clamp(142px, 13vw, 195px); height: auto; } +.services-menu { position: relative; } +.services-menu summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: .45rem; } +.services-menu summary::-webkit-details-marker { display: none; } +.services-menu summary span { transition: transform .2s ease; } +.services-menu[open] summary span { transform: rotate(45deg); } +.services-submenu { + position: absolute; + top: calc(100% + 1.1rem); + left: 50%; + transform: translateX(-50%); + width: 390px; + padding: .7rem; + background: var(--paper); + border: 1px solid var(--ink); + box-shadow: 9px 9px 0 var(--green); +} +.services-submenu::before { content: ''; position: absolute; left: 0; right: 0; top: -1.2rem; height: 1.2rem; } +.services-submenu a { display: grid; grid-template-columns: 32px 1fr; gap: .65rem; padding: .8rem; border-bottom: 1px solid var(--line); } +.services-submenu a:last-child { border-bottom: 0; } +.services-submenu a:hover { background: var(--green); color: var(--ink); } +.services-submenu b { font: 700 .65rem Manrope, sans-serif; color: #2D5A47; } +.services-submenu span { display: flex; flex-direction: column; font: 700 .78rem Manrope, sans-serif; } +.services-submenu small { margin-top: .2rem; font: 500 .75rem 'IBM Plex Sans Thai', sans-serif; color: var(--muted); } +.mobile-nav-label { padding: .6rem .35rem .3rem; font: 700 .64rem Manrope, sans-serif; letter-spacing: .1em; color: #2D5A47; } + +.hero { + position: relative; + isolation: isolate; + overflow: clip; +} + +.hero::before { + content: ''; + position: absolute; + z-index: -1; + width: clamp(240px, 34vw, 550px); + aspect-ratio: 1; + border-radius: 50%; + background: var(--green); + left: -13vw; + top: 7%; + opacity: 0.22; + filter: blur(1px); +} + +.hero-illustration { + margin: 0; + aspect-ratio: 3 / 2; + overflow: hidden; + border: 1px solid var(--ink); + box-shadow: 18px 18px 0 var(--green); + transform: rotate(-1.25deg); + background: #D9D3C4; +} +.hero-illustration img { width: 100%; height: 112%; object-fit: cover; display: block; will-change: transform; } + +.hero h1 em, +.section-heading h2 em, +.case-intro h2 em { color: #2D5A47; } + +.button-primary { + background: var(--green); + box-shadow: 7px 7px 0 var(--ink); + transition: transform .25s ease, box-shadow .25s ease, background .25s ease; +} + +.button-primary:hover { + background: #FEB400; + transform: translate(-2px, -2px); + box-shadow: 10px 10px 0 var(--ink); +} + +.signal-panel { + background: var(--green); + color: var(--ink); + box-shadow: 18px 18px 0 var(--ink); + transform: rotate(-1.25deg); +} + +.signal-panel::before, +.signal-panel::after { border-color: rgba(23, 21, 15, 0.24); } +.signal-panel::after { border-color: rgba(23, 21, 15, 0.55); } +.panel-topline, .signal-legend, .orbit-label { color: var(--ink); } +.profit-orbit strong { background: var(--ink); color: var(--green); } +.signal-legend .green { background: var(--ink); } +.signal-legend .blue { background: #2D5A47; } +.signal-legend .ink { background: #fff4d8; border: 1px solid var(--ink); } + +.motion-ticker { + width: 100%; + overflow: hidden; + background: var(--green); + border-block: 1px solid var(--ink); + padding: .85rem 0; + font: 800 .78rem/1 Manrope, sans-serif; + letter-spacing: .12em; + white-space: nowrap; +} + +.motion-ticker div { + width: max-content; + animation: ticker 22s linear infinite; +} + +.motion-ticker span { margin: 0 1.3rem; } + +@keyframes ticker { to { transform: translateX(-50%); } } + +.problem-strip { background: #FEB400; } +.problem-list { border-top-color: rgba(23, 21, 15, .45); } +.problem-list p { border-bottom-color: rgba(23, 21, 15, .45); } +.problem-list b, .service-index, .service-arrow, .project-number, .project-arrow { color: #2D5A47; } + +.service-card { + position: relative; + transition: background .3s ease, padding-inline .3s ease; +} + +.service-card::before { + content: ''; + position: absolute; + inset: 0; + background: var(--green); + transform: scaleX(0); + transform-origin: right; + transition: transform .45s cubic-bezier(.2,.75,.2,1); + z-index: -1; +} + +.service-card:hover { padding-inline: 1.2rem; } +.service-card:hover::before { transform: scaleX(1); transform-origin: left; } +.service-card li::before { color: #2D5A47; } +.service-detail { display: inline-flex; gap: .65rem; margin-top: 1.15rem; padding-bottom: .2rem; border-bottom: 1px solid #201F1B; font-weight: 700; font-size: .88rem; } +.service-detail:hover { color: #2D5A47; } + +.case-study { background: #201F1B; } +.case-intro h2 em, .case-link, .data-story b, .metrics strong { color: var(--green); } + +.case-visual { + margin: 3rem 0 0; + overflow: hidden; + border: 1px solid rgba(254, 180, 0, .5); + aspect-ratio: 1.53; +} + +.case-visual img { width: 100%; height: 100%; object-fit: contain; display: block; } + +.process-grid li { position: relative; overflow: hidden; } +.process-grid li::after { + content: ''; + position: absolute; + width: 75px; + aspect-ratio: 1; + border-radius: 50%; + background: var(--green); + right: -38px; + bottom: -38px; + opacity: .7; +} + +.portfolio-grid { gap: 1.1rem; background: transparent; align-items: stretch; } +.portfolio-card { + align-self: stretch; + padding: 0; + justify-content: flex-start; + background: var(--paper); + border: 1px solid var(--ink); + box-shadow: 6px 6px 0 transparent; + transition: transform .35s ease, box-shadow .35s ease; +} + +.portfolio-card:hover { + background: var(--paper); + transform: translate(-5px, -5px); + box-shadow: 9px 9px 0 var(--green); +} + +.project-image { + margin: 0; + height: auto; + aspect-ratio: 1200 / 785; + overflow: hidden; + background: #ddd5c4; + border-bottom: 1px solid var(--ink); +} + +.project-image img { + width: 100%; + height: 100%; + object-fit: contain; + display: block; +} + +.project-copy { padding: 1.35rem; } +.project-type, .project-number { + z-index: 2; + top: .8rem; + padding: .35rem .5rem; + background: var(--green); + color: var(--ink); + border: 1px solid var(--ink); + max-width: 70%; +} +.project-type { left: .8rem; } +.project-number { right: .8rem; } +.project-arrow { right: 1.15rem; bottom: 1.05rem; } + +.contact { + background: var(--brand-green); + color: var(--paper); + border-top: 1px solid var(--ink); +} +.contact .eyebrow, .contact > div > p:not(.eyebrow) { color: var(--paper); } +.contact-direct { display: flex; flex-wrap: wrap; gap: .7rem 1.2rem; margin-top: 2rem; } +.contact-direct a { border-bottom: 1px solid rgba(255, 253, 247, .6); font-size: .9rem; } + +.contact-form { align-self: start; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; padding: clamp(1.2rem, 3vw, 2rem); background: var(--paper); color: var(--ink); border: 1px solid var(--ink); box-shadow: 10px 10px 0 var(--yellow); } +.form-field { display: flex; flex-direction: column; gap: .4rem; } +.form-field-wide, .form-submit-row, .form-status { grid-column: 1 / -1; } +.form-field label { font-size: .82rem; font-weight: 700; } +.form-field input, .form-field textarea { width: 100%; border: 1px solid var(--border, #D9D3C4); border-radius: 0; background: #FFFDF7; color: #201F1B; padding: .82rem .9rem; font: inherit; } +.form-field textarea { resize: vertical; min-height: 7.5rem; } +.form-field input:focus-visible, .form-field textarea:focus-visible { outline: 3px solid #FEB400; outline-offset: 2px; border-color: #201F1B; } +.form-field [aria-invalid="true"] { border-color: #9d2f2f; } +.field-error { min-height: 1.2em; margin: 0; color: #8a2424; font-size: .75rem; } +.form-trap { position: absolute !important; width: 1px !important; height: 1px !important; overflow: hidden !important; clip: rect(1px, 1px, 1px, 1px) !important; white-space: nowrap !important; } +.form-submit-row { display: flex; align-items: center; gap: 1rem; } +.form-submit-row button { flex: 0 0 auto; border: 1px solid var(--ink); background: var(--yellow); color: var(--ink); padding: .9rem 1.1rem; font: 700 .92rem 'IBM Plex Sans Thai', sans-serif; box-shadow: 5px 5px 0 var(--ink); cursor: pointer; transition: transform .2s ease, box-shadow .2s ease; } +.form-submit-row button:hover { transform: translate(-2px, -2px); box-shadow: 7px 7px 0 var(--ink); } +.form-submit-row button:focus-visible { outline: 3px solid var(--brand-green); outline-offset: 3px; } +.form-submit-row button:disabled { opacity: .7; cursor: wait; transform: none; } +.form-privacy { margin: 0; color: var(--muted); font-size: .72rem; } +.form-status { min-height: 1.4em; margin: 0; font-weight: 600; font-size: .85rem; } + +@media (max-width: 800px) { + .brand-logo img { width: 142px; } + .signal-panel { transform: rotate(-.5deg); box-shadow: 10px 10px 0 var(--ink); } + .hero-illustration { box-shadow: 10px 10px 0 var(--green); transform: rotate(-.5deg); } + .motion-ticker div { animation-duration: 16s; } + .contact-form { margin-top: .5rem; } +} + +@media (max-width: 480px) { + .project-image { height: auto; } + .contact-form { grid-template-columns: 1fr; } + .form-submit-row { align-items: flex-start; flex-direction: column; } +} + +@media (prefers-reduced-motion: reduce) { + html { scroll-behavior: auto; } + .motion-ticker div { animation: none; } + .service-card, .service-card::before, .portfolio-card { transition: none; } +} diff --git a/src/styles/service-page.css b/src/styles/service-page.css new file mode 100644 index 0000000..e977e81 --- /dev/null +++ b/src/styles/service-page.css @@ -0,0 +1,179 @@ +.landing-header { + position: sticky; + top: 0; + z-index: 20; + height: 84px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 2rem; + padding: 0 clamp(1.25rem, 4vw, 4.75rem); + background: rgba(248, 244, 233, .92); + border-bottom: 1px solid rgba(32, 31, 27, .2); + backdrop-filter: blur(12px); +} +.landing-logo img { width: clamp(145px, 13vw, 195px); height: auto; display: block; } +.landing-header nav { display: flex; gap: 1.7rem; font-size: .86rem; font-weight: 600; } +.landing-header nav a:hover { color: #2D5A47; } +.landing-contact { font-size: .86rem; font-weight: 700; border-bottom: 1px solid; } +.landing-services-menu { position: relative; } +.landing-services-menu summary { list-style: none; cursor: pointer; display: flex; gap: .45rem; align-items: center; } +.landing-services-menu summary::-webkit-details-marker { display: none; } +.landing-services-menu summary span { transition: transform .2s; } +.landing-services-menu[open] summary span { transform: rotate(45deg); } +.landing-services-menu > div { position: absolute; top: calc(100% + 1.1rem); left: 50%; transform: translateX(-50%); width: 410px; padding: .7rem; background: #FFFDF7; border: 1px solid #201F1B; box-shadow: 9px 9px 0 #FEB400; } +.landing-services-menu > div::before { content: ''; position: absolute; left: 0; right: 0; top: -1.2rem; height: 1.2rem; } +.landing-services-menu > div a { display: grid; grid-template-columns: 32px 1fr; gap: .65rem; padding: .8rem; border-bottom: 1px solid #D9D3C4; } +.landing-services-menu > div a:last-child { border-bottom: 0; } +.landing-services-menu > div a:hover, .landing-services-menu > div a.active { color: #201F1B; background: #FEB400; } +.landing-services-menu b { font: 700 .65rem Manrope, sans-serif; color: #2D5A47; } +.landing-services-menu a > span { display: flex; flex-direction: column; font: 700 .78rem Manrope, sans-serif; } +.landing-services-menu small { margin-top: .2rem; font: 500 .75rem 'IBM Plex Sans Thai', sans-serif; color: #625F55; } + +.landing-hero { + min-height: calc(100vh - 84px); + padding: clamp(5rem, 9vw, 9rem) clamp(1.25rem, 7vw, 8rem); + display: grid; + grid-template-columns: 1.1fr .9fr; + gap: clamp(3rem, 7vw, 8rem); + align-items: center; + overflow: clip; + position: relative; +} +.landing-hero::before { content: ''; position: absolute; width: 38vw; aspect-ratio: 1; background: #FEB400; border-radius: 50%; left: -19vw; top: 4rem; opacity: .18; } +.landing-eyebrow { margin: 0 0 1.25rem; font: 700 .7rem/1.3 Manrope, sans-serif; letter-spacing: .11em; text-transform: uppercase; color: #625F55; } +.landing-hero h1, .landing-heading h2, .urgency h2, .landing-case h2, .related-section h2, .landing-cta h2 { + margin: 0; + font-size: clamp(3rem, 6vw, 6.4rem); + line-height: .98; + letter-spacing: -.07em; +} +.landing-lead { max-width: 44rem; margin: 2rem 0; font-size: 1.08rem; line-height: 1.8; color: #504a40; } +.landing-button { display: inline-flex; gap: 3rem; padding: 1rem 1.2rem; background: #FEB400; border: 1px solid #201F1B; box-shadow: 7px 7px 0 #201F1B; font-weight: 700; transition: .25s ease; } +.landing-button:hover { transform: translate(-3px, -3px); box-shadow: 10px 10px 0 #201F1B; } +.landing-signal { min-height: 390px; padding: 1.4rem; display: flex; flex-direction: column; justify-content: space-between; background: #FEB400; border: 1px solid #201F1B; box-shadow: 18px 18px 0 #201F1B; transform: rotate(-1deg); position: relative; overflow: hidden; } +.landing-signal::before, .landing-signal::after { content: ''; position: absolute; width: 340px; aspect-ratio: 1; border: 1px solid rgba(23,21,15,.35); border-radius: 50%; left: 52%; top: 52%; transform: translate(-50%, -50%); } +.landing-signal::after { width: 215px; } +.landing-signal > span, .landing-signal small { position: relative; z-index: 1; font: 700 .65rem Manrope, sans-serif; letter-spacing: .12em; } +.landing-signal strong { position: relative; z-index: 1; font: 800 clamp(3.2rem, 7vw, 6rem)/.85 Manrope, sans-serif; letter-spacing: -.09em; overflow-wrap: anywhere; } +.signal-line { position: absolute; height: 1px; width: 75%; background: #201F1B; left: 12%; top: 53%; transform: rotate(-22deg); } +.landing-visual { margin: 0; aspect-ratio: 3 / 2; overflow: hidden; border: 1px solid #201F1B; box-shadow: 18px 18px 0 #FEB400; transform: rotate(-1deg); background: #D9D3C4; } +.landing-visual img { width: 100%; height: 112%; object-fit: cover; display: block; } + +.landing-ticker { overflow: hidden; background: #FEB400; border-block: 1px solid #201F1B; padding: .85rem 0; white-space: nowrap; font: 800 .78rem Manrope, sans-serif; letter-spacing: .12em; } +.landing-ticker div { width: max-content; animation: landingTicker 22s linear infinite; } +.landing-ticker span { margin: 0 1.2rem; } +@keyframes landingTicker { to { transform: translateX(-50%); } } + +.landing-section, .related-section { padding: clamp(5rem, 9vw, 9rem) clamp(1.25rem, 7vw, 8rem); } +.landing-heading { display: grid; grid-template-columns: 1.1fr .9fr; margin-bottom: 4rem; } +.landing-heading .landing-eyebrow { grid-column: 1 / -1; } +.landing-heading h2 { font-size: clamp(2.7rem, 5vw, 5.2rem); } +.pain-section { display: grid; grid-template-columns: .85fr 1.15fr; gap: 5rem; } +.pain-section .landing-heading { display: block; margin: 0; } +.pain-list { list-style: none; padding: 0; margin: 0; border-top: 1px solid #8d8577; } +.pain-list li { display: grid; grid-template-columns: 50px 1fr; gap: 1rem; padding: 1.3rem 0; border-bottom: 1px solid #8d8577; } +.pain-list span { font: 700 .7rem Manrope, sans-serif; color: #2D5A47; } +.pain-list p { margin: 0; font-size: clamp(1.08rem, 1.6vw, 1.35rem); } + +.urgency { padding: clamp(5rem, 9vw, 9rem) clamp(1.25rem, 13vw, 15rem); background: #FEB400; border-block: 1px solid #201F1B; } +.urgency h2 { max-width: 70rem; font-size: clamp(2.6rem, 5.2vw, 5.4rem); } +.urgency > p:last-child { max-width: 52rem; margin: 2rem 0 0 auto; font-size: 1.05rem; line-height: 1.8; } + +.approach-section { background: #FFFDF7; } +.approach-grid { display: grid; grid-template-columns: repeat(2, 1fr); border: 1px solid #201F1B; } +.approach-grid article { min-height: 220px; padding: 1.5rem; border-right: 1px solid #201F1B; border-bottom: 1px solid #201F1B; position: relative; } +.approach-grid article:nth-child(2n) { border-right: 0; } +.approach-grid article:nth-last-child(-n+2) { border-bottom: 0; } +.approach-grid span { font: 700 .7rem Manrope, sans-serif; color: #2D5A47; } +.approach-grid p { max-width: 33rem; margin: 4rem 0 0; font-size: 1.1rem; } + +.outcome-section { padding: clamp(5rem, 9vw, 9rem) clamp(1.25rem, 7vw, 8rem); background: #201F1B; color: #FFFDF7; display: grid; grid-template-columns: .9fr 1.1fr; gap: 5rem; } +.outcome-section .landing-heading { display: block; margin: 0; } +.outcome-section .landing-eyebrow { color: #d1c8b5; } +.outcome-section ul { list-style: none; padding: 0; margin: 0; border-top: 1px solid #554f43; } +.outcome-section li { display: flex; gap: 1.2rem; padding: 1.25rem 0; border-bottom: 1px solid #554f43; font-size: 1.08rem; } +.outcome-section li span { color: #FEB400; font-weight: 800; } + +.package-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1rem; } +.package-grid article { padding: clamp(1.5rem, 3vw, 2.5rem); border: 1px solid #201F1B; background: #FFFDF7; box-shadow: 8px 8px 0 #FEB400; } +.package-name { font: 700 .72rem Manrope, sans-serif; letter-spacing: .1em; text-transform: uppercase; color: #2D5A47; } +.package-grid h3 { min-height: 4.7rem; font-size: 1.45rem; line-height: 1.35; } +.package-grid ul { padding-left: 1.2rem; min-height: 9rem; color: #504a40; } +.package-price { display: flex; flex-direction: column; border-top: 1px solid #D9D3C4; padding-top: 1rem; } +.package-price strong { font-size: 1.15rem; } +.package-price span { color: #625F55; } +.package-note { margin: 2rem 0 0; color: #625F55; } + +.landing-case { padding: clamp(5rem, 9vw, 9rem) clamp(1.25rem, 7vw, 8rem); background: #201F1B; color: #FFFDF7; display: grid; grid-template-columns: .8fr 1.2fr; gap: 5rem; } +.landing-case .landing-eyebrow { color: #d1c8b5; } +.landing-case h2 { font-size: clamp(2.7rem, 5vw, 5.2rem); } +.landing-case > div > p:last-child { color: #cbc2af; line-height: 1.8; } +.landing-case figure { margin: 0; overflow: hidden; border: 1px solid rgba(254,180,0,.6); aspect-ratio: 1.53; } +.landing-case figure img { width: 100%; height: 100%; object-fit: contain; display: block; } +.landing-metrics { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(3, 1fr); border-top: 1px solid #554f43; } +.landing-metrics div { padding: 1.4rem; border-right: 1px solid #554f43; } +.landing-metrics div:last-child { border-right: 0; } +.landing-metrics strong { display: block; color: #FEB400; font: 800 clamp(2.2rem, 4vw, 4rem)/1 Manrope, sans-serif; letter-spacing: -.08em; } +.landing-metrics span { font: 700 .7rem Manrope, sans-serif; letter-spacing: .08em; color: #cbc2af; } + +.faq-section { background: #FFFDF7; } +.faq-list { border-top: 1px solid #201F1B; } +.faq-list details { border-bottom: 1px solid #201F1B; } +.faq-list summary { list-style: none; cursor: pointer; display: grid; grid-template-columns: 50px 1fr 30px; padding: 1.35rem 0; font-size: 1.08rem; font-weight: 600; } +.faq-list summary::-webkit-details-marker { display: none; } +.faq-list summary span { font: 700 .7rem Manrope, sans-serif; color: #2D5A47; } +.faq-list summary b { text-align: right; transition: transform .2s; } +.faq-list details[open] summary b { transform: rotate(45deg); } +.faq-list details > p { max-width: 55rem; margin: 0 0 1.5rem 50px; color: #504a40; line-height: 1.8; } + +.related-section { background: #FEB400; } +.related-section h2 { font-size: clamp(2.6rem, 4.8vw, 5rem); max-width: 55rem; } +.related-grid { display: grid; grid-template-columns: repeat(3, 1fr); margin-top: 3rem; gap: 1px; background: #201F1B; border: 1px solid #201F1B; } +.related-grid a { min-height: 230px; padding: 1.3rem; background: #FFFDF7; position: relative; transition: background .25s; } +.related-grid a:hover { background: #FEB400; } +.related-grid span { font: 700 .7rem Manrope, sans-serif; color: #2D5A47; } +.related-grid h3 { margin: 4.5rem 0 0; font-size: 1.25rem; line-height: 1.3; } +.related-grid b { position: absolute; right: 1.3rem; bottom: 1.1rem; } + +.landing-cta { padding: clamp(5rem, 9vw, 9rem) clamp(1.25rem, 7vw, 8rem); background: #2D5A47; color: #FFFDF7; border-top: 1px solid #201F1B; display: grid; grid-template-columns: 1fr 1fr; gap: 4rem; } +.landing-cta h2 { font-size: clamp(2.8rem, 5vw, 5.2rem); } +.landing-cta .landing-eyebrow { color: #FFFDF7; } +.landing-cta > div > p { max-width: 33rem; line-height: 1.8; } +.landing-contact-list { margin-top: 2rem; border-top: 1px solid rgba(255,253,247,.45); } +.landing-contact-list a { display: flex; gap: 1rem; justify-content: space-between; padding: 1.1rem 0; border-bottom: 1px solid rgba(255,253,247,.45); font-weight: 650; } +.landing-contact-list span { width: 3.5rem; font: 700 .65rem Manrope, sans-serif; letter-spacing: .08em; } + +@media (max-width: 800px) { + .landing-header { height: 68px; } + .landing-header nav { display: flex; margin-left: auto; } + .landing-header nav > a { display: none; } + .landing-header nav > a.landing-blog-link { display: inline; } + .landing-services-menu > div { position: fixed; top: 68px; left: auto; right: 1.25rem; transform: none; width: min(410px, calc(100vw - 2.5rem)); } + .landing-contact { font-size: .75rem; } + .landing-hero, .pain-section, .outcome-section, .landing-case, .landing-cta { grid-template-columns: 1fr; } + .landing-hero { min-height: auto; } + .landing-signal { min-height: 330px; box-shadow: 10px 10px 0 #201F1B; } + .landing-visual { box-shadow: 10px 10px 0 #FEB400; transform: rotate(-.5deg); } + .pain-section, .outcome-section, .landing-case { gap: 3rem; } + .landing-heading { grid-template-columns: 1fr; } + .approach-grid, .package-grid { grid-template-columns: 1fr; } + .approach-grid article { border-right: 0; border-bottom: 1px solid #201F1B !important; } + .approach-grid article:last-child { border-bottom: 0 !important; } + .related-grid { grid-template-columns: 1fr; } +} + +@media (max-width: 480px) { + .landing-header { gap: .75rem; } + .landing-logo img { width: 130px; } + .landing-contact { display: none; } + .landing-hero h1 { font-size: 3rem; } + .landing-metrics { grid-template-columns: 1fr; } + .landing-metrics div { border-right: 0; border-bottom: 1px solid #554f43; } + .landing-metrics div:last-child { border-bottom: 0; } +} + +@media (prefers-reduced-motion: reduce) { + .landing-ticker div { animation: none; } + .landing-button, .related-grid a, .faq-list summary b { transition: none; } +} diff --git a/src/styles/site-footer.css b/src/styles/site-footer.css new file mode 100644 index 0000000..bd58698 --- /dev/null +++ b/src/styles/site-footer.css @@ -0,0 +1,144 @@ +.site-footer { + width: 100%; + overflow: hidden; + border-top: 1px solid var(--ink); + background: var(--ink); + color: var(--paper); +} + +.site-footer-main { + display: grid; + grid-template-columns: minmax(220px, .7fr) minmax(0, 1.3fr); + align-items: end; + gap: clamp(2rem, 7vw, 7rem); + padding: clamp(2.6rem, 6vw, 5rem) clamp(1.25rem, 7vw, 8rem); +} + +.site-footer-brand > a { + display: inline-block; + max-width: 100%; +} + +.site-footer-brand img { + display: block; + width: min(195px, 100%); + height: auto; + filter: invert(1); +} + +.site-footer-brand p { + max-width: 25rem; + margin: 1.1rem 0 0; + color: #c9c3b6; + font-size: .9rem; + line-height: 1.7; +} + +.site-footer-main nav { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: .25rem 1.5rem; +} + +.site-footer a { + color: inherit; +} + +.site-footer-main nav a, +.site-footer-legal nav a { + min-height: 44px; + display: inline-flex; + align-items: center; + width: fit-content; + font-weight: 700; +} + +.site-footer-main nav a { + border-bottom: 1px solid rgba(255, 253, 247, .22); + font-size: clamp(.88rem, 1.2vw, 1rem); +} + +.site-footer-main nav a:hover, +.site-footer-legal nav a:hover { + color: var(--yellow); +} + +.site-footer-legal { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1.5rem; + padding: .7rem clamp(1.25rem, 7vw, 8rem); + border-top: 1px solid #49463f; + background: #171613; +} + +.site-footer-legal nav { + display: flex; + flex-wrap: wrap; + gap: .25rem 1.5rem; +} + +.site-footer-legal nav a { + color: #f3eddf; + font-size: .78rem; + text-decoration: underline; + text-decoration-color: rgba(243, 237, 223, .45); + text-underline-offset: 4px; +} + +.site-footer-legal p { + flex: 0 0 auto; + margin: 0; + color: #aaa398; + font: 600 .7rem/1.5 Manrope, sans-serif; +} + +.site-footer a:focus-visible { + outline: 3px solid var(--yellow); + outline-offset: 3px; +} + +@media (max-width: 800px) { + .site-footer-main { + grid-template-columns: 1fr; + align-items: start; + gap: 2rem; + } + + .site-footer-main nav { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width: 520px) { + .site-footer-main { + padding-block: 2.5rem; + } + + .site-footer-main nav { + grid-template-columns: 1fr; + gap: 0; + } + + .site-footer-main nav a { + width: 100%; + } + + .site-footer-legal { + align-items: flex-start; + flex-direction: column; + gap: .5rem; + padding-block: 1rem; + } + + .site-footer-legal nav { + width: 100%; + flex-direction: column; + gap: 0; + } + + .site-footer-legal nav a { + width: 100%; + } +} diff --git a/src/styles/static-pages.css b/src/styles/static-pages.css new file mode 100644 index 0000000..9a9bf1b --- /dev/null +++ b/src/styles/static-pages.css @@ -0,0 +1,58 @@ +.static-header { position: sticky; z-index: 20; top: 0; height: 78px; display: flex; align-items: center; justify-content: space-between; gap: 2rem; padding: 0 clamp(1.25rem, 5vw, 6rem); border-bottom: 1px solid rgba(32,31,27,.2); background: rgba(248,244,233,.92); backdrop-filter: blur(12px); } +.static-logo { display: inline-flex; } +.static-logo img { display: block; width: clamp(145px,14vw,195px); height: auto; } +.static-header nav { display: flex; gap: 1.6rem; font-size: .86rem; font-weight: 700; } +.static-header nav a:hover { color: var(--brand-green); } +.static-contact { border-bottom: 1px solid var(--ink); font-size: .84rem; font-weight: 700; } +.static-mobile-nav { display: none; } +.static-page { min-height: 70vh; } +.static-hero { display: grid; grid-template-columns: 1.2fr .8fr; gap: 2rem 5rem; align-items: end; padding: clamp(4.5rem,9vw,8rem) clamp(1.25rem,7vw,8rem); border-bottom: 1px solid var(--ink); } +.static-eyebrow { grid-column: 1/-1; margin: 0; color: var(--muted); font: 700 .7rem/1.2 Manrope,sans-serif; letter-spacing: .12em; text-transform: uppercase; } +.static-hero h1 { margin: 0; font-size: clamp(2.8rem,6vw,6rem); line-height: .98; letter-spacing: -.065em; } +.static-hero > p:last-child { max-width: 35rem; margin: 0; color: var(--muted); font-size: 1.05rem; line-height: 1.8; } +.static-section { padding: clamp(4rem,8vw,7rem) clamp(1.25rem,7vw,8rem); } +.static-section.paper { background: var(--paper); } +.static-section.green { background: var(--brand-green); color: var(--paper); } +.static-section.yellow { background: var(--yellow); border-block: 1px solid var(--ink); } +.static-heading { max-width: 66rem; margin-bottom: 3rem; } +.static-heading .static-eyebrow { margin-bottom: 1rem; } +.static-heading h2 { margin: 0; font-size: clamp(2.2rem,4.5vw,4.6rem); line-height: 1.05; letter-spacing: -.055em; } +.static-heading > p { max-width: 48rem; color: var(--muted); line-height: 1.8; } +.static-section.green .static-eyebrow, .static-section.green .static-heading > p { color: #e8e1d1; } +.static-grid { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); align-items: stretch; gap: 1rem; } +.static-card { padding: clamp(1.3rem,3vw,2rem); border: 1px solid var(--ink); background: var(--paper); color: var(--ink); box-shadow: 7px 7px 0 var(--yellow); } +.static-card .card-index { color: var(--brand-green); font: 700 .68rem Manrope,sans-serif; letter-spacing: .1em; } +.static-card h2, .static-card h3 { margin: 1rem 0 .7rem; font-size: clamp(1.35rem,2.3vw,2rem); line-height: 1.2; letter-spacing: -.035em; } +.static-card p { margin: .6rem 0 0; color: var(--muted); line-height: 1.75; } +.static-card .card-objective { color: var(--ink); font-weight: 700; } +.static-card-link { display: inline-flex; margin-top: 1.2rem; padding-bottom: .15rem; border-bottom: 1px solid; font-weight: 700; } +.static-list { margin: 0; padding: 0; list-style: none; border-top: 1px solid var(--line); } +.static-list li { display: grid; grid-template-columns: 3rem 1fr; gap: 1rem; padding: 1.2rem 0; border-bottom: 1px solid var(--line); } +.static-list span { color: var(--brand-green); font: 700 .68rem Manrope,sans-serif; } +.static-list h3 { margin: 0 0 .4rem; font-size: 1.2rem; } +.static-list p { margin: 0; color: var(--muted); line-height: 1.7; } +.faq-static { display: grid; gap: .8rem; } +.faq-static details { border: 1px solid var(--ink); background: var(--paper); } +.faq-static summary { list-style: none; cursor: pointer; display: grid; grid-template-columns: 7rem 1fr 1.5rem; gap: 1rem; padding: 1.15rem; font-weight: 700; } +.faq-static summary::-webkit-details-marker { display: none; } +.faq-static summary span { color: var(--brand-green); font: 700 .68rem Manrope,sans-serif; letter-spacing: .05em; } +.faq-static summary b { text-align: right; } +.faq-static details[open] summary b { transform: rotate(45deg); } +.faq-static details p { max-width: 65rem; margin: 0; padding: 0 1.15rem 1.25rem 9.15rem; color: var(--muted); line-height: 1.75; } +.legal-layout { display: grid; grid-template-columns: minmax(180px,.35fr) minmax(0,1fr); gap: clamp(2rem,6vw,6rem); } +.legal-rail { align-self: start; position: sticky; top: 110px; padding: 1.25rem; border: 1px solid var(--ink); background: var(--yellow); } +.legal-rail h2 { margin: .7rem 0; font-size: 1.3rem; } +.legal-rail p { margin: 0; font-size: .84rem; line-height: 1.7; } +.legal-copy { min-width: 0; max-width: 780px; line-height: 1.82; } +.legal-copy h2 { margin: 2.8rem 0 .9rem; padding-top: .7rem; border-top: 1px solid var(--line); font-size: clamp(1.45rem,2.5vw,2rem); } +.legal-copy h2:first-child { margin-top: 0; } +.legal-copy h3 { margin: 1.8rem 0 .7rem; } +.legal-copy p, .legal-copy ul { margin: 0 0 1.15rem; } +.legal-copy a { color: var(--brand-green); text-decoration: underline; text-underline-offset: 3px; } +.static-cta { display: flex; justify-content: space-between; align-items: center; gap: 2rem; padding: clamp(2rem,5vw,4rem) clamp(1.25rem,7vw,8rem); background: var(--yellow); border-block: 1px solid var(--ink); } +.static-cta h2 { max-width: 55rem; margin: 0; font-size: clamp(1.8rem,3.5vw,3.4rem); line-height: 1.15; letter-spacing: -.04em; } +.static-cta a { flex: 0 0 auto; padding: .85rem 1rem; border: 1px solid var(--ink); background: var(--paper); box-shadow: 5px 5px 0 var(--ink); font-weight: 700; } +.static-header a:focus-visible, .static-page a:focus-visible, .static-page summary:focus-visible { outline: 3px solid var(--yellow); outline-offset: 4px; } +@media(max-width:800px){.static-header{height:68px}.static-desktop-nav,.static-contact{display:none!important}.static-mobile-nav{display:block}.static-mobile-nav summary{list-style:none;cursor:pointer;border:1px solid var(--ink);padding:.42rem .6rem;font:700 .78rem Manrope,sans-serif}.static-mobile-nav summary::-webkit-details-marker{display:none}.static-mobile-nav[open] summary span{display:inline-block;transform:rotate(45deg)}.static-mobile-nav nav{position:absolute;right:1.25rem;top:58px;width:min(18rem,calc(100vw - 2.5rem));display:flex;flex-direction:column;gap:0;padding:.75rem;border:1px solid var(--ink);background:var(--paper);box-shadow:8px 8px 0 var(--yellow)}.static-mobile-nav nav a{padding:.8rem .35rem;border-bottom:1px solid var(--line)}.static-hero{grid-template-columns:1fr}.static-eyebrow{grid-column:auto}.static-grid{grid-template-columns:1fr}.legal-layout{grid-template-columns:1fr}.legal-rail{position:static}.static-cta{align-items:flex-start;flex-direction:column}} +@media(max-width:560px){.faq-static summary{grid-template-columns:1fr 1.5rem}.faq-static summary span{grid-column:1/-1}.faq-static details p{padding-left:1.15rem}} +@media(prefers-reduced-motion:reduce){.faq-static summary b{transition:none}}