> ## Documentation Index
> Fetch the complete documentation index at: https://developer.jobmojito.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Branding

# Branding configuration

Generates the document imported by **Portal customisation → Branding → Import configuration**.

Output a single JSON object. Every field is optional: whatever you omit keeps its current value in the portal. Nothing is written to the database on import — the operator reviews the result in the form and presses Save.

## Document

```json theme={null}
{
  "kind": "branding",
  "version": 1,

  "portal_name": "Northgate Nursing Simulation",
  "business_description": "Clinical simulation training for undergraduate nursing students.",

  "style": "minimal",

  "colors": {
    "primary": "212 84% 38%",
    "primary_opposite": "0 0% 100%",
    "secondary": "18 92% 60%",
    "background": "210 33% 98%",
    "font_black": "0 0% 17%",
    "font_grey": "0 0% 48%"
  },

  "logo_url": "https://cdn.example.org/northgate/logo.svg",
  "icon_url": "https://cdn.example.org/northgate/icon.png",
  "logo_external_url": "https://northgate.example.org",

  "email_sender_name": "Northgate Simulation",
  "show_demo_data": false,

  "speech_term_boosting": [
    "auscultation",
    "tachycardia",
    "Northgate Nursing",
    "myocardial infarction"
  ]
}
```

## Fields

| Field                  | Type                    | Notes                                                            |
| ---------------------- | ----------------------- | ---------------------------------------------------------------- |
| `portal_name`          | string                  | Shown as the portal's name; used in page titles and email.       |
| `business_description` | string                  | Internal description of the organisation. Not shown to learners. |
| `style`                | `"bold"` \| `"minimal"` | Rejected if any other value. See below.                          |
| `colors.*`             | string                  | **HSL channels without the `hsl()` wrapper** — see below.        |
| `logo_url`             | url                     | Must already be hosted. The importer does not upload files.      |
| `icon_url`             | url                     | Favicon / app icon.                                              |
| `logo_external_url`    | url                     | Where the logo links to. Omit to keep the logo unlinked.         |
| `email_sender_name`    | string                  | From-name on automated email.                                    |
| `show_demo_data`       | boolean                 | Demo data in the admin. Normally `false` for a real portal.      |
| `speech_term_boosting` | string\[]               | Terms the speech-to-text engine should favour. See below.        |

### `style`

* `bold` — Instrument Serif headings, large display type, gradient accents.
* `minimal` — headings drop to the body font at reduced sizes, gradients collapse to flat colour.

This is a single switch across the whole portal; there is no per-page override.

### Colour format

Colours are **space-separated HSL channels**, not hex and not `hsl(...)`:

```text theme={null}
"212 84% 38%"      correct
"#1565c0"          WRONG — will be stored and render as an invalid colour
"hsl(212 84% 38%)" WRONG
```

Convert hex to HSL before writing the document. The values are injected straight into CSS custom properties, so an invalid string produces an unstyled portal rather than an error.

`primary_opposite` is the text colour placed *on top of* `primary` — it needs to contrast with it, normally white or near-black. Getting this wrong is the most common cause of unreadable buttons.

### Speech term boosting

Domain vocabulary that speech-to-text would otherwise mishear. Supply the words a learner will actually say out loud that a general-purpose recogniser gets wrong: clinical terms, drug names, product names, the organisation's own name.

Constraints, enforced on import exactly as the form enforces them:

* **5 to 50 characters** per term. Shorter or longer terms are dropped and reported. This rules out most short abbreviations — `ECG` is too short to qualify, `electrocardiogram` is fine.
* **Maximum 100 terms.** Beyond that the first 100 are kept and the rest reported.
* Only supported in some speech languages. The branding tab lists which; a term set for an unsupported language is simply inert.

Quality matters more than quantity. A long list of common words dilutes the boost and can make recognition worse, so prefer 10-40 genuinely domain-specific terms over a hundred generic ones. Write each term as it is spoken, in normal capitalisation.

## Rules

* Reject the document rather than guess if `style` is not one of the two values.
* Do not invent colours to fill the palette. Supply only what the brand defines and let the rest keep their current values.
* Check contrast between `primary` and `primary_opposite` before emitting.
