AI Brand Guide
PRIMER · AI READ ME FIRST
If you read nothing else, follow the ten rules to the right. They are sufficient for ~90% of Parcion visual output. Refer to the linked sections for any case the rules don't cover.
# Parcion Private Wealth — condensed rules 01. Surface bias is BLACK (#0B0B0B), not navy. Never use #00141C as a page background. Navy is data-viz only. → §04 Color 02. Gold (#B99A38) is an accent only. Never H1, never a headline color, never a fill. → §04 Color 03. Type: Brother 1816 (display), Palatino Linotype (body), Aptos (tables/data). H1 is ALL CAPS, 32–44px, never serif. MS files (Word/PPT/Outlook): swap Brother 1816 for Century Gothic — same ALL-CAPS rules, same sizes, same hierarchy. Body and data fonts unchanged. → §05 Type 04. Default hero, NON-financial topics → default_hero_general Default hero, FINANCIAL topics → default_hero_financial → §02 Imagery 05. Logo: primary lockup on most surfaces. Switch to alternate (no descriptor) when the placement is under 80px tall. → §01 Logos 06. Voice: clear, confident, warm, precise. No em dashes. Oxford commas. Active voice. Short sentences. → §03 Voice 07. Forbidden words: "leverage" (verb), "holistic," "quarterback," "synergy," "best-in-class," "HNWIs," "heirs," "solutions." → §03 Voice 08. Charts: gold for ONE emphasis series only. Secondary palette for the rest. No 3D, shadows, gradients, truncated y-axes. → §06 Tables 09. For copy-paste output templates → §09 Recipes 10. For URLs in machine-readable form → §10 Manifest 11. Motion is scroll-driven and additive. Elements arrive quietly. Nothing animates on click alone. JS off = fully usable. → §12 Dev 12. Green means positive return and nothing else. The five performance colors (#5BBE8C → #8E3E48) are labels only — never fills, never decoration. Sage #8DB999 is a chart series, not a performance color. → §06 Tables 13. Cards are flat. No border-radius, no shadows, on any surface, ever. → §07 Components # Full pasteable AI brief lives at §08. When in doubt: black # surface, fewer words, restraint reads as quality.
Global Brand Guide
Clarity creates confidence. This guide governs how Parcion looks and sounds across every channel: web, email, document, deck, advertising, and AI-assisted content. Pair the HTML reference with the standalone stylesheet so any colleague can drop the brand onto a page.
01 — Logos
The Parcion icon is always rendered in Gold Dark (#8D762E). The full lockup pairs the icon with the wordmark and the descriptor "PRIVATE WEALTH." Use the lockup in most applications; reserve the icon-only and wordmark-only treatments for genuine sizing constraints. As of v4.7.0 the marks shown here are embedded in this file as inline vectors, so they render with no internet connection. Click any file below to download the distributable assets.
Use sparingly. Reserved for tight spaces like website headers, footers, and small-format placements where "PRIVATE WEALTH" would be illegible or cut off.
Use sparingly. Reserved for social avatars, favicons, app icons, and secondary brand patterns where the full lockup cannot sit comfortably. Always rendered in Parcion Gold.
Developer note:
Hatched links above are placeholders. Drop the files into the same folder as this guide using the exact filenames shown in the tooltip (e.g. parcion-primary.svg), and each download will start working without further edits.
02 — Imagery
A working library of brand-approved images. Editorial restraint: think film photography, natural light, lived-in luxury. Black and white for people; Kodak Portra-inspired color for environments. No generic stock. No hyper-saturated processing. One strong image, used well, beats three competing visuals.
Two hero defaults govern most outputs. Use these unless there's a defensible reason to pick another. All other images below are options the AI or layout designer may reach for when the default doesn't fit.
Black-and-white, Ilford Delta tonal feel. Used for team, clients, and human moments. Often paired with dark backgrounds. Portraits are one-off commissions per person, sourced from the team page on parcionpw.com or directly from the Parcion photo library; not download-managed from this guide.
Kodak Portra-inspired tonal range. Used for lifestyle, environment, and editorial moments. Best on dark surfaces with a soft gradient transition between image and background.
Landscape format for LinkedIn banners and full-width web heroes. Aspirational scenery, nature, open road, depth. Always pair with a dark gradient overlay on the side where the logo will sit.
Sourcing stock outside the library
If the approved Parcion library doesn't have what you need, unsplash.com is the approved fallback for editorial-quality stock. Always check with Marketing before publishing anything sourced this way. Do not use Getty, Shutterstock, or generic corporate libraries.
To populate this section: Send a URL (Vercel, Sanity, Cloudflare, any public CDN) and a one-line description for each image. I'll wire each one into the appropriate category card.
03 — Voice
Parcion sounds like a trusted advisor who has seen this before, knows exactly what to do, and is completely focused on you. Calm, confident, and deeply knowledgeable.
Clear. Not simple, but never needlessly complex. Every sentence earns its place.
Confident. Not arrogant, not hedged. "We recommend," not "we think."
Warm. Human, not corporate. "Families," not "high-net-worth individuals."
Precise. No wasted words, no vague claims. Specificity builds credibility.
Off-brand
Hope this email finds you well! I wanted to touch base on our holistic wealth management solutions and how we can leverage your upcoming liquidity event.
On-brand
As your business sale approaches, I wanted to make sure we have the right plan in place for the proceeds. There are a few decisions in the next 90 days that will have a meaningful impact on your tax situation, and I'd like to walk you through them before anything is finalized.
Off-brand
Parcion Private Wealth offers a full suite of best-in-class wealth management solutions for high-net-worth individuals and their families.
On-brand
Parcion works exclusively with business owners and their families, guiding them leading up to, through, and after the sale of their business. We help them keep more of what they've built, take care of the people they love, and figure out what comes next.
04 — Color
Parcion's dominant surface is black, not navy. Off-Black (#0B0B0B) is the brand's anchor color across web, deck, and print. Gold is a small, deliberate accent: eyebrows, rules, key data points, and the icon. White space carries the weight.
Neutrals first, then the three values of gold. All values are official Parcion brand colors. The surface bias is black, not navy; navy lives in the secondary palette below.
These colors exist exclusively for charts, graphs, and financial tables that require multiple colors to separate meaning. They are not for backgrounds, headings, or decorative use. As of v4.8.0 every secondary hue sits at the gold's saturation level, so charts read as one family and gold keeps its job as the single point of emphasis. Series order: Gold, Ink, Forest, Clay, Slate Blue, Stone, with Sage as overflow. Navy lives here because the brand book preserves it; reserve it for chart contexts only. Burgundy is no longer a general series: it marks negative returns and nothing else.
Burgundy carries loss and drawdown, nowhere else. It never appears as a general chart series, so when a reader sees it, it means one thing. On dark surfaces use Burgundy Mid.
05 — Typography
Brother 1816 for headings, Palatino Linotype for body, Aptos for tables and data. Century Gothic substitutes for Brother 1816 in Microsoft environments. The scale is intentionally restrained: H1 sits between 32px and 44px, matched to parcionpw.com. Gold is never used as heading text.
Type specimens
Clarity creates confidence.
On dark
Headings on dark surfaces remain white. Gold is reserved for the eyebrow above and for small accent moments inside the layout. H1 padding stays generous below, restrained above.
06 — Tables & Data
Tables read at 15px (one step up from prior templates) for comfortable on-screen and printed reading. Headers sit on Parcion Gold with black type. Rows alternate white and #F2F2F2. Numbers tabulate.
| Beneficiary | Age | Share | Amount | Distribution method & notes |
|---|---|---|---|---|
| Child 1, Eldest Married, operating own business |
32 | 16.67% | $10,000,000 | Outright distribution at age 32 with optional trust overlay for asset protection. |
| Child 2 Mid-career professional |
29 | 16.67% | $10,000,000 | Discretionary trust until 35; HEMS standard for trustee distributions. |
| Child 3, Youngest Completing graduate studies |
25 | 16.67% | $10,000,000 | Education and incentive trust; staggered access at 30, 35, and 40. |
| Family Foundation Donor-advised structure |
— | 50.00% | $30,000,000 | Donor-advised fund funded at closing for ongoing philanthropic strategy. |
Charts use the secondary palette for differentiation. Gold (#B99A38) is reserved for the line, bar, or segment that carries the point. Neutral tones do the rest of the work. Axis labels in Aptos, light gray, secondary to the data itself.
Performance
Portfolio growth vs. benchmark
Hypothetical, indexed to 100 at start. Gold line is the moment of emphasis.
Allocation
Asset class allocation
Sample target allocation. Gold marks the conviction overweight.
Distribution
Where the proceeds went
Sample post-liquidity allocation across family priorities. Hypothetical.
| Investment portfolio | 40% | $24.0M |
| Estate & next-gen trusts | 25% | $15.0M |
| Philanthropy (DAF) | 20% | $12.0M |
| Reserves & liquidity | 15% | $9.0M |
Light surface deliverables — print briefs, leave-behinds, landscape investment decks — follow the same palette logic with a few specific adjustments for a white field. These are not a separate design system; they are the same tokens applied to a different background.
The standard layout for landscape investment slides and client decks. Left column carries the headline and one or two lines of supporting copy. Right column (roughly 2/3 of width) holds the chart. This ratio gives the chart room to breathe without making the copy feel cramped.
Same data, same palette, dark background. Axis lines and labels lift to a lighter neutral so they remain legible without competing with the data.
Performance
Portfolio growth vs. benchmark
Hypothetical, indexed to 100 at start. Gold line is the moment of emphasis.
Allocation
Asset class allocation
Sample target allocation. Gold marks the conviction overweight.
Distribution
Where the proceeds went
Sample post-liquidity allocation across family priorities. Hypothetical.
| Investment portfolio | 40% | $24.0M |
| Estate & next-gen trusts | 25% | $15.0M |
| Philanthropy (DAF) | 20% | $12.0M |
| Reserves & liquidity | 15% | $9.0M |
06 — Tables & Data
Returns get five colors of their own, and those colors do exactly one job: tell the reader whether a number is good news. They color text and labels only. The moment one becomes a fill or a background, it stops being a signal and starts being decoration.
Green appears nowhere else in the Parcion palette. That absence is the design. When a reader sees green anywhere in a Parcion document, it can only mean one thing: a positive return. No green buttons, no green icons, no green section accents, ever. Spend the color nowhere and it pays for itself everywhere.
The caution slot is Warm Gold, not orange and not yellow. Orange reads as alarm. Yellow reads as cheap. Warm Gold says "watch this" without saying "loss," and it already belongs to the family, so the table stays calm. The negative is Burgundy (#8E3E48), and as of v4.8.0 burgundy does no other work in the palette. Pure #FF0000 shouts. Parcion does not shout, even about drawdowns. A client reading a hard quarter should feel informed, not punished. Because burgundy appears nowhere else, it works the way green does: one color, one meaning.
The ramp ships as five custom properties. Use the tokens, not the raw hex, so a future recalibration lands everywhere at once.
/* Performance ramp — lives in :root alongside the core tokens */ :root { --pp-perf-strong: #5BBE8C; /* strong positive */ --pp-perf-mid: #7EC9A6; /* mid positive */ --pp-perf-mild: #9DCDB8; /* mild positive */ --pp-perf-caution: #CDB365; /* caution / mild negative — Warm Gold */ --pp-perf-negative: #8E3E48; /* negative — never pure red */ } /* Usage classes. Color only. Never background, border, or fill. */ .pp-return-strong { color: var(--pp-perf-strong); font-family: var(--pp-font-ui); font-weight: 600; } .pp-return-mid { color: var(--pp-perf-mid); font-family: var(--pp-font-ui); font-weight: 600; } .pp-return-mild { color: var(--pp-perf-mild); font-family: var(--pp-font-ui); font-weight: 600; } .pp-return-caution { color: var(--pp-perf-caution); font-family: var(--pp-font-ui); font-weight: 600; } .pp-return-negative { color: var(--pp-perf-negative); font-family: var(--pp-font-ui); font-weight: 600; }
| Meaning | Token | Hex | Sample | Why this value |
|---|---|---|---|---|
| Strong positive | --pp-perf-strong |
#5BBE8C | +11.6% | A medium green that reads clearly at 15px without glowing. Unmistakable gain. |
| Mid positive | --pp-perf-mid |
#7EC9A6 | +5.1% | One step quieter. A solid quarter that does not need a headline. |
| Mild positive | --pp-perf-mild |
#9DCDB8 | +1.2% | Pale, nearly neutral. Positive, but barely. The color matches the news. |
| Caution, mild negative | --pp-perf-caution |
#CDB365 | -1.6% | Warm Gold. "Watch this," not "panic." Shares the family, keeps the calm. |
| Negative | --pp-perf-negative |
#8E3E48 | -4.0% | Desaturated red. Honest about the loss, measured about it. Never #FF0000. |
When no analyst judgment is supplied, automate with these bands: strong positive at +8% and above, mid positive from +3% to +8%, mild positive from 0% to +3%, caution from 0% to -3%, and negative below -3%. Judgment beats bands. A flat quarter in cash is fine; a flat quarter in small caps during a broad rally deserves the caution color. The bands are a floor for automation, not a ceiling for thinking.
| Asset Class | Q2 2026 | YTD 2026 | 3-Year Ann. |
|---|---|---|---|
| US Large Cap Equity | +3.9% | +7.2% | +11.4% |
| International Developed Equity | +4.6% | +10.8% | +7.9% |
| Core Fixed Income | +1.2% | +2.6% | +1.8% |
| Private Real Estate | -0.8% | -1.6% | +2.2% |
| Emerging Markets Debt | -2.1% | -4.0% | -0.9% |
Hypothetical figures for illustration. Past performance is not indicative of future results.
--pp-perf-*), never the raw hex, so a recalibration propagates.The secondary palette (Ink, Forest, Clay, Slate Blue, Stone, Sage, Warm Gold) separates chart series. It carries no meaning. Burgundy sits outside both systems: it marks negative returns only. The performance ramp carries only meaning. The two systems share one value, Warm Gold #CDB365, and that is deliberate: as a series color it is just a color, and as a performance color it means caution. Context resolves it. Do not let the systems trade places anywhere else.
07 — Components
Primary on light is Off-Black with white text. Hover swaps to gold. Ghost variant carries an Off-Black outline. Buttons never use gold as the default fill.
A neutral hairline for content separation, and a gold hairline reserved for moments of brand emphasis.
A liquidity event is more than a transaction. It is a transition.
Pillar 03
Clients who can see their numbers, visualize their future, and trust their plan make better decisions and face the transition with less fear. Parcion's job is to produce that clarity.
07 — Components
A translucent panel for KPI rows, stat cards, and chart containers. It reads as a quiet layer of the surface, not an object floating above it. Square corners, no shadows, on any surface, ever.
A shadow implies the card floats. Nothing at Parcion floats. The panel is Carbon at 52% opacity with a one-pixel light border, which is enough to separate it from Off-Black without inventing a third dimension. On photography, the translucency lets the image breathe through the panel instead of being blocked by it. Skip backdrop-filter: blur() as well: frosted blur reads as an app, and this is a document.
The emphasis variant adds a single gold top border at 25% opacity. One card per row gets it, the card the reader should see first. If every card is emphasized, none is.
Assets Under Advisement
$3.2B
Emphasis variant. One per row.
Client Families
110
A select group, by design.
Wealth Events Guided
60+
Leading up to, through, and after.
Same component, same anatomy. On light slides and print-first pages the panel goes opaque paper with a faint dark border, because translucency has nothing to be translucent over.
Years Serving Founders
20+
Independent
100%
Fiduciary Standard
Always
/* Glass panel — complete component */ .pp-glass { background: rgba(22, 22, 22, 0.52); /* Carbon #161616 at 52% */ border: 1px solid rgba(255, 255, 255, 0.08); padding: 28px 24px; /* No border-radius. No box-shadow. No backdrop-filter. Not negotiable. */ } /* Emphasis: one per row, the card the reader should see first */ .pp-glass--emph { border-top: 1px solid rgba(185, 154, 56, 0.25); } /* Gold #B99A38 at 25% */ /* Light surface: opaque paper, faint dark border */ .pp-glass--light { background: var(--pp-paper); border: 1px solid rgba(0, 0, 0, 0.06); } /* Anatomy */ .pp-glass-label { font-family: var(--pp-font-ui); font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--pp-mid-gray); margin: 0 0 10px; } .pp-glass-value { font-family: var(--pp-font-display); font-weight: 300; font-size: 36px; color: var(--pp-off-white); margin: 0; } .pp-glass--light .pp-glass-value { color: var(--pp-off-black); } .pp-glass-context { font-family: var(--pp-font-body); font-size: 13px; color: #9A9A9A; margin: 10px 0 0; }
<div class="pp-glass pp-glass--emph"> <p class="pp-glass-label">Assets Under Advisement</p> <p class="pp-glass-value">$3.2B</p> <p class="pp-glass-context">Emphasis variant. One per row.</p> </div>
08 — AI Brief
This file is designed to be uploaded into Claude Projects, ChatGPT custom GPTs, Copilot, Gemini, or pasted into a single prompt. The block below is the condensed brief. Everything in this document is authoritative for visual production. When the AI is unsure, it should default to the rules in this section.
SYSTEM BRIEF — Parcion Private Wealth visual production
=======================================================
ROLE
You are producing branded visual output for Parcion Private Wealth,
a private family office serving business owners and their families.
Match the rules below exactly. When in doubt, default to: black
surfaces, gold used sparingly as an accent, Brother 1816 headings,
restrained padding, no decoration.
COLOR (the brand reads BLACK, not navy)
- Primary dark surface: #0B0B0B (Off-Black) — use for every dark background
- Pure black: #000000 (logo backgrounds only)
- Carbon inset panel: #161616
- Body type on light: #2D2D2C (Charcoal)
- Primary light surface: #F7F7F7 (Off-White)
- Paper / white: #FFFFFF (use sparingly)
- Light gray divider: #D6D6D6
- Hairline on light: #E5E5E5
- Hairline on dark: #2A2A2A
- Mid gray secondary: #6B6B6B
- Accent (sparingly): #B99A38 (Parcion Gold)
- Accent on light bg: #8D762E (Gold Dark)
- Gold deeper: #6C5A23 (shadow, depth)
- NEVER use #00141C as a primary background. Navy is reserved for
data visualization only.
DATA-VIZ SECONDARY PALETTE (charts/graphs only, never page background)
Series order: Gold → Ink → Forest → Clay → Slate Blue → Stone; Sage overflow.
- Clay Dark: #7E4830 Clay: #9C5B3C Clay Light: #C08A66 (dark surfaces)
- Warm Gold Lt: #C3A446 Warm Gold: #CDB365 Warm Gold Pl: #D7C284
- Forest Dark: #48614A Forest: #446459 Sage: #8DB999
- Ink Dark: #122735 Ink: #22333B
- Slate Blue: #52697A Slate Blue Lt: #8FA5B3 (dark surfaces; also the overlay line series)
- Warm Gold Cream:#E5D9A9 (lightest step of the six-step gold bar ramp)
- Stone: #A39A88 Stone Light: #B8AF9E (dark surfaces)
- Navy Deep: #00141C (data only)
- Burgundy: #8E3E48 NEGATIVE RETURNS ONLY (Dark #6A2E35; Mid #AA4B56 on dark surfaces)
TYPE — EXTERNAL / WEB (default)
- Display: Brother 1816.
- Body: Palatino Linotype.
- UI/data: Aptos.
- H1 — Brother 1816, Book, 40pt, ALL CAPS, generous tracking
- H2 — Brother 1816, Book, 24pt, ALL CAPS, same tracking as H1
- H3 — Brother 1816, Bold, 18pt, Mixed-Case
- H4 — Brother 1816, Book, 14pt, Mixed-Case
- Body — Palatino Linotype, Regular, 12–14pt
- Paragraph callout — Palatino Linotype, Bold, 14pt
- Functional / table body — Aptos, Regular or Medium, 12pt
- Legal callout — Aptos Narrow, 12pt, ALL CAPS
- Table headers / chart legends — Aptos, Bold
TYPE — MICROSOFT OFFICE (REQUIRED SWAP)
If the deliverable is a Microsoft Office file — Word (.docx),
PowerPoint (.pptx), Outlook email, Teams, or any other MS surface
— substitute Brother 1816 with Century Gothic on every heading.
This is not just a font swap; it carries the same structural rules:
- H1 — Century Gothic, Regular, 40pt, ALL CAPS
- H2 — Century Gothic, Regular, 24pt, ALL CAPS
- H3 — Century Gothic, Bold, 18pt, Mixed-Case
- H4 — Century Gothic, Bold, 14pt, ALL CAPS
- Body — Palatino Linotype (unchanged)
- Functional / table / data — Aptos (unchanged)
DO NOT substitute another sans-serif (Arial, Calibri, Helvetica)
for Century Gothic in internal materials. Brother 1816 is not
reliably installed on MS systems and falls back to a default font
that breaks the brand. Apply the swap to every heading level
before export.
- H1: ALL CAPS, clamp(32px, 3.2vw, 44px), letter-spacing 0.06em.
White on dark, off-black on light. NEVER gold. NEVER serif.
- H2: ALL CAPS, 22px, letter-spacing 0.18em.
- H3: Mixed case, 17px, weight 600.
- Body: 16px / line-height 1.6.
- Tables: 15px Aptos. Gold header, black text on header.
- Pull quotes: ALL CAPS, Brother 1816, gold left rule.
- Italics: reserved for direct quotations and titles of works only. Never for emphasis. Never decorative. Never in headings.
VOICE (rules)
- Clear, confident, warm, precise.
- Never use em dashes. Use commas, colons, or short breaks.
- Always Oxford comma. Active voice. Short declarative sentences.
- No exclamation points in client-facing or thought-leadership copy.
- Italics: direct quotations and titles of works only. Never for emphasis. Never decorative. Never in headings.
- Open with the reader's situation, not the firm's credentials.
- "We" is fine; don't start three sentences in a row with it.
VOCABULARY — USE (preferred Parcion terms)
- "wealth event" / "liquidity event" (not "transaction" alone)
- "business sale" / "business exit" (not just "the deal")
- "families" (over "clients" when possible)
- "business owners" / "entrepreneurs" (not "high-net-worth individuals")
- "leading up to, through, and after" (the three-stage phrase)
- "unlock the true potential" (approved core phrase)
- "clarity" / "confidence" (pair these intentionally)
- "what's next" (natural concept, use freely)
- "advocate" (how Parcion describes its role)
- "partner" / "partnership" (how Parcion describes the relationship)
- "coordinated" / "coordination" (how Parcion aligns advisors)
- "life-changing" (appropriate for wealth events)
- "generational" (wealth, impact, planning)
- "bespoke" (Parcion's approach)
- "fiduciary" (standard Parcion holds itself to)
- "independent" (Parcion is fully independent)
- "private family office" (how Parcion categorizes itself)
- "the causes they care about" (philanthropic framing)
- "taking care of the people they love" (family legacy framing)
- "life simplification" (post-event complexity management)
- "smart planning, strong advocacy" (describes Parcion's service)
- "select group of families" (conveys exclusivity without arrogance)
- "your values, your vision, your concerns" (opening discovery framing)
VOCABULARY — AVOID (with replacements)
- "quarterback" → "advocate," "coordinator," "trusted partner"
- "one-stop shop" → too transactional; remove or rewrite
- "holistic" → overused, vague; remove or be specific
- "seamless" → overused; remove
- "synergy" / "synergistic" → corporate jargon; remove
- "leverage" (as a verb) → "use," "draw on"
- "robust" → overused; be specific
- "best-in-class" → hyperbolic; remove
- "world-class" → use only in direct quotes from approved materials
- "revolutionary" → not Parcion's register; remove
- "cutting-edge" → not Parcion's register; remove
- "thought leadership" → "perspective," "point of view," or the actual topic
- "wealth management firm" → "private family office"
- "high-net-worth individuals" / "HNWIs" → "business owners," "families"
- "solutions" → describe what Parcion actually does
- "product" (for services) → "planning," "advice," specific service name
- "portfolio-centric" → Parcion is not portfolio-first; rephrase
- "transaction" (alone) → "wealth event," "sale" — never standalone
- "heirs" → "children," "next generation," "family members"
- "assets" → "wealth," "what they've built," or specific terms
GRAMMAR / MECHANICS
- Always Oxford comma. Never em dashes.
- Numbers vs. words: use numerals for statistics and data ("60+
wealth events," "$3 billion"). Use words for small numbers in
sentence context ("two goals," "three stages").
- Capitalize "Parcion" and "Parcion Private Wealth" exactly.
- "Signature Event" is capitalized. "What's Next?" includes the
question mark.
LOGO RULES
- Default: full primary lockup (icon + wordmark + "PRIVATE WEALTH").
- On dark backgrounds: reversed lockup.
- Headers, footers, anywhere small or cramped: alternate lockup
(icon + wordmark only, no descriptor).
- Icon (gold diamond mark): use sparingly — avatars, favicons, app
icons. Always Gold Dark (#8D762E). Never recolored.
- Clear space: at least the height of the icon on every side.
LAYOUT DEFAULTS
- Generous white space. Restraint reads as quality.
- Hero padding: 96–120px top, 120–144px bottom.
- Section padding: 96px top and bottom.
- Max content width: 1180px. Gutter: clamp(20px, 4vw, 56px).
- Card-style: 1px solid hairline, no shadows, no gradients.
- Hairline rules at 1px, in hairline gray or sparingly in gold.
CHART RULES
- Series colors from the secondary palette. Gold for emphasis only.
- Axis labels in Aptos, neutral gray, secondary to the data.
- Legends bias toward bottom or right. Never over the data field.
- One idea per chart. Title states the takeaway, not the topic.
- No 3D, drop shadows, glows, gradients, or truncated y-axes.
WHEN IN DOUBT
- Default to client-facing voice.
- Default to black-and-white with a thin gold thread.
- Default to fewer words.
- Default to the layout patterns in the "Recipes" section.
ASSETS
- All logo and image URLs are in the "Asset Manifest" section.
- Use those exact URLs when embedding imagery or logos.
- For decision logic ("which logo? which surface? gold or not?"),
see the "Decision Rules" section.
IMAGE DEFAULTS (use these unless you have a defensible reason to pick another)
- Hero, non-financial topic → manifest.imagery.default_hero_general
https://cdn.sanity.io/images/3phttdbo/production/
01548b6a067a3fee4c7a1c9c42ca391074b9f44c-5760x3600.jpg
- Hero, financial topic → manifest.imagery.default_hero_financial
https://cdn.sanity.io/images/3phttdbo/production/
fc1a057432b72c5e344f2c9c65443e3fdf9da30f-4032x3024.jpg
- Banner / LinkedIn header → manifest.imagery.banners (any of the three)
- Anything else → pick from manifest.imagery.editorial_options
Before generating any HTML deliverable, ask the person requesting the work to confirm these five things. Do not assume defaults without checking. The answers determine the surface, layout, chart rules, and whether a compliance disclosure footer is required.
BEFORE YOU BUILD — HTML DELIVERABLE SETUP
==========================================
Ask these five questions before writing a single line of code or
copy. Wrong answers here mean a rebuilt file later.
01. DOCUMENT TYPE — financial or general?
Financial: investment content, market commentary, portfolio
reporting, planning analysis, transaction briefings, tax
strategy, ESOP, M&A, business sale materials.
→ Use default_hero_financial as hero.
→ Add compliance disclosure footer.
→ Flag any performance data for compliance review.
General: brand, About, team, events, recruiting, lifestyle,
philanthropy, general marketing.
→ Use default_hero_general as hero.
→ Disclosure optional; omit unless content warrants it.
02. FORMAT — scrollable document or paginated slides?
Scrollable: one continuous page. Arrow keys advance by section
(.section blocks). Most client briefs and one-pagers.
Paginated: full-viewport slides. Arrow keys advance by slide
(.slide blocks). Presentation decks, investment overview
decks, educational content built for a conference room.
03. SURFACE — dark or light background?
Dark (default): Off-Black (#0B0B0B) backgrounds, white type.
Use for web-first, digital-primary deliverables.
Light: White (#FFFFFF) backgrounds, Charcoal (#2D2D2C) type.
Use for print-first deliverables: leave-behinds, briefing
books, advisor slide decks, anything handed to a client
on paper or printed from a browser.
When in doubt: dark is the Parcion default. Choose light only
if the deliverable will clearly be printed or shared as a PDF
in a print context.
04. PRINT ORIENTATION — portrait or landscape?
Portrait (8.5 × 11 in): client letters, one-pagers, planning
briefs, narrative reports.
Landscape (11 × 8.5 in): investment decks, dashboards,
data-heavy financial presentations, educational slide sets.
The print default goes in the PDF control bar. List the
default orientation first; include both buttons.
05. RECIPIENT — client-facing, prospect-facing, or internal?
Client-facing or prospect-facing: include compliance
disclosure footer on any piece referencing investment
performance, historical returns, or asset class projections.
Flag for compliance review before delivery.
Internal: disclosure is best practice. Omit only for
clearly internal-only materials with no external audience.
Once confirmed, build the file using the layout defaults from
this brief and add the interactive controls from §12 before
handing it off. See §11 Decision Rules for any edge cases.
08 — AI Brief
The machine-readable rules for motion, interaction, and performance color. Paste the block below into any AI tool building Parcion HTML, alongside the main brief above. It is the contract: if a generated file violates it, the file is wrong, not the brief.
One idea governs everything in this block. Motion at Parcion is a consequence of reading, not a reward for clicking. Things arrive as the reader scrolls to them, quietly, once. A page with the JavaScript stripped out must remain complete: every number present, every chart drawn, every section visible. If removing a script removes content, the pattern was used wrong.
PARCION SCROLL + INTERACTION PRIMER
====================================
MOTION REGISTER
- Scroll-driven, never click-driven. Quiet arrival: no bounce, no flip,
no spin, no parallax theatrics.
- Vanilla CSS + minimal JS only. No GSAP, no Lottie, no scroll libraries.
- Every animation is additive. The page must be complete with JS off:
real values in the HTML, charts visible, sections readable.
- Honor prefers-reduced-motion. When it is set, skip count-ups and
draw-ons entirely; the finished state is already in the markup.
PATTERN INDEX (use when the content calls for it, never decoratively)
1. SECTION REVEAL — fade up 16px on viewport entry, 500ms ease, once.
Default for long-form documents. Mandatory fallbacks: opacity 1 in
@media print and in @supports not (selector(:focus-visible)).
2. SCROLL-DRIVEN ANIMATION (native) — animation-timeline: view(),
always wrapped in @supports. Chromium 115+, Safari 18+, Firefox 132+.
Element fully visible outside @supports. Purely additive.
3. KPI COUNT-UP — 900ms cubic ease-out, fires once at threshold 0.5.
The real value lives in the HTML text; JS replays it.
HTML: <span data-countup data-target="3.2" data-prefix="$"
data-suffix="B" data-decimals="1">$3.2B</span>
4. CHART DRAW-ON-SCROLL — SVG stroke-dasharray technique, 1.2s
cubic-bezier(0.4, 0, 0.2, 1). IntersectionObserver adds .drawn at
threshold 0.4. Dasharray must cover the true path length; measure
with getTotalLength() when in doubt.
5. STICKY STORYTELLING — sticky visual left, scrolling chapters right.
Only for financial narratives with 3+ chapters where the visual
changes per chapter. Never for short documents. See §09c.
Optional per-chapter DATA CALLOUT: one straight gold hairline
(1px) drawn toward the data point over 600ms, a 3px dot landing
at the data end, and a number that counts up once when its
chapter first activates. At most one callout per chapter; most
chapters get none. No arrowheads, no curves, no boxed labels.
6. READING PROGRESS — 1px gold bar (.pp-progress) fixed top.
Long-form documents and decks. Never thicker than 1px.
7. SLIDE SHELL — 16:9 slides (height: 56.25vw, min-height: 100vh),
html { scroll-snap-type: y mandatory }, snap-align start.
Light slide bg: #F5F4F0. Dark slide bg: var(--pp-off-black).
Dark slides persuade; light slides get read. See §09b.
A slide taller than the viewport (sticky story inside a deck)
scrolls freely inside; the browser snaps to its start and end.
EXTENDED PATTERNS (flagship deliverables only — pick 2-3, never all)
8. GOLD THREAD SPINE — fixed 1px vertical hairline at left: 24px;
gold fill height tracks scroll progress. Hidden under 900px.
9. SCROLL-SCRUBBED CHART DRAW — stroke-dashoffset mapped directly
to scroll progress, transition: none. Flagship form of 4.
10. SURFACE CROSS-FADE — body background dissolves between #F5F4F0
and #0B0B0B (600ms ease) as the active slide changes. Slides go
transparent only when JS adds .fx-crossfade to html.
11. PINNED PHRASE REVEAL — 240vh slide, sticky 100vh stage; lines
gain .in as progress passes 0.10 + i*0.16. Reversible. Max 4 lines.
12. ODOMETER DIGITS — digits roll on 1.04em strips, 900ms
cubic-bezier(0.22,1,0.36,1), 80ms stagger. Same data-countup HTML.
13. TABLE ROW CASCADE — tbody rows fade in at 40ms stagger.
Opacity only; transforms on tr are unreliable.
14. COVER SLOW ZOOM — cover photo on ::before scales 1.00→1.05
across the first viewport of scroll. Gradient holds still.
15. CHART POINT HOVER DETAIL — invisible 11px hit circle; 3.5px dot
and value label on hover/focus. tabindex="0" for keyboards.
ALL EIGHT: hidden starts gated behind html.fx (JS adds it only when
motion is allowed); finished state in print and reduced motion.
NAV DOTS (decks)
6px circles fixed right, var(--pp-mid-gray); active dot gold,
scale(1.5). Track the active slide by the viewport MIDPOINT, not by
an IntersectionObserver threshold: thresholds fail on slides taller
than the viewport. Canonical JS lives in §12b, pattern 1.
GLASS PANEL (see §07b for full component)
Dark: background: rgba(22,22,22,0.52);
border: 1px solid rgba(255,255,255,0.08)
Emphasis adds: border-top: 1px solid rgba(185,154,56,0.25)
Light: background: var(--pp-paper); border: 1px solid rgba(0,0,0,0.06)
NO border-radius. NO shadows. NO backdrop-filter. Any surface.
PERFORMANCE COLORS (see §06b — text and labels only, never fills)
--pp-perf-strong: #5BBE8C strong positive
--pp-perf-mid: #7EC9A6 mid positive
--pp-perf-mild: #9DCDB8 mild positive
--pp-perf-caution: #CDB365 caution / mild negative (Warm Gold)
--pp-perf-negative: #8E3E48 negative (never pure red)
Green = positive return, nowhere else in the palette.
Sage #8DB999 is a chart series color, NOT a performance color.
REQUIRED CONTROLS (every HTML deliverable — see §12)
Fullscreen toggle (button + F key), arrow-key navigation across
.slide or .section blocks, and PDF export via window.print() with
orientation default by document type and a visible override.
09 — Recipes
When an AI is asked to "make a one-pager," "write a hero," or "build a LinkedIn slide," these are the working structures it should adapt. All recipes assume the brand stylesheet (parcion-brand.css) is loaded; tokens are provided inline as fallbacks. Do not invent new layouts when one of these will fit.
Use for: landing pages, microsite opens, email headers, top of a one-pager. The image is muted under a layered gradient so the headline stays readable.
<section style="
position: relative;
background:
linear-gradient(180deg, rgba(11,11,11,0.78) 0%,
rgba(11,11,11,0.62) 60%, rgba(11,11,11,0.88) 100%),
url('IMAGE_URL') center/cover no-repeat, #0B0B0B;
color: #F7F7F7;
padding: 120px 56px 144px;
">
<p class="eyebrow">EYEBROW LABEL</p>
<h1>Your headline, all caps, restrained.</h1>
<p class="lede" style="max-width: 60ch; color: #D6D6D6;">
Lede paragraph. Open with the reader's situation, not
Parcion's credentials.
</p>
</section>
Use for: any standard section on a light surface. The default content block.
<section style="padding: 96px 0; background: #F7F7F7;
border-bottom: 1px solid #E5E5E5;">
<div style="max-width: 1180px; margin: 0 auto;
padding: 0 clamp(20px, 4vw, 56px);">
<p class="eyebrow">SECTION LABEL</p>
<h2>Section headline in all caps.</h2>
<div style="display: grid;
grid-template-columns: 1fr 1fr; gap: 48px;
margin-top: 32px;">
<div><p>Left column copy.</p></div>
<div><p>Right column copy.</p></div>
</div>
</div>
</section>
Use for: firm facts, key data points, executive summary numbers. Three or four at most per row.
<div style="display: grid;
grid-template-columns: repeat(4, 1fr); gap: 32px;
border-top: 1px solid #2A2A2A;
border-bottom: 1px solid #2A2A2A;
padding: 28px 0;">
<div>
<p style="font-family: Aptos, sans-serif; font-size: 10px;
letter-spacing: 0.22em; text-transform: uppercase;
color: #B99A38; margin: 0 0 8px;">LABEL</p>
<p style="font-family: 'Brother 1816', 'Century Gothic',
sans-serif; font-size: 34px; font-weight: 500;
color: inherit; margin: 0; line-height: 1;">$3B</p>
<p style="font-family: 'Palatino Linotype', serif;
font-size: 13px; color: #9A9A9A; margin: 8px 0 0;">
Short context line.</p>
</div>
<!-- repeat for each stat -->
</div>
Use for: callouts, executive statements, the one line that has to land. ALL CAPS, gold left rule.
<blockquote style=" margin: 24px 0; padding: 24px 24px 24px 40px; border-left: 2px solid #B99A38; font-family: 'Brother 1816', 'Century Gothic', sans-serif; font-size: 22px; line-height: 1.35; letter-spacing: 0.06em; text-transform: uppercase; color: #0B0B0B; "> Clarity creates confidence. </blockquote>
Use for: financial tables, beneficiary breakdowns, fee schedules, comparison grids. Aptos, gold header, alternating rows.
<table style="width: 100%; border-collapse: collapse;
font-family: Aptos, sans-serif; font-size: 15px;
color: #0B0B0B; border: 1px solid #E5E5E5;">
<thead>
<tr>
<th style="background: #B99A38; color: #0B0B0B;
font-weight: 700; font-size: 12px;
letter-spacing: 0.14em; text-transform: uppercase;
text-align: left; padding: 14px 18px;
border-bottom: 1px solid #8D762E;">Column</th>
<!-- more headers -->
</tr>
</thead>
<tbody>
<tr><td style="padding: 14px 18px;
border-bottom: 1px solid #E5E5E5;">Cell</td></tr>
<tr style="background: #F2F2F2;"><td style="padding:
14px 18px; border-bottom: 1px solid #E5E5E5;">Cell</td></tr>
</tbody>
</table>
Use for: a single slide in a LinkedIn carousel or standalone post. Dark, full-bleed, one strong line of type.
<div style="
width: 1080px; height: 1350px;
background: #0B0B0B; color: #F7F7F7;
padding: 80px;
display: flex; flex-direction: column;
justify-content: space-between;
">
<!-- Top: logo (use alternate reversed lockup, ~60px tall) -->
<img src="LOGO_ALT_REVERSED_URL" style="height: 60px;
width: auto;">
<!-- Middle: the line that has to land -->
<h1 style="font-family: 'Brother 1816', 'Century Gothic',
sans-serif; font-size: 64px; line-height: 1.05;
letter-spacing: 0.04em; text-transform: uppercase;
color: #F7F7F7; margin: 0; max-width: 18ch;">
Your single, strong line.
</h1>
<!-- Bottom: small supporting line or URL -->
<p style="font-family: Aptos, sans-serif; font-size: 14px;
letter-spacing: 0.22em; text-transform: uppercase;
color: #B99A38; margin: 0;">PARCIONPW.COM</p>
</div>
Use for: marketing emails, event invitations, whitepaper distribution. Table-based for Outlook compatibility.
<table role="presentation" width="600" cellpadding="0"
cellspacing="0" style="background: #0B0B0B; color: #F7F7F7;
font-family: Aptos, Arial, sans-serif; width: 600px;">
<tr>
<td style="padding: 32px 40px;">
<img src="LOGO_ALT_REVERSED_PNG_URL"
alt="Parcion Private Wealth"
width="180" style="height: auto; display: block;">
</td>
</tr>
<tr>
<td style="padding: 0 40px 56px;">
<p style="font-family: Aptos, sans-serif; font-size: 11px;
letter-spacing: 0.22em; text-transform: uppercase;
color: #B99A38; margin: 24px 0 12px;">EYEBROW</p>
<h1 style="font-family: Georgia, serif;
font-size: 32px; line-height: 1.15;
color: #F7F7F7; margin: 0 0 16px;
text-transform: uppercase; letter-spacing: 0.04em;
font-weight: normal;">Subject as headline.</h1>
<p style="font-family: Georgia, serif; font-size: 16px;
line-height: 1.6; color: #D6D6D6; margin: 0;">
Open with the reader's situation.</p>
</td>
</tr>
</table>
09 — Recipes
Client-facing HTML decks use a 16:9 paginated shell that matches PowerPoint and Keynote dimensions exactly. Each slide is one viewport. Scroll snaps to slide boundaries. The same file presents on a conference room screen, prints to landscape PDF, and reads on a laptop. The shell below is complete: paste it, then replace the content.
Surface follows job, not position. If the slide's job is to persuade or set context (cover, section divider, quote, narrative close), it goes dark. If the slide's job is to be read closely (table, chart, KPI grid, methodology), it goes light. Two light slides in a row is fine if both carry data. A deck that alternates dark and light for rhythm alone is decorating, and the reader can tell.
Light slides use #F5F4F0, a warm off-white, not pure white. Paper white next to a projector's brightness reads clinical; the warm field keeps the deck in the same register as the dark slides around it. Decks exported on pure white predate this standard.
Every slide carries the alternate lockup (icon + PARCION, no descriptor) in the top-left corner, about 28px tall, before any other content. This is how shipped Parcion decks behave, and it is now the rule. The footer still carries the firm name, the confidentiality line, and the slide number. The mark and the footer do different jobs; neither replaces the other. On the cover, the mark may sit larger or give way to a fuller lockup.
<!-- First child of every .slide. Reversed lockup on dark slides,
primary on light. URLs from the Asset Manifest. -->
<div class="slide-mark">
<img src="https://cdn.sanity.io/images/3phttdbo/production/f033ca90bf20089f20577dfc14da56c7ea3b9250-1952x451.png"
alt="Parcion" style="height: 28px; width: auto;">
</div>
<style>
.slide-mark { flex-shrink: 0; margin-bottom: 14px; }
.slide-mark img { height: 28px; width: auto; display: block; }
</style>
/* Deck shell — complete. Assumes the brand tokens are loaded. */ html { scroll-snap-type: y mandatory; } body { margin: 0; background: var(--pp-off-black); } .slide { width: 100vw; height: 56.25vw; /* exact 16:9 */ min-height: 100vh; scroll-snap-align: start; display: flex; flex-direction: column; padding: 2.8% 4.5%; /* proportional — scales from laptop to projector */ box-sizing: border-box; overflow: hidden; } /* A slide allowed to be taller than the viewport, for sticky storytelling inside a deck (§09c). Snap rests at its start and end; the middle scrolls freely. */ .slide--tall { height: auto; min-height: 100vh; overflow: visible; } .slide-light { background: #F5F4F0; color: var(--pp-off-black); } .slide-dark { background: var(--pp-off-black); color: var(--pp-off-white); } .slide-header { flex-shrink: 0; } .slide-header .eyebrow { color: var(--pp-gold); margin-bottom: 12px; } .slide-light .slide-header .eyebrow { color: var(--pp-gold-dark); } .slide-content { flex: 1; display: flex; align-items: center; min-height: 0; /* required: lets flex children shrink */ } .slide-footer { flex-shrink: 0; display: flex; justify-content: space-between; align-items: center; font-family: var(--pp-font-ui); font-size: 10px; letter-spacing: 0.08em; color: var(--pp-mid-gray); padding-top: 14px; border-top: 1px solid rgba(0, 0, 0, 0.06); } .slide-dark .slide-footer { border-top-color: var(--pp-hairline-dark); } /* Photo cover. Image from the Asset Manifest — default_hero_financial shown here. The gradient guarantees the headline sits on at least 78% Off-Black regardless of the photograph. */ .slide-cover { background: linear-gradient(to bottom, rgba(11, 11, 11, 0.30) 0%, rgba(11, 11, 11, 0.78) 100%), url('https://cdn.sanity.io/images/3phttdbo/production/fc1a057432b72c5e344f2c9c65443e3fdf9da30f-4032x3024.jpg?w=2400&auto=format') center / cover no-repeat, var(--pp-off-black); color: var(--pp-off-white); } /* Print: one slide per landscape page */ @media print { html { scroll-snap-type: none; } .slide { width: auto; height: auto; min-height: 0; page-break-after: always; } .deck-nav, .control-bar, .pp-progress { display: none !important; } * { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; } }
<div class="slide slide-light" id="s3">
<div class="slide-header">
<p class="eyebrow">Portfolio Review</p>
<h1>Q2 2026 Performance</h1>
<p style="font-family: var(--pp-font-ui); font-size: 11px; color: var(--pp-mid-gray);">
Net of fees, as of June 30, 2026</p>
</div>
<div class="slide-content">
<!-- one table, one chart, or one KPI grid — one idea -->
</div>
<div class="slide-footer">
<span>Parcion Private Wealth</span>
<span>Confidential. For client use only.</span>
<span>03</span>
</div>
</div>
Six-pixel dots, fixed right, one per slide. The active dot tracks the viewport midpoint rather than an IntersectionObserver threshold. A threshold of 0.6 can never fire on a slide three viewports tall, so the midpoint method is the canonical one for decks. It costs one scroll listener and works on every slide height.
<nav class="deck-nav" aria-label="Slides">
<button class="deck-dot" data-slide="s1" aria-label="Slide 1"></button>
<button class="deck-dot" data-slide="s2" aria-label="Slide 2"></button>
<button class="deck-dot" data-slide="s3" aria-label="Slide 3"></button>
</nav>
<style>
.deck-nav {
position: fixed; right: 18px; top: 50%;
transform: translateY(-50%);
display: flex; flex-direction: column; gap: 10px; z-index: 100;
}
.deck-dot {
width: 6px; height: 6px; border-radius: 50%; border: none;
padding: 0;
background: var(--pp-mid-gray); cursor: pointer;
transition: background 200ms ease, transform 200ms ease;
}
.deck-dot.active { background: var(--pp-gold); transform: scale(1.5); }
</style>
<script>
// Click: go to slide
document.querySelectorAll('.deck-dot').forEach(dot => {
dot.addEventListener('click', () =>
document.getElementById(dot.dataset.slide)
.scrollIntoView({ behavior: 'smooth' }));
});
// Active state: track the viewport midpoint (works on tall slides)
(function () {
const slides = Array.from(document.querySelectorAll('.slide'));
const dots = Array.from(document.querySelectorAll('.deck-dot'));
function setActive() {
const mid = window.innerHeight / 2;
let active = 0;
slides.forEach((s, i) => {
const r = s.getBoundingClientRect();
if (r.top <= mid && r.bottom > mid) active = i;
});
dots.forEach((d, i) => d.classList.toggle('active', i === active));
}
window.addEventListener('scroll', setActive, { passive: true });
setActive();
})();
</script>
width: 100vw; height: 56.25vw; min-height: 100vh.09 — Recipes
One chart, several chapters. The visual holds still on the left while the reader scrolls through the argument on the right, and the chart answers each chapter as it arrives. This is the standard pattern for investment commentaries that build a case across connected data points.
The pattern earns its scaffolding only when the visual changes meaning chapter by chapter. The reader should feel the chart listening: chapter two arrives, and the fixed income line steps forward while the others step back. If the chart would sit unchanged through the whole scroll, use a plain two-column section and spend the saved attention on the writing.
Scroll through the three chapters. The active allocation series lifts to full opacity; the others recede to 25%. This is the entire effect. It is enough.
Allocation
The portfolio is drifting toward what is paid to wait
Hypothetical target allocation by quarter, Q2 2025 to Q2 2026.
Chapter 1 · Equities
After the spring rally we rebalanced back to target rather than letting the win ride. Valuations are full, breadth is healthy, and the plan does not need more equity risk to do its job. Trimming a winner is not pessimism. It is the discipline the plan was built on.
Chapter 2 · Fixed income
With real yields above two percent, fixed income earns its place again. We extended duration modestly in April, and we would extend further on weakness. The allocation has drifted up two points in a year, and every one of them was deliberate.
Chapter 3 · Private markets
We continue to add private credit at sensible spreads while trimming legacy real estate where the recovery story has gotten slower, not better. The line moves little because the work is happening inside it: the mix is changing more than the number.
/* Two columns: visual sticks, chapters scroll */ .story-wrap { display: grid; grid-template-columns: 1fr 1fr; gap: 0; align-items: start; } .story-visual { position: sticky; top: 120px; /* clear any fixed header; 96px without one */ } .story-chapter { min-height: 70vh; /* tall enough to read at rest, short enough to keep moving */ padding: var(--pp-space-2xl) var(--pp-space-lg); display: flex; flex-direction: column; justify-content: center; } /* The visual responds to the active chapter via a data attribute. Opacity is the entire effect. Resist adding more. */ .story-visual .series { opacity: 0.25; transition: opacity 400ms ease; } .story-visual[data-active="1"] .series-1 { opacity: 1; } .story-visual[data-active="2"] .series-2 { opacity: 1; } .story-visual[data-active="3"] .series-3 { opacity: 1; } /* Below 720px the pattern degrades to plain stacked sections */ @media (max-width: 720px) { .story-wrap { grid-template-columns: 1fr; } .story-visual { position: static; } .story-visual .series { opacity: 1; } }
<div class="story-wrap">
<div class="story-visual" id="story-chart" data-active="1">
<svg viewBox="0 0 480 300" role="img" aria-label="Describe the chart">
<polyline class="series series-1" fill="none" style="stroke: var(--pp-stone)" stroke-width="3" points="..." />
<polyline class="series series-2" fill="none" style="stroke: var(--pp-ink)" stroke-width="3" points="..." />
<polyline class="series series-3" fill="none" style="stroke: var(--pp-forest)" stroke-width="3" points="..." />
</svg>
</div>
<div class="story-chapters">
<div class="story-chapter" data-chapter="1">
<h3>Holding at target, on purpose.</h3>
<p>Chapter copy. Each chapter makes one decision legible.</p>
</div>
<div class="story-chapter" data-chapter="2">
<h3>Bonds are paid to wait.</h3>
<p>Chapter copy.</p>
</div>
<div class="story-chapter" data-chapter="3">
<h3>Quality over vintage.</h3>
<p>Chapter copy.</p>
</div>
</div>
</div>
<script>
const visual = document.getElementById('story-chart');
const chapterObs = new IntersectionObserver(entries => {
entries.forEach(e => {
if (e.isIntersecting) visual.dataset.active = e.target.dataset.chapter;
});
}, { threshold: 0.55 });
document.querySelectorAll('.story-chapter').forEach(ch => chapterObs.observe(ch));
</script>
Two flourishes for the chapter that has one number worth the reader's eye. An annotation line, one pixel and gold, draws from the label toward the data and lands on a small dot at the data point. A number counts up once, the first time its chapter arrives. The demo above uses one on chapter two.
These are optional, and the option is the point. A callout on every chapter is wallpaper. Reserve it for the moment the whole piece exists to deliver: the two points of duration, the spread that paid for the trade, the number the client will repeat to their spouse. One callout per chapter at most, and most chapters need none.
/* CSS — the callout group hides until its chapter is active. Line draws toward the data; dot lands after the line. */ .story-note { opacity: 0; transition: opacity 300ms ease; } .story-note .note-line { stroke-dasharray: 200; /* at least the line's true length */ stroke-dashoffset: 200; transition: stroke-dashoffset 600ms cubic-bezier(0.4, 0, 0.2, 1) 150ms; } .story-note .note-dot { opacity: 0; transition: opacity 200ms ease 700ms; } /* Wire each note to its chapter, same as the series */ .story-visual[data-active="2"] .note-2 { opacity: 1; } .story-visual[data-active="2"] .note-2 .note-line { stroke-dashoffset: 0; } .story-visual[data-active="2"] .note-2 .note-dot { opacity: 1; } /* Fallbacks: finished state when stacked, printed, or reduced motion */ @media (max-width: 720px), print { .story-note { opacity: 1; } .story-note .note-line { stroke-dasharray: none; stroke-dashoffset: 0; } .story-note .note-dot { opacity: 1; } }
<!-- HTML — inside the story SVG. The finished number lives in the
markup; JS replays it (§12b pattern 4). Straight hairline, dot
at the data end. No arrowheads, no boxes. -->
<g class="story-note note-2">
<text x="296" y="66" font-family="Aptos, Inter, sans-serif" font-size="12"
font-weight="600" style="fill: var(--pp-gold)" text-anchor="end"><tspan
data-countup data-target="2" data-prefix="+" data-suffix=" pts"
data-decimals="0">+2 pts</tspan></text>
<text x="296" y="80" font-family="Aptos, Inter, sans-serif" font-size="9"
style="fill: var(--pp-light-gray)" text-anchor="end">duration added since Q2 '25</text>
<line class="note-line" x1="304" y1="74" x2="446" y2="132"
style="stroke: var(--pp-gold)" stroke-width="1" />
<circle class="note-dot" cx="450" cy="135" r="3" style="fill: var(--pp-gold)" />
</g>
<!-- JS — extend the chapter observer. The callout number is
chapter-triggered, fires once, and skips on reduced motion.
Exclude .story-note numbers from the page-level count-up
observer so they wait for their chapter. -->
<script>
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const chapterObs = new IntersectionObserver(entries => {
entries.forEach(e => {
if (!e.isIntersecting) return;
visual.dataset.active = e.target.dataset.chapter;
const note = visual.querySelector('.note-' + e.target.dataset.chapter + ' [data-countup]');
if (note && !note.dataset.played) {
note.dataset.played = '1';
if (!reduceMotion) countUp(note); // countUp from §12b pattern 4
}
});
}, { threshold: 0.55 });
document.querySelectorAll('.story-chapter').forEach(ch => chapterObs.observe(ch));
// Page-level count-up setup: leave callout numbers to their chapters
document.querySelectorAll('[data-countup]').forEach(el => {
if (el.closest('.story-note')) return;
countObs.observe(el);
});
</script>
.slide--tall slide (§09b) so snap and story coexist.10 — Asset Manifest
A single machine-readable lookup. When the AI needs to embed a logo or photograph, it should pull from this list rather than reconstructing URLs from elsewhere in the file. Public CDN, no auth required.
{
"logos": {
"_embedded": "As of v4.7.0, all marks are also embedded inline in this guide and render offline. The URLs below remain the canonical downloadable assets.",
"primary_lockup_light": {
"use": "Default. Light backgrounds. Icon + wordmark + PRIVATE WEALTH.",
"svg": "https://cdn.sanity.io/images/3phttdbo/production/5c79bbe89858ce500f6723bfdafd44ec6bd3fbc1-2000x2000.svg",
"png": "https://cdn.sanity.io/images/3phttdbo/production/9736c9d5462904dd3ebdcccf926d2ea53f98f50a-1905x441.png"
},
"primary_lockup_dark": {
"use": "Default. Dark backgrounds. Icon + wordmark + PRIVATE WEALTH, reversed.",
"svg": "https://cdn.sanity.io/images/3phttdbo/production/71a5aad964c39f3794afb2f24406083c071dccbc-2000x2000.svg",
"png": "https://cdn.sanity.io/images/3phttdbo/production/56a4894b1fa953de245165aa4f99587d03f1a94a-1885x435.png"
},
"alternate_lockup_light": {
"use": "Use sparingly. Small spaces on light bg. No PRIVATE WEALTH descriptor.",
"svg": "https://cdn.sanity.io/images/3phttdbo/production/10f6008aec939c4771682e5ad55e09056b5902a8-2000x2000.svg",
"png": "https://cdn.sanity.io/images/3phttdbo/production/cb140fa2f41515e06a4c75dbac9bea47a54a2de6-1934x388.png"
},
"alternate_lockup_dark": {
"use": "Use sparingly. Small spaces on dark bg. No PRIVATE WEALTH descriptor.",
"svg": "https://cdn.sanity.io/images/3phttdbo/production/48b29b92920ec727a2793db9e3f2cc84ba2f0a17-2000x2000.svg",
"png": "https://cdn.sanity.io/images/3phttdbo/production/f033ca90bf20089f20577dfc14da56c7ea3b9250-1952x451.png"
},
"icon": {
"use": "Use sparingly. Avatars, favicons, app icons. Gold diamond mark only.",
"svg": "https://cdn.sanity.io/images/3phttdbo/production/0f940e5a73a80ffcd00088df5f77d5ce02932a8d-396x414.svg"
}
},
"imagery": {
"default_hero_general": {
"use": "DEFAULT hero image for any non-financial content: brand pages, About, Approach, Team, Signature Event, philanthropy, lifestyle, general marketing. Use this unless the topic is explicitly financial. Apply layered dark gradient overlay for legibility.",
"url": "https://cdn.sanity.io/images/3phttdbo/production/01548b6a067a3fee4c7a1c9c42ca391074b9f44c-5760x3600.jpg"
},
"default_hero_financial": {
"use": "DEFAULT hero image for any financial content: market commentary, Parcion Perspectives, investment strategy, portfolio updates, tax planning, business sale planning, ESOP/M&A pieces, financial whitepapers. Apply layered dark gradient overlay for legibility.",
"url": "https://cdn.sanity.io/images/3phttdbo/production/fc1a057432b72c5e344f2c9c65443e3fdf9da30f-4032x3024.jpg"
},
"editorial_options": [
{
"id": "image_01",
"url": "https://cdn.sanity.io/images/3phttdbo/production/544b946d779b5a95b4ba65b8ab48c344d417c283-4030x3022.jpg",
"subject": "Ferry on Puget Sound with Mount Rainier in the background, sunset",
"mood": "hopeful, inspiring, warm",
"use_for": "non-financial editorial when a scenic mood is wanted; alternate to default_hero_general for variety"
},
{
"id": "image_02",
"url": "https://cdn.sanity.io/images/3phttdbo/production/b78b21cb33b7cfedd8fd3a50ff34d9b67b4052db-5472x3072.jpg",
"subject": "Ferry on Puget Sound, Olympic Mountains in background",
"mood": "quiet, restrained, scenic",
"use_for": "alternate to image_01 when the layout needs a less dramatic scenic"
},
{
"id": "image_03",
"url": "https://cdn.sanity.io/images/3phttdbo/production/eb6786d273c6dc9646b0f1aca18714ac4f4db21e-4856x3427.jpg",
"subject": "Container ship",
"mood": "industrial, in motion, grounded",
"use_for": "business-owner content; signals Parcion's connection to blue-collar, business, and industrial clients"
},
{
"id": "image_04",
"url": "https://cdn.sanity.io/images/3phttdbo/production/25189a227ed2bfb82b05fe2d6553f1137d46dfc4-4000x2667.jpg",
"subject": "Construction jobsite at sunset",
"mood": "inspiring, ambitious, well-framed",
"use_for": "FAVORED editorial option. Business owners, builders, developers, industrial clients. Prefer this over image_03 when warmth is appropriate.",
"priority": "favored"
},
{
"id": "image_05",
"url": "https://cdn.sanity.io/images/3phttdbo/production/da838fbf8a5f323cd5fd79f92b544c6da63f26cd-5472x3648.jpg",
"subject": "Shipping abstract, framed through a ferry",
"mood": "atmospheric, abstract, industrial",
"use_for": "alternate to image_03 when the layout wants atmosphere over literal subject"
},
{
"id": "image_06",
"url": "https://cdn.sanity.io/images/3phttdbo/production/cf3aaa2b4b2e4ac6c5dda7d7b4ecc938b5aac32d-3072x2304.jpg",
"subject": "Seattle / Puget Sound, place-led composition",
"mood": "rooted, regional, grounded",
"use_for": "alternate to image_01 when establishing place (Seattle, Puget Sound) is more important than mood"
},
{
"id": "image_07",
"url": "https://cdn.sanity.io/images/3phttdbo/production/8ab3fce6837fe78e9c4cbdbc1a5419070ca82c68-4771x3181.jpg",
"subject": "Mountain climbers",
"mood": "resilient, ambitious, scale-driven",
"use_for": "long-term planning, generational wealth, post-liquidity content, 'what comes next' themes"
},
{
"id": "image_08",
"url": "https://cdn.sanity.io/images/3phttdbo/production/5c55387c0c1b20f2bb5ee79aa10fe0a0318b0479-8256x5504.jpg",
"subject": "Parcion logo on frosted glass, shot in the Parcion office",
"mood": "confident, restrained, real",
"use_for": "About, Approach, recruiting. Use when the firm should appear in image without staging or model talent."
}
],
"banners": [
"https://cdn.sanity.io/images/3phttdbo/production/c7ea7faf99c9ddecd421cd53b836cee651af3333-2048x1365.jpg",
"https://cdn.sanity.io/images/3phttdbo/production/8a73aabcf8ed53e34dfcf4688e5acf05b126592f-4240x2832.jpg",
"https://cdn.sanity.io/images/3phttdbo/production/66003e0de4417670c7cbc9ebb1e2d5132f515e42-5184x3456.jpg"
],
"fallback_stock": {
"use": "If the approved library doesn't fit, source from Unsplash and flag for Marketing review before publishing.",
"url": "https://unsplash.com"
}
},
"colors": {
"primary": {
"off_black": "#0B0B0B",
"pure_black": "#000000",
"carbon": "#161616",
"charcoal": "#2D2D2C",
"off_white": "#F7F7F7",
"paper": "#FFFFFF",
"light_gray": "#D6D6D6",
"mid_gray": "#6B6B6B",
"hairline": "#E5E5E5",
"hairline_dark": "#2A2A2A",
"gold": "#B99A38",
"gold_dark": "#8D762E",
"gold_deeper": "#6C5A23"
},
"data_viz_only": {
"series_order": "gold, ink, forest, clay, slate_blue, stone; sage as overflow",
"clay_dark": "#7E4830",
"clay": "#9C5B3C",
"clay_light": "#C08A66",
"warm_gold_light": "#C3A446",
"warm_gold": "#CDB365",
"warm_gold_pale": "#D7C284",
"warm_gold_cream": "#E5D9A9",
"forest_dark": "#48614A",
"forest": "#446459",
"sage": "#8DB999",
"ink_dark": "#122735",
"ink": "#22333B",
"slate_blue": "#52697A",
"slate_blue_light": "#8FA5B3",
"stone": "#A39A88",
"stone_light": "#B8AF9E",
"navy_deep": "#00141C"
},
"negative_returns_only": {
"use": "Burgundy marks negative returns and drawdowns, nowhere else. Never a general chart series.",
"burgundy_dark": "#6A2E35",
"burgundy": "#8E3E48",
"burgundy_mid": "#AA4B56"
},
"performance_ramp": {
"use": "Return signals only. Text and labels, never fills or backgrounds. See §06b.",
"strong_positive": "#5BBE8C",
"mid_positive": "#7EC9A6",
"mild_positive": "#9DCDB8",
"caution": "#CDB365",
"negative": "#8E3E48"
}
},
"type": {
"external_web": {
"display": "Brother 1816",
"body": "Palatino Linotype",
"ui_data": "Aptos",
"h1": { "font": "Brother 1816", "weight": "Book", "size": "40pt (web: clamp 32–44px)", "case": "ALL CAPS" },
"h2": { "font": "Brother 1816", "weight": "Book", "size": "24pt", "case": "ALL CAPS" },
"h3": { "font": "Brother 1816", "weight": "Bold", "size": "18pt", "case": "Mixed Case" },
"h4": { "font": "Brother 1816", "weight": "Book", "size": "14pt", "case": "Mixed Case" },
"body": { "font": "Palatino Linotype", "weight": "Regular", "size": "12–14pt (web: 16px)" },
"callout": { "font": "Palatino Linotype", "weight": "Bold", "size": "14pt" },
"data": { "font": "Aptos", "weight": "Regular or Medium", "size": "12pt" },
"table": { "font": "Aptos", "weight": "Bold (header) / Regular (body)", "size": "15px web / 12pt print" },
"legal": { "font": "Aptos Narrow", "size": "12pt", "case": "ALL CAPS" }
},
"microsoft_office": {
"applies_to": "Word (.docx), PowerPoint (.pptx), Outlook, Teams, and any other Microsoft surface.",
"rule": "Substitute Brother 1816 with Century Gothic on every heading. Same sizes, same cases, same hierarchy. Body (Palatino Linotype) and data (Aptos) are unchanged. Required, not optional.",
"h1": { "font": "Century Gothic", "weight": "Regular", "size": "40pt", "case": "ALL CAPS" },
"h2": { "font": "Century Gothic", "weight": "Regular", "size": "24pt", "case": "ALL CAPS" },
"h3": { "font": "Century Gothic", "weight": "Bold", "size": "18pt", "case": "Mixed Case" },
"h4": { "font": "Century Gothic", "weight": "Bold", "size": "14pt", "case": "ALL CAPS" },
"body": "Palatino Linotype (unchanged)",
"data": "Aptos (unchanged)",
"do_not_substitute": "Arial, Calibri, Helvetica, or any other sans-serif. Century Gothic only."
}
}
}
11 — Decision Rules
Explicit decision logic the AI should follow when picking between options. Read top to bottom; the first matching rule wins.
| If | Then |
|---|---|
| The output has a background | Default to Off-Black (#0B0B0B). Never use Navy (#00141C) for surfaces. |
| The output needs a hero image and the topic is NOT financial | Use default_hero_general. Brand, About, Approach, Team, Signature Event, philanthropy, lifestyle, recruiting, general marketing all fall here. |
| The output needs a hero image and the topic IS financial | Use default_hero_financial. Market commentary, Parcion Perspectives, investment strategy, portfolio reporting, tax planning, business sale, ESOP, M&A, performance pieces all fall here. |
| The default hero doesn't fit the specific piece | Pick from editorial_options only if a defensible reason exists. Otherwise stay with the default. Banners (LinkedIn header, web rotator) pull from banners, not editorial. |
| The output has a logo and the surface is dark | Use primary_lockup_dark. If the placement is under 80px tall, use alternate_lockup_dark instead. |
| The output has a logo and the surface is light | Use primary_lockup_light. If the placement is under 80px tall, use alternate_lockup_light instead. |
| The placement is a favicon, avatar, or app icon | Use the icon only. Gold Dark (#8D762E) on Black (#000000). |
| You need an H1 color | White (#F7F7F7) on dark surfaces, Off-Black (#0B0B0B) on light. Never gold. Never serif. |
| You're adding emphasis to a heading | Use weight or position, not gold. Gold belongs on the eyebrow, the rule, or one chart series. |
| You need to set type for body copy | Palatino Linotype, 16px, line-height 1.6. On dark, color is #D6D6D6, not pure white. |
| The output is a Microsoft Office file (Word, PowerPoint, Outlook, Teams) | Replace Brother 1816 with Century Gothic on every heading. Palatino Linotype (body) and Aptos (data) stay unchanged. Required, not optional. |
| You're building a chart with one key insight | Use gold (#B99A38) on that single series. Everything else uses the secondary palette. |
| You're building a chart with no single emphasis | Skip gold entirely. Use Ink, Forest, Clay, Slate Blue, Stone in that priority order. |
| You need an italicized word | Use italic only for a direct quotation or the title of a work (book, film, publication, vessel). For emphasis, use small caps, bold, or a rephrase. Never italic in headings. |
| You'd reach for an em dash | Replace with a comma, a colon, or two short sentences. |
| You need a stock photograph not in the library | Source from Unsplash and add a note that Marketing should review before publication. |
| You need a portrait of a Parcion team member | Pull from parcionpw.com/team. Do not generate or composite portraits. |
| You'd use "clients," "HNWIs," or "heirs" | Replace with "families," "business owners," or "the next generation." |
| You'd use "quarterback," "holistic," "leverage" (verb), or "synergy" | Replace with "coordinate," "comprehensive," "use," or remove entirely. |
| You'd open with "Hope this finds you well" | Open with the reader's situation instead. |
| The output references investment performance | Include the standard disclosure: "Past performance is not indicative of future results." Flag for compliance review. |
12 — For Developers
Colleagues building landing pages, microsites, internal tools, or HTML emails can link the Parcion brand stylesheet to inherit the tokens, type system, and base components. The CSS file is independent of this guide.
<link rel="stylesheet" href="parcion-brand.css">
.my-hero {
background: var(--pp-off-black);
color: var(--pp-off-white);
padding: var(--pp-space-2xl) 0;
}
.my-hero h1 { color: var(--pp-off-white); } /* never gold */
.my-hero .eyebrow { color: var(--pp-gold); } /* gold here is fine */
<p class="eyebrow">Client Exclusive Events</p> <h1>Signature Event</h1> <a href="/rsvp" class="pp-btn">Request invitation</a> <div class="pp-table-wrap"> <table class="pp-table">...</table> </div> <blockquote class="pp-pullquote"> Clarity creates confidence. </blockquote>
| Token | Value | Use |
|---|---|---|
| --pp-off-black | #0B0B0B | Primary dark surface |
| --pp-off-white | #F7F7F7 | Primary light surface |
| --pp-charcoal | #2D2D2C | Body type on light |
| --pp-gold | #B99A38 | Accent — eyebrows, key data |
| --pp-gold-dark | #8D762E | Accent on light backgrounds |
| --pp-navy | #00141C | Data visualization only |
| --pp-font-display | Brother 1816 … | Headings, display |
| --pp-font-body | Palatino Linotype … | Body copy |
| --pp-font-ui | Aptos … | Tables, data, labels |
| --pp-h1 | clamp(32px, 3.2vw, 44px) | H1 display size |
| --pp-table | 15px | Table body size |
Any HTML deliverable built off this brand, including presentations, internal microsites, briefings, or long-form reports, must support the three behaviors below. They are not optional.
| Behavior | Specification |
|---|---|
| Fullscreen toggle | A visible control in the page's fixed nav or floating control bar that calls requestFullscreen() and exitFullscreen(). Keyboard shortcut F. Icon-only is fine; label the button with a title attribute for accessibility. |
| Arrow-key navigation | For paginated deliverables (decks, multi-page briefings), ArrowDown, ArrowRight, PageDown, and Space advance. ArrowUp, ArrowLeft, and PageUp retreat. Scroll smoothly to the next or previous section. Disable when focus is inside a form field. |
| Download as PDF | A visible control in the same nav or control bar that triggers window.print() against a print stylesheet. Default orientation by document type: presentations and dashboards default to landscape, long-form documents and reports default to portrait. Always expose an override (a small toggle, a second button, or a one-click prompt) so the user can flip it before printing. |
All three controls sit inside the standard Parcion control surface: gold fill, Aptos label at 12px with 0.22em tracking, gold-dark on hover. Do not invent a separate visual treatment for these controls. They behave like any other Parcion UI button.
Paste these three blocks into any Parcion HTML deliverable to satisfy the standards above. The order of the PDF buttons declares the default: list landscape first for decks and dashboards, portrait first for documents and reports. Either button works regardless of order; ordering is the visual cue to the reader about what the document expects.
1 · HTML — place before </body>
<div class="control-bar">
<button class="ctrl-btn ctrl-btn-icon" title="Fullscreen (F)" onclick="toggleFullscreen()">⛶</button>
<button class="ctrl-btn" onclick="downloadPDF('landscape')">PDF · Landscape</button>
<button class="ctrl-btn" onclick="downloadPDF('portrait')">PDF · Portrait</button>
</div>
2 · CSS — add to your stylesheet
.control-bar {
position: fixed; bottom: 24px; right: 24px;
display: flex; gap: 8px; z-index: 200;
}
.ctrl-btn {
font-family: var(--pp-font-ui);
font-size: 12px; font-weight: 600;
letter-spacing: 0.22em; text-transform: uppercase;
padding: 12px 16px;
background: var(--pp-gold); color: var(--pp-off-black);
border: none; cursor: pointer;
transition: background 120ms ease;
}
.ctrl-btn:hover { background: var(--pp-gold-dark); }
.ctrl-btn-icon { padding: 14px; }
@media print {
.control-bar, .nav-bar { display: none !important; }
* {
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
}
}
3 · JavaScript — place before </body>
<script>
// Fullscreen toggle (button + F key)
function toggleFullscreen() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen().catch(() => {});
} else {
document.exitFullscreen();
}
}
// PDF download with orientation override
function downloadPDF(orientation) {
const existing = document.getElementById('pp-print-orientation');
if (existing) existing.remove();
const style = document.createElement('style');
style.id = 'pp-print-orientation';
const size = orientation === 'portrait'
? '8.5in 11in portrait'
: '11in 8.5in landscape';
style.textContent = '@page { size: ' + size + '; margin: 0; }';
document.head.appendChild(style);
window.print();
}
// Arrow-key navigation across .slide or .section blocks
(function() {
const sections = Array.from(
document.querySelectorAll('.slide, .section')
);
if (!sections.length) return;
let current = 0;
function goTo(i) {
if (i < 0 || i >= sections.length) return;
current = i;
sections[current].scrollIntoView({
behavior: 'smooth', block: 'start'
});
}
function updateCurrent() {
const mid = window.innerHeight / 2;
sections.forEach((s, i) => {
const r = s.getBoundingClientRect();
if (r.top <= mid && r.bottom > mid) current = i;
});
}
window.addEventListener('scroll', updateCurrent, { passive: true });
document.addEventListener('keydown', function(e) {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
if (['ArrowDown','ArrowRight','PageDown',' '].includes(e.key)) {
e.preventDefault(); updateCurrent(); goTo(current + 1);
} else if (['ArrowUp','ArrowLeft','PageUp'].includes(e.key)) {
e.preventDefault(); updateCurrent(); goTo(current - 1);
} else if (e.key === 'f' || e.key === 'F') {
toggleFullscreen();
}
});
})();
</script>
Working with AI to build a deliverable? Hand it this whole file. The snippet above is the canonical pattern. Tell the AI to paste it in without modification.
12 — For Developers
Fifteen canonical patterns in two tiers, all vanilla CSS and minimal JavaScript. Patterns 1 through 7 are the core tier, open to every deliverable. Patterns 8 through 15 are the flagship tier: scroll-scrubbed moves reserved for showcase pieces, used two or three at a time, never all at once. No GSAP, no Lottie, no scroll libraries. Motion at Parcion is scroll-driven and quiet: things arrive, they do not perform. Every pattern is additive, which means a page with JavaScript disabled is still a complete page.
prefers-reduced-motion. The block at the end of this section handles it; include it in every deliverable.The three pieces every paginated deck needs, in one block. Snap handles pagination. Dots handle orientation. The active dot tracks the viewport midpoint, not an IntersectionObserver threshold, because a threshold of 0.6 can never fire on a slide three viewports tall (a sticky-story slide, for instance). The midpoint method works on every slide height and costs one passive scroll listener.
/* CSS — snap */ html { scroll-snap-type: y mandatory; } .slide { scroll-snap-align: start; } /* A snap area taller than the viewport scrolls freely inside; the browser snaps to its start and end. This is what lets a sticky story live inside a deck. */ /* CSS — dots */ .deck-nav { position: fixed; right: 18px; top: 50%; transform: translateY(-50%); display: flex; flex-direction: column; gap: 10px; z-index: 100; } .deck-dot { width: 6px; height: 6px; border-radius: 50%; border: none; padding: 0; background: var(--pp-mid-gray); cursor: pointer; transition: background 200ms ease, transform 200ms ease; } .deck-dot.active { background: var(--pp-gold); transform: scale(1.5); }
<nav class="deck-nav" aria-label="Slides">
<button class="deck-dot" data-slide="s1" aria-label="Slide 1"></button>
<button class="deck-dot" data-slide="s2" aria-label="Slide 2"></button>
</nav>
<script>
document.querySelectorAll('.deck-dot').forEach(dot => {
dot.addEventListener('click', () =>
document.getElementById(dot.dataset.slide)
.scrollIntoView({ behavior: 'smooth' }));
});
(function () {
const slides = Array.from(document.querySelectorAll('.slide'));
const dots = Array.from(document.querySelectorAll('.deck-dot'));
function setActive() {
const mid = window.innerHeight / 2;
let active = 0;
slides.forEach((s, i) => {
const r = s.getBoundingClientRect();
if (r.top <= mid && r.bottom > mid) active = i;
});
dots.forEach((d, i) => d.classList.toggle('active', i === active));
}
window.addEventListener('scroll', setActive, { passive: true });
setActive();
})();
</script>
A one-pixel gold bar fixed to the top of the page. One element, two rules, one listener. Use it on long-form documents and decks. Never thicker than 1px: at 2px it becomes a feature, and it is not a feature.
<!-- immediately after <body> -->
<div class="pp-progress"></div>
<style>
.pp-progress {
position: fixed; top: 0; left: 0; height: 1px;
background: var(--pp-gold); width: 0%; z-index: 9999;
transition: width 60ms linear;
}
</style>
<script>
window.addEventListener('scroll', () => {
const p = window.scrollY / (document.body.scrollHeight - window.innerHeight);
document.querySelector('.pp-progress').style.width = (Math.min(p, 1) * 100) + '%';
}, { passive: true });
</script>
Sections fade up 16 pixels as they enter the viewport. A quiet arrival, once, and done. Because sections start invisible, the two fallbacks are mandatory. Skip them and you will ship blank pages to printers and old browsers.
/* CSS */ .section { opacity: 0; translate: 0 16px; transition: opacity 500ms ease, translate 500ms ease; } .section.visible { opacity: 1; translate: 0 0; } /* Mandatory fallbacks — do not omit */ @media print { .section { opacity: 1; translate: 0 0; } } @supports not (selector(:focus-visible)) { .section { opacity: 1; translate: 0 0; } } /* JS */ const revealObs = new IntersectionObserver(entries => entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('visible'); revealObs.unobserve(e.target); } }), { threshold: 0.08 }); document.querySelectorAll('.section').forEach(el => revealObs.observe(el));
Big numbers count from zero when half visible, once. The finished value lives in the HTML text, so screen readers, search, and no-JS environments see the real number. The script replays what is already there.
<!-- HTML: the real value is the text content -->
<span data-countup data-target="3.2" data-prefix="$" data-suffix="B" data-decimals="1">$3.2B</span>
<script>
function countUp(el) {
const target = parseFloat(el.dataset.target);
const prefix = el.dataset.prefix || '';
const suffix = el.dataset.suffix || '';
const decimals = el.dataset.decimals || 0;
const duration = 900;
const start = performance.now();
function step(now) {
const p = Math.min((now - start) / duration, 1);
const ease = 1 - Math.pow(1 - p, 3); /* cubic ease-out */
el.textContent = prefix + (target * ease).toFixed(decimals) + suffix;
if (p < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const countObs = new IntersectionObserver(entries => {
entries.forEach(e => {
if (e.isIntersecting) {
if (!reduceMotion) countUp(e.target); /* reduced motion: keep the finished value */
countObs.unobserve(e.target);
}
});
}, { threshold: 0.5 });
document.querySelectorAll('[data-countup]').forEach(el => countObs.observe(el));
</script>
SVG lines draw themselves as the chart enters view, using stroke-dasharray. The dasharray must be at least the true path length: 1000 covers guide-scale charts, and getTotalLength() gives the exact figure when a long path falls short. Lines stay visible without JavaScript because the class that hides them is only applied by script.
/* CSS — .pending is added by JS, so no-JS pages show finished lines */ .chart-line.pending { stroke-dasharray: 1000; stroke-dashoffset: 1000; } .chart-line { transition: stroke-dashoffset 1.2s cubic-bezier(0.4, 0, 0.2, 1); } .chart-line.drawn { stroke-dashoffset: 0; } /* JS */ const lines = document.querySelectorAll('.chart-line'); const reduceDraw = window.matchMedia('(prefers-reduced-motion: reduce)').matches; if (!reduceDraw) { lines.forEach(l => l.classList.add('pending')); const drawObs = new IntersectionObserver(entries => { entries.forEach(e => { if (e.isIntersecting) { e.target.querySelectorAll('.chart-line').forEach(l => l.classList.add('drawn')); drawObs.unobserve(e.target); } }); }, { threshold: 0.4 }); document.querySelectorAll('.pp-chart, .chart-frame').forEach(c => drawObs.observe(c)); }
The native API ties animation progress directly to scroll position. Supported in Chromium 115+, Safari 18+, and Firefox 132+. Always wrap it in @supports: outside the wrapper, the element simply sits there, fully visible, which is exactly the right fallback.
@keyframes reveal {
from { opacity: 0; translate: 0 20px; }
to { opacity: 1; translate: 0 0; }
}
@supports (animation-timeline: view()) {
.section {
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 0% entry 25%;
}
}
/* Outside @supports: elements stay visible. Purely additive. */
Every hex value in §04 and §06b is clickable. Click copies the value and shows a two-second toast. Inline handlers are acceptable in a self-contained guide file; in a production page, bind listeners instead.
function copySwatch(hex, el) {
if (!navigator.clipboard) return;
navigator.clipboard.writeText(hex).then(() => {
const t = document.createElement('div');
t.textContent = hex + ' copied';
t.style.cssText =
'position:fixed;bottom:80px;right:24px;' +
'background:var(--pp-off-black);color:var(--pp-gold);' +
'font-family:var(--pp-font-ui);font-size:11px;letter-spacing:0.18em;' +
'text-transform:uppercase;padding:10px 16px;' +
'border:1px solid var(--pp-gold);z-index:9999;pointer-events:none;';
document.body.appendChild(t);
setTimeout(() => t.remove(), 2000);
});
}
/* On each swatch hex element:
style="cursor:pointer" onclick="copySwatch('#B99A38', this)" */
Eight further patterns for showcase deliverables: the flagship deck, the annual outlook, anything a prospect sees before they meet us. They share one mechanic: progress is scrubbed to scroll position, so the page moves exactly as fast as the reader. The register stays quiet. These are not eight effects to stack; pick the two or three the content calls for.
One gate governs the whole tier. Every hidden-start effect lives behind an fx class that JavaScript adds to the html element only when motion is allowed. No-JS, print, and reduced-motion readers always get the finished page, because nothing was ever hidden from them.
var reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!reduceMotion) {
document.documentElement.classList.add('fx'); /* gates hidden-start effects */
document.documentElement.classList.add('fx-crossfade'); /* only if pattern 10 is in use */
}
A fixed one-pixel vertical hairline at the left edge whose gold fill grows with scroll progress. The brand's thin gold thread, made literal: progress becomes spatial instead of abstract. Hide it below 900px, where it crowds the content.
<div class="pp-spine" aria-hidden="true"><div class="fill"></div></div>
<style>
.pp-spine {
position: fixed; left: 24px; top: 0; bottom: 0;
width: 1px; background: var(--pp-hairline-dark); z-index: 90;
}
.pp-spine .fill {
position: absolute; top: 0; left: 0;
width: 1px; height: 0%;
background: var(--pp-gold);
}
@media (max-width: 900px) { .pp-spine { display: none; } }
@media print { .pp-spine { display: none !important; } }
</style>
<script>
(function () {
var fill = document.querySelector('.pp-spine .fill');
window.addEventListener('scroll', function () {
var p = Math.min(window.scrollY / (document.body.scrollHeight - window.innerHeight), 1);
fill.style.height = (p * 100) + '%';
}, { passive: true });
})();
</script>
The flagship upgrade of pattern 5. Instead of drawing once on entry, the lines draw in proportion to scroll position: the data moves exactly as fast as the reader. Scrub back and the line recedes. Kill the transition; a scrubbed line that also eases fights the hand on the wheel.
(function () {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
var host = document.getElementById('story-slide'); /* the tall slide or section */
var lines = host.querySelectorAll('.chart-line');
lines.forEach(function (l) {
l.style.strokeDasharray = '1000';
l.style.transition = 'none'; /* scrubbed, not eased */
});
function scrub() {
var r = host.getBoundingClientRect();
/* the draw completes across the first 1.2 viewports of travel */
var p = Math.min(Math.max((window.innerHeight - r.top) / (window.innerHeight * 1.2), 0), 1);
lines.forEach(function (l) { l.style.strokeDashoffset = String(1000 * (1 - p)); });
}
window.addEventListener('scroll', scrub, { passive: true });
scrub();
})();
The background dissolves between light and dark as the deck crosses slide boundaries, instead of hard-cutting. The body carries the surface color and transitions it; the slides go transparent, but only when JavaScript declares the effect live. With JS off, slides keep their solid backgrounds and the cuts are hard, which is the correct fallback.
/* CSS */ html.fx-crossfade body { transition: background-color 600ms ease; } html.fx-crossfade .slide-light, html.fx-crossfade .slide-dark { background: transparent; } @media print { html.fx-crossfade .slide-light { background: #F5F4F0 !important; } html.fx-crossfade .slide-dark { background: var(--pp-off-black) !important; } } /* JS — inside the nav-dot midpoint tracker (§12b pattern 1) */ if (document.documentElement.classList.contains('fx-crossfade')) { document.body.style.backgroundColor = slides[active].classList.contains('slide-light') ? '#F5F4F0' : '#0B0B0B'; }
The closing statement stays pinned while its lines arrive one at a time as the reader scrolls. Reversible by design: scrub back and the lines recede. Reserve it for the one statement the whole piece was building toward, and keep it to three or four lines. Five is a speech.
<div class="slide slide-dark slide--pin" id="s5">
<div class="pin-stage">
<h1>
<span class="pin-line" style="display: block;">The plan was built</span>
<span class="pin-line" style="display: block;">for a year like this.</span>
</h1>
<p class="pin-line">Supporting paragraph.</p>
<p class="pin-line"><a class="pp-btn" href="...">Call to action</a></p>
</div>
</div>
<style>
.slide--pin { height: auto; min-height: 240vh; display: block; padding: 0; }
.slide--pin .pin-stage {
position: sticky; top: 0; height: 100vh;
display: flex; flex-direction: column;
padding: 2.8% 4.5%; box-sizing: border-box;
}
html.fx .pin-line { opacity: 0; translate: 0 14px; transition: opacity 600ms ease, translate 600ms ease; }
html.fx .pin-line.in { opacity: 1; translate: 0 0; }
@media print {
.slide--pin { min-height: 0; }
.slide--pin .pin-stage { position: static; height: auto; }
html.fx .pin-line { opacity: 1 !important; translate: 0 0 !important; }
}
</style>
<script>
(function () {
var host = document.getElementById('s5');
var lines = host.querySelectorAll('.pin-line');
function update() {
var r = host.getBoundingClientRect();
var scrollable = r.height - window.innerHeight;
if (scrollable <= 0) return;
var p = Math.min(Math.max(-r.top / scrollable, 0), 1);
lines.forEach(function (el, i) {
el.classList.toggle('in', p > 0.10 + i * 0.16);
});
}
window.addEventListener('scroll', update, { passive: true });
update();
})();
</script>
KPI digits roll like a mechanical counter instead of interpolating. Quieter and more precise than the count-up, and the right voice for a firm that deals in exact numbers. Each digit is a strip of 0 through 9 that translates into place, staggered 80ms per digit. The markup is built by JavaScript from the same data-countup attributes as pattern 4, so the finished value stays in the HTML for everyone else.
/* CSS */ .odo { display: inline-flex; font-variant-numeric: tabular-nums; } .odo > span, /* static characters: $ . % ~ share the digit grid */ .odo .odo-col { display: block; height: 1.04em; line-height: 1.04em; overflow: hidden; } .odo .odo-strip { display: block; translate: 0 0; transition: translate 900ms cubic-bezier(0.22, 1, 0.36, 1); } .odo .odo-strip span { display: block; height: 1.04em; line-height: 1.04em; text-align: center; } /* text-align keeps digits centered when Brother 1816 falls back to a font with proportional figures (Century Gothic). */ /* JS */ function odometerBuild(el) { var text = (el.dataset.prefix || '') + parseFloat(el.dataset.target).toFixed(el.dataset.decimals || 0) + (el.dataset.suffix || ''); el.textContent = ''; el.classList.add('odo'); var strips = []; text.split('').forEach(function (ch) { if (/[0-9]/.test(ch)) { var col = document.createElement('span'); col.className = 'odo-col'; var strip = document.createElement('span'); strip.className = 'odo-strip'; for (var d = 0; d <= 9; d++) { var sp = document.createElement('span'); sp.textContent = d; strip.appendChild(sp); } col.appendChild(strip); el.appendChild(col); strips.push({ strip: strip, digit: +ch }); } else { var st = document.createElement('span'); st.textContent = ch; el.appendChild(st); } }); return strips; } function odometerRun(strips) { strips.forEach(function (o, i) { o.strip.style.transitionDelay = (i * 80) + 'ms'; o.strip.style.translate = '0 ' + (-o.digit * 1.04) + 'em'; }); } var odoObs = new IntersectionObserver(function (entries) { entries.forEach(function (e) { if (!e.isIntersecting) return; if (e.target._odo) requestAnimationFrame(function () { odometerRun(e.target._odo); }); odoObs.unobserve(e.target); }); }, { threshold: 0.5 }); document.querySelectorAll('[data-countup]').forEach(function (el) { if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return; el._odo = odometerBuild(el); odoObs.observe(el); });
Table rows arrive in a fast stagger, 40 milliseconds apart, as the table enters view. At that speed it reads as assembly, not animation. Opacity only: transforms on table rows are unreliable across browsers, and the cascade does not need them.
/* CSS */ html.fx .pp-table tbody tr { opacity: 0; transition: opacity 400ms ease; } html.fx .pp-table tbody tr.in { opacity: 1; } @media print { html.fx .pp-table tbody tr { opacity: 1 !important; } } /* JS */ (function () { var table = document.querySelector('.pp-table'); if (!table || !('IntersectionObserver' in window)) return; var rows = table.querySelectorAll('tbody tr'); var obs = new IntersectionObserver(function (entries) { entries.forEach(function (e) { if (!e.isIntersecting) return; rows.forEach(function (r, i) { setTimeout(function () { r.classList.add('in'); }, i * 40); }); obs.unobserve(e.target); }); }, { threshold: 0.3 }); obs.observe(table); })();
The cover photograph scales from 1.00 to 1.05 across the first viewport of scroll while the gradient holds still. A Ken Burns the reader drives, not a video that plays at them. The image moves to a ::before layer so the transform never touches the content.
/* CSS — image on ::before, content above it */ .slide-cover { position: relative; background: var(--pp-off-black); } .slide-cover::before { content: ""; position: absolute; inset: 0; z-index: 0; background: linear-gradient(to bottom, rgba(11,11,11,0.30) 0%, rgba(11,11,11,0.78) 100%), url('HERO_URL_FROM_MANIFEST') center / cover no-repeat, var(--pp-off-black); transform: scale(var(--cover-zoom, 1)); transform-origin: center; will-change: transform; } .slide-cover > * { position: relative; z-index: 1; } /* JS */ (function () { if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return; var cover = document.querySelector('.slide-cover'); window.addEventListener('scroll', function () { var p = Math.min(window.scrollY / window.innerHeight, 1); cover.style.setProperty('--cover-zoom', String(1 + 0.05 * p)); }, { passive: true }); })();
Hover or focus a data point: a dot appears and the value with it. Information on demand, no motion spent. The hit area is an invisible eleven-pixel circle so fingers and imprecise mice both land; the visible dot is 3.5 pixels. Keyboard users reach every point through tabindex.
<!-- One group per data point, inside the chart SVG -->
<g class="pt" tabindex="0">
<circle cx="160" cy="148" r="11" fill="transparent"/>
<circle class="pt-dot" cx="160" cy="148" r="3.5" style="fill: var(--pp-slate-blue)"/>
<text class="pt-label" x="160" y="136" text-anchor="middle"
font-family="Aptos, Inter, sans-serif" font-size="10" font-weight="600"
style="fill: var(--pp-off-white)">28%</text>
</g>
<style>
.pt { cursor: default; outline: none; }
.pt .pt-dot, .pt .pt-label { opacity: 0; transition: opacity 150ms ease; }
.pt:hover .pt-dot, .pt:focus .pt-dot,
.pt:hover .pt-label, .pt:focus .pt-label { opacity: 1; }
@media print { .pt .pt-dot, .pt .pt-label { opacity: 0 !important; } }
</style>
Include this block in every deliverable that uses any pattern above. Readers who ask for less motion get the finished page immediately. Nothing is lost, because nothing was ever hidden.
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
html { scroll-behavior: auto; }
}