diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json
index d41b519..c239f04 100644
--- a/.claude-plugin/marketplace.json
+++ b/.claude-plugin/marketplace.json
@@ -1,8 +1,8 @@
{
"name": "brand-studio",
"owner": {
- "name": "REPLACE-WITH-YOUR-NAME",
- "url": "https://github.com/REPLACE-WITH-YOUR-GITHUB-USER"
+ "name": "Shadowtor",
+ "url": "https://github.com/shadowtor"
},
"metadata": {
"description": "Brand identity and logo design skill: brief to sketches to full brand kit.",
diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json
index 672a51d..882fc65 100644
--- a/.claude-plugin/plugin.json
+++ b/.claude-plugin/plugin.json
@@ -3,11 +3,11 @@
"description": "Brand identity and logo design from a business brief: research, three greyscale sketches for approval, independent sub-agent review, then colour, type, lockups and a full kit (logos in colour/dark/black/white/greyscale as SVG/PNG/PDF/JPG, tested favicons, social banners, stationery, merch mockups chosen for the business, tokens, brand guidelines PDF).",
"version": "1.0.0",
"author": {
- "name": "REPLACE-WITH-YOUR-NAME",
- "url": "https://github.com/REPLACE-WITH-YOUR-GITHUB-USER"
+ "name": "Shadowtor",
+ "url": "https://github.com/shadowtor"
},
- "homepage": "https://github.com/REPLACE-WITH-YOUR-GITHUB-USER/brand-studio",
- "repository": "https://github.com/REPLACE-WITH-YOUR-GITHUB-USER/brand-studio",
+ "homepage": "https://github.com/shadowtor/BrandDesign",
+ "repository": "https://github.com/shadowtor/BrandDesign",
"license": "MIT",
"keywords": ["logo", "branding", "brand-identity", "svg", "favicon", "brand-guidelines", "mockups", "design"]
}
diff --git a/.gitattributes b/.gitattributes
index dfe0770..544f341 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,2 +1,12 @@
# Auto detect text files and perform LF normalization
* text=auto
+*.svg text eol=lf
+*.py text eol=lf
+*.md text eol=lf
+*.json text eol=lf
+*.png binary
+*.jpg binary
+*.jpeg binary
+*.pdf binary
+*.ico binary
+*.gz binary
diff --git a/.gitignore b/.gitignore
index 2fd15c8..cabc6d3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,12 +1,37 @@
+# OS / editor
.DS_Store
Thumbs.db
+desktop.ini
+.vscode/
+.idea/
+
+# Python
__pycache__/
*.pyc
+.venv/
+venv/
+
+# build output
dist/
+*.zip
+*.log
+
+# working folders and full kits: examples/ holds the curated previews instead
+brand-studio-work/
*-workspace/
+kit/
+deliver/
preview*.html
-# working files and downloaded third-party mockup PSDs must never be committed
-brand-studio-work/
+
+# third-party mockup PSDs are downloaded to ~/.cache/brand-studio and must never be committed
*.psd
*.psb
+*.ai
+*.eps
mockups-cache/
+.cache/
+
+# secrets
+.env
+.env.*
+*.pem
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..668df6e
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,20 @@
+# Contributing
+
+Thanks for helping. The most useful contributions:
+
+- **Merch items**: add a row to `ITEMS` in `skills/brand-studio/scripts/merch_catalog.py` (label, mockups-design.com page slug or `None`,
+ logo form, scale, cylinder). If there is no free PSD, add an illustration function to `merch.py`. If a PSD needs placement tweaks,
+ add an entry to `ADJUST` after comparing the result with the mockup's own preview image.
+- **Business types**: extend `BUSINESS_TYPES` / `ALIASES` with what that kind of business really makes (not a generic mug and shirt).
+- **Reference data**: sector palettes and logos you have the right to redistribute (see `references/library-guide.md`).
+- **Evals**: prompts and expected behaviour in `skills/brand-studio/evals/`.
+- **Bug reports**: include your OS, `python skills/brand-studio/scripts/check_setup.py` output and the failing command.
+
+## Before opening a pull request
+1. `python tools/smoke_test.py` passes (needs a Chromium browser and internet).
+2. Any new script has a docstring with usage, runs with `--help`, and keeps working on Windows, macOS and Linux (no shell-specific code).
+3. New third-party data or fonts: add them to `THIRD_PARTY_NOTICES.md` with their licence. Never commit PSD/PSB files, client work or
+ anything you do not have the right to publish.
+4. Keep claims honest: tests say what they do not cover; heuristics are labelled as heuristics.
+
+By contributing you agree your work is released under the repository's MIT licence.
diff --git a/README.md b/README.md
index c56037a..2584c9b 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,206 @@
-# BrandDesign
+# brand-studio
+[](https://github.com/shadowtor/BrandDesign/actions/workflows/test.yml) [](LICENSE)
+
+**A brand identity and logo design skill for AI coding agents** (Claude Code, Codex CLI, Gemini CLI and anything that reads
+Agent Skills folders: a directory with a `SKILL.md`). Give it a business brief; it researches the category, shows **three greyscale
+logo sketches** for you to choose from, has an **independent sub-agent review them**, and only then builds the full brand kit.
+
+
+
+Kiln (coffee roastery), Rookery (gaming community) and Marigold Press (illustrator): fictional briefs, every file in
+[`examples/`](examples) was produced by the skill. Merch is chosen for each business, not a generic mug and shirt.
+
+**Contents:** [What you get](#what-you-get) · [Install](#install) · [Use](#use) · [Merch by business type](#merch-is-chosen-for-the-business) ·
+[Examples](#examples) · [Limits](#what-it-will-not-do-for-you) · [Develop](#develop) · [Credits](#credits-and-attribution)
+
+## What you get
+
+| | |
+|---|---|
+| **Logos** | square, horizontal, vertical, symbol, wordmark × colour (light), colour (dark), black, white, greyscale × **SVG / PNG / JPG / PDF** |
+| **Favicons** | favicon.svg/.ico, 16–48 px, apple-touch, 192/512, maskable, web manifest, head snippet, plus a **test sheet and report** (ICO structure, sizes, manifest, safe zone, 16 px legibility on light and dark tabs) |
+| **Social** | avatars (3 backgrounds × 3 sizes) and banners for LinkedIn, X, Facebook, YouTube, link previews, Instagram |
+| **Stationery** | A4 letterhead (sample + blank), header and footer strips, business card print file with bleed, email signature |
+| **Merch mockups** | items chosen for the business type (a coffee bean bag and travel mug for a roaster; jersey, mouse mat and hoodie for a gaming community; USB stick, cables and lanyards for tech…), **photographic** via free PSD mockups (opt-in) or labelled illustrations |
+| **Guidelines** | a PDF built from the real files: idea, logo family, clear space, versions, colour (HEX/RGB/HSL/OKLCH, contrast), type, misuse (including ones only this mark can suffer), favicon test, social, stationery, merch |
+| **Tokens** | CSS, SCSS, Tailwind, DTCG JSON, GIMP palette, swatches; font embed notes |
+
+**How it works:** intake → category research (bundled 1,400-logo and brand-palette libraries + live Fontsource/colour APIs) →
+8–12 one-line concepts → **three different sketches** (black SVG, audited at 16 px, one colour, reversed) → independent review →
+**you pick** → refine, colour (WCAG-audited), type (real outlined fonts), lockups → `build_kit.py` → kit review → handover with the
+open items stated plainly (trademark search, print proofing, real-browser favicon check).
+
+## Install
+
+You need Python 3.9+, a Chromium-based browser (Chrome, Edge, Brave, Chromium) and internet access for fonts. [`uv`](https://docs.astral.sh/uv/)
+is recommended (it installs Pillow, pypdf, fonttools, uharfbuzz and psd-tools on first use).
+
+### A. Claude Code plugin marketplace
+
+```text
+/plugin marketplace add shadowtor/BrandDesign
+/plugin install brand-studio@brand-studio
+```
+
+### B. Installer script (Claude Code, Codex CLI, Gemini CLI, any agent)
+
+```bash
+git clone https://github.com/shadowtor/BrandDesign.git
+cd BrandDesign
+python tools/install.py --claude --codex # or --gemini, --project, --dest , --link (symlink for development)
+python skills/brand-studio/scripts/check_setup.py # verifies Python, uv, browser, network
+```
+
+Manual alternative: copy the `skills/brand-studio` folder to `~/.claude/skills/brand-studio` (Claude Code), `~/.codex/skills/brand-studio`
+(Codex), `~/.gemini/skills/brand-studio` (Gemini) or your agent's skills folder. Start a **new session** afterwards.
+
+### C. Let your AI agent install it (copy-paste)
+
+Paste this into your agent. It follows [`docs/INSTALL-FOR-AGENTS.md`](docs/INSTALL-FOR-AGENTS.md), which it can also read from the repo.
+
+```text
+Install the "brand-studio" Agent Skill for me.
+
+1. Clone https://github.com/shadowtor/BrandDesign (or git pull if I already have it) and read
+ docs/INSTALL-FOR-AGENTS.md in it. Follow that file exactly.
+2. Install it for the agent I am using right now (python tools/install.py --claude, --codex or --gemini; use --dest for others).
+ Only copy the skills/brand-studio folder; do not change anything inside it and do not run any other script from it.
+3. Run: python skills/brand-studio/scripts/check_setup.py. If it reports a FAIL, show me the line and its fix and stop. If it reports
+ WARN lines (for example uv missing), explain what is limited and offer the fix.
+4. Tell me where it was installed, the check result, and that I must start a new session. Do not commit, push or download anything
+ else. Then give me one example request I can try.
+```
+
+### D. Upload as a skill file
+
+`python tools/package_skill.py` writes `dist/brand-studio.zip` (and a `-lite` zip without the reference SVGs) for UIs that take a skill upload.
+Note: the skill runs scripts and a browser; use an environment that allows both (a code-capable agent, not plain chat).
+
+## Use
+
+Just ask:
+
+> Design a brand identity for **Rookery**, a competitive-but-welcoming gaming community. Three logo sketches first.
+
+> Here is our logo (logo.svg): what is wrong with it, and how would you refresh it without losing recognition?
+
+> Turn this symbol into a favicon set and social avatars, and test the favicon at 16 px.
+
+It asks a few questions (or states assumptions), shows the concept sheet and **stops**. Pick a direction and say go; it builds the kit
+into the folder you name (on Windows keep the work folder on a short path).
+
+Build a kit by hand from approved artwork:
+
+```bash
+cp skills/brand-studio/templates/brand.example.json brand.json # edit names, colours, logo file paths, business_type
+python skills/brand-studio/scripts/build_kit.py brand.json --out deliver # everything except photo mockups
+python skills/brand-studio/scripts/build_kit.py brand.json --out deliver --photo-mockups # + photographic merch previews
+```
+
+Photographic merch previews are opt-in: the first run downloads free PSDs from mockups-design.com into `~/.cache/brand-studio`
+(roughly 5 minutes; see [credits](#credits-and-attribution)).
+
+## Merch is chosen for the business
+
+`scripts/merch_catalog.py` maps a `business_type` to the items that kind of business really makes (override with an explicit list).
+
+| Business | Items |
+|---|---|
+| Coffee | bean bag, travel mug, takeaway cup, mug, apron, tote, tee, cap |
+| Gaming / esports | team jersey, mouse mat, hoodie, tee, cap, sticker sheet, lanyard, keyring |
+| Tech / SaaS | business card, USB stick, charging cable, lanyard, phone case, laptop sleeve, tee, sticker sheet |
+| Artist / illustrator | sticker sheet, business card, tee, tote, notebook, poster, pin badge |
+| + fashion, fitness, finance, services, health, beauty, events, music, education, community, retail, trades, kids, pets, bakery, bar | see [`merch-catalogue.md`](skills/brand-studio/references/merch-catalogue.md) |
+
+Each item uses a photographic mockup when a free PSD exists, otherwise a drawn illustration, and every file is labelled
+*photographic mockup* or *illustration* in the guidelines. Nothing is shown that was not actually made.
+
+## Examples
+
+Fictional brands, built end to end. Each folder has the `brand.json`, the final SVG artwork, every SVG logo version, and previews of
+the guidelines PDF, favicon test, social banner, letterhead and merch mockups.
+
+| Example | Business | Mark | Merch set |
+|---|---|---|---|
+| [Kiln](examples/kiln) | specialty coffee roastery | arched kiln mouth with an ember | bean bag, takeaway cup, mug, travel mug, apron, tote |
+| [Rookery](examples/rookery) | competitive gaming community | pixel-block rook with one lit window | jersey, mouse mat, hoodie, tee, cap, sticker sheet |
+| [Marigold Press](examples/marigold) | independent illustrator | eight-petal marigold seen from above | sticker sheet, business card, tee, tote, notebook, poster |
+
+## What it will not do for you
+
+- **No trademark clearance.** Search before launch (IP Australia, WIPO Global Brand Database, EUIPO TMview, USPTO). The skill says so every time.
+- **CMYK is indicative, Pantone is blank.** Print needs the printer's profile and a proof.
+- **The favicon test is a simulated tab.** Open the live site in a real browser once.
+- **Mockups are previews**, not publishable artwork; third-party PSDs are never redistributed.
+- It cannot guarantee a mark is unique. The reference libraries are for study; never copy or trace them.
+
+## Develop
+
+```bash
+python tools/smoke_test.py # runs the scripts end to end (needs a Chromium browser + internet)
+python tools/package_skill.py # dist/brand-studio.zip
+python tools/install.py --link # symlink the skill into your agent folders and edit in place
+```
+
+Layout: [`docs/architecture.md`](docs/architecture.md). Contributions welcome: see [`CONTRIBUTING.md`](CONTRIBUTING.md) (new merch items,
+business types, sector reference data, evals).
+
+## Licence
+
+MIT, see [LICENSE](LICENSE). Incorporates MIT work (see credits). The reference logo and palette libraries, fonts and mockup PSDs are
+third-party material with their own terms: [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
+
+---
+
+## Credits and attribution
+
+### Built on
+- **[kaankiziltug/logo-design-skill](https://github.com/kaankiziltug/logo-design-skill)** (MIT): the design process, principles, SVG
+ construction and testing tooling, concept sheet, 1,400-logo reference library and its example boards.
+- **[fatihaydost/brand-identity-skill](https://github.com/fatihaydost/brand-identity-skill)** (MIT): the one-mechanism idea, AI-defaults
+ reflex test, OKLCH palette build and WCAG/colour-blindness audit, honest print guidance, kit and critique structure, token export.
+
+### Mockup sources (used for previews only)
+
+The photographic previews in `examples/` were made with free PSD mockups from **[mockups-design.com](https://mockups-design.com/)**
+(licence: free for any purpose including commercial use, no attribution required, but the PSD files themselves may not be
+redistributed; flattened finished images may be shared: [licence](https://mockups-design.com/license/)). We credit them anyway. The PSDs
+are **not** in this repository: the skill downloads them on demand into `~/.cache/brand-studio/mockups` when you run `--photo-mockups`.
+
+| Mockup | Pack | Used in |
+|---|---|---|
+| [Coffee cup](https://mockups-design.com/paper-coffee-cup-mockup/) | MD049 Free Coffee Paper Cup Mockup | Kiln |
+| [Food pouch (coffee bean bag stand-in)](https://mockups-design.com/free-food-pouch-mockup/) | MD474 Free Food Bag Mockup | Kiln |
+| [Oval mug](https://mockups-design.com/round-bellied-mug-mockup/) | MD1258 Oval Mug Mockup | Kiln |
+| [Apron](https://mockups-design.com/stitched-bib-apron-mockup/) | MD1260 Apron Mockup | Kiln |
+| [Hanging tote bag](https://mockups-design.com/hanging-tote-bag-mockup/) | MDP071 Hanging Tote Bag Mockup | Kiln, Marigold Press |
+| [Large mouse pad](https://mockups-design.com/oversized-mouse-pad-mockup/) | MD1237 Large Mousepad Mockup | Rookery |
+| [Hoodie](https://mockups-design.com/free-hoodie-mockup/) | MDP059 Hoodie Mockup | Rookery |
+| [T-shirt](https://mockups-design.com/mens-t-shirt-mockup/) | MD815 T-Shirt Mockup | Rookery, Marigold Press |
+| [Baseball cap](https://mockups-design.com/free-baseball-cap-mockup/) | MD341 Free Baseball Cap Mockup | Rookery |
+| [Business card](https://mockups-design.com/free-business-card-mockup/) | MD050 Business Card Mockup | Marigold Press |
+| [Spiral notebook](https://mockups-design.com/free-spiral-notebook-mockup/) | MD095 Spiral Notebook Mockup | Marigold Press |
+| [Poster in frame](https://mockups-design.com/free-posters-mockup/) | MD078 Free Poster Mockup | Marigold Press |
+
+Merch items with no free PSD on that site (jersey, lanyard, phone case, cable, travel mug, sticker sheet…) are drawn by the skill and
+labelled **illustration** in every kit. **[Unblast](https://unblast.com/mockups/)** has many more (lanyards, jerseys, phone cases,
+USB cards, tumblers, coffee packaging): its mockups come from third-party authors with their own licences and a JavaScript
+download step, so the skill does not download them automatically; download one yourself, read its licence, and pass it with
+`psd_mockup.py --psd`. Thank you to both sites and to the mockup authors.
+
+### Inspiration and reference sites
+[Brand New](https://www.underconsideration.com/brandnew/) · [Fonts In Use](https://fontsinuse.com/) ·
+[Branding Style Guides](https://brandingstyleguides.com/) · [Standards Manual](https://standardsmanual.com/) (NASA 1976, NYCTA 1970) ·
+[Google Fonts Knowledge](https://fonts.google.com/knowledge) · [Are.na](https://www.are.na/) · guideline anatomy from NASA, NYCTA, Mozilla,
+GitLab, Spotify, Uber Eats, Atlassian and Mailchimp (links in `skills/brand-studio/references/bi-kit.md`).
+
+### Services queried at run time
+[Google Fonts](https://fonts.google.com/) and the [Fontsource API](https://fontsource.org/docs/api) (fonts, licences),
+[TheColorAPI](https://www.thecolorapi.com/) and [Colormind](http://colormind.io/) (palette suggestions), [Iconify](https://iconify.design/)
+(symbol inspiration only). Fonts in the examples are SIL OFL families: Young Serif, Hanken Grotesk, Chakra Petch, Barlow, Instrument Serif,
+Instrument Sans.
+
+### Research behind the guidance
+Colour, type and logo guidance cites its sources inline in the `bi-*` reference cards (WCAG 2.2, Labrecque et al., Phillips et al.,
+Henderson & Cote and others, each tagged strong / limited / practice).
diff --git a/docs/INSTALL-FOR-AGENTS.md b/docs/INSTALL-FOR-AGENTS.md
index 4166f86..94977cf 100644
--- a/docs/INSTALL-FOR-AGENTS.md
+++ b/docs/INSTALL-FOR-AGENTS.md
@@ -14,8 +14,8 @@ You are an AI coding agent (Claude Code, Codex CLI, Gemini CLI, Cursor, Copilot
## 1. Get the repository
```bash
-git clone https://github.com/REPLACE-WITH-YOUR-GITHUB-USER/brand-studio.git
-cd brand-studio
+git clone https://github.com/shadowtor/BrandDesign.git
+cd BrandDesign
```
If the user already has a clone, run `git pull` instead. If `git` is not available, download the repository ZIP from GitHub and extract it.
@@ -37,7 +37,7 @@ Use `python3` instead of `python` on macOS/Linux when `python` is missing; on Wi
Add `--link` to symlink instead of copy (useful if the user will edit the repository).
**Claude Code plugin alternative** (no clone needed): tell the user to run
-`/plugin marketplace add REPLACE-WITH-YOUR-GITHUB-USER/brand-studio` and then `/plugin install brand-studio@brand-studio`.
+`/plugin marketplace add shadowtor/BrandDesign` and then `/plugin install brand-studio@brand-studio`.
You cannot run slash commands yourself; give them these two lines.
## 3. Verify
@@ -49,7 +49,7 @@ python skills/brand-studio/scripts/check_setup.py
Expected: `PASS` for Python and a Chromium browser; `PASS` or `WARN` for uv and network. A `FAIL` means the skill cannot work:
fix it first (the line includes the fix). `INFO` lines for Pillow/pypdf/fonttools are normal: they are installed on demand.
If `uv` is missing, offer to install it (command is in the `WARN` line) or run
-`pip install pillow pypdf fonttools uharfbuzz` for the same effect.
+`pip install pillow pypdf fonttools uharfbuzz pymupdf` (plus `"psd-tools[composite]" numpy scipy scikit-image` for `--photo-mockups`) for the same effect.
Optional deeper check (needs internet, ~2 minutes): `python tools/smoke_test.py`.
diff --git a/examples/kiln/README.md b/examples/kiln/README.md
new file mode 100644
index 0000000..9518949
--- /dev/null
+++ b/examples/kiln/README.md
@@ -0,0 +1,32 @@
+# Kiln
+
+*Specialty coffee roastery, Istanbul (fictional). Not a real business: made to show what the skill produces.*
+
+- Business type: **coffee** (decides the merch set)
+- Merch: Coffee bean bag (photo), Takeaway coffee cup (photo), Mug (photo), Travel mug (illustration), Apron (photo), Tote bag (photo)
+- Brand data: [`brand.json`](brand.json) · final artwork: [`final/`](final) · all SVG logo versions: [`logo/svg/`](logo/svg)
+- Guidelines PDF: [`preview/kiln-brand-guidelines.pdf`](preview/kiln-brand-guidelines.pdf)
+
+## Logo versions (horizontal and symbol × colour-light, colour-dark, black, white, greyscale)
+
+
+
+## Merch chosen for this business
+
+
+
+## Guidelines pages
+
+
+
+## Favicon test
+
+
+
+## Social banner and letterhead
+
+
+
+
+
+Photographic mockups are previews built from free PSDs by mockups-design.com (see the main README credits). The PSDs are not included.
diff --git a/examples/kiln/brand.json b/examples/kiln/brand.json
new file mode 100644
index 0000000..4674b20
--- /dev/null
+++ b/examples/kiln/brand.json
@@ -0,0 +1,61 @@
+{
+ "name": "Kiln",
+ "slug": "kiln",
+ "tagline": "Small-batch coffee, roasted in Istanbul",
+ "business_type": "coffee",
+ "adjectives": [
+ "warm",
+ "crafted",
+ "modern"
+ ],
+ "mechanism": "The arched firing mouth of a kiln with one ember inside: where the coffee is made.",
+ "rationale": "Fictional example brand. A specialty roastery that wants a crafted, modern look without rustic clich\u00e9. The arch is the kiln; the ember is the roast; the arch also reads as a K-less initial 'n'.",
+ "colors": {
+ "primary": "#D9480F",
+ "secondary": "#F2B544",
+ "ink": "#1F1A17",
+ "paper": "#F7F5F0",
+ "accent": "#F2B544"
+ },
+ "color_names": {
+ "primary": "Kiln Orange",
+ "secondary": "Ember",
+ "accent": "Ember",
+ "ink": "Roast",
+ "paper": "Parchment"
+ },
+ "logo": {
+ "symbol": "final/symbol.svg",
+ "horizontal": "final/horizontal.svg",
+ "vertical": "final/vertical.svg",
+ "square": "final/square.svg",
+ "wordmark": "final/wordmark.svg",
+ "favicon_source": "final/favicon-source.svg",
+ "app_icon": {
+ "bg": "#F7F5F0",
+ "fg": "keep"
+ },
+ "dark_map": {
+ "#1f1a17": "#f7f5f0"
+ },
+ "dark_ground": "#14100E"
+ },
+ "fonts": {
+ "display": "Young Serif",
+ "text": "Hanken Grotesk",
+ "display_weight": 400
+ },
+ "contact": {
+ "address": "1 Example Street, Istanbul",
+ "email": "hello@kiln.example",
+ "web": "kiln.example"
+ },
+ "merch": [
+ "coffee-bag",
+ "coffee-cup",
+ "mug",
+ "travel-mug",
+ "apron",
+ "tote"
+ ]
+}
\ No newline at end of file
diff --git a/examples/kiln/final/favicon-source.svg b/examples/kiln/final/favicon-source.svg
new file mode 100644
index 0000000..104e9c0
--- /dev/null
+++ b/examples/kiln/final/favicon-source.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/final/horizontal.svg b/examples/kiln/final/horizontal.svg
new file mode 100644
index 0000000..361f3a4
--- /dev/null
+++ b/examples/kiln/final/horizontal.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/final/square.svg b/examples/kiln/final/square.svg
new file mode 100644
index 0000000..d11e2ef
--- /dev/null
+++ b/examples/kiln/final/square.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/final/symbol.svg b/examples/kiln/final/symbol.svg
new file mode 100644
index 0000000..65147e1
--- /dev/null
+++ b/examples/kiln/final/symbol.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/final/vertical.svg b/examples/kiln/final/vertical.svg
new file mode 100644
index 0000000..be8313d
--- /dev/null
+++ b/examples/kiln/final/vertical.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/final/wordmark.svg b/examples/kiln/final/wordmark.svg
new file mode 100644
index 0000000..f9666c5
--- /dev/null
+++ b/examples/kiln/final/wordmark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-horizontal-black.svg b/examples/kiln/logo/svg/kiln-horizontal-black.svg
new file mode 100644
index 0000000..43025ee
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-horizontal-black.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-horizontal-colour-dark.svg b/examples/kiln/logo/svg/kiln-horizontal-colour-dark.svg
new file mode 100644
index 0000000..8599daa
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-horizontal-colour-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-horizontal-colour-light.svg b/examples/kiln/logo/svg/kiln-horizontal-colour-light.svg
new file mode 100644
index 0000000..361f3a4
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-horizontal-colour-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-horizontal-greyscale.svg b/examples/kiln/logo/svg/kiln-horizontal-greyscale.svg
new file mode 100644
index 0000000..de7839f
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-horizontal-greyscale.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-horizontal-white.svg b/examples/kiln/logo/svg/kiln-horizontal-white.svg
new file mode 100644
index 0000000..56bba2f
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-horizontal-white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-square-black.svg b/examples/kiln/logo/svg/kiln-square-black.svg
new file mode 100644
index 0000000..76965af
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-square-black.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-square-colour-dark.svg b/examples/kiln/logo/svg/kiln-square-colour-dark.svg
new file mode 100644
index 0000000..76c2b21
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-square-colour-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-square-colour-light.svg b/examples/kiln/logo/svg/kiln-square-colour-light.svg
new file mode 100644
index 0000000..d11e2ef
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-square-colour-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-square-greyscale.svg b/examples/kiln/logo/svg/kiln-square-greyscale.svg
new file mode 100644
index 0000000..f961691
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-square-greyscale.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-square-white.svg b/examples/kiln/logo/svg/kiln-square-white.svg
new file mode 100644
index 0000000..cf869d6
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-square-white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-symbol-black.svg b/examples/kiln/logo/svg/kiln-symbol-black.svg
new file mode 100644
index 0000000..e2ae0b1
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-symbol-black.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-symbol-colour-dark.svg b/examples/kiln/logo/svg/kiln-symbol-colour-dark.svg
new file mode 100644
index 0000000..ddeccf7
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-symbol-colour-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-symbol-colour-light.svg b/examples/kiln/logo/svg/kiln-symbol-colour-light.svg
new file mode 100644
index 0000000..65147e1
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-symbol-colour-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-symbol-greyscale.svg b/examples/kiln/logo/svg/kiln-symbol-greyscale.svg
new file mode 100644
index 0000000..70a4e6b
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-symbol-greyscale.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-symbol-white.svg b/examples/kiln/logo/svg/kiln-symbol-white.svg
new file mode 100644
index 0000000..87abc11
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-symbol-white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-vertical-black.svg b/examples/kiln/logo/svg/kiln-vertical-black.svg
new file mode 100644
index 0000000..17dc766
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-vertical-black.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-vertical-colour-dark.svg b/examples/kiln/logo/svg/kiln-vertical-colour-dark.svg
new file mode 100644
index 0000000..d45b313
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-vertical-colour-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-vertical-colour-light.svg b/examples/kiln/logo/svg/kiln-vertical-colour-light.svg
new file mode 100644
index 0000000..be8313d
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-vertical-colour-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-vertical-greyscale.svg b/examples/kiln/logo/svg/kiln-vertical-greyscale.svg
new file mode 100644
index 0000000..ec99f29
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-vertical-greyscale.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-vertical-white.svg b/examples/kiln/logo/svg/kiln-vertical-white.svg
new file mode 100644
index 0000000..4fc699d
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-vertical-white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-wordmark-black.svg b/examples/kiln/logo/svg/kiln-wordmark-black.svg
new file mode 100644
index 0000000..cedacd7
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-wordmark-black.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-wordmark-colour-dark.svg b/examples/kiln/logo/svg/kiln-wordmark-colour-dark.svg
new file mode 100644
index 0000000..8c4cea2
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-wordmark-colour-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-wordmark-colour-light.svg b/examples/kiln/logo/svg/kiln-wordmark-colour-light.svg
new file mode 100644
index 0000000..f9666c5
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-wordmark-colour-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-wordmark-greyscale.svg b/examples/kiln/logo/svg/kiln-wordmark-greyscale.svg
new file mode 100644
index 0000000..5898ebb
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-wordmark-greyscale.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/logo/svg/kiln-wordmark-white.svg b/examples/kiln/logo/svg/kiln-wordmark-white.svg
new file mode 100644
index 0000000..5a17e2f
--- /dev/null
+++ b/examples/kiln/logo/svg/kiln-wordmark-white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/kiln/preview/favicon-test.jpg b/examples/kiln/preview/favicon-test.jpg
new file mode 100644
index 0000000..f3247fa
Binary files /dev/null and b/examples/kiln/preview/favicon-test.jpg differ
diff --git a/examples/kiln/preview/guidelines-pages.jpg b/examples/kiln/preview/guidelines-pages.jpg
new file mode 100644
index 0000000..8fc029f
Binary files /dev/null and b/examples/kiln/preview/guidelines-pages.jpg differ
diff --git a/examples/kiln/preview/kiln-brand-guidelines.pdf b/examples/kiln/preview/kiln-brand-guidelines.pdf
new file mode 100644
index 0000000..1d4a308
Binary files /dev/null and b/examples/kiln/preview/kiln-brand-guidelines.pdf differ
diff --git a/examples/kiln/preview/letterhead.jpg b/examples/kiln/preview/letterhead.jpg
new file mode 100644
index 0000000..85d67d4
Binary files /dev/null and b/examples/kiln/preview/letterhead.jpg differ
diff --git a/examples/kiln/preview/logo-versions.jpg b/examples/kiln/preview/logo-versions.jpg
new file mode 100644
index 0000000..a90dd93
Binary files /dev/null and b/examples/kiln/preview/logo-versions.jpg differ
diff --git a/examples/kiln/preview/merch.jpg b/examples/kiln/preview/merch.jpg
new file mode 100644
index 0000000..e392454
Binary files /dev/null and b/examples/kiln/preview/merch.jpg differ
diff --git a/examples/kiln/preview/mockups/apron-photo.jpg b/examples/kiln/preview/mockups/apron-photo.jpg
new file mode 100644
index 0000000..ba7c489
Binary files /dev/null and b/examples/kiln/preview/mockups/apron-photo.jpg differ
diff --git a/examples/kiln/preview/mockups/coffee-bag-photo.jpg b/examples/kiln/preview/mockups/coffee-bag-photo.jpg
new file mode 100644
index 0000000..ebab169
Binary files /dev/null and b/examples/kiln/preview/mockups/coffee-bag-photo.jpg differ
diff --git a/examples/kiln/preview/mockups/coffee-cup-photo.jpg b/examples/kiln/preview/mockups/coffee-cup-photo.jpg
new file mode 100644
index 0000000..c8c0935
Binary files /dev/null and b/examples/kiln/preview/mockups/coffee-cup-photo.jpg differ
diff --git a/examples/kiln/preview/mockups/mug-photo.jpg b/examples/kiln/preview/mockups/mug-photo.jpg
new file mode 100644
index 0000000..d4f80e4
Binary files /dev/null and b/examples/kiln/preview/mockups/mug-photo.jpg differ
diff --git a/examples/kiln/preview/mockups/tote-photo.jpg b/examples/kiln/preview/mockups/tote-photo.jpg
new file mode 100644
index 0000000..7788875
Binary files /dev/null and b/examples/kiln/preview/mockups/tote-photo.jpg differ
diff --git a/examples/kiln/preview/mockups/travel-mug-illustration.jpg b/examples/kiln/preview/mockups/travel-mug-illustration.jpg
new file mode 100644
index 0000000..198fd25
Binary files /dev/null and b/examples/kiln/preview/mockups/travel-mug-illustration.jpg differ
diff --git a/examples/kiln/preview/social-banner.jpg b/examples/kiln/preview/social-banner.jpg
new file mode 100644
index 0000000..1d55900
Binary files /dev/null and b/examples/kiln/preview/social-banner.jpg differ
diff --git a/examples/marigold/README.md b/examples/marigold/README.md
new file mode 100644
index 0000000..e407cb6
--- /dev/null
+++ b/examples/marigold/README.md
@@ -0,0 +1,32 @@
+# Marigold Press
+
+*Independent illustrator selling prints, stickers and totes (fictional). Not a real business: made to show what the skill produces.*
+
+- Business type: **artist** (decides the merch set)
+- Merch: Sticker sheet (illustration), Business card (photo), T-shirt (photo), Tote bag (photo), Notebook (photo), Poster (photo)
+- Brand data: [`brand.json`](brand.json) · final artwork: [`final/`](final) · all SVG logo versions: [`logo/svg/`](logo/svg)
+- Guidelines PDF: [`preview/marigold-brand-guidelines.pdf`](preview/marigold-brand-guidelines.pdf)
+
+## Logo versions (horizontal and symbol × colour-light, colour-dark, black, white, greyscale)
+
+
+
+## Merch chosen for this business
+
+
+
+## Guidelines pages
+
+
+
+## Favicon test
+
+
+
+## Social banner and letterhead
+
+
+
+
+
+Photographic mockups are previews built from free PSDs by mockups-design.com (see the main README credits). The PSDs are not included.
diff --git a/examples/marigold/brand.json b/examples/marigold/brand.json
new file mode 100644
index 0000000..97f2c43
--- /dev/null
+++ b/examples/marigold/brand.json
@@ -0,0 +1,61 @@
+{
+ "name": "Marigold Press",
+ "slug": "marigold",
+ "tagline": "Small prints, big colour",
+ "business_type": "artist",
+ "adjectives": [
+ "warm",
+ "playful",
+ "handmade"
+ ],
+ "mechanism": "A marigold seen from above: eight round petals around a deep teal heart, like a printed dot or a sticker.",
+ "rationale": "Fictional example brand. An independent illustrator selling prints, stickers and tote bags at markets and online. Circles are the shape of stickers, dots and presses; marigold is a flower that grows in anyone's garden.",
+ "colors": {
+ "primary": "#1F6F6B",
+ "secondary": "#F2A900",
+ "ink": "#23222B",
+ "paper": "#FAF8F5",
+ "accent": "#F2A900"
+ },
+ "color_names": {
+ "primary": "Press Teal",
+ "secondary": "Marigold",
+ "accent": "Marigold",
+ "ink": "Ink",
+ "paper": "Paper"
+ },
+ "logo": {
+ "symbol": "final/symbol.svg",
+ "horizontal": "final/horizontal.svg",
+ "vertical": "final/vertical.svg",
+ "square": "final/square.svg",
+ "wordmark": "final/wordmark.svg",
+ "favicon_source": "final/favicon-source.svg",
+ "app_icon": {
+ "bg": "#FAF8F5",
+ "fg": "keep"
+ },
+ "dark_map": {
+ "#23222b": "#faf8f5",
+ "#1f6f6b": "#5FC3BC"
+ },
+ "dark_ground": "#17161C"
+ },
+ "fonts": {
+ "display": "Instrument Serif",
+ "text": "Instrument Sans",
+ "display_weight": 400
+ },
+ "contact": {
+ "email": "hello@marigold.example",
+ "web": "marigold.example"
+ },
+ "merch": [
+ "sticker-sheet",
+ "business-card",
+ "tee",
+ "tote",
+ "notebook",
+ "poster"
+ ]
+}
\ No newline at end of file
diff --git a/examples/marigold/final/favicon-source.svg b/examples/marigold/final/favicon-source.svg
new file mode 100644
index 0000000..c91b006
--- /dev/null
+++ b/examples/marigold/final/favicon-source.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/final/horizontal.svg b/examples/marigold/final/horizontal.svg
new file mode 100644
index 0000000..30502e0
--- /dev/null
+++ b/examples/marigold/final/horizontal.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/final/square.svg b/examples/marigold/final/square.svg
new file mode 100644
index 0000000..3404637
--- /dev/null
+++ b/examples/marigold/final/square.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/final/symbol.svg b/examples/marigold/final/symbol.svg
new file mode 100644
index 0000000..c4f34fe
--- /dev/null
+++ b/examples/marigold/final/symbol.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/final/vertical.svg b/examples/marigold/final/vertical.svg
new file mode 100644
index 0000000..f31ca26
--- /dev/null
+++ b/examples/marigold/final/vertical.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/final/wordmark.svg b/examples/marigold/final/wordmark.svg
new file mode 100644
index 0000000..8fed3be
--- /dev/null
+++ b/examples/marigold/final/wordmark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-horizontal-black.svg b/examples/marigold/logo/svg/marigold-horizontal-black.svg
new file mode 100644
index 0000000..9f42805
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-horizontal-black.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-horizontal-colour-dark.svg b/examples/marigold/logo/svg/marigold-horizontal-colour-dark.svg
new file mode 100644
index 0000000..4494bd0
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-horizontal-colour-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-horizontal-colour-light.svg b/examples/marigold/logo/svg/marigold-horizontal-colour-light.svg
new file mode 100644
index 0000000..30502e0
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-horizontal-colour-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-horizontal-greyscale.svg b/examples/marigold/logo/svg/marigold-horizontal-greyscale.svg
new file mode 100644
index 0000000..0a5de46
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-horizontal-greyscale.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-horizontal-white.svg b/examples/marigold/logo/svg/marigold-horizontal-white.svg
new file mode 100644
index 0000000..5d39328
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-horizontal-white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-square-black.svg b/examples/marigold/logo/svg/marigold-square-black.svg
new file mode 100644
index 0000000..2d3fd87
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-square-black.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-square-colour-dark.svg b/examples/marigold/logo/svg/marigold-square-colour-dark.svg
new file mode 100644
index 0000000..00b81c0
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-square-colour-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-square-colour-light.svg b/examples/marigold/logo/svg/marigold-square-colour-light.svg
new file mode 100644
index 0000000..3404637
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-square-colour-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-square-greyscale.svg b/examples/marigold/logo/svg/marigold-square-greyscale.svg
new file mode 100644
index 0000000..a2b783f
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-square-greyscale.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-square-white.svg b/examples/marigold/logo/svg/marigold-square-white.svg
new file mode 100644
index 0000000..61d0806
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-square-white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-symbol-black.svg b/examples/marigold/logo/svg/marigold-symbol-black.svg
new file mode 100644
index 0000000..496cfea
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-symbol-black.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-symbol-colour-dark.svg b/examples/marigold/logo/svg/marigold-symbol-colour-dark.svg
new file mode 100644
index 0000000..0143d49
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-symbol-colour-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-symbol-colour-light.svg b/examples/marigold/logo/svg/marigold-symbol-colour-light.svg
new file mode 100644
index 0000000..c4f34fe
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-symbol-colour-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-symbol-greyscale.svg b/examples/marigold/logo/svg/marigold-symbol-greyscale.svg
new file mode 100644
index 0000000..671f4ed
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-symbol-greyscale.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-symbol-white.svg b/examples/marigold/logo/svg/marigold-symbol-white.svg
new file mode 100644
index 0000000..b7757fc
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-symbol-white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-vertical-black.svg b/examples/marigold/logo/svg/marigold-vertical-black.svg
new file mode 100644
index 0000000..b7b6d22
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-vertical-black.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-vertical-colour-dark.svg b/examples/marigold/logo/svg/marigold-vertical-colour-dark.svg
new file mode 100644
index 0000000..5dd9053
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-vertical-colour-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-vertical-colour-light.svg b/examples/marigold/logo/svg/marigold-vertical-colour-light.svg
new file mode 100644
index 0000000..f31ca26
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-vertical-colour-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-vertical-greyscale.svg b/examples/marigold/logo/svg/marigold-vertical-greyscale.svg
new file mode 100644
index 0000000..4f83867
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-vertical-greyscale.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-vertical-white.svg b/examples/marigold/logo/svg/marigold-vertical-white.svg
new file mode 100644
index 0000000..407fe8f
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-vertical-white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-wordmark-black.svg b/examples/marigold/logo/svg/marigold-wordmark-black.svg
new file mode 100644
index 0000000..516109c
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-wordmark-black.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-wordmark-colour-dark.svg b/examples/marigold/logo/svg/marigold-wordmark-colour-dark.svg
new file mode 100644
index 0000000..4e8481b
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-wordmark-colour-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-wordmark-colour-light.svg b/examples/marigold/logo/svg/marigold-wordmark-colour-light.svg
new file mode 100644
index 0000000..8fed3be
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-wordmark-colour-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-wordmark-greyscale.svg b/examples/marigold/logo/svg/marigold-wordmark-greyscale.svg
new file mode 100644
index 0000000..02fdb8e
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-wordmark-greyscale.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/logo/svg/marigold-wordmark-white.svg b/examples/marigold/logo/svg/marigold-wordmark-white.svg
new file mode 100644
index 0000000..090c2cb
--- /dev/null
+++ b/examples/marigold/logo/svg/marigold-wordmark-white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/marigold/preview/favicon-test.jpg b/examples/marigold/preview/favicon-test.jpg
new file mode 100644
index 0000000..32ecdcf
Binary files /dev/null and b/examples/marigold/preview/favicon-test.jpg differ
diff --git a/examples/marigold/preview/guidelines-pages.jpg b/examples/marigold/preview/guidelines-pages.jpg
new file mode 100644
index 0000000..02681c7
Binary files /dev/null and b/examples/marigold/preview/guidelines-pages.jpg differ
diff --git a/examples/marigold/preview/letterhead.jpg b/examples/marigold/preview/letterhead.jpg
new file mode 100644
index 0000000..ff70dc7
Binary files /dev/null and b/examples/marigold/preview/letterhead.jpg differ
diff --git a/examples/marigold/preview/logo-versions.jpg b/examples/marigold/preview/logo-versions.jpg
new file mode 100644
index 0000000..7ca11cb
Binary files /dev/null and b/examples/marigold/preview/logo-versions.jpg differ
diff --git a/examples/marigold/preview/marigold-brand-guidelines.pdf b/examples/marigold/preview/marigold-brand-guidelines.pdf
new file mode 100644
index 0000000..c38be2d
Binary files /dev/null and b/examples/marigold/preview/marigold-brand-guidelines.pdf differ
diff --git a/examples/marigold/preview/merch.jpg b/examples/marigold/preview/merch.jpg
new file mode 100644
index 0000000..10dc753
Binary files /dev/null and b/examples/marigold/preview/merch.jpg differ
diff --git a/examples/marigold/preview/mockups/business-card-photo.jpg b/examples/marigold/preview/mockups/business-card-photo.jpg
new file mode 100644
index 0000000..1967a0c
Binary files /dev/null and b/examples/marigold/preview/mockups/business-card-photo.jpg differ
diff --git a/examples/marigold/preview/mockups/notebook-photo.jpg b/examples/marigold/preview/mockups/notebook-photo.jpg
new file mode 100644
index 0000000..370a94c
Binary files /dev/null and b/examples/marigold/preview/mockups/notebook-photo.jpg differ
diff --git a/examples/marigold/preview/mockups/poster-photo.jpg b/examples/marigold/preview/mockups/poster-photo.jpg
new file mode 100644
index 0000000..be86929
Binary files /dev/null and b/examples/marigold/preview/mockups/poster-photo.jpg differ
diff --git a/examples/marigold/preview/mockups/sticker-sheet-illustration.jpg b/examples/marigold/preview/mockups/sticker-sheet-illustration.jpg
new file mode 100644
index 0000000..ff03e98
Binary files /dev/null and b/examples/marigold/preview/mockups/sticker-sheet-illustration.jpg differ
diff --git a/examples/marigold/preview/mockups/tee-photo.jpg b/examples/marigold/preview/mockups/tee-photo.jpg
new file mode 100644
index 0000000..a6622ee
Binary files /dev/null and b/examples/marigold/preview/mockups/tee-photo.jpg differ
diff --git a/examples/marigold/preview/mockups/tote-photo.jpg b/examples/marigold/preview/mockups/tote-photo.jpg
new file mode 100644
index 0000000..55756c2
Binary files /dev/null and b/examples/marigold/preview/mockups/tote-photo.jpg differ
diff --git a/examples/marigold/preview/social-banner.jpg b/examples/marigold/preview/social-banner.jpg
new file mode 100644
index 0000000..202ff59
Binary files /dev/null and b/examples/marigold/preview/social-banner.jpg differ
diff --git a/examples/overview.png b/examples/overview.png
new file mode 100644
index 0000000..5c08049
Binary files /dev/null and b/examples/overview.png differ
diff --git a/examples/rookery/README.md b/examples/rookery/README.md
new file mode 100644
index 0000000..7f80ced
--- /dev/null
+++ b/examples/rookery/README.md
@@ -0,0 +1,32 @@
+# Rookery
+
+*Competitive but welcoming gaming community (fictional). Not a real business: made to show what the skill produces.*
+
+- Business type: **gaming** (decides the merch set)
+- Merch: Team jersey (illustration), Mouse mat (photo), Hoodie (photo), T-shirt (photo), Cap (photo), Sticker sheet (illustration)
+- Brand data: [`brand.json`](brand.json) · final artwork: [`final/`](final) · all SVG logo versions: [`logo/svg/`](logo/svg)
+- Guidelines PDF: [`preview/rookery-brand-guidelines.pdf`](preview/rookery-brand-guidelines.pdf)
+
+## Logo versions (horizontal and symbol × colour-light, colour-dark, black, white, greyscale)
+
+
+
+## Merch chosen for this business
+
+
+
+## Guidelines pages
+
+
+
+## Favicon test
+
+
+
+## Social banner and letterhead
+
+
+
+
+
+Photographic mockups are previews built from free PSDs by mockups-design.com (see the main README credits). The PSDs are not included.
diff --git a/examples/rookery/brand.json b/examples/rookery/brand.json
new file mode 100644
index 0000000..800aa0a
--- /dev/null
+++ b/examples/rookery/brand.json
@@ -0,0 +1,60 @@
+{
+ "name": "Rookery",
+ "slug": "rookery",
+ "tagline": "Play together. Win together.",
+ "business_type": "gaming",
+ "adjectives": [
+ "competitive",
+ "welcoming",
+ "sharp"
+ ],
+ "mechanism": "A rook (the chess piece that guards the corners) built from pixel-square blocks, with one lit window.",
+ "rationale": "Fictional example brand. A community of competitive players who also want newcomers to feel welcome. Chess supplies strategy and teamwork; the blocky crenellations nod to pixels; the lime window is the one light left on for new players.",
+ "colors": {
+ "primary": "#12141C",
+ "secondary": "#8BE01E",
+ "ink": "#12141C",
+ "paper": "#F4F6F8",
+ "accent": "#8BE01E"
+ },
+ "color_names": {
+ "primary": "Night Shift",
+ "secondary": "Lobby Lime",
+ "accent": "Lobby Lime",
+ "ink": "Night Shift",
+ "paper": "Daylight"
+ },
+ "logo": {
+ "symbol": "final/symbol.svg",
+ "horizontal": "final/horizontal.svg",
+ "vertical": "final/vertical.svg",
+ "square": "final/square.svg",
+ "wordmark": "final/wordmark.svg",
+ "favicon_source": "final/favicon-source.svg",
+ "app_icon": {
+ "bg": "#12141C",
+ "fg": "keep"
+ },
+ "dark_map": {
+ "#12141c": "#f4f6f8"
+ },
+ "dark_ground": "#0B0C11"
+ },
+ "fonts": {
+ "display": "Chakra Petch",
+ "text": "Barlow",
+ "display_weight": 700
+ },
+ "contact": {
+ "email": "hello@rookery.example",
+ "web": "rookery.example"
+ },
+ "merch": [
+ "jersey",
+ "mousemat",
+ "hoodie",
+ "tee",
+ "cap",
+ "sticker-sheet"
+ ]
+}
\ No newline at end of file
diff --git a/examples/rookery/final/favicon-source.svg b/examples/rookery/final/favicon-source.svg
new file mode 100644
index 0000000..101ced9
--- /dev/null
+++ b/examples/rookery/final/favicon-source.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/final/horizontal.svg b/examples/rookery/final/horizontal.svg
new file mode 100644
index 0000000..951e872
--- /dev/null
+++ b/examples/rookery/final/horizontal.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/final/square.svg b/examples/rookery/final/square.svg
new file mode 100644
index 0000000..34f9729
--- /dev/null
+++ b/examples/rookery/final/square.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/final/symbol.svg b/examples/rookery/final/symbol.svg
new file mode 100644
index 0000000..039881b
--- /dev/null
+++ b/examples/rookery/final/symbol.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/final/vertical.svg b/examples/rookery/final/vertical.svg
new file mode 100644
index 0000000..c6170b5
--- /dev/null
+++ b/examples/rookery/final/vertical.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/final/wordmark.svg b/examples/rookery/final/wordmark.svg
new file mode 100644
index 0000000..dc07762
--- /dev/null
+++ b/examples/rookery/final/wordmark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-horizontal-black.svg b/examples/rookery/logo/svg/rookery-horizontal-black.svg
new file mode 100644
index 0000000..45ee01a
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-horizontal-black.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-horizontal-colour-dark.svg b/examples/rookery/logo/svg/rookery-horizontal-colour-dark.svg
new file mode 100644
index 0000000..457e906
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-horizontal-colour-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-horizontal-colour-light.svg b/examples/rookery/logo/svg/rookery-horizontal-colour-light.svg
new file mode 100644
index 0000000..951e872
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-horizontal-colour-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-horizontal-greyscale.svg b/examples/rookery/logo/svg/rookery-horizontal-greyscale.svg
new file mode 100644
index 0000000..acbed3d
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-horizontal-greyscale.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-horizontal-white.svg b/examples/rookery/logo/svg/rookery-horizontal-white.svg
new file mode 100644
index 0000000..3545f8b
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-horizontal-white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-square-black.svg b/examples/rookery/logo/svg/rookery-square-black.svg
new file mode 100644
index 0000000..557cd34
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-square-black.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-square-colour-dark.svg b/examples/rookery/logo/svg/rookery-square-colour-dark.svg
new file mode 100644
index 0000000..ff1ef1d
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-square-colour-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-square-colour-light.svg b/examples/rookery/logo/svg/rookery-square-colour-light.svg
new file mode 100644
index 0000000..34f9729
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-square-colour-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-square-greyscale.svg b/examples/rookery/logo/svg/rookery-square-greyscale.svg
new file mode 100644
index 0000000..10ec50f
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-square-greyscale.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-square-white.svg b/examples/rookery/logo/svg/rookery-square-white.svg
new file mode 100644
index 0000000..8030145
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-square-white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-symbol-black.svg b/examples/rookery/logo/svg/rookery-symbol-black.svg
new file mode 100644
index 0000000..ad54fd2
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-symbol-black.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-symbol-colour-dark.svg b/examples/rookery/logo/svg/rookery-symbol-colour-dark.svg
new file mode 100644
index 0000000..694d605
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-symbol-colour-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-symbol-colour-light.svg b/examples/rookery/logo/svg/rookery-symbol-colour-light.svg
new file mode 100644
index 0000000..039881b
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-symbol-colour-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-symbol-greyscale.svg b/examples/rookery/logo/svg/rookery-symbol-greyscale.svg
new file mode 100644
index 0000000..0f27269
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-symbol-greyscale.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-symbol-white.svg b/examples/rookery/logo/svg/rookery-symbol-white.svg
new file mode 100644
index 0000000..249736b
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-symbol-white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-vertical-black.svg b/examples/rookery/logo/svg/rookery-vertical-black.svg
new file mode 100644
index 0000000..b7c8bb4
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-vertical-black.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-vertical-colour-dark.svg b/examples/rookery/logo/svg/rookery-vertical-colour-dark.svg
new file mode 100644
index 0000000..8e3337d
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-vertical-colour-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-vertical-colour-light.svg b/examples/rookery/logo/svg/rookery-vertical-colour-light.svg
new file mode 100644
index 0000000..c6170b5
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-vertical-colour-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-vertical-greyscale.svg b/examples/rookery/logo/svg/rookery-vertical-greyscale.svg
new file mode 100644
index 0000000..112d62c
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-vertical-greyscale.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-vertical-white.svg b/examples/rookery/logo/svg/rookery-vertical-white.svg
new file mode 100644
index 0000000..f910faf
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-vertical-white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-wordmark-black.svg b/examples/rookery/logo/svg/rookery-wordmark-black.svg
new file mode 100644
index 0000000..abaa7d2
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-wordmark-black.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-wordmark-colour-dark.svg b/examples/rookery/logo/svg/rookery-wordmark-colour-dark.svg
new file mode 100644
index 0000000..9800174
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-wordmark-colour-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-wordmark-colour-light.svg b/examples/rookery/logo/svg/rookery-wordmark-colour-light.svg
new file mode 100644
index 0000000..dc07762
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-wordmark-colour-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-wordmark-greyscale.svg b/examples/rookery/logo/svg/rookery-wordmark-greyscale.svg
new file mode 100644
index 0000000..daf633d
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-wordmark-greyscale.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/logo/svg/rookery-wordmark-white.svg b/examples/rookery/logo/svg/rookery-wordmark-white.svg
new file mode 100644
index 0000000..212f47b
--- /dev/null
+++ b/examples/rookery/logo/svg/rookery-wordmark-white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/rookery/preview/favicon-test.jpg b/examples/rookery/preview/favicon-test.jpg
new file mode 100644
index 0000000..e6e4eb0
Binary files /dev/null and b/examples/rookery/preview/favicon-test.jpg differ
diff --git a/examples/rookery/preview/guidelines-pages.jpg b/examples/rookery/preview/guidelines-pages.jpg
new file mode 100644
index 0000000..da7903a
Binary files /dev/null and b/examples/rookery/preview/guidelines-pages.jpg differ
diff --git a/examples/rookery/preview/letterhead.jpg b/examples/rookery/preview/letterhead.jpg
new file mode 100644
index 0000000..bbee86f
Binary files /dev/null and b/examples/rookery/preview/letterhead.jpg differ
diff --git a/examples/rookery/preview/logo-versions.jpg b/examples/rookery/preview/logo-versions.jpg
new file mode 100644
index 0000000..541c6cf
Binary files /dev/null and b/examples/rookery/preview/logo-versions.jpg differ
diff --git a/examples/rookery/preview/merch.jpg b/examples/rookery/preview/merch.jpg
new file mode 100644
index 0000000..49846d9
Binary files /dev/null and b/examples/rookery/preview/merch.jpg differ
diff --git a/examples/rookery/preview/mockups/cap-photo.jpg b/examples/rookery/preview/mockups/cap-photo.jpg
new file mode 100644
index 0000000..7ae006e
Binary files /dev/null and b/examples/rookery/preview/mockups/cap-photo.jpg differ
diff --git a/examples/rookery/preview/mockups/hoodie-photo.jpg b/examples/rookery/preview/mockups/hoodie-photo.jpg
new file mode 100644
index 0000000..4df2e64
Binary files /dev/null and b/examples/rookery/preview/mockups/hoodie-photo.jpg differ
diff --git a/examples/rookery/preview/mockups/jersey-illustration.jpg b/examples/rookery/preview/mockups/jersey-illustration.jpg
new file mode 100644
index 0000000..7f6856d
Binary files /dev/null and b/examples/rookery/preview/mockups/jersey-illustration.jpg differ
diff --git a/examples/rookery/preview/mockups/mousemat-photo.jpg b/examples/rookery/preview/mockups/mousemat-photo.jpg
new file mode 100644
index 0000000..ffbc183
Binary files /dev/null and b/examples/rookery/preview/mockups/mousemat-photo.jpg differ
diff --git a/examples/rookery/preview/mockups/sticker-sheet-illustration.jpg b/examples/rookery/preview/mockups/sticker-sheet-illustration.jpg
new file mode 100644
index 0000000..0abc440
Binary files /dev/null and b/examples/rookery/preview/mockups/sticker-sheet-illustration.jpg differ
diff --git a/examples/rookery/preview/mockups/tee-photo.jpg b/examples/rookery/preview/mockups/tee-photo.jpg
new file mode 100644
index 0000000..500000c
Binary files /dev/null and b/examples/rookery/preview/mockups/tee-photo.jpg differ
diff --git a/examples/rookery/preview/rookery-brand-guidelines.pdf b/examples/rookery/preview/rookery-brand-guidelines.pdf
new file mode 100644
index 0000000..96c661d
Binary files /dev/null and b/examples/rookery/preview/rookery-brand-guidelines.pdf differ
diff --git a/examples/rookery/preview/social-banner.jpg b/examples/rookery/preview/social-banner.jpg
new file mode 100644
index 0000000..facb2f1
Binary files /dev/null and b/examples/rookery/preview/social-banner.jpg differ
diff --git a/skills/brand-studio/SKILL.md b/skills/brand-studio/SKILL.md
index b00e9cf..11da56c 100644
--- a/skills/brand-studio/SKILL.md
+++ b/skills/brand-studio/SKILL.md
@@ -96,7 +96,7 @@ picks. Skip the pause only if they said not to check in.
`--sym-scale/--gap/--word-width` by eye, render, keep the numbers. Colour the final files with palette hexes.
5. Favicon legibility: a dark mark vanishes on dark browser tabs. Give the favicon its own source with a light backing disc (`logo.favicon_source`) and set `logo.app_icon` to `{"bg": paper, "fg": "keep"}` so the app icon keeps the colour mark on a light tile (the test flags the failure; fix it, don't just report it).
6. Fill `final/` and `brand.json` (`templates/brand.example.json`), add 1–2 misuse drawings only this mark can suffer.
-6. `build_kit.py brand.json --out deliver` (≈2 min; stages independent). Add `--photo-mockups` for photographic previews: it downloads the sector's free PSD mockups from mockups-design.com on demand (user-approved; cached in `~/.cache/brand-studio/mockups`, never redistributed, previews only) and places the logo in them with `psd_mockup.py` (mug, tee, sweatshirt, apron, card, signboard; adds ~5 min the first time). Always compare with the mockup's own JPG; if a PSD cannot be filled cleanly say so and keep the flat mockups. **Open and look at**: `guidelines/pages/*.png`,
+6. `build_kit.py brand.json --out deliver` (≈2 min; stages independent). **Merch is chosen for the business, not generic**: set `business_type` (and, for unusual brands, an explicit `merch` list) from `merch_catalog.py` — a coffee roaster gets a bean bag, travel mug and apron; a gaming community a jersey, mouse mat and hoodie; a tech company a business card, USB stick, cable, lanyard and laptop sleeve; an illustrator a sticker sheet, cards, tote and tee (`references/merch-catalogue.md`). Add `--photo-mockups` for photographic previews: it downloads the free PSD mockups for those items from mockups-design.com on demand (user-approved; cached in `~/.cache/brand-studio/mockups`, never redistributed, previews only) and places the logo in them with `psd_mockup.py` (~5 min the first time). Items with no PSD page get a drawn illustration; the guidelines "In use" pages show only the real mockup files, labelled photographic or illustration. Always compare with the mockup's own JPG; if a PSD cannot be filled cleanly say so and keep the illustration. **Open and look at**: `guidelines/pages/*.png`,
`favicons/favicon-test.png` + report, `mockups/*-merch-overview.png`, one banner, the letterhead PNG, one PDF.
7. **Independent review of the kit** with the review agent (colour, type, favicon sheet, mockups, guideline pages). Fix, rebuild.
8. Present: where the files are, the favicon test result (say what it does not cover), review outcome, and the
@@ -147,10 +147,11 @@ existing logo (the library is for learning, never tracing).
| `identity-system.md` · `bi-tokens-export.md` | system, tokens |
| `critique.md` · `bi-critique.md` · `redesign.md` | critique/refresh modes |
| `review-agent.md` | the independent review |
+| `merch-catalogue.md` | choosing merch items, adding PSD sources |
| `live-sources.md` · `library-guide.md` · `examples.md` | APIs/sites; library; worked examples (`assets/examples/`) |
## 9. Script cheat-sheet
`search_library.py` · `palette/search_palettes.py` · `live_sources.py` · `outline_text.py` · `svg_audit.py` · `preview_sheet.py`
· `concept_sheet.py` · `render_png.py` · `export_variants.py` · `compose_lockup.py` · `review_pack.py` · `palette/palette_build.py`
· `palette/palette_audit.py` · `build_kit.py` (calls `favicon_test.py`, `collateral.py`, `merch.py`, `guidelines.py`)
-· `psd_mockup.py` (optional photo mockups; `--set`, `--page`, `--psd`) · `presentation_board.py` (extra 6-mockup client board per concept, `--list-mockups`). Each has `--help`.
+· `merch_catalog.py` (business type → items) · `psd_mockup.py` (optional photo mockups; `--set`, `--item`, `--psd`) · `check_setup.py` · `presentation_board.py` (extra 6-mockup client board per concept, `--list-mockups`). Each has `--help`.
diff --git a/skills/brand-studio/references/merch-catalogue.md b/skills/brand-studio/references/merch-catalogue.md
new file mode 100644
index 0000000..b0c43ee
--- /dev/null
+++ b/skills/brand-studio/references/merch-catalogue.md
@@ -0,0 +1,41 @@
+# Merchandise: choose items the brand would really make
+
+Merch mockups exist to judge the logo *in use*, so the items must be things this kind of business would plausibly produce, not a
+generic mug-and-shirt set. `scripts/merch_catalog.py` holds the catalogue; `brand.json` selects from it.
+
+## How items are chosen
+1. Set `business_type` in brand.json (free word; aliases map it: `cafe`→coffee, `esports`→gaming, `illustrator`→artist,
+ `fintech`→finance, `wishlist`→retail...). If absent, `industry` is used; unknown → `general`.
+2. The business type gives an ordered list (first 6–8 used). Examples:
+ - **coffee**: coffee bean bag, travel mug, takeaway cup, mug, apron, tote, tee, cap
+ - **gaming**: team jersey, mouse mat, hoodie, tee, cap, sticker sheet, lanyard, keyring
+ - **tech / saas**: business card, USB stick, charging cable, lanyard, phone case, laptop sleeve, tee, sticker sheet
+ - **artist**: sticker sheet, business card, tee, tote, notebook, poster, pin badge
+ - also fashion, fitness, finance, services, health, beauty, event, music, education, community, retail, trades, kids, pets, bakery, bar, food
+3. **Override when the brand is unusual**: set `"merch": ["item", ...]` using keys from `merch_catalog.ITEMS`. Think about who the
+ customer is and what they would actually hold, wear or have on their desk (a climbing gym: chalk bag is not in the catalogue,
+ so pick the nearest: tote + bottle + tee, and say so). Do not pad the list: 6 good items beat 12 generic ones.
+
+## Where each mockup comes from (in this order)
+1. **Photographic** (`--photo-mockups`): the free PSD from mockups-design.com for that item (`ITEMS[...][1]` is the page slug),
+ downloaded on demand into `~/.cache/brand-studio/mockups`, logo placed in the smart-object layer by `psd_mockup.py`.
+2. **Illustration**: a flat vector scene drawn by `merch.py` for items with no PSD page (jersey, lanyard, phone case, cable,
+ travel mug, sticker sheet...) or when the PSD cannot be filled cleanly.
+3. Never invent an item that has neither: leave it out and say so.
+
+`mockups/manifest.json` lists every real mockup file (`kind: photo | illustration`); the guidelines "In use" pages are built from it,
+so the guideline PDF only ever shows files that exist in the kit and labels each one.
+
+## Adding items or sources
+- New item: add a row to `ITEMS` (label, mockups-design page slug or `None`, logo form, scale, cylinder degrees) and, if there is no
+ PSD page, a scene function in `merch.py` (`SCENES`).
+- New PSD: run `psd_mockup.py brand.json --psd file.psd --out out.png --item `; check against the mockup's own JPG; tune `scale` /
+ `cylinder` in the catalogue. PSD layouts vary: the script looks for a smart object named Design (or Your design / Logo), hides
+ "delete this layer" slides, maps the artboard onto the smart object's transform quad and clips with its mask.
+- Unblast (unblast.com) hosts many more mockups (lanyards, jerseys, phone cases, USB cards, tumblers, coffee packaging) but each is
+ third-party with its own licence and a JavaScript download step: download by hand, read the licence on the page, then use `--psd`.
+
+## Licences (preview use)
+mockups-design.com: free for any purpose incl. commercial, no attribution required, but do not redistribute the PSD files; sharing a
+flattened finished image is allowed. Keep PSDs in the cache folder, never in a deliverable or repository. Credit the site in any
+public write-up (see the repository README).
diff --git a/skills/brand-studio/scripts/check_setup.py b/skills/brand-studio/scripts/check_setup.py
new file mode 100644
index 0000000..f0abcee
--- /dev/null
+++ b/skills/brand-studio/scripts/check_setup.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+"""Check that this machine can run brand-studio: Python, uv, a Chromium-based browser, optional packages, network.
+
+ python scripts/check_setup.py # prints PASS / WARN / FAIL lines; exit code 1 only on a FAIL
+ python scripts/check_setup.py --offline
+
+FAIL = the skill cannot work. WARN = a feature is limited (the line says which and how to fix it).
+"""
+import argparse
+import importlib.util
+import os
+import shutil
+import sys
+import urllib.request
+
+HERE = os.path.dirname(os.path.abspath(__file__))
+sys.path.insert(0, HERE)
+
+
+def line(level, msg, fix=""):
+ print(f"{level:<5} {msg}" + (f"\n fix: {fix}" if fix and level != "PASS" else ""))
+ return level == "FAIL"
+
+
+def main():
+ ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
+ ap.add_argument("--offline", action="store_true")
+ a = ap.parse_args()
+ bad = False
+ v = sys.version_info
+ bad |= line("PASS" if v >= (3, 9) else "FAIL", f"Python {v.major}.{v.minor}.{v.micro}", "install Python 3.9 or newer")
+ uv = shutil.which("uv")
+ line("PASS" if uv else "WARN", "uv " + ("found" if uv else "not found (needed to auto-install Pillow, pypdf, fonttools, psd-tools)"),
+ "" if uv else "Windows: powershell -c \"irm https://astral.sh/uv/install.ps1 | iex\" macOS/Linux: curl -LsSf https://astral.sh/uv/install.sh | sh (or, without uv: pip install pillow pypdf fonttools uharfbuzz pymupdf; add \"psd-tools[composite]\" numpy scipy scikit-image for --photo-mockups)")
+ try:
+ import render_png
+ chrome = render_png.find_chrome()
+ except Exception:
+ chrome = None
+ bad |= line("PASS" if chrome else "FAIL", "Chromium-based browser " + (chrome if chrome else "not found (renders every PNG/PDF)"),
+ "" if chrome else "install Chrome, Edge, Brave or Chromium")
+ for mod, pkg, why in (("PIL", "pillow", "PNG/JPG output"), ("pypdf", "pypdf", "PDF output"), ("fontTools", "fonttools", "wordmark outlining"),
+ ("uharfbuzz", "uharfbuzz", "wordmark kerning"), ("psd_tools", "psd-tools[composite]", "optional photographic mockups"),
+ ("fitz", "pymupdf", "guideline page PNGs")):
+ have = importlib.util.find_spec(mod) is not None or (mod == "fitz" and importlib.util.find_spec("pymupdf") is not None)
+ line("PASS" if have else "INFO", f"{pkg} {'installed' if have else 'not installed (' + why + '; installed on demand through uv)'}")
+ if not a.offline:
+ for name, url in (("Google Fonts", "https://fonts.googleapis.com/css2?family=Figtree"), ("Fontsource API", "https://api.fontsource.org/v1/fonts/figtree"),
+ ("mockups-design.com (optional photo mockups)", "https://mockups-design.com/")):
+ try:
+ urllib.request.urlopen(urllib.request.Request(url, headers={"User-Agent": "curl/8"}), timeout=15).read(100)
+ line("PASS", f"network: {name}")
+ except Exception as exc:
+ line("WARN", f"network: {name} unreachable ({exc})", "outlining fonts and photo mockups need internet; the rest works offline")
+ print("\n" + ("Setup has a blocking problem." if bad else "Ready."))
+ sys.exit(1 if bad else 0)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/skills/brand-studio/scripts/guidelines.py b/skills/brand-studio/scripts/guidelines.py
index 5fce5e1..ae98031 100644
--- a/skills/brand-studio/scripts/guidelines.py
+++ b/skills/brand-studio/scripts/guidelines.py
@@ -247,8 +247,24 @@ def exists(*parts):
def img_uri(path):
+ """data: URI for a raster file. Large images (mockup photos, banners) are re-encoded as JPEG <= 1600 px so the PDF stays small."""
import base64
+ import io
ext = os.path.splitext(path)[1].lower().lstrip(".")
+ if ext in ("png", "jpg", "jpeg") and os.path.getsize(path) > 300_000:
+ from PIL import Image
+ im = Image.open(path)
+ if im.mode in ("RGBA", "LA", "P"):
+ im = im.convert("RGBA")
+ bg = Image.new("RGB", im.size, "white")
+ bg.paste(im, mask=im.split()[3])
+ im = bg
+ else:
+ im = im.convert("RGB")
+ im.thumbnail((1600, 1600))
+ buf = io.BytesIO()
+ im.save(buf, "JPEG", quality=82, optimize=True)
+ return "data:image/jpeg;base64," + base64.b64encode(buf.getvalue()).decode("ascii")
mime = {"jpg": "jpeg", "svg": "svg+xml"}.get(ext, ext)
return f"data:image/{mime};base64," + base64.b64encode(open(path, "rb").read()).decode("ascii")
diff --git a/skills/brand-studio/scripts/kitlib.py b/skills/brand-studio/scripts/kitlib.py
index e0575ca..f0e7bf5 100644
--- a/skills/brand-studio/scripts/kitlib.py
+++ b/skills/brand-studio/scripts/kitlib.py
@@ -403,7 +403,7 @@ def pick_variant(variants, ground):
reach 2:1 and at least one reaches 3:1 against it (accents may be lighter than the main colour); else the best."""
for n in ("colour-light", "colour-dark"):
cs = [contrast(c, ground) for c in (used_colors(variants[n]) or ["#000000"])]
- if min(cs) >= 1.5 and max(cs) >= 3.0:
+ if min(cs) >= 1.2 and max(cs) >= 3.0:
return variants[n], n
n = "white" if contrast("#ffffff", ground) >= contrast("#000000", ground) else "black"
return variants[n], n
diff --git a/skills/brand-studio/scripts/merch_catalog.py b/skills/brand-studio/scripts/merch_catalog.py
index a97295d..873b648 100644
--- a/skills/brand-studio/scripts/merch_catalog.py
+++ b/skills/brand-studio/scripts/merch_catalog.py
@@ -50,6 +50,15 @@
"coaster": ("Coaster", "free-round-coaster-mockup", "symbol", 0.6, 0),
}
+# placement tweaks learned from checking each PSD against its own preview: dx/dy are fractions of the design area
+# (dy > 0 moves down), form/scale override the ITEMS defaults. Everything else uses ITEMS.
+ADJUST = {
+ "hoodie": {"form": "symbol", "scale": 0.1, "dx": 0.2, "dy": -0.1}, # left chest, clear of the drawstrings
+ "tote": {"scale": 0.36, "dy": 0.22}, # below the handles
+ "coffee-cup": {"scale": 0.55},
+ "mousemat": {"scale": 0.35},
+}
+
BUSINESS_TYPES = {
"coffee": ["coffee-bag", "travel-mug", "coffee-cup", "mug", "apron", "tote", "tee", "cap"],
"food": ["apron", "paper-bag", "coffee-cup", "box", "tote", "label", "tee", "cap"],
@@ -125,4 +134,6 @@ def page_url(item):
def spec(item):
label, slug, form, scale, cyl = ITEMS[item]
- return {"label": label, "page": page_url(item), "form": form, "scale": scale, "cylinder": cyl}
+ s = {"label": label, "page": page_url(item), "form": form, "scale": scale, "cylinder": cyl, "dx": 0.0, "dy": 0.0}
+ s.update(ADJUST.get(item, {}))
+ return s
diff --git a/skills/brand-studio/scripts/psd_mockup.py b/skills/brand-studio/scripts/psd_mockup.py
index e03fe0c..8223d0c 100644
--- a/skills/brand-studio/scripts/psd_mockup.py
+++ b/skills/brand-studio/scripts/psd_mockup.py
@@ -90,18 +90,18 @@ def find_psds(folder):
return sorted(os.path.join(d, f) for d, _, fs in os.walk(folder) for f in fs if f.lower().endswith(".psd"))
-def find_design_layer(psd):
- best = None
- for l in psd.descendants():
- if l.kind == "smartobject" and l.visible and l.name.lower() in ("design", "your design", "logo", "your logo", "artwork"):
- best = l
- break
- if best is None:
- for l in psd.descendants():
- if l.kind == "smartobject" and l.visible:
- best = l
- break
- return best
+DESIGN_NAMES = re.compile(r"design|project|logo|artwork|print|mouse ?pad|your|label|cover|front|place", re.I)
+# whole words only: "lace" (shoe-lace layers) must not match inside "Place your design here"
+SKIP_NAMES = re.compile(r"\b(laces?|colou?rs?|shadows?|backgrounds?|highlights?|textures?|masks?|base|delete)\b", re.I)
+
+
+def find_design_layers(psd):
+ """Every visible smart object that carries the printed design (a cup PSD has one per cup; a desk scene may also have a laptop screen).
+ A layer whose name says design/logo/artwork wins over the skip list."""
+ strong = re.compile(r"design|logo|artwork|project|mouse ?pad|your", re.I)
+ cand = [l for l in psd.descendants() if l.kind == "smartobject" and l.visible and (strong.search(l.name) or not SKIP_NAMES.search(l.name))]
+ named = [l for l in cand if DESIGN_NAMES.search(l.name)]
+ return named or cand[:1]
def cylinder_map(img, deg):
@@ -119,108 +119,104 @@ def cylinder_map(img, deg):
def build(brand, psd_path, out, variant=None, cylinder=None, scale=None, preset=None, show_ref=True, form=None):
- from PIL import Image
+ from PIL import Image, ImageChops
from psd_tools import PSDImage
from psd_tools.api.layers import PixelLayer
+ import numpy as np
import render_png
psd = PSDImage.open(psd_path)
for l in psd.descendants(): # optimisation slides the authors ask you to delete
if re.search(r"delete|read me|instruction", l.name, re.I) and l.kind in ("pixel", "type", "smartobject"):
l.visible = False
- layer = find_design_layer(psd)
- if layer is None:
+ layers = find_design_layers(psd)
+ if not layers:
print("No smart-object design layer found. Layers:")
for l in psd.descendants():
print(f" {l.kind:12} {l.name!r} visible={l.visible}")
raise SystemExit(2)
- so = layer.smart_object
- tb = so.transform_box
- xs, ys = tb[0::2], tb[1::2]
- box = (min(xs), min(ys), max(xs), max(ys))
- bounds = so.warp.get(b"bounds", {})
- sw, sh = int(bounds.get(b"Rght", box[2] - box[0])), int(bounds.get(b"Btom", box[3] - box[1]))
- if preset in merch_catalog.ITEMS:
- sp = merch_catalog.spec(preset)
- pre = {"cylinder": sp["cylinder"], "scale": sp["scale"]}
- form = form or sp["form"]
- else:
- pre = PRESETS["default"]
- cyl = pre["cylinder"] if cylinder is None else cylinder
- sc = pre["scale"] if scale is None else scale
+ sp = merch_catalog.spec(preset) if preset in merch_catalog.ITEMS else {"cylinder": 0, "scale": 0.6, "form": None, "dx": 0.0, "dy": 0.0}
+ form = form or sp["form"]
+ cyl = sp["cylinder"] if cylinder is None else cylinder
+ sc = sp["scale"] if scale is None else scale
- # colour of the item under the design area (hide design, composite, sample)
- layer.visible = False
- base = psd.composite(force=True).convert("RGB")
- cx, cy = int((layer.bbox[0] + layer.bbox[2]) / 2), int((layer.bbox[1] + layer.bbox[3]) / 2)
- r, g, b = base.getpixel((cx, cy))
- ground = kitlib.to_hex(r, g, b)
+ for l in layers:
+ l.visible = False
+ base = psd.composite(force=True).convert("RGB") # the item without any design: used to pick the logo version
- # artboard with the logo
vs = None
- order = [form] if form else []
- for form_ in order + ["horizontal", "vertical", "symbol"]:
- p = kitlib.logo_path(brand, form_)
- if p and os.path.exists(p):
- vs = kitlib.variants_of(kitlib.tight_viewbox(kitlib.read(p)), {**brand["logo"], **brand["colors"]})
+ for form_ in ([form] if form else []) + ["horizontal", "vertical", "symbol"]:
+ pth = kitlib.logo_path(brand, form_)
+ if pth and os.path.exists(pth):
+ vs = kitlib.variants_of(kitlib.tight_viewbox(kitlib.read(pth)), {**brand["logo"], **brand["colors"]})
break
if vs is None:
raise SystemExit("error: no logo files in brand.json")
- raw, vname = kitlib.pick_variant(vs, ground)
- g_ = kitlib.geometry(raw)
- # the smart object is usually stretched into a box of a different shape than its own canvas: pre-compensate so the
- # logo keeps its proportions on the item (desired size is measured in the box, converted back to canvas units)
- import math as _m
- qd = lambda a, b: _m.hypot(tb[2 * b] - tb[2 * a], tb[2 * b + 1] - tb[2 * a + 1])
- bwq, bhq = (qd(0, 1) + qd(3, 2)) / 2, (qd(0, 3) + qd(1, 2)) / 2
- fx, fy = sw / bwq, sh / bhq # canvas units per box pixel
- wd = sc * bwq
- hd = wd * g_["h"] / g_["w"]
- if hd > bhq * 0.7:
- hd = bhq * 0.7
- wd = hd * g_["w"] / g_["h"]
- lw, lh = max(2, int(wd * fx)), max(2, int(hd * fy))
- with tempfile.TemporaryDirectory() as tmp:
- svgp = os.path.join(tmp, "l.svg")
- open(svgp, "w", encoding="utf-8").write(kitlib.fit_attrs(raw))
- lp = os.path.join(tmp, "l.png")
- nat_h = max(2, int(lw * g_["h"] / g_["w"]))
- render_png.render(svgp, lp, lw, nat_h)
- logo = Image.open(lp).convert("RGBA").resize((lw, lh), Image.LANCZOS) # deliberate stretch: the smart object is squeezed back into its box
- art = Image.new("RGBA", (sw, sh), (0, 0, 0, 0))
- art.alpha_composite(logo, ((sw - lw) // 2, (sh - lh) // 2))
- if cyl:
- art = cylinder_map(art, cyl)
- # map the artboard onto the smart object's transform quad (handles rotation and perspective)
- import numpy as np
- q = [(tb[0], tb[1]), (tb[2], tb[3]), (tb[4], tb[5]), (tb[6], tb[7])]
- src = [(0, 0), (sw, 0), (sw, sh), (0, sh)]
- A, B = [], []
- for (x, y), (u, v) in zip(q, src):
- A.append([x, y, 1, 0, 0, 0, -u * x, -u * y]); B.append(u)
- A.append([0, 0, 0, x, y, 1, -v * x, -v * y]); B.append(v)
- coef = np.linalg.solve(np.array(A, float), np.array(B, float))
- canvas = art.transform(psd.size, Image.PERSPECTIVE, coef.tolist(), Image.BICUBIC)
- # clip with the smart object's own layer mask when it has one (not its placeholder pixels, which can be sparse)
- try:
- if layer.has_mask():
- from PIL import ImageChops
- m = layer.mask.topil()
- full = Image.new("L", psd.size, 0)
- full.paste(m, (layer.mask.left, layer.mask.top))
- canvas.putalpha(ImageChops.multiply(canvas.split()[3], full))
- except Exception:
- pass
- parent = layer.parent
- idx = list(parent).index(layer)
- newl = PixelLayer.frompil(canvas, psd, name="Brand design", top=0, left=0)
- parent.insert(idx + 1, newl) if hasattr(parent, "insert") else parent.append(newl)
+
+ notes = []
+ for layer in layers:
+ so = layer.smart_object
+ tb = so.transform_box
+ bounds = so.warp.get(b"bounds", {})
+ xs, ys = tb[0::2], tb[1::2]
+ sw = int(bounds.get(b"Rght", max(xs) - min(xs)))
+ sh = int(bounds.get(b"Btom", max(ys) - min(ys)))
+ x0, y0, x1, y1 = (max(0, layer.bbox[0]), max(0, layer.bbox[1]), min(psd.size[0], layer.bbox[2]), min(psd.size[1], layer.bbox[3]))
+ if x1 - x0 < 4 or y1 - y0 < 4:
+ x0, y0, x1, y1 = 0, 0, psd.size[0], psd.size[1]
+ reg = base.crop((x0, y0, x1, y1)).resize((24, 24))
+ arr = np.asarray(reg).reshape(-1, 3)
+ px = arr[np.argsort(arr.sum(axis=1))] # median of the area: shadows and creases do not decide the logo colour
+ r, g, b = (int(v) for v in px[len(px) // 2])
+ ground = kitlib.to_hex(r, g, b)
+ raw, vname = kitlib.pick_variant(vs, ground)
+ g_ = kitlib.geometry(raw)
+ # the smart object is stretched into a box of another shape than its own canvas: pre-compensate so the logo keeps
+ # its proportions on the item (size measured in the box, converted back to canvas units)
+ qd = lambda i, j: math.hypot(tb[2 * j] - tb[2 * i], tb[2 * j + 1] - tb[2 * i + 1])
+ bwq, bhq = max(1.0, (qd(0, 1) + qd(3, 2)) / 2), max(1.0, (qd(0, 3) + qd(1, 2)) / 2)
+ fx, fy = sw / bwq, sh / bhq
+ wd = sc * bwq
+ hd = wd * g_["h"] / g_["w"]
+ if hd > bhq * 0.7:
+ hd = bhq * 0.7
+ wd = hd * g_["w"] / g_["h"]
+ lw, lh = max(2, int(wd * fx)), max(2, int(hd * fy))
+ with tempfile.TemporaryDirectory() as tmp:
+ svgp = os.path.join(tmp, "l.svg")
+ open(svgp, "w", encoding="utf-8").write(kitlib.fit_attrs(raw))
+ lp = os.path.join(tmp, "l.png")
+ render_png.render(svgp, lp, lw, max(2, int(lw * g_["h"] / g_["w"])))
+ logo = Image.open(lp).convert("RGBA").resize((lw, lh), Image.LANCZOS) # deliberate stretch (see above)
+ art = Image.new("RGBA", (sw, sh), (0, 0, 0, 0))
+ art.alpha_composite(logo, (int((sw - lw) / 2 + sp["dx"] * sw), int((sh - lh) / 2 + sp["dy"] * sh)))
+ if cyl:
+ art = cylinder_map(art, cyl)
+ q = [(tb[0], tb[1]), (tb[2], tb[3]), (tb[4], tb[5]), (tb[6], tb[7])]
+ src = [(0, 0), (sw, 0), (sw, sh), (0, sh)]
+ A, B = [], []
+ for (x, y), (u, v) in zip(q, src):
+ A.append([x, y, 1, 0, 0, 0, -u * x, -u * y]); B.append(u)
+ A.append([0, 0, 0, x, y, 1, -v * x, -v * y]); B.append(v)
+ coef = np.linalg.solve(np.array(A, float), np.array(B, float))
+ canvas = art.transform(psd.size, Image.PERSPECTIVE, coef.tolist(), Image.BICUBIC)
+ try: # clip with the smart object's own mask when it has one (not its placeholder pixels, which can be sparse)
+ if layer.has_mask():
+ m_ = layer.mask.topil()
+ full = Image.new("L", psd.size, 0)
+ full.paste(m_, (layer.mask.left, layer.mask.top))
+ canvas.putalpha(ImageChops.multiply(canvas.split()[3], full))
+ except Exception:
+ pass
+ parent = layer.parent
+ idx = list(parent).index(layer)
+ newl = PixelLayer.frompil(canvas, psd, name="Brand design", top=0, left=0)
+ parent.insert(idx + 1, newl) if hasattr(parent, "insert") else parent.append(newl)
+ notes.append(f"{layer.name}: {vname} on {ground}")
result = psd.composite(force=True).convert("RGB")
os.makedirs(os.path.dirname(os.path.abspath(out)), exist_ok=True)
result.save(out)
- # also write a contact image next to the mockup's own preview JPG when present
- note = f"logo version {vname} on item colour {ground}; cylinder {cyl}°, width {sc:.2f}"
- return out, note
+ return out, "; ".join(notes) + f"; cylinder {cyl} deg, width {sc:.2f}"
def build_set(brand, out_dir, items=None):
diff --git a/skills/brand-studio/templates/brand.example.json b/skills/brand-studio/templates/brand.example.json
index 5494bb2..3a614db 100644
--- a/skills/brand-studio/templates/brand.example.json
+++ b/skills/brand-studio/templates/brand.example.json
@@ -3,6 +3,7 @@
"slug": "kiln",
"tagline": "Small-batch coffee, roasted in Istanbul",
"industry": "coffee",
+ "business_type": "coffee",
"adjectives": ["warm", "crafted", "modern"],
"mechanism": "The arched mouth of a kiln, with one ember inside: the place where the coffee is made.",
"rationale": "One sentence of why this answers the brief. Only claims you can stand behind; no invented stats.",
@@ -26,6 +27,6 @@
"contact": {"address": "12 Roaster Lane, Istanbul", "phone": "+90 212 000 0000", "email": "hello@kiln.example", "web": "kiln.example"},
"contact_name": "Name Surname",
"contact_role": "Head Roaster",
- "merch": ["mug", "tote", "apron", "tee", "cap", "sticker"],
+ "merch": ["coffee-bag", "coffee-cup", "mug", "travel-mug", "apron", "tote"],
"misuse_extra": [{"svg": "final/misuse-1.svg", "label": "Don't close the arch"}]
}