branding — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited branding (Agent Skill) and scored it 45/100 (orange). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 4 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 4 flagged
A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
The site is `site/` — Astro 5 + Tailwind v4, static-first ($0/mo on Netlify). No React runtime, no SSR adapter, no backend. Three pages: the home (/), the `@piprail/mcp` setup guide (`/mcp`), and the demo (/demo). The brand is dark, minimal, emerald-accented ("emerald = paid"). Design working files live in this skill's own `design/` folder (.claude/skills/branding/design/); the assets the site actually serves live in `site/public/` (repo). Never confuse the two.
design/ (bundled with this skill) — MASTER + working filesLives at `.claude/skills/branding/design/` — beside this SKILL, so the skill and its assets travel together and the repo root stays clean. One strict rule governs it: every file is SOURCE, PUBLISHED, GALLERY, or RENDER — read design/README.md, it's the law for this folder. SOURCE (templates/scripts), PUBLISHED (masters + profile assets), and GALLERY (a short, named allowlist of example renders) are tracked; every other RENDER a script generates is gitignored and regenerable.
.claude/skills/branding/design/
├── README.md # the rule (SOURCE / PUBLISHED / GALLERY / RENDER) — read it
├── source/ # ① MASTERS (tracked): logo-source.png · logo-512/256
├── social/ # ② SOCIAL — ALL external/brand imagery; source + gallery tracked, rest ignored
│ ├── CAMPAIGN.md # the campaign plan ("one chain a day")
│ ├── profile/ # PUBLISHED profile deliverables (tracked PNGs): og-github (→ GitHub social
│ │ # preview) · x-banner-1500x500 (→ X header) + x-banner.html (its source)
│ ├── chain-cards/ # per-chain announce cards — render.mjs (template in the script); gallery: solana, base
│ ├── post-cards/ # themed posts — 5 essential templates (one per layout) + render.mjs; gallery: open, explainer, plan
│ └── launch-cards/ # 4 canonical one-offs (png+html+script): hermes · payai · solana-gasless · multichain
├── video/ # ③ PROMO-VIDEO pipeline — own README; scene.html · capture/genassets/synth
│ # (renders gitignored)
└── deck/ # ④ PITCH DECK — the investor/partner deck: a generated, EDITABLE .pptx
# (pptxgenjs + embedded brand fonts). Own README; `npm run all` to build.The example gallery (the[gallery]cards above) is the answer to "where are the good cards?" — a curated, named set of finished renders committed to git via an explicit!…/*.pngallowlist in the repo.gitignore, so the best work is never lost locally. Everything else a script renders stays gitignored and regenerable. To keep a new card, promote it into the gallery (allowlist line + a note in that folder's README); don'tgit add -fstrays.
`deck/` is the one design pipeline tracked WHOLE (its own.gitignoreoverrides the renders-ignored rule): the deck is a PUBLISHED, shareable deliverable — the.pptx/design/deck/README.md.
Why `social/profile/` and not a separate `brand/`? Everything external-facing (profile headers and campaign cards) now lives undersocial/— one home for brand imagery. Theprofile/subfolder holds the few permanent, uploaded deliverables (tracked, even though sibling card renders are gitignored — a.gitignoreexception keepssocial/profile/*.pngin git).
Each pipeline folder (social/*, video/) has its own README with the exact render commands — read that folder's README before touching it; don't reverse-engineer the scripts. Don't dump a finished render into `source/` or `social/profile/` — if a script made it, it's a RENDER and stays under its pipeline, gitignored.
site/public/ — SHIPPED assets (served verbatim at piprail.com/…)Only optimized, final files go here. Everything is referenced by absolute path (/logo.png).
| File | What | Size |
|---|---|---|
logo.png | header/footer mark | square (≥256) |
favicon-16.png favicon-32.png favicon-48.png | browser favicons | 16/32/48 |
apple-touch-icon.png | iOS home-screen icon | 180×180 |
og.png | Open Graph / Twitter card (site) | 1200×630 |
chains/<chain>.svg | one logo per built-in chain (filename = lowercase SDK chain key/slug, e.g. base.svg, xrpl.svg); the set in site/public/chains/ is the source of truth for the current count | SVG |
tokens/<sym>.svg | one logo per default token (usdc usdt eurc rlusd); add one when a new default token ships | SVG |
robots.txt llms.txt llms-full.txt | crawler/LLM hints | — |
Rule: edit/keep originals indesign/; export only the final, optimized file intosite/public/. The GitHub social card (og-github-1280x640.png) stays indesign/social/profile/(the PUBLISHED profile-deliverables folder) — it's uploaded via the GitHub repo UI, not served by the site.
site/src/styles/global.css)This is the canonical visual identity. Every PipRail surface — the site, social cards, the promo video, the GitHub/X profiles — matches THESE tokens. The site's global.css @theme block is the literal source; change a brand value HERE and everything references it. The card/video templates (in design/) and the content studio's media standard (content-studio/MEDIA.md) restate the summary below for convenience, but this section is authoritative — when anything disagrees, this wins.
| Token | Value | Use |
|---|---|---|
--color-bg | oklch(0.145 0.005 260) ≈ `#0a0b0c` | near-black canvas (true black, not gray); also <meta theme-color> |
--color-fg | oklch(0.985 0 0) | near-white text |
--color-muted | oklch(0.62 0.01 260) | secondary / sub text |
--color-line | oklch(1 0 0 / 0.08) | hairline borders |
| `--color-accent` | `oklch(0.78 0.17 162)` ≈ `#2ee6a6` | emerald — "paid". THE one brand color; it does all the work. |
--color-accent-soft | accent at 12% | tints / fills |
| _lighter emerald_ | oklch(0.84–0.86 0.13 162) | glows, particles, the code-string token |
| _blue (hero only)_ | oklch(0.70 0.16 250) | the secondary hero-orb glow — low alpha, never a 2nd brand color |
A chain's OWN brand color (Base blue, Solana purple, …) is allowed only as that chain's logo — the contrast against black+emerald is what makes it pop. Brand purple #6e56cf appears ONLY in the root README's x402 shields.io badge — it is not a site token and never a brand color; don't add it.
h1, h2, headlines, the wordmark, big stat numbers. Ageometric grotesk that gives headings their own voice against the Inter body. Weights 500–700 (700 = bold display; it has no 800/900, so a heading is never heavier than 700 — don't fake it).
Layout.astro from Google Fonts — mirror this exact set in any template that draws aheading: Inter:wght@400;500;600;700 · Space+Grotesk:wght@500;600;700 · JetBrains+Mono:wght@400;500.
soft ambient drop-shadow. `lift` = the depth only (for hairline-grid cells). (utilities in global.css)
oklch(0.78 0.17 162 / .17) +blue oklch(0.70 0.16 250 / .13)) behind faint emerald particles (oklch(0.86 0.13 162)) — gentle, slow, decorative; the static content sits in front. Social cards echo this with a soft emerald (+ a hint of blue) radial glow behind the focal element — a whisper, not a strobe.
.tok-*): kw oklch(0.62 0.01 260) · str oklch(0.84 0.13 162) · fnoklch(0.8 0.11 235) · chain `oklch(0.85 0.14 85)` (amber — the eye lands on the chain word) · num oklch(0.8 0.12 25) · com oklch(0.5 0.01 260) italic.
prefers-reduced-motion (orbs / particles / marquee / fade-in hold still — handled in global.css).The feel in one line: dark, minimal, emerald-accented, contrast-not-glow. The restraint IS the brand.
Verbal identity (voice, messaging, what we say) lives in the [`content-studio`](../content-studio/SKILL.md) skill (BRAND.md+PLAYBOOK.md); its media standard (MEDIA.md) defers to THIS section for the visual tokens. branding = how it looks; content-studio = what it says. Together they're the brand.
site/src/
├── pages/ index.astro · mcp.astro · demo.astro # / · /mcp (@piprail/mcp guide) · /demo
├── layouts/ Layout.astro # <head>: SEO meta, OG, favicons, JSON-LD
├── components/ Navigation · Footer · CodeWindow · FeatureGrid · SectionHeading (.astro)
├── data/ chains.ts · snippets.ts # content data (no hardcoding in markup)
└── styles/ global.css # the @theme tokens abovenpm run dev -w @piprail/site · npm run build -w @piprail/site (output → site/dist/, gitignored).Layout.astro (og:image → /og.png, theme-color #0a0b0c, full Open Graph + Twitter summary_large_image, hreflang/canonical, keywords). It emits five application/ld+json blocks — SoftwareApplication (#sdk), Organization, WebSite, SoftwareSourceCode, and a distinct SoftwareApplication (#mcp) for @piprail/mcp (url /mcp). Update there.@astrojs/sitemap integration in astro.config.mjs (custom serialize() sets per-page priority/changefreq; output sitemap-index.xml).site/public/: llms.txt (concise index) + llms-full.txt (full pitch). Both carry a header line pinning SDK-Version, MCP-Version, and Last-Updated — refresh all three on every SDK/MCP publish (this is part of the release + docs-sync flow, not just a site edit). Any chain count quoted in these files must be re-synced with the rest of the site.chains.ts/snippets.ts over hardcoding in the page.demo.astro).sips, from the master)All raster assets derive from design/source/logo-source.png. Regenerate, then copy the final into site/public/:
# run from the repo root — the masters travel with this skill
SRC=.claude/skills/branding/design/source/logo-source.png
sips -s format png -Z 256 "$SRC" --out site/public/logo.png
sips -s format png -z 16 16 "$SRC" --out site/public/favicon-16.png
sips -s format png -z 32 32 "$SRC" --out site/public/favicon-32.png
sips -s format png -z 48 48 "$SRC" --out site/public/favicon-48.png
sips -s format png -z 180 180 "$SRC" --out site/public/apple-touch-icon.pngsips -Z 1200 in.png; sips -c 630 1200 in.png. Keep it under ~300 KB.design/social/profile/ (PUBLISHED deliverables), upload via repo Settings → Social preview.sips, sharp (already an Astro dep) or an SVG master works too.site/public/chains/<chain>.svg, filename = the lowercase SDK chain key (base, bnb, solana, xrpl, …) — one per built-in chain; ls site/public/chains/ is the current count, never a number quoted here.site/public/tokens/ (usdc, usdt, eurc, rlusd).https://piprail.com/chains/<chain>.svg).{ v: '…', l: 'chains built in' } stat tile in index.astro, the feature/FAQ copy, the EVM ×N families strip, the snippet comment in data/snippets.ts, and the JSON-LD in Layout.astro. When the built-in set changes, bump every occurrence: grep -rn "chains built in" site/src && grep -rnE "[0-9]+ chains|EVM ×|EVM mainnets|families" site/src. The authoritative total lives in sdk/README.md + sdk/CHAINS.md; the full count-propagation procedure is owned by the add-chain-integration and docs-sync skills — follow those rather than treating any number here as fixed.site/public/ — only final, optimized assets the site serves.global.css.prefers-reduced-motion or the dark theme.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.