Rep Llama

Brand & design system

The spec for everything visual in the Rep Llama suite. Read this before writing UI.

Machine-readable: brand.md · tokens.json · tokens.css · llms.txt

The spec for everything visual in the Rep Llama suite. Read this before writing UI.

This file is published at https://www.repllama.com/brand — unlisted (served noindex) but public, so agent tooling can fetch it without a login. This repo is the source; the served copy is generated. After editing, run scripts/sync-brand-site.sh from the meta-repo root and commit the result in wanaku-portal, or the published page silently goes stale. scripts/sync-brand-site.sh --check proves it hasn't.

The name is two words — Rep Llama — everywhere except the lockup, where it is set solid as RepLlama. Running text, tab titles, legal copy, alt text, App Store text: two words. See §3.

In copy addressed to a user, the product is Rep Llama — not Rep Llama Suite. Decided 2026-08-06 (EM-10601), after the invite and approval emails were reported for reading "Rep Llama Suite". Applies to anything a user reads: email bodies and subjects, sender display names, marketing pages, in-app copy, App Store text.

Rep Llama Suite survives only as an internal collective noun — architecture comments, repo docs, the wanaku-suite meta-repo itself — where the point is specifically "the set of tools" rather than the product a user is being sold or sent mail from.

Not yet propagated. At the time of writing the string still appears in roughly 120 files across 14 repos, including several that must not be changed casually — see docs/NAMING-MIGRATION.md.


#How to use this (for agents)

  1. Use the tokens. Never hardcode a hex that already has a token. Light and dark are both required — a token without its dark value is an unfinished token.
  2. The code is the source of truth, not this file. If they disagree, the repo wins and this doc gets updated. Canonical sources:
    • Web tokens — wanaku-portal/app/globals.css
    • Web brand components — wanaku-orders/components/brand/, components/suite/
    • iOS tokens/components — RepLlamaKit/Sources/RepLlamaDesign/
  3. Fixed, do not restyle without an explicit request: the colour tokens, the per-tool accents, the type stack, and spacing/radius/elevation/motion. The MARK is not on that list — it changed on 2026-08-04 (see §4); the 2×2 grid is retired.
  4. The tool catalog is runtime-editable in /admin/tools and served from public.tools. Treat §5 here as the seeded fallback; the database wins.

#1. Colour tokens

Mirrored from wanaku-portal/app/globals.css. repllama-tokens.css in this folder is a copy for non-Next surfaces; tokens.json is the machine-readable form.

TokenLightDark
--brand#2c6ad4#6fa6ff
--brand-hover#21539f#8fbaff
--brand-fg#ffffff#06223f
--brand-subtle#e6f0fe#15294a
--accent (copper)#e8b338#e8b338
--accent-fg#002952#1a1410
--background#f6f8fb#0b1622
--surface#ffffff#122033
--surface-2#e9edf2#1a2b40
--foreground#002952#e8eef5
--muted#5c6b79#9db0c2
--muted-2#8b97a2#6f8298
--border#dee4ec#26384f
--border-strong#c5cfda#38506b
--success / -subtle#1b9a66 / #e3f4ec#34c58a / #0f3325
--warning / -subtle#b5840f / #fbf0d4#e0a52e / #3a2d10
--danger / -subtle#d8493c / #fbe7e4#f0625a / #3a1a18
--ring#508ff7#6fa6ff
Every colour token, light value left and dark value right. Rendered from tokens.json.
  • --brand #2c6ad4 / #6fa6ff
  • --brand-hover #21539f / #8fbaff
  • --brand-fg #ffffff / #06223f
  • --brand-subtle #e6f0fe / #15294a
  • --accent #e8b338 / #e8b338
  • --accent-fg #002952 / #1a1410
  • --background #f6f8fb / #0b1622
  • --surface #ffffff / #122033
  • --surface-2 #e9edf2 / #1a2b40
  • --foreground #002952 / #e8eef5
  • --muted #5c6b79 / #9db0c2
  • --muted-2 #8b97a2 / #6f8298
  • --border #dee4ec / #26384f
  • --border-strong #c5cfda / #38506b
  • --success #1b9a66 / #34c58a
  • --success-subtle #e3f4ec / #0f3325
  • --warning #b5840f / #e0a52e
  • --warning-subtle #fbf0d4 / #3a2d10
  • --danger #d8493c / #f0625a
  • --danger-subtle #fbe7e4 / #3a1a18
  • --ring #508ff7 / #6fa6ff

There is no --info token on the web; iOS maps info to brand.

The bridge contract. The canonical brand components use raw CSS-variable utilities only (bg-[var(--surface)]), never semantic classes — shadcn's --muted/--accent are backgrounds, the opposite of ours, so raw vars in the un-prefixed namespace never collide. A repo lacking these vars declares ONE block scoped to .repllama-chrome, the hook class every brand component root carries. Never remap a host's own tokens. Contract: --surface, --surface-2, --brand, --brand-subtle, --brand-hover, --brand-fg, --foreground, --muted, --muted-2, --border, --ring, --accent, --warning, --font-hanken, --font-inter.


#2. Typography

RoleFaceWeights
Display / wordmarkHanken Grotesk500, 700, 800
UI / bodyInter400, 500, 600, 700
Code / numeralsJetBrains Mono400, 500

500 is load-bearing — the lockup sets Rep at 500. Omit it and the browser synthesizes the weight or snaps to 700, flattening the hierarchy the brand is built on.

Loaded via next/font in every app (self-hosted, preloaded). The two non-Next surfaces — wanaku-prospect and the marketing site — use a Google Fonts <link> with the same weights. iOS bundles no custom font, so the lockups use the system face at matching weights (.medium / .heavy); the hierarchy is preserved, the face is not.


#3. The wordmark and its three lockups

Canonical: wanaku-orders/components/brand/Wordmark.tsx (web), RepLlamaKit/Sources/RepLlamaDesign/Wordmark.swift (iOS).

#Solid vs spaced — the rule

  • In the lockup: RepLlama, set solid, no space.
  • Everywhere else: Rep Llama, two words. Running text, page titles, legal copy, alt text, accessibility labels, App Store metadata.

Two words need a hierarchy, so within the solid form:

Rep    500, letter-spacing 0.01em
Llama  800
outer  letter-spacing -0.02em   ← tracks the pair back together

At small sizes (the endorsement line) Llama drops to 700 — 800 is too dense there.

#The three lockups

LockupCompositionUse
Horizontal (default)mark + wordmark, gap 0.2 × the mark's height, optical centrethe everyday lockup
Stackedmark above; REP at 500/10px/0.22em tracked uppercase in --muted, Llama at 800 belowsquare containers: app icons, avatars, favicons ≥32px
Endorsementtool name at 800, by RepLlama at 500/700 in --muted beneathwhere a single tool is named and the mark already appears once — this is what every tool's nav renders

Horizontal proportions are fixed, at a 50px reference: mark 50 / wordmark 25 / tagline 10 / gap 10 — the mark is exactly 2 × the wordmark and 5 × the tagline. The gap is deliberately tight because the mark is a framed badge and its frame already carries padding; a generous gap double-counts it and the lockup drifts apart. The endorsement lockup takes the same gap for the same reason (that rationale is a property of the mark, not the variant), and keeps its own tool-name and by RepLlama ratios.

Clear space: one quarter of the mark's height on every side. The frame is part of the mark, not padding. Exported as clearSpaceFor(size). Minimum mark size: 20px (MARK_MIN_SIZE), enforced in the component. The one exception is the browser favicon, fixed at 16px by the OS — that surface uses the frameless master in app/icon.svg, never the component.


#4. The family mark

A llama head in a rounded frame. Canonical: wanaku-orders/components/brand/RepLlamaMark.tsx (web, byte-identical across the ten tool repos and the portal under check-shell-parity.sh) and RepLlamaKit/Sources/RepLlamaDesign/RepLlamaMark.swift (iOS). Geometry lives in ONE table per platform — RepLlamaMarkGeometry on iOS, exported path constants on web — transcribed mechanically from the delivered masters. If you are re-drawing the animal, that's the bug.

The 2×2 rounded-tile grid is RETIRED (2026-08-04). It said "a suite of tools" and nothing else — no name recall, no warmth — and it collided with the tool tiles it sat above. Anything describing the mark as a suite grid is stale documentation.

#Three crops — the filenames are not self-explanatory

"Flat" means no sheen. It never means no frame. The crop is a separate axis from the master:

CropAssetWhat it isWhere
Framedmark-primary, master-flat, master-sheenField + gold frame + animalThe standalone mark, app tiles, anywhere it needs its own ground. This is what RepLlamaMark.tsx draws.
Unframedmark-small (shipped as exports/icon.svg)Field + animal, no gold frameFavicon and small UI. It keeps the navy field — never let it go transparent or it vanishes into dark browser chrome.
Glyphmark-glyph-ink, mark-glyph-reverseAnimal only — no field, no frame, transparentThe one case the mark sits directly beside type. Nothing else.
The delivered masters. Click any mark to download the SVG.

Every derivative except the two glyphs carries the frame. Beside type a framed mark reads as a button, which is why the glyph exists. mark-glyph-ink is for light grounds, mark-glyph-reverse for dark — unlike the framed mark, the glyph has no field of its own, so it is the one part of the mark that must follow colour scheme.

⚠️ The kit's own artwork draws the endorsement lockup with no mark at all — verified in its rendered DOM: the variants row contains exactly two images, for the horizontal and stacked lockups, and the endorsement card has none. Its caption is explicit: the endorsement "labels a single tool without repeating the mark twice." A glyph belongs there only when the lockup is a standalone tool header with no tool icon beside it, and then it is sized by type metrics — its height spans the cap of the tool name to the baseline of the by RepLlama line (32px at the 19/11 reference), anchored top and bottom rather than vertically centred.

#Two masters, and the split is a hard rule

MasterUseWhy
Sheen128px and upGold sweep down the neck and back, delicate 16-unit frame. Reads as crafted at size.
Flatbelow 128pxSame silhouette, no sweep, heavier 54-unit frame that survives the smaller render.

⚠️ The delivered brand kit says 48px. We ship 128 deliberately — do not "fix" it back. A stroke renders at units / 2048 × size, so the sheen master's 16-unit frame is sub-pixel until 128px: at 48 it computes to 0.38px and smears into a dark olive edge, while the flat master one pixel smaller renders a clean 1.24px copper ring. Switching at 128 uses each master only in the range it was drawn for and alters no geometry. Owner-reviewed.

128 is now consistent everywhere it is asserted (2026-08-06). MARK_SHEEN_THRESHOLD (web) and sheenThreshold (iOS) were always 128; the Wordmark prop doc that still said 48 was corrected across all eleven byte-identical repos, and the published kit at /brand/kit.html is patched on copy — scripts/sync-brand-site.sh rewrites the five places the kit asserts 48, and fails loudly if that copy ever changes. The kit file in this folder is the untouched design deliverable and still says 48; that is the one remaining place, and it is deliberate. The cost is that the sweep is effectively reserved for login, covers and marketing — which is where the cloth-weight brand surfaces live anyway.

#Colour — fixed hexes, not tokens

Field #002952, frame and sweep #e8b338, animal white. The field is deliberately darker than --brand so the mark reads as the parent above the twelve tool accents, and must never be recoloured to --brand. Because it carries its own field it is a solid badge that works on any surface — and, unlike the retired grid, it does not adapt to dark mode.

In a mark with no field of its own, the eye is a HOLE, not a colour (corrected 2026-08-06). The rule is exactly one line, and it splits the family cleanly:

Carries its own field → solid eye in that field's colour. No field → the eye is punched out with fill-rule="evenodd" and takes whatever is behind the mark.

mark-primary, master-sheen, master-flat, mark-small and mark-mono-gold carry a field, so their eye is solid and matches it. The five frameless assets — mark-glyph-ink, mark-glyph-reverse, mark-mono-ink, mark-mono-reverse, mark-mono-grey — are cut-outs.

A fixed eye colour on a frameless mark is only ever correct by accident, and it was wrong in two visible ways at once. mark-glyph-reverse had a white eye on a white animal, so the eye was simply absent. mark-mono-reverse — the asset §"Installable icons" and the kit both name for the per-tool app icon, reverse mark on the tool's accent field — had a navy eye that stayed navy on Email Blast amber, Prospect lime and every other accent. As cut-outs both are right on any ground for free. Never re-solidify one, and if you redraw the animal keep the muzzle subpath merged into the body path rather than stacking it on top.

#Installable icons

app/icon.svg and app/apple-icon.png are the llama alone on solid #002952, frame cropped off — a rounded frame inside an OS-rounded icon reads as an icon within an icon. They ship flat and opaque; iOS masks the corners itself, so do not pre-round them. app/apple-icon.tsx was deleted — there is no ImageResponse for it. opengraph-image.tsx still generates, but the mark is inlined there as a PNG data URI, because satori only decodes raster images and cannot render the component.

#Misuse, in priority order

The sheen master below 128px; recolouring the animal or the field; rotation; non-uniform scaling (the delivered masters ship preserveAspectRatio="none" — it is stripped in both implementations); drop shadows; placing the mark on a mid-tone ground where the navy field stops separating.


#5. The weave — the brand device

Canonical: wanaku-orders/components/suite/WeaveBand.tsx, RepLlamaKit/Sources/RepLlamaDesign/WeaveBand.swift.

A woven band after the striped aguayo a llama wears. It is the one brand element that is not the logo, and it exists so a surface can feel like us without a mark on it.

One implementation per platform. The motif lives in a single geometry table that the component, the email rasterizer and the PDF renderers all read. If you are hand-rolling an SVG <pattern>, that is the bug — grep -rn "<pattern" should only find WeaveBand.

#Geometry (1× units)

  • Tile 18px wide, two diamonds per tile → 9px diamond pitch.
  • Ground var(--brand) (or the tool accent — see below).
  • Copper hairlines var(--accent), 1px, at y 2.1 and 6.9 in the 10px band.
  • Light thread white at 50% — not a hex, so it survives dark mode.
  • Diamond 4.8px tall, centred between the hairlines (so it spans exactly hairline-to-hairline).

Rendered with an SVG <pattern> in user-space units, with no viewBox — a viewBox would map the tile onto the full width and stretch the motif. Pattern ids come from useId(); two bands on one page must not collide.

#Three weights

HeightContents
4 hairlineground + one copper hairline. No diamonds.
10 bandground + two copper hairlines + diamond row. The default.
28 cloth+ light bands top and bottom, copper diamond core, stepped selvedge. Tiles vertically as a field.

#Per-tool variant

The ground takes the tool accent, and the thread pair becomes white at 88% and #002952 at 55%. Copper only reads on the indigo ground — it disappears on the warm and teal accents — so it must never be used there. This is enforced in the API: passing a tint switches the threads, and there is no way to request copper on a tinted ground.

#Rules

  • Full-bleed to both edges, at the top of a surface or the foot of a printed page. Never inset, never independently rounded, never floating mid-layout.
  • One per surface.
  • Never set type on it. Never place the mark on it.
  • Under 120px long, drop to the 4px hairline.
  • Cloth (28px+) only for login, empty states, covers, marketing. Never behind body copy.
  • Inside a tool the ground is that tool's accent; the hub and anything cross-tool uses --brand. Never two accents on one surface.

#Where it ships

Auth/login card (top), printed line sheets and order confirmations (foot), the signing certificate (foot), login split-screen cloth panel.

NOT transactional email. This section used to list "transactional email headers", and it disagreed with what actually ships. The Resend-hosted templates — stripe-connect-setup and status-update, which are the reference for suite transactional mail — open on a centred wordmark.png above a #dee4ec rule, with no weave anywhere. Sign Docs matches them (EM-10601). Only the portal's own mailer still renders a band, and it is the outlier.

Resolved 2026-08-06 in favour of the templates: the wordmark identifies the sender at a glance in an inbox, which is the job of an email masthead, and a 10px band does not. If you are building a transactional email, use the masthead in §"Email masthead" below and do not reach for the weave.

If the weave is ever wanted in email again, the technique is a repeating background-image over a background-color ground, so a client that blocks images still gets a correct solid band. The PNG is generated from the shared geometry by npm run brand:weave in the portal — never hand-drawn.

#Email masthead

The canonical transactional email header, as shipped by the hosted templates and Sign Docs:

Card560px max-width, #ffffff, border-radius:6px, page ground #f6f8fb
Logowordmark.png at width="240", centred, alt="Rep Llama - Tools reps actually need"
Padding32px above the logo, 28px below, 36px gutters
Divider1px #dee4ec rule between masthead and body
TypeInter,Arial,Helvetica,sans-serif; body 16px/26px in #002952
CTA#2c6ad4, white, 15px/600, 14px×34px padding, border-radius:6px

Art is fetched from https://app.repllama.com/brand/one origin, never vendored into a tool repo. A local copy is how the retired 2×2 grid mark survived in Sign Docs email for two weeks after the rest of the suite had moved on. Mail clients strip inline SVG and block data: URIs, so a hosted absolute URL is the only form that renders.


#6. Tool catalog — twelve tools

Seeded fallback. public.tools is authoritative and /admin/tools edits it live.

ToolslugAccentlucideSF SymbolSectionSubdomain
Ordersorders#3B82F6clipboard-listlist.bullet.clipboardSellorders
B2B Siteb2b#0EA5E9storestorefront.fillSellb2b
Catalog Flipflip#A855F7book-openbook.pagesCatalogflip
Catalog Pricerpricer#14B8A6tagtag.fillCatalogpricer
Product Infoproducts#06B6D4package-searchshippingbox.fillCatalogproducts
Catalog Studiostudio#EC4899book-markedbooks.vertical.fillCatalogstudio
Image Flowflow#F97316imagesphoto.stackCatalogflow
Sign Docssign#10B981signaturesignatureOutreachsign
Email Blastmail#F59E0Bsendpaperplane.fillOutreachmail
Rallyrally#F43F5Ecalendar-clockcalendar.badge.clockOutreachrally
Fancy Formsforms#6366F1list-checkschecklistOutreachforms
Prospectprospect#84CC16map-pinmappin.and.ellipseOutreachprospect
The twelve tool accents, drawn as the 4px top rail the launcher card uses. The database winspublic.tools is authoritative and /admin/tools edits it live.
  • Orders orders #3B82F6 · Sell
  • B2B Site b2b #0EA5E9 · Sell
  • Catalog Flip flip #A855F7 · Catalog
  • Catalog Pricer pricer #14B8A6 · Catalog
  • Product Info products #06B6D4 · Catalog
  • Catalog Studio studio #EC4899 · Catalog
  • Image Flow flow #F97316 · Catalog
  • Sign Docs sign #10B981 · Outreach
  • Email Blast mail #F59E0B · Outreach
  • Rally rally #F43F5E · Outreach
  • Fancy Forms forms #6366F1 · Outreach
  • Prospect prospect #84CC16 · Outreach

All subdomains are <sub>.repllama.com; the hub is app.repllama.com.

This table now matches public.tools row for row — it previously carried four values the shipped catalog never had (b2b's accent and SF symbol, Studio's glyphs, Image Flow's and Prospect's SF symbols). Where the spec and the database disagree, the database wins; these were reconciled to it on 2026-08-04, and b2b's accent was moved to the kit's #0EA5E9 by owner decision at the same time.

The launcher card renders the accent as a 4px hairline TOP rail, not a left border — see components/tools/ToolRail.tsx. Accent ground, one 1px thread, no diamonds, full-bleed. The thread is white at 88% or ink #002952 at 55%, whichever contrasts better against that ground — a fixed white thread vanishes on the light accents. The kit's "luminance > 0.55" rule is not implementable as written: no accent here exceeds 0.55 under WCAG, so a literal reading paints every rail white. Locked tiles take a flat --border-strong rail with no thread.

Admin's tile is slate #64748b, deliberately not a tool accent.

Adding a tool needs a migration (/admin/tools can only UPDATE), the lucide glyph in wanaku-portal/components/tools/ToolIcon.tsx in the same change (an unmapped icon silently renders a clipboard), and a waffle re-sync. See docs/RepLlamaWebVault/Adding a Tool.md. Accent hues are nearly exhausted.

b2b is catalog-only: a row and a locked tile, no repo, no app.

CORRECTED 2026-08-04: studio used to be listed alongside it. That was wrong. Catalog Studio is a real, deployed, in-use app — repo wanaku-catalog-studio, its own Vercel project, serving studio.repllama.com (the subdomain is mid-cutover; see meta-repo CLAUDE.md gotcha 0b), with the full suite wiring: auth gate, lib/suite, components/suite, waffle. It was missed by the rebrand sweep only because the repo had never been cloned to ~/GitHub, and the parity scripts read the standalone clones — no clone, no scan, no complaint.


#7. Page titles

export const metadata: Metadata = {
  metadataBase: new URL("https://<subdomain>.repllama.com"),
  title: {
    default: "<Tool Name> · Rep Llama",
    template: "%s · Rep Llama",
  },
  description: "<the tool's one-line sizzle from the catalog>",
};
  • Separator is · (U+00B7), never a hyphen. Always ends in Rep Llama.
  • default carries the suffix too — the template does not apply to it.
  • The portal hub is the lone exception: plain "Rep Llama", no template.
  • Descriptions come from the catalog sizzle, so the tab, the launcher tile and the OG card all say the same thing.

#8. Spacing, radius, elevation

Unchanged by the rebrand. Spacing 4 / 8 / 12 / 16 / 24 — shared by both platforms. Tinted-surface wash 0.12, icon chip 0.10 (matching the web's 1A alpha suffix).

Radius is platform-split, and this section used to hide that. It read badge 8, card 12 with no platform qualifier — which is the iOS scale, quoted incompletely (it drops surface 16). 12px appears nowhere in the suite's web UI: wanaku-orders ships rounded-md ×419, rounded-lg ×131, rounded-full ×26 and zero rounded-xl. A web surface built "to spec" from the old line would have been wrong.

Source of truthScale
iOSRepLlamaKit/Sources/RepLlamaDesign/Theme.swiftRepLlamaTheme.Radiusbadge 8, card 12, surface 16
WebTailwind, used directly — there are no web radius tokens, by designrounded-md 6px for controls, rounded-lg 8px for surfaces (cards, modals, popovers)

Do not add web radius tokens, and do not copy the iOS numbers onto a web surface. The generated repllama-tokens.css emits no radius on purpose, and scripts/sync-tokens.sh fails on any token the canon invents. The one non-Next surface that cannot use Tailwind classes (wanaku-prospect) names the two it mirrors — --radius-md / --radius-lg, after the class rather than a role, so porting a canonical component stays mechanical — in its own stylesheet, never in the generated token file.

iOS values are quoted here for orientation; Theme.swift is authoritative. If the two disagree the code wins and this table is what gets edited (chain of truth, §0).


#9. Voice

Plain, specific, unglamorous. Sizzle lines are one sentence, lowercase-ish, and describe the job — "Price a vendor catalog, fast." Not "Revolutionize your catalog workflow." The tagline is "Tools reps actually need", with actually in --warning. Hub only.


#10. Known cross-platform gaps

  • iOS bundles no custom font, so lockups use the system face at matching weights.
  • The static waffle lists in each repo are an offline fallback; the live catalog comes from /api/suite/access. A stale name there is cosmetic.
  • Announcements are dormant — authorable in the admin, but nothing renders them.