Files
adr-sml/docs/reference/brand-assets.md
T
Pouya LajevardiandClaude Opus 5 67847d94fa fix: regenerate favicon.ico with a transparent ground; tick Pouya's read-through
public/favicon.ico shipped with no transparency: all three frames declared a
32-bit alpha channel and then carried alpha=255 on every one of their 256/1024/
2304 pixels, ground opaque cream rgba(250,247,242,255). Pouya's read-through
finding, confirmed by parsing the ICO container directly.

The render source carries true alpha (2,272,386 transparent px, 20,795 partial),
so this is an export, not a mask derived from the cream ground — R13's harder
branch did not fire and R13 is unchanged on its own terms.

New scripts/icons.mjs + npm run icons re-derives the icon from the committed
master: asserts the source is still the documented crop (R14), verifies a
candidate file and renames on success so a rejected build cannot replace a good
favicon, and runs a boundary-colour halo test. Composition is unchanged —
ink bbox and pixel count identical at all three sizes.

apple-touch-icon.png is byte-identical and stays opaque cream deliberately; the
reason lives in docs/reference/brand-assets.md §The icon set, with the bar and a
pointer in BaseLayout.astro, docs/06 and R13.

docs/06: the read-through is ticked, and the cutover callout drops to ONE
blocker — Q60's waiting period.

Two adversarial review rounds, 15 findings, all resolved, none declined; stopped
at two per D19. claims-auditor correctly deferred to cutover per D20. Gates on
the committed bytes, exit status read: check 0, build 0 (23 pages),
check:claims 0, check:intake 0, og:proof 0, lint 0, lighthouse 0 (worst of 23
99/100/100/100). Nothing deployed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Md3GndFqWPzK78xAoebsg5
2026-09-02 16:14:35 -04:00

266 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Brand assets — provenance
The repository holds the artwork it makes claims about. That is not tidiness: it
is `CLAUDE.md`'s rule and `AGENTS.md` R14, and this file exists because the
infinity mark was reconstructed wrongly and **two adversarial review passes could
not catch it**, since the real artwork was not in the repo to compare against.
**The measurements here are computed with `sharp` against the files in this
repository**, and are re-derivable by anyone from the commands given.
**Unless a section says otherwise, every figure below is `[verified
2026-08-26]`. Where a section carries its own stamp, that stamp wins** — the
render ladders are `[measured 2026-08-27]` and §The icon set is `[verified
2026-09-02]`. ⚠️ *This line has been wrong in both directions in one week: first
as a blanket 2026-08-26 that was already stale for the ladders, then as
"each section carries its own stamp" when five sections carry none. It is a floor
plus overrides because that is the only shape that covers every section without
mis-dating one* (`adversarial-reviewer`, rounds 1 and 2).
⚠️ **Two things in §The icon set are NOT read off this repository and are
labelled where they appear:** the tab-strip colours used in the contrast figures,
and iOS's handling of a transparent touch icon. Neither is derivable from
anything committed, so neither is stamped as if it were.
## Files
| File | What it is | Use |
|---|---|---|
| `src/assets/brand/sml-infinity-mark-master.png` | The mark as supplied: **3000 × 3000, alpha**, mark inset within the canvas | **The master.** Committed so the crop below can be re-derived and checked in-repo, not taken on trust |
| `src/assets/brand/sml-infinity-mark.png` | The same mark tight-cropped to its ink bounding box: **2668 × 1704, alpha, 1.5657:1** | **The render source.** `InfinityMark.astro` feeds it to Astro's `<Picture>` |
| `src/assets/brand/sml-logo-full.png` | Mark **plus** the "SML Company" wordmark, 3000 × 3000, alpha; ink bbox 2414 × 1440 = 1.676:1 | Not currently rendered. Held for the OG-image template (`docs/04`) and print |
| `src/assets/brand/sml-logo-source.svg` | 1500 × 1500 viewBox, **257,278 bytes** | Reference. Faithful, but not what is served — see below |
## Reproducing the crop, in-repo
The crop is derived, so it is re-derivable — and now from a file that is here,
which is the whole point of R14. Scan the master for the first and last pixel
that is neither transparent (`alpha < 24`) nor near-white (`r,g,b > 243`):
```
ink bbox of sml-infinity-mark-master.png -> 2668 x 1704 at (159, 646)
sharp(master).extract({ left: 159, top: 646, width: 2668, height: 1704 })
```
Crop to that box and the file's own aspect ratio becomes the mark's, so layout
can be tuned against the asset directly rather than against a number written
down beside it. `InfinityMark.astro` pins `aspect-ratio: 667 / 426`, which is
`2668 / 1704` reduced — **1.56573**.
## Why the SVG is held but not served
It renders **faithfully**. Rasterised at 8333 px it reproduces the master
exactly, at the same **1.566:1**. It is not bad artwork, and an earlier version
of this file implied it was; that was wrong.
What rules it out is weight and composition:
- **257,278 bytes**, against **3,063 bytes** for the AVIF a Retina device
actually takes in the header. **84×.** *(This line said "9,468 bytes... 27×"
until 2026-08-27. 9,468 was the DPR-1 figure — the number for the devices the
performance budget does **not** target. Quote the figure for the device the
budget is written for.)*
- **7 embedded base64 PNGs** (`<image>` elements), so it is a hybrid rather than
pure vector — inlining it would breach `CLAUDE.md`'s rule against
base64-inlining images, which is one of the specific faults of the build this
project replaces.
- 10 `<linearGradient>` carrying **1,225 `<stop>`** elements, 64 `<path>`,
59 `<clipPath>`, 23 `<mask>`, 1 `<filter>`. Expensive to rasterise.
What `AGENTS.md` Q38 asks for is a master that is faithful **and** light.
## What the browser actually downloads
`<Picture>` emits AVIF, WebP and a PNG fallback at `densities` 1×, 2× and 3× of
whatever intrinsic `width` the call site passes. **There are now TWO ladders,
because there are two sizes of call site** `[measured 2026-08-27 — every figure
below read from the file on disk]`.
**`width={64}` — the default. The header (50.1 px wide) and footer (56.4 px).**
| | 1× (64 px) | 2× (128 px) | 3× (192 px) |
|---|---|---|---|
| AVIF | **1,720 B** | **3,063 B** | **4,843 B** |
| WebP | 2,368 B | 5,368 B | 8,664 B |
| PNG (fallback) | 6,137 B | 16,996 B | 29,780 B |
**`width={232}` — the home page's approach section, which renders at 225.5 px.**
| | 1× (232 px) | 2× (464 px) | 3× (696 px) |
|---|---|---|---|
| AVIF | **6,017 B** | **14,555 B** | **22,639 B** |
That instance is `loading="lazy"`: it sits roughly a screen and a half down, so
it is not on the LCP path. The header and footer marks stay `eager`.
**THE LADDER HAS NOW BEEN WRONG IN BOTH DIRECTIONS, which is why `width` is a
prop rather than a constant.**
- *Too big, 2026-08-26.* Sized at 320 px, justified by a 4 rem sample on the
proof sheet (a page step 2 has since deleted), with `densities` stacked on
top — so the ladder double-counted its own headroom and every DPR-2 device
pulled a 640 px image into a 56 px slot: **20,629 B**, while the docs claimed
9 KB because that is what DPR 1 took.
- *Too small, 2026-08-27.* The home page added a 225.5 px call site and
inherited the 64 px ladder, whose largest file is 192 px: **3.52× upscale at
DPR 3.** Now 232/464/696, and 696 covers the 676 device px a DPR-3 screen
asks for. All three instances measure ≤1.0× upscale at DPR 1, 2 and 3.
**Do not measure this with `img.naturalWidth`.** For an image chosen from a
`srcset` with an `x` descriptor it is **density-corrected**: the 192 px file
selected at `3x` reports 64, so reading it at DPR 1, 2 and 3 returns 64 every
time — which looks exactly like the ladder not being generated at all. Read the
files on disk.
Passing an explicit `width` is load-bearing: without it Astro emits the
untouched 2668 px master as the `<img src>` fallback — **1,146,406 bytes**
which any client without AVIF or WebP support would actually download.
## The icon set
`[verified 2026-09-02 — every figure below read off the icon files in this
repository, EXCEPT the two external constants flagged inline]`
| File | What it is | Ground |
|---|---|---|
| `public/favicon.ico` | 16, 32 and 48 px frames, each a PNG-encoded 32-bit RGBA image inside the ICO container | **Transparent** |
| `public/apple-touch-icon.png` | 180 × 180 | **Opaque cream `#faf7f2`, and that is deliberate — see below** |
**The two grounds differ on purpose, and this is the line that stops someone
"fixing" it.** ⚠️ *The reason is an external platform behaviour, not a repository
fact, and it is recorded here as what it is:* **iOS does not honour transparency
in a touch icon — it composites it onto black**, so a transparent touch icon
ships a black tile on the home screen. Stated by Pouya in his ruling of
**2026-09-02** and not independently re-tested here; **it has not been checked on
a handset in this repository, and the place that does check it is the unticked
`docs/06` item "Tested on iOS Safari…".** The tab favicon has the opposite
requirement: a tab strip is dark for many readers, and an opaque ground shows
there as a visible rectangle around the mark. So the favicon is transparent, the
touch icon is matted, and **neither should be changed to match the other.**
### Composition
The mark spans **7/8 of the canvas width**, centred on both axes — 14/16, 28/32,
42/48, and 158/180 on the touch icon. That was measured off the icons as they
already shipped, so a regeneration reproduces the composition rather than
restyling the mark.
### Regenerating
```
npm run icons
```
`scripts/icons.mjs` resizes `src/assets/brand/sml-infinity-mark.png` onto a
transparent square canvas and assembles the ICO container itself, because
`sharp` does not write `.ico`. **It writes the favicon only** and never touches
the touch icon. It refuses to run if the render source has no alpha channel, and
it asserts that the source is still byte-identical to the documented crop of
`sml-infinity-mark-master.png` — R14, so the icon stays traceable to committed
artwork. It verifies a **candidate** file and renames it into place only on
success, so a rejected build cannot replace a good favicon.
**It also carries the halo test**, because the check that matters was living
only in prose here while the generator could not run it
(`adversarial-reviewer`, round 2). For each frame it takes every painted pixel
touching a fully transparent one and measures how many sit within 20 of cream:
| | boundary px | near cream | share |
|---|---|---|---|
| correct, 16 / 32 / 48 px | 61 / 146 / 258 | 1 / 2 / 5 | **1.6 / 1.4 / 1.9 %** |
| haloed fixture, 16 px | 56 | 11 | **19.6 %** |
The gate is **10 %**, roughly 5× clear of both. ⚠️ **The first version of this
guard inspected only partial-alpha pixels and MISSED the fixture completely** —
a knockout sets alpha per pixel and leaves **no partial alpha at all**, so there
was nothing for it to look at. The fixture is built by matting the mark on cream
and then knocking the ground out by alpha, which is the defect exactly; it fires
at 19.6 % and leaves `public/favicon.ico` untouched. **A guard that cannot see
the defect it is named for is worse than none**, and this one passed the real
icon while blind, which is the shape that ends a check instead of starting one.
### Why the favicon was regenerated, 2026-09-02
It had **no transparency at all**: all three frames declared a 32-bit alpha
channel and then carried `alpha = 255` on every one of their 256 / 1,024 / 2,304
pixels, with the ground opaque cream `rgba(250,247,242,255)`. Found by Pouya on a
read-through, confirmed by parsing the container directly.
Three measurements stand behind the replacement.
**1. The composition did not change.** Composited back onto cream, the new icon
reproduces the old matted one to within **1 of 255 on every channel of every
pixel** at all three sizes (mean delta 0.010.02, 0 pixels over a delta of 8).
So the regeneration did not restyle, rescale or reposition the mark.
⚠️ **That is NOT a test for a halo, and an earlier draft of this file filed it as
one.** Composite-onto-cream returns ~0 whether the edge is correct *or* is a
cream-matted edge that has merely had its background knocked out — the second
case composites straight back to what it came from. Both branches pass, so the
test cannot discriminate. Found by `adversarial-reviewer`, 2026-09-02.
**2. No halo — the measurement that does discriminate.** Read the RGB the
partial-alpha pixels actually carry. A cream halo means that RGB is near cream; a
correct export means it is the ribbon's own colour. Of the partial-alpha pixels,
**0 of 94 / 300 / 603** are within 12 of `rgb(250,247,242)`; the nearest is 13
away, the mean distance is **174.0 / 178.3 / 177.0**, and the mean colour per
frame is about `rgb(118,86,74)` — maroon-brown, not cream and not black.
**3. Legible on dark and on light, and NOT at the same cost.** ⚠️ *The ground
colours below are external constants, not repository facts: `#202124` is
Chrome's dark tab strip and `#f1f3f4` its light one, both `[observed
2026-09-02]`.* All ratios are computed on **composited** pixels, and the ink
colours are **fully opaque** pixels only — a ratio taken from a raw channel value
is a claim about a colour that is never painted, which is how an earlier draft
came to quote `14.02:1` for a pixel whose alpha is 251.
| | darkest opaque ink | lightest opaque ink | vs `#202124` | vs `#f1f3f4` |
|---|---|---|---|---|
| 16 px | `rgb(70,33,33)` | `rgb(216,188,143)` | champagne **8.82:1** | maroon **12.58:1** |
| 32 px | `rgb(60,29,29)` | `rgb(234,214,172)` | champagne **11.28:1** | maroon **13.62:1** |
| 48 px | `rgb(61,26,26)` | `rgb(238,219,175)` | champagne **11.80:1** | maroon **13.86:1** |
Whichever ground it sits on, one end of the ribbon carries the silhouette. **But
the other end does not merely dim — on dark it goes.** The maroon lobe
composites to **1.041.15:1**, and counting pixels that reach 3:1 against each
ground gives **23 / 87 / 189** on dark against **40 / 168 / 378** on light, at
16 / 32 / 48 px. **So about half as many pixels reach 3:1 on dark as on light.**
⚠️ *Those pixels are not absent — they are painted and fall below 3:1, and the
mark's full outline is still there, dim. An earlier draft said "roughly half the
mark's visible pixels are absent", which is neither what was counted (pixels at
`alpha > 0` are 109 / 388 / 805) nor what a render shows* — `adversarial-reviewer`,
round 2. The previous opaque-cream icon was still more legible on dark.
**That is the price of the change and it was accepted, not overlooked.** What was
bought is the removal of a cream rectangle from every dark tab strip and from
white. Raised by `adversarial-reviewer`, 2026-09-02, against a draft that
recorded the change as costless.
**The obvious alternative is an icon pair keyed on `prefers-color-scheme`, and it
was NOT ruled out on measurement.** ⚠️ *An earlier draft dismissed it on a
mechanism that is wrong: it said the query keys off the page's colour scheme. It
does not — it reports the user's system or browser preference, and this site
declares `color-scheme` nowhere (`git grep -n color-scheme -- src`, exit 1), so
it would resolve to the same preference that makes the tab strip dark.* The real
objections are **untested here** and are recorded as such: `media` on
`<link rel="icon">` is unevenly supported for raster icons, a browser theme can
be set independently of the system preference, and it doubles the artefact
`npm run icons` has to keep in sync. **If this cost is ever revisited, that is
the option to test** — do not re-dismiss it on the reason struck above.
## The colours are the artwork's, not the palette's
`tokens.css` is not involved. The ribbon carries its own gradient and it is
close to but not identical with `--maroon` `#5a1a1c` and `--gold` `#c9a876`.
Do not "correct" the artwork toward the tokens, and do not derive tokens from
the artwork — D7 keeps the palette *and* the mark, as they are.
## Where these came from
Supplied by Pouya. `sml-logo-source.svg` was added by him directly to the repo;
the PNGs were taken from the `smlcompany.ca` Google Drive, under `SML/Designs/`
and `Research/Law/ADR Personal Branding Project/`. **The Drive copies are not the
record — these files are.** That is the point of R14: an artefact that lives only
in Drive cannot be compared against a claim by any reviewer, which is exactly how
the traced mark survived two review passes.