{"meta":{"schema_version":1,"version":"1.0.0","generated_at":"2026-06-04T16:46:10Z","generated_from_repo_sha":"cd8f012","maintainer":"Sphere web team","notes":"v1.0.0 — initial release. Converts SphereIQ \"Sphere Quarterly\" mockups into site-styled /blogs articles: a Payload `posts` record (listing/SEO) plus a code-owned bespoke layout, restyled from the mockups' warm/per-issue palette to our brand. Encodes the pipeline (scope generator → register → shared overrides → seed), the brand conversion (DM Sans / Lora / Geist Mono, neutral gray scale, brand blue #1677ff, brand green #3bac52, brand purple #7F71FB held in reserve), the canonical repo files, and the regeneration-safety caveats. Validated by porting the first batch: agent-ready-sites, self-rewriting-site, compliance-as-runtime."},"online":{"manifest_url":"https://www.sphereinc.com/dev/skills/sphereiq-to-sphere-blog/manifest.json","version_url":"https://www.sphereinc.com/dev/skills/sphereiq-to-sphere-blog/version.json","changelog_url":"https://www.sphereinc.com/dev/skills/sphereiq-to-sphere-blog/changelog.md","self_update":"On skill activation, fetch version_url (5-min cache) and compare its `version` to this manifest's meta.version. If the remote is newer, re-fetch manifest_url + changelog_url, replace the local snapshot, and surface the changelog before acting."},"project":{"repo":"SITE/sphereinc-com","stack":"Next.js 16 App Router + Payload + Tailwind v4 + Neon Postgres","run_from":"repo root (so node resolves the project's postcss)","trigger":"porting/converting a SphereIQ \"Sphere Quarterly\" mockup (or any standalone bespoke editorial HTML + sphere.css) into a sphereinc.com /blogs article"},"why":["Mockups are full standalone microsites (own fonts, ~45KB sphere.css, canvas particle hero, heavy inline JS) — they cannot go through the Lexical blog renderer, so the layout lives in code keyed by slug.","They are articles, not microsites — each is a Payload `posts` record (lists in /blogs, sitemap, SEO), rendered at /blogs/<slug> inside the real site Header/Footer; the mockup's own topbar/footer are dropped.","sphere.css is scoped under `.quarterly-scope` so it can't leak into the site chrome; overrides.css recolors the warm/per-issue palette to our brand."],"architecture":{"render_route":"src/app/(frontend)/blogs/[slug]/page.tsx — `if (isQuarterly(slug)) return <QuarterlyArticle slug={slug} />` (no change needed for new slugs)","component":"src/components/QuarterlyArticle — loads fonts, imports scoped CSS + overrides, injects BODY + STYLE + SCRIPT","content_module":"src/content/quarterly/<slug>.ts — generated { meta, STYLE, BODY, SCRIPT } per article","registry":"src/content/quarterly/index.ts — QUARTERLY map + isQuarterly(slug)","scoped_css":"src/content/quarterly/sphere.scoped.css — the mockups' shared sphere.css scoped under .quarterly-scope (generated once per batch)","overrides":"src/content/quarterly/overrides.css — the brand conversion (regeneration-safe; imported AFTER the scoped css)","seed":"src/scripts/seed-quarterly-articles.ts — creates/updates the posts records","generator":".claude/skills/sphereiq-to-sphere-blog/scripts/gen-quarterly.mjs"},"pipeline":[{"n":1,"name":"Get inputs","do":"Pull the mockup HTML(s) + sphere.css (usually Monday assets via get_assets → signed S3 URLs; the protected_static links 404). Save to a temp dir."},{"n":2,"name":"Scope sphere.css (once per batch)","cmd":"node .claude/skills/sphereiq-to-sphere-blog/scripts/gen-quarterly.mjs --scope-css <sphere.css> src/content/quarterly/sphere.scoped.css","note":"Only when the batch's sphere.css changed; all issues in a batch share it."},{"n":3,"name":"Generate the content module (per article)","cmd":"node .claude/skills/sphereiq-to-sphere-blog/scripts/gen-quarterly.mjs <mockup>.html src/content/quarterly/<slug>.ts <slug>","note":"Extracts inline <style> (scoped), <body> (mockup topbar/footer stripped), trailing <script>; scopes CSS under .quarterly-scope; recolors the hero-particle RGB to brand blue + neutral ink. Pick <slug> from the mockup's canonical /quarterly/<slug>."},{"n":4,"name":"Register","do":"Add `import * as x from './<slug>'` and a `'<slug>': x` entry to src/content/quarterly/index.ts → QUARTERLY. The /blogs/[slug] route already renders any registered slug."},{"n":5,"name":"Confirm brand conversion","do":"overrides.css is shared so fonts/gray/green/cards/labels apply automatically. The one per-issue thing is the accent: force ALL issues to brand blue with `.quarterly-scope { --accent:#1677ff; --accent-2:#e3f0ff; --accent-dark:#0a53cc; }` (base, for no-data-accent issues) + `.quarterly-scope[data-accent] { --accent:#1677ff !important; ... }` (override inline indigo/amber/etc.)."},{"n":6,"name":"Seed the Payload record","do":"Add the slug's publishedAt to seed-quarterly-articles.ts (it reads title/deck/meta from the content module), then `pnpm payload run src/scripts/seed-quarterly-articles.ts`. Category defaults to Data & AI (id 6). The layout renders from code even without a record; the record exists for listing/sitemap/SEO."},{"n":7,"name":"Verify","do":"curl http://localhost:3000/blogs/<slug> → 200; eyeball: site Header/Footer wrap it, mockup chrome gone, brand-blue accent + particles, gray (not cream) surfaces, readable meta labels."}],"brand_conversion":{"fonts":{"sans_body":"DM Sans (var(--font-dm-sans), already on <html>) — was Inter","serif_display":"Lora (var(--font-lora)) — was Newsreader","mono":"Geist Mono for real <code>/<pre> only; mono LABELS (eyebrows/crumbs/byline/tool pills) use DM Sans"},"palette":{"paper":"neutral gray #f6f7f8 / #eef0f2 / #e3e5e8 — was warm cream #faf9f6 / #f4f2ec / #ebe7dd","ink":"#1f2025 (site ink) + neutral gray scale; --ink-4 darkened to #74777f so 10px meta labels read — was warm brown #1c1612","accent":"brand blue #1677ff (tint #e3f0ff, dark #0a53cc) — was per-issue purple/amber/coral","green":"brand green #3bac52 (design-system green-500, tint #e1f7e5) — was olive #5b8051","purple":"brand purple #7F71FB — third brand color, held in reserve for a secondary accent only if a mockup needs a second hue; default stays brand blue","hero_particles":"dimmed (opacity .35) + recolored neutral-ink → brand-blue (JS, baked by the generator)","cards":"bordered *-sidebar panels get a soft #f1f3f5 surface + #d6dae1 outline so they stand out from the gray page"}},"caveats":["overrides.css is the regeneration-safe place for CSS tweaks (imported after sphere.scoped.css). Never hand-edit the generated sphere.scoped.css or <slug>.ts for styling.","The particle recolor is JS, so it lives in the generated <slug>.ts — the generator bakes it in, so regenerating preserves it, but a manual re-port must go through the generator, not a hand-copy.","The mockup's inline <style> renders in the body, so it out-orders head stylesheets on equal specificity — that's why the accent override uses !important.","Content copy edits made directly in <slug>.ts (e.g. spelling/currency fixes) are NOT in the upstream Monday mockup; regenerating from the pristine mockup would lose them. For one-time articles this is acceptable; re-apply after any regenerate."]}