# Triumph Design System v4 - documents

The system for anything a person READS on paper: multi-page proposals, reports, guides,
registers, and pricing plans that end as a PDF. This is NOT the Print v2 flyer system at
`/print/`. That one is graphic-led, one sheet, weave bands, and a Fabiola hero. This one
is text-led and table-led, many sheets, flat white paper, no weave anywhere, a full-colour
crest header bar over a navy rule, a running head, and page numbers. The one exception is
the `.sheet` variant in RULE 12, which carries a header bar and one centred footer line
instead of a running head and a page footer.

Link order in any page: `tokens.css` -> `logos.css` -> `document.css`. There is no
`pattern.css` in this folder. That is not an oversight and it is not a judgment call.

The three shipped consumers are the worked examples. Read one before you start a fourth:

- **2027 Ticketing Proposal**, 26 pages, the approved reference grammar:
  `Triumph Projects\2027 Season Planning - Master Folder\2027 Ticket Proposal\`
- **Audience Reach Report**, a report with no figures:
  `Triumph Projects\Audience Reach Report\`
- **Ticket Access Guide v5**, the compact procedural variant that runs on part bars and
  step rows: `Triumph Projects\Ticket Access Guide\`

## Which system am I in?

| Reader is doing this | System | Sheet to link |
|---|---|---|
| Staff reading data in a browser | Dashboards | `design.greenvilletriumph.club/tokens.css` |
| A member or a guest using a screen | Screens | `design.greenvilletriumph.club/screens/tokens.css` |
| A flyer, an ad, a poster, or one printed sheet, graphic-led | Print | `design.greenvilletriumph.club/print/tokens.css` |
| A multi-page proposal, report, or guide read as a PDF | Documents | `design.greenvilletriumph.club/documents/tokens.css` |

More than one page, a running head, or page numbers means Documents. One sheet built from
type, colour blocks, and the weave means Print.

## The routing test

Answer in order and stop at the first yes.

| # | Question | Yes routes to |
|---|---|---|
| 1 | Is the reader looking at a picture or a graphic composition, with the words as labels on it? | **Print v2 flyers** |
| 2 | Does it fit on one sheet by design, and would spilling to two sheets make it wrong? | **Print v2** if it is promotional, **the Documents v4 `.sheet` variant** if it is a staff or client lookup table |
| 3 | Does it want a weave band, a navy masthead, or a full bleed colour field? | **Print v2** |
| 4 | Is there a Fabiola hero on more than the first sheet? | **Print v2** |
| 5 | Does it run to two or more sheets of continuous reading, with sections, tables, and cross references? | **Documents v4** |

The four discriminators, stated as a pair of columns:

| | Print v2 flyer | Documents v4 |
|---|---|---|
| Led by | Graphic composition | Text and tables |
| Extent | One sheet, fixed | Many sheets, grows |
| Ground | Navy or Fog canvas, full bleed bands | Flat white paper, no bleed |
| Weave | One bounded band per sheet, never a ground | Banned. Not at any opacity, not on any band |
| Masthead | Navy band, weave, Fabiola hero, often 2in tall | Crest header bar over a 2pt navy rule, content starts immediately below |
| Fabiola | Hero only, once per sheet | Cover h1 only, once in the whole document, and optional |
| Chrome | None. A flyer has no running head | Running head and page footer on every sheet after the cover |
| Page size | Whatever the piece is, 4:5 social included | US Letter, always |
| Render check | Eyeball the weave and the lockups | Page count assert, footer presence, and the below-the-rule character count |

The one honest edge case is the one sheet price sheet. It is one sheet, it is text-led and
table-led, it has no weave, and it carries a crest header bar. It is Documents v4 in genre
and Print v2 in extent, which is why it gets the named `.sheet` variant in RULE 12 rather
than being forced into either parent whole.

## RULE 0 - THE PAPER IS WHITE. NEVER FOG.

`html,body{background:var(--paper)}` and `--paper` is `#ffffff`. Not Upstate Fog, not
Mist, not a tint, not a wash, not a gradient. Fog is a SCREEN ground. Through a printer it
reads as a dirty off-register white and burns toner across the whole sheet for nothing.
Fog survives on paper only as a small zone: a table row stripe, a callout panel, a footer
strip. The ban is on the PAGE, not on the swatch. If a v2 reference or a token comment
says "fog canvas", that is screen guidance and it does not survive contact with paper.

## RULE 1 - NO WEAVE. ANYWHERE IN THIS CLASS.

Not a band, not a masthead, not a footer strip, not a watermark, not at 4% opacity. The
folder ships no `pattern.css`, so the weave has no class to paint it with, and linking one
in from another folder is not a decision you get to make. The tile art itself still sits at
`images/brand/triumph-weave.svg`, because `tokens.css` names that file as the editable
source for the embedded tile. It is a file, not a class, and nothing in this folder draws
it. The approved print
reference carries no weave, so neither does this class. When you re-vendor `_ds\` into a
project, copy FIVE items from `documents\`: `tokens.css`, `logos.css`, `document.css`,
`fonts\`, and `images\`. There is no sixth, and there is no `pattern.css` here to copy by
mistake.

The weave ban above is a PAPER rule. On a SCREEN, the continuous weave field is Screens v3
territory and is approved there, including on fog. See `/screens/agents.md`. It has no
bearing on this system, which only ever ends as a printed sheet or a PDF.

## RULE 2 - Emphasis is a rule and a lead-in, NEVER a filled box

`.callout` is a flat Fog panel with a solid navy rule down its left edge and nothing else.
No navy ground. No weave fill. No border on the other three sides. No shadow. No radius,
here or anywhere: the system radius is 0. A navy patterned block floating on the paper is
banned in every variation, at every size, at every opacity, and at every arrangement, 2-up,
3-up, resized, or restacked. When a shape keeps failing on rework, the fix is to delete the
shape, not to iterate on it.

Four forms, and no fifth:

- `.callout` is the standard Fog panel with a 3pt navy rule and an uppercase `.lab`
  lead-in.
- `.callout.hint` is the compact one-line aside inside a step or beside a table.
- `.callout.accent` swaps the rule to Triumph Green. **Green rides the rule, never the
  lead-in text.** Green does not carry small type on white paper, so `.lab` stays navy.
  One accent callout per page at most.
- `.quote` holds language lifted from a source verbatim, in quotation marks. Its rule is
  Mist, not navy, and it takes no fill and no lead-in label, so it reads as quieter than a
  callout. `.warnings` is a two-up layout for two short notices, not a fifth shape.

THE ACCENT LAW on paper: green is the accent, and the only green in the class is the
`.callout.accent` rule. Lime and sky are data and mark colours; neither is text, a label,
chrome, a hairline, or decoration on a sheet. Yellow is warning UI and never appears here.

Nothing on the paper touches the running head or the footer. Every block clears both by
real space. A block set flush to a rule reads as one broken shape with a notch cut in it.

## RULE 3 - The crest is full colour, on light, at four sizes

- Full-colour crest (`images/brand/triumph-main-color.png`) ONLY. This class has no dark
  ground, so the on-dark marks do not ship in this folder at all.
- Cover bar 96pt (`--crest-cover`). Working cover that also carries content, 74pt
  (`--crest-cover-work`). Running head 22pt (`--crest-run`). One sheet reference, 40pt
  (`--crest-sheet`). Those are the four, as tokens.
- The stacked GREENVILLE / TRIUMPH wordmark is BANNED in the header bar and the running
  head. It is a two-line lockup, so a page title set beside it reads as a third and fourth
  line of the same mark and the whole header looks broken. Two legal headers: the sTc mark
  alone at header height, or type only with no mark. The wordmark stays correct where it
  stands BY ITSELF with clear space, never with words next to it.
- Never recolor, outline, rotate, stretch, flip, or crop a logo.
- Logos have a legibility floor. A venue or partner mark with interior detail stops
  reading at about one inch. GE Vernova Park at 21pt renders as mush. Below roughly an
  inch of height, set the name in type instead of placing the mark. Nothing but the
  Triumph crest goes in the running head.

## RULE 4 - Type

- **Fabiola Capitals appears ONCE per document, on the cover h1, at weight 400, or not at
  all.** It is optional: the approved one sheet opens on Inter 800 and carries no Fabiola.
  Never in the running head, never on an h2, never on a part bar, never on a table. It
  synthetic-bolds to mush at working sizes, which is why `font-synthesis:none` stays.
- Its vertical fix is the `tokens.css` metric override plus `text-box-trim:trim-both` and
  `text-box-edge:cap alphabetic`, which is cap-height sizing. NEVER add margin,
  line-height, or a translate on top of that; it double-corrects. The permitted inline
  values are three, and no fourth: a left side bearing nudge on the first glyph, whose
  value comes from the per-glyph table in `tokens.css`, which runs from `J +0.068` to
  `7 -0.044`; `--mast-chars` on the cover hero; and the `--w` and `--h` pair on
  `.figure.shot`. `document.css` documents the last two and supplies a fallback for each,
  so an unset value still renders. The nudge belongs to the glyph, not to the document:
  recompute it whenever the title changes.
- The cover title OWNS the bar. Set `--mast-chars` to the character count of the title,
  spaces included, and the cqi formula spans the column. Recount it when you retitle.
  Never re-shrink a cover title to leave room, and never cap it with a max-width in ch.
- Inter is everything else, weights 400 to 900. h2 is Inter 900. Mono is JetBrains Mono
  and it carries figures, page numbers, and leader-row prices, nothing else.
- **No fallback stacks.** `"Inter"`, not `Inter, system-ui, sans-serif`. A fallback renders
  the wrong face and the failed load never gets noticed.
- **Every font-size is a `--text-doc-*` token.** Ten of them: `hero`, `h2`, `step`, `part`,
  `h3`, `lede`, `h4`, `body`, `label`, `chrome`. The `.sheet` variant adds four more,
  `--text-sheet-title`, `--text-sheet-body`, `--text-sheet-note`, and
  `--text-sheet-chrome`, and nothing outside `.sheet` may use them. If a size is not on
  one of those two lists, it does not go on the page. The `rem` and `clamp()` `--text-*`
  scale in `tokens.css` is the SCREEN scale and is meaningless on a fixed sheet.

## RULE 5 - Print floors. Every row is a minimum, not a target.

| Element | Floor |
|---|---|
| Cover hero | 32pt to 36pt rendered |
| h2, section opener | 20pt to 24pt |
| h3 | 12pt to 14pt |
| Body | 10.5pt |
| Table head, caption, eyebrow | 10pt |
| Running head tag, footer, page number | 9pt, absolute |
| `.sheet` variant body | 10pt |
| `.sheet` variant note | 8.5pt, and only inside `.sheet` |
| `.sheet` variant chrome | 8pt, and only inside `.sheet` |

"Fits on one page" is never a reason to drop below a floor. Two clean pages beat one
cramped page. Sizes read differently on paper: if a print element reads like dashboard
chrome, it is wrong.

## RULE 6 - Running head, footer, and page numbers

Every sheet after the cover opens with `.runhead` (crest, document title, section tag
pushed right, 1.5pt navy rule) and closes with `.pagefoot` (provenance line left, mono
page number right, hairline above). The cover carries neither, and neither does the
`.sheet` variant: that one runs `.header-bar` at the top and one centred `.footer` line at
the foot, with no `.runhead` and no `.pagefoot` at all.

The last sheet may close on `.close` carrying its page number in `.cnum` INSTEAD of a
footer. Never both: two footers land on the same 20 points and overprint.

Page numbers, contents rows, and `{{p:page-id}}` cross-references are resolved by the
build from page div order. Never hand-maintain one. Shipped source carries a literal `0`
in every footer so nobody mistakes a source number for truth. A cross-reference to an id
no page carries is a hard build failure, which is the point.

## RULE 7 - Spill before shrinking

`.page` carries NO `overflow:hidden`, permanently. A page that outgrows its 11in box
spills onto an extra sheet, the page-count assert catches it, and you move content to
another page. With `overflow:hidden` the last row disappears and every check passes.

Never reduce a type size to make content fit. Never let a table run wider than the page:
that triggers the browser's shrink-to-fit, which scales the whole PDF and drops every size
below the floor at once. Table headers WRAP. Fix a wide table by wrapping its heads and
shortening a cell, never by setting `white-space:nowrap` and hoping.

`.page.flow` is the opt-in flex column for a sheet whose blocks size themselves. Inside it,
`margin-top:auto` on the last block is banned: pinned, that block sits below the content
box whenever the page runs long, and it lands on top of the footer.

## RULE 8 - Tables and figures

- Show the full build. Never post a total whose arithmetic a reader cannot follow line by
  line. Two worked examples side by side in `.cols2` is the pattern.
- Mist head, navy 0.75pt underrule, hairline rows, Fog on even rows, mono right-aligned
  figures in `td.num`, a quiet trailing column for classification, and a total row bounded
  by two 1pt navy rules that ties to the summary above it.
- One value per cell.
- A register is three columns: the item, what is open about it, and the next step. A next
  step that names nobody is not a next step.
- Figures sit straight on the white page: no frame, no rule, no shadow. The one exception
  is a UI screenshot, `.figure.shot`, which is white to its own edge and takes a 0.5pt
  hairline.
- Images stay in their native format. Never re-encode, never resample down to preview,
  never upscale past native pixels. Pick the width class so the image lands above roughly
  190dpi at print scale.
- Two-column body text is banned. It was rejected twice on the same document. At roughly
  25 characters a line the copy breaks into short ragged lines while space pools at the
  foot of the sheet. The only multi-column blocks of body text in the class are `.contents`,
  `.cols2` holding two tables, and `.warnings` holding two short notices. `.figure-row` and
  `.step` are multi-column layouts as well; neither carries running copy, which is why
  neither is the thing this bans.

## RULE 9 - Step rows and part bars

A procedural guide uses `.steps`, and each row is `.step` holding `.step-n` and
`.step-body`. The number is NAVY, not green: green does not carry small type on white
paper. Rows sit at their natural height with a hairline between them, and the copy centres
against the screenshot, because a two-line instruction beside a 3in portrait shot leaves a
dead white block under every step. `.step.stack` switches back to top alignment for a shot
that sits under the copy.

`.part` is a run-in divider for a document organised in named parts. It is NOT a shrunken
h2 and it never replaces one. A document uses `.part` bars or h2 sections at a given level,
never both, and a 15pt part bar never stands in for a missing 22pt h2.

## RULE 10 - The render pipeline

Headless Edge OR headless Chrome, whichever is on the machine, to PDF. Both are Chromium,
and `document.config.json` carries both paths in `render_exe`; the build takes the first
that exists and fails naming both if neither does. Absolute paths on both sides: a Git Bash
`$PWD` expands to `/c/Users/...`, which the browser reads as relative, answers with
ERR_FILE_NOT_FOUND, and then "succeeds" at about 60 KB.

All three checks live in `archive\` under `templates\proposal-document\` and run from the
project root, which is where `document.config.json` sits. That folder holds this pipeline's
machinery, not superseded work. Run them in order and read the output:

1. `archive\build_document.py` renumbers footers, fills the contents, resolves cross references,
   renders, and asserts the page count equals the page div count, every sheet after the
   cover carries its own numbered footer, nothing draws below the footer rule, and nothing
   spilled onto the next sheet.
2. `archive\verify_document.py` checks the rendered text: no blank pages, every `key_figure`
   present, no `banned_phrase` anywhere, and the below-the-rule character count per page.
3. `archive\check_completeness.py` checks the content probes, one per source topic. This is what
   catches a whole subject dropped during a page split.

**The footer-overflow blind spot is real: the build prints PASS on a broken PDF.** Spilled text that
lands on the footer bottoms out past the footer band, so the geometry splitter classifies
it as footer and the check prints PASS while the PDF is visibly broken. The test that works
is counting characters drawn below y 761.5 per page: a clean page carries the footer string
plus its number and nothing else. That count now runs in `verify_document.py` as a hard
failure. Run it after any edit, and rasterize the changed pages and look at them. Fix by
cutting a line of copy from that page, never by shrinking type.

Both scripts match page divs on a class TOKEN, so `class="page flow"` and any future
modifier are counted. Do not go back to an exact-attribute regex.

## RULE 11 - No status chrome, no attribution, no ceremony

- No status chrome on the page: no working-copy label, no WIP tag, no preliminary or
  preview stamp, and no line saying who may or may not see the document.
- No restricted-access framing of any kind. The `banned_phrases` list in
  `document.config.json` carries every one of those strings with a leading JSON escape, so
  the phrase itself never appears literally in a source file and cannot be copied out of
  the config into a document by accident. Read the list there; do not retype the strings
  anywhere else, this file included. The scan runs over the rendered PDF text, not the
  source, so a phrase that reaches the page fails the build.
- No on-behalf-of line of any kind: no prepared-for, prepared-by, or compiled-by credit.
  Footers carry data provenance only: the source system, the as-of stamp, and the basis
  caveat. No names, no titles.
- The cover kicker is organization plus subject, never organization plus a department. A
  department line is one edit away from an attribution line.
- A title says what the document IS. It never editorializes about the document's status.

## RULE 12 - The one sheet variant

`.sheet` is the dense staff or client lookup table that fits one sheet by design, where
spilling to a second sheet would make it wrong. It keeps the class grammar, white paper,
crest header bar, tokenised colour, and no weave, and what it relaxes is size:

- the crest drops to 40pt and the rule under the bar to 1.5pt, because the bar is chrome on
  a working sheet, not a cover;
- chrome type and table heads run at 8pt (`--text-sheet-chrome`), because the sheet is a
  lookup table, not continuous reading;
- a note runs at 8.5pt (`--text-sheet-note`);
- the section title runs at the chrome size, 8pt.

BODY STAYS AT 10pt. An 8pt body was rejected outright.

Everything else holds. The variant gets no colour literals of its own, no fallback font
stacks, and no green that is not in the seven. Fabiola is optional here and the approved
sheet carries none: its `h1` is Inter 800 at `--text-sheet-title`.

The shipped 2027 price sheet is an approved exemption. Its off-palette greens and its
7.5pt table heads stay exactly as they are and that file is not edited. New one sheet work
uses the tokens above.

## RULE 13 - Copy

- Oxford comma, always.
- ZERO em-dashes. The banned-phrase scan carries the character itself and will fail the
  build on one.
- Write "%" as the sign, not the word. The word is on the banned list.
- Full club names: **Greenville Triumph SC**, **Greenville Liberty**. Never "GVL", which is
  on the banned list. Never "Triumph" alone in a title.
- Plain, literal, and direct. No hype opener, no punchy closer, no metaphor standing in for
  a statement, no fake warmth. Lead with the answer, then the reasoning.
- A note carries a caveat, the arithmetic behind a figure, or something a reader must
  confirm. It never carries new facts.
- A quote block holds language lifted from a source verbatim, in quotation marks, never
  paraphrased into house voice.
- Verify the day of the week on any match date.

## RULE 14 - Versioned deliverable names

The rendered PDF is named for what it is and which version it is:
`Access Your Tickets - 2026 v5.pdf`, `2027 Ticketing Proposal - v3.pdf`,
`Who We Reach - 2026 Audience and Community Report.pdf`. A superseded render moves to
`archive\`, it is never overwritten in place, and the project top level shows the current
deliverable and one `archive\` folder, nothing else.

## MOTION AND STATES

There are none. A printed document does not animate, count up, reveal, hover, focus, or
press. Do not design a state onto it and do not copy one in from `components.css`.
`tokens.css` ships the global reduced-motion kill for this system's on-screen reference
pages; never re-implement it per page and never remove it.

The on-screen reference pages, `index.html`, the six cards in `preview/`, and the specimen,
each carry the dark-mode lock and the green sTc favicon set, and all six parts are
required: `data-darkreader-mode="off"` on `<html>`, `<meta name="color-scheme"
content="only light">`, `<meta name="darkreader-lock">`, `<meta name="theme-color"
content="#002855">`, the `#light-lock` style block, and the four favicon links. An
extension that inverts a paper specimen turns the specimen into a lie about the printer.

## DRIFT AND SYNC

- `document.css` carries a `@version` stamp on line 1. Bump it on ANY edit, even a
  one-liner. `index.html` prints the current stamp in its footer, so the two disagreeing is
  itself the signal.
- `documents\tokens.css` and `documents\logos.css` are BYTE COPIES of the files in
  `print\`. They are never edited here. A palette change happens in `print\` and gets
  copied over.
- **This repo folder is CANON.** The standalone `Triumph Design System v4` folder is a
  GENERATED COPY: `ds-sync.py` writes it, and writes its `SKILL.md` as a byte copy of this
  file. That script writes to disk only and makes no platform call, so the Claude Design
  "Documents v4" project is pushed from that mirror by hand. Never hand-write a `SKILL.md`
  anywhere.
- Never patch a deployed copy. When the live deploy, a mirror, or a platform project
  differs from this folder, the repo wins and the copies get re-synced.
- A project vendors `_ds\` from `documents\` because this folder is canon. The standalone
  `Triumph Design System v2`, `v3`, and `v4` folders are generated mirrors of the hub, and
  v2 mirrors `print\`, so it carries the print sheets rather than the document sheets.
- A project never forks `document.css` to delete a class it is not using. That single prune
  is the whole reason this system needed extracting.

---

### Files

- `tokens.css` - byte copy of `print/tokens.css`: palette, the Fabiola and Inter
  `@font-face` metric overrides, and the CORE block
- `logos.css` - byte copy of `print/logos.css`: lockups and the GEVP padding crop
- `document.css` - the document grammar: page box, cover bar, running chrome, the point
  scale, headings, tables, callouts, figures, contents, step rows, and the one sheet variant
- `agents.md` - this file, the single contract for the system
- `README.md` - the short system readme
- `index.html` - the system door
- `preview/` - cover, page anatomy, tables, emphasis, figures, and one sheet cards
- `templates/proposal-document/` - the full working project: skeleton, config, build,
  verify, completeness, and the Notion refresh cycle. Its first page div carries
  `class="page cover"`, where `cover` is a human marker only: `document.css` styles no
  such class and the build reads no meaning from it.
- `fonts/` - self-hosted faces
- `images/` - the full-colour crest, the light-ground brand marks, the weave tile as
  editable source art, and the venue lockups. No on-dark marks: this class has no dark
  ground

There is deliberately no `pattern.css` and no `components.css` in this folder. The Print v2
components are screen and flyer atoms; a document uses the classes in `document.css`.

### The class registry

Every class `document.css` ships, so a reader can tell a system class from an invented one.
If a name is not here, the sheet does not carry it.

| Group | Classes |
|---|---|
| Sheet | `.page`, `.page.flow` |
| Cover | `.cover-bar`, `.cover-bar.work`, `.cover-head`, `.cover-kicker`, `h1.hero`, `h1.hero.inter`, `.cover-sub`, `.lede`, `.facts`, `.contents` with `.row`, `.t`, `.d`, `.p` |
| Running chrome | `.runhead` with `.doc` and `.sec`, `.pagefoot` with `.num`, `.close` with `.cnum`, `.org`, `.q`, `.eyebrow` |
| Text | `.intro`, `.note`, `ul.tight`, `.part` with `.n` and `.rng`, `.part.lead` |
| Emphasis | `.callout` with `.lab`, `.callout.hint`, `.callout.accent`, `.quote`, `.warnings` |
| Tables | `.cols2`, `tr.total`, `td.k`, `td.num`, `td.q`, `td.st`, `td.lvl`, `td.c`, `td.r`, `.nb` |
| Figures | `.figure` with `.cap`, `.figure.w52`, `.figure.w56`, `.figure.w70`, `.figure.shot`, `.figure-row` |
| Steps | `.steps`, `.step`, `.step.text`, `.step.stack`, `.step-n`, `.step-body` |
| One sheet | `.sheet` with `.header-bar`, `.section-title`, `.note`, `.price`, `.footer` |
