# The brand: how to rename the product or swap its artwork

The brand layer (plan CORE §3.8, built in S0 on 24 October 2026; the family's layer, rebrand playbook §4.1, as
TuteDesk built it and TuteCampus carried it to Inertia). Every word, colour and image that names the product comes
from one place, so a rename is a few words in `.env` and new artwork is a file swap. `BrandGuardTest` keeps it that
way.

Atrium has no app colour and no app logo of its own (handbook 13.1): its primary is the parent's navy, cobalt is
Info only (CORE-D1), and the shell shows the parent's lockup with the app's name in plain type (CORE-D2). An app copied
from Atrium gives itself both (plan CORE §6).

## Where it lives

| What | Where | Read through |
|---|---|---|
| The words: company, product, name, short name, legal name, registration, address, website, host, tagline, statement | `config/brand.php`, each from a `BRAND_*` key in `.env` (most built from `BRAND_COMPANY`, `BRAND_PRODUCT` and `BRAND_SHORT`) | `App\Support\Brand\Identity` (`name()`, `shortName()`, `appName()`, `legalLine()`, `word()`…); in Vue `useBrand()`, from the `brand` prop |
| The joining words: "by Tutelora", "Powered by…", "Registered in…" | `lang/en/brand.php` | `Identity::byLine()`, `poweredBy()`, `legalLine()` |
| The colours: navy, cobalt, primary, primary on dark, ink 2 and 3, the lines, mist, success, warning, error, info | `config/brand.php` `colours`, each from `BRAND_<ROLE>` | `Identity::colour()`; in CSS `var(--brand-primary)` and the rest, written by `<x-brand-tokens />` |
| The theme's shades, light and dark | Derived from the colours by the recipes in `App\Support\Brand\Palette` (a share of one brand colour mixed into another) | `--brand-light-<token>`, `--brand-dark-<token>` (and `-rgb`); `resources/css/brand.css` maps shadcn-vue's variables and the family's roles onto them, and writes no colour of its own |
| The pages the app doesn't serve: the mockups (`reji/ui`) and the docs (`reji/docs`) | `public/brand/tokens.css` and `public/brand/brand.json`, written by `php artisan brand:tokens`; `reji/ui/brand` is a link to `public/brand` | the mockup kit and the docs frame read `brand.json` and `tokens.css` |
| The artwork | `public/brand/` under fixed role names: `app-icon.svg`, `favicon.ico`, `icons/icon-<size>.png`, `parent/lockup*.svg`, `parent/mark*.svg`. A copy adds its own (`lockup`, `short_lockup`, `mark`…) | `Identity::asset($role)`, `Identity::icon($size)` (addresses versioned by content); `BrandLogo.vue` (`role`, `tone`) |
| Stored and declared wording (Input Fields S5, settings help S6, templates S7) | `{brand:name}`, `{brand:app_name}`, `{brand:legal_line}` … in the text | `BrandWords::fill()` |
| The head | `<x-brand-head />` in `resources/views/app.blade.php`: the favicon (`/favicon.ico`), the SVG icon, the touch icon, the manifest (`/site.webmanifest`), `theme-color`, the app's name for the tab; `<x-brand-tokens />` last in `<head>` | `BrandController` |
| The tab | "Page – Atrium" (`Identity::TITLE_JOIN`) | `resources/js/app.ts` reads the name from `<meta name="application-name">` |
| The sign-in panel's statement | `BRAND_STATEMENT`; words between `*…*` take the panel's accent | `Identity::statementHtml()` (escaped), `Identity::statement()` (plain) |

Fixed on purpose, so a rename never signs everyone out or empties the cache: the session cookie `app_session` (no product's name, plan AF §4),
the cache prefix `atrium-cache-` and the Redis prefix `atrium-database-` (`SESSION_COOKIE`, `CACHE_PREFIX`,
`REDIS_PREFIX`). An app copied from Atrium changes these once, to its own short name (plan CORE §6).

## To rename the product

1. In `.env`: `BRAND_COMPANY`, `BRAND_PRODUCT` and `BRAND_SHORT` (and `BRAND_REGISTRATION`, `BRAND_ADDRESS`… if the
   company's facts change). `APP_NAME` is built from the short name, so leave it.
2. `php artisan config:clear`, then `php artisan brand:check`: the words, the artwork, and every shade of the theme
   read in light and dark (48 pairs: 4.5:1 for text, 3:1 for placeholders). A new primary that fails says which
   pair. Then `php artisan brand:tokens` refreshes the mockups' and docs' copy.
3. In the repository: the defaults in `config/brand.php` and the pins in `phpunit.xml`. A copy of the starter pack retires
   the pack's name by itself (`retired` in `config/brand.php` holds the pack's name whenever the short name is
   something else), so `php artisan test --filter=Brand` lists every trace left to rename.

## To swap the artwork

1. `php artisan brand:import "<the kit>" --dry-run`: what would be copied, by the kit's suffixes (`-horizontal.svg`,
   `-horizontal-dark.svg`, `-stacked`, `-wordmark`, `-mark`, `-app-icon`, `app-icons/*-<size>.png`, `favicon.ico`;
   the company's `-lockup-horizontal-navy`, `-white`, `-mark-navy` and `-mark-white` at the kit's root). The app's
   folder is `apps/<product word>/` (`--app=` to name another). With no folder for the app, as for Atrium, the
   company's tile, favicon and icons come from the kit's root. Only roles in `config('brand.assets')` are written;
   a role the kit lacks keeps its current file. The kit's `apps/palette.json` is compared with config.
2. The same without `--dry-run`.
3. `php artisan brand:tokens`, then `php artisan brand:check` until "The brand is whole".
4. Commit `public/brand/`. Browsers fetch the new files at once (the addresses carry the content's hash).

Atrium's artwork came from the full kit, `Brand Identity/tutelora-brand` (the company's set; 24 October 2026).

## The guard

`tests/Feature/Brand/BrandGuardTest.php` fails when the company's name, a retired name or a brand colour's hex is
written outside the brand layer (`config/brand.php`, `app/Support/Brand/`, the brand commands), in `app/`,
`bootstrap/`, `config/`, `database/`, `lang/`, `resources/` and `routes/` (`*.php *.ts *.vue *.css *.js *.json *.md
*.html`), comments included. A line that must name it ends `brand-guard: allow`.

Atrium's baseline (`tests/Support/brand-guard-baseline.json`) is `{}`. In a copy, the first run with
`BRAND_GUARD_WRITE=1 php artisan test --filter=BrandGuard` counts what is still to rename per file; a count may then
only fall, and the copy is done when the file is `{}` again.

**Stored wording** that names a retired name is rewritten once by `php artisan brand:retoken` (dry run first; history
untouched). Each slice that stores platform wording lists its store in `config('brand.retoken')`.
