# CCM scrolling report template

Build reports, research papers, and narrative web pages from one editable JSON file. The output is standalone HTML. The default export opens from disk with no server, no package install, and no network request.

Start at the [guide](https://jamditis.com/ccm-report-template/guide.html). It shows the art, the palette, the type, and the section types on one page.

## Make a report

1. Copy this folder for your new report.
2. Edit `report.json` with a text editor.
3. Run `python3 build.py` from the copied folder.
4. Open `index.html` in a browser.

Python 3.11 or later runs the build with nothing else installed.

To keep several reports beside the template:

```bash
cp report.json my-report.json
python3 build.py my-report.json --output ../my-report/index.html
```

The build replaces the selected output file. Keep your JSON and edited source files in Git. Rebuild after each edit. Changes made directly to the exported HTML disappear on the next build.

## Hosted demo and asset links

The demo is live at `https://jamditis.com/ccm-report-template/`. The browser guide is at `guide.html`, and `report.css`, `report.js`, and `favicon.svg` sit beside it.

Use the complete `starter.html` markup with these asset links:

```html
<link rel="stylesheet" href="https://jamditis.com/ccm-report-template/report.css">
<link rel="icon" type="image/svg+xml" href="https://jamditis.com/ccm-report-template/favicon.svg">
<script defer src="https://jamditis.com/ccm-report-template/report.js"></script>
```

The kit styles a full report document. These links need the report structure in the starter. They do not convert an arbitrary page into a scrolling report.

To generate a report with hosted assets:

```bash
python3 build.py report.json --output starter.html --assets-url https://jamditis.com/ccm-report-template
```

The default build embeds the stylesheet, the script, and the icon in the page. Hosted-asset mode is the exception: that page fetches `report.css` and `report.js` from the base URL you pass, so it needs the network. The base URL must be absolute HTTP(S) with no query string and no fragment. The public asset URLs follow the deployed version. Copy the files beside your report when you need a fixed version. Do not treat a query string as version pinning.

The icon is the exception inside the exception. `favicon.svg` is drawn in the default palette and `build.py` paints your theme into it, so a report with its own colors carries its icon in the page even in hosted-asset mode. One shared file cannot be five palettes.

## Content settings

| Field | Use |
| --- | --- |
| `title` | Cover headline, page title, and social title |
| `subtitle` | Standfirst under the headline, meta description, and social description |
| `organization` | Publisher: the header brand, the page title suffix, and the footer colophon |
| `series` | Series line on the cover and in the footer |
| `authors` | Byline on the cover |
| `date` | Byline date and footer date |
| `hero_caption` | Caption under the cover drawing |
| `tagline` | Optional. Left footnote at the foot of the cover |
| `scroll_cue` | Optional. Right footnote at the foot of the cover |
| `notice` | Optional. Specimen, draft, or editorial notice under the cover |
| `template_url` | Optional. Adds a "Use this template" link to the notice; remove for finished reports |
| `pdf_url` | Optional. Adds a "Read the paper" button to the cover; absolute HTTP(S) |
| `url` | Absolute final report URL. Canonical link, social URL, and the address printed in the footer |
| `image_url` | Absolute URL of the report's 1200 × 630 social image |
| `theme` | Five six-digit hex colors |
| `sections` | Ordered list of report sections |

Every text field is text. A number, a list, or an object where a sentence belongs stops the build and names the field, the section, and the step or item it is in, because `str()` on a list would otherwise render its Python form on the page.

`tagline` and `scroll_cue` are independent. Set both, one, or neither. The footnote band at the foot of the cover appears only when at least one of them is set.

The sample contains invented examples. Replace them and their notes before publication. Use the publisher's approved name and identity. The small CCM text mark is a template treatment, not an official logo asset.

All content fields use plain text. HTML is escaped. Links accept absolute HTTP(S) URLs. Use Unicode characters directly in JSON.

## Section types

Give every section a unique `id`. It must start with a lowercase letter and may then use lowercase letters, digits, and hyphens, such as `key-findings`. An id like `1-intro` fails the build. The ids `main` and `top` are reserved.

Every type except `quote` needs a `title`. It becomes the section heading, the contents link, and the address the heading links to, so an empty one would ship an empty heading and a link with no name.

| Type | Fields beyond `type` and `id` |
| --- | --- |
| `prose` | `label`, `title`, `paragraphs` (list of text) |
| `scrolly` | `label`, `title`, optional `center_label`, `steps` |
| `findings` | `label`, `title`, `items`: each has `value`, `title`, `text`, and optional `unit` and `note` |
| `quote` | `text`, `attribution` |
| `methods` | `title`, `paragraphs`, `sources`: each has `label`, `url` |

Reorder, repeat, or remove sections. The contents menu follows their order and uses each section's title. Quote sections carry no title, so they do not appear in the contents menu.

In a findings item, `unit` prints under the numeral and `note` prints under the text. Both are optional, and leaving one out leaves no empty element behind. Use them for the measure and the caveat: what the number counts, and over what period.

A scrolly section needs at least one step. Each step has:

```json
{
  "title": "Explain one change.",
  "text": "Connect the visual change to the argument in the report.",
  "caption": "Describe the visual state in plain text.",
  "layout": "groups",
  "highlight": 4
}
```

Available layouts are `scatter`, `groups`, `network`, and `orbit`. `highlight` takes either an integer from 0 to 12, which colors that many marks from the start of the set, or a list of mark indices from 0 to 11, such as `[0, 4, 8]`, with no index repeated. `center_label` changes the word inside the orbit ring. Its default is `Community`, and it holds at most 14 characters, because a longer word does not fit the ring.

The visual always contains 12 decorative marks. It is a narrative illustration, not a data chart. Do not use mark counts, areas, or distances to imply measured values. Put verified numbers in findings with units, dates, and source context. Add custom data charts as a separate implementation when your research needs them.

## The guide

`guide.html` is generated, like the report. Edit `guide.template.html`, which holds the copy and the guide's own layout, and rebuild:

```bash
python3 build.py --guide --output guide.html
```

Everything on that page that comes from `report.json` or from the generator is a `{{name}}` placeholder: the theme block, the icon, the hero drawing, the three mark specimens, the palette and its hexes, the organization, the date, the canonical URL, the social image, the mark count, and the layout names. A new theme moves the guide with the report, and the build fails if the template and the generator disagree about what is on the page.

## Motion

The stage holds four layouts: `scatter`, `groups`, `network`, and `orbit`. Marks travel from one arrangement to the next, connection lines are drawn in rather than faded in, and the cover draws its own marks once per load. The header carries a pause control. Reduced motion and the pause control run the same path: every state change becomes instant, so the reader still gets the right state and the correct caption, with nothing moving.

## Print

The browser print button gives a compact reading version. Paper is white, ink is black, and the screen-only chrome goes away: the grain, the sticky stage, the controls, and the cover buttons. The stage cannot scroll on paper, so the page prints one static copy of it per step: the sample's four states run as small multiples, two by two, above the step texts, each with its caption. The header keeps the brand and the footer keeps the canonical URL and the date, which is what a printed copy is passed around with. Every step and caption prints. It is not a tagged or publication-ready academic PDF.

## Responsive

Below 1000px the stage stops being a column and becomes a band pinned under the header, with the drawing, the caption, and the progress rule inside it. Each step card then gets a screen of its own, clear of the band and of the soft edge under it. At 760px the cover, the findings grid, and the footer go to one column. At 420px the header sheds the organization name. Nothing scrolls sideways from 1440 down to 320, and nothing interactive is shorter than 44px.

## Choose a format

- **Report:** cover, introduction, scrolly explanation, findings, implications, methods.
- **Research paper:** cover, abstract (`prose`), findings, discussion (`prose`), methods and sources. Add `pdf_url` for the full paper.
- **Short web page:** cover, one prose section, one scrolly section, and sources.

## Change the visual theme

Edit the five `theme` colors. Keep text contrast readable. Default colors are a suggested CCM treatment, not a claim about official brand standards.

| Setting | Default | Purpose |
| --- | --- | --- |
| `paper` | `#f6f3eb` | Page and text-card background |
| `ink` | `#292925` | Text, lines, buttons |
| `accent` | `#a33e36` | Highlights and large numbers |
| `secondary` | `#466958` | Connections and cover caption |
| `wash` | `#e7e4d9` | Dividers and methods background |

For a cool treatment, try paper `#f1f4f3`, ink `#243535`, accent `#315d73`, secondary `#51694b`, and wash `#dce5e2`.

Those five are the whole theme. Every other color on the page, including every hairline, every muted label, the focus ring, and the icon, is mixed from them, so a new theme moves the report, the guide, and the icon, and leaves no stranded gray behind. `DESIGN.md` lists every token, every mix, the contrast each pair holds, and the rules the stylesheet follows. Read it before adding a rule.

`report.css` holds typography, spacing, responsive behavior, and print rules. `report.js` holds the four visual layouts and the scrolling behavior. The generator embeds both in each default export. The type is two system families, a serif and a sans, plus Times for large figures, so no export asks a font server for anything.

The reading column is set in ems, so it follows the type instead of guessing at it. It holds 60 to 72 characters a line on the named serifs, about 60 in Iowan Old Style or Palatino Linotype and about 68 in Georgia or Charter. A reader who has none of them installed falls through to Times, which is narrower than all of them and runs to about 73.

## Design reference

Reference: the locally saved "Scenarios for our Economic Future" Anthropic report, captured September 9, 2026.

What was studied: the reading approach. Warm paper, faint ruling, irregular ink marks, generous space, a narrow measure, a visual stage that pins while short narrative cards pass it, and captions that name each state. The saved file contains no JavaScript, so it showed the visual structure but not the original timing or interactive behavior.

What was built new: the marks and the four layouts, the stylesheet and its token system, the generator, the icon, and every line of demonstration copy. No text, font, mark, image, or saved-page asset was copied. The page uses native scrolling, and intercepts no wheel or touch event.

## Check before publication

Read the full output at desktop and phone widths. Follow every contents link. Test the motion control and print preview. Review custom theme contrast. Confirm all claims and links.

Set the final `url` and `image_url`. Replace the demo's `og-image.png` with a 1200 × 630 image for your own report; the ZIP does not ship it. Check the icon in a light and a dark browser tab, since it carries its own dark treatment. Remove the demonstration notice and the template link.

The output keeps the full text without JavaScript. Motion preferences disable transitions; visual states still change instantly with scroll. Each step includes a text description. The default export makes no external requests, stores no visitor data, and needs no backend.

Upload the built HTML and social image through your normal publication process.

## Refresh the downloadable demo

Run `python3 package.py` to rebuild the offline demo, the hosted-asset starter, the guide, and `template.zip`. It refuses to build if a file it means to ship is missing, or if a source file in the folder is on neither its list nor its list of deliberate omissions. The ZIP contains the template sources, the guide and the template it is built from, and `DESIGN.md`. It omits the social preview image; supply your own image for each published report.

## Tests

The suite covers the generator's validation and escaping, the themed icon, the guide against a fresh render of its template, the reproducibility of the committed demo, and the contents of the ZIP. Run it from the repository root:

```bash
python3 -m unittest tests.test_ccm_report_template -v
```

No packages to install.
