# GUIDE DE MARQUE BINKS — MODE D’EMPLOI POUR AGENTS IA

Ce document s’adresse aux agents IA (Claude Code, Codex, ou tout agent qui pilote un navigateur via Playwright / CDP, ou qui exécute du JavaScript dans la page). Il explique comment utiliser le **générateur de guide de marque** (`app.html`) : importer un ou deux `.binksy`, partir de zéro, ajouter des assets, choisir un style, régler les crédits du graphiste et exporter le site, avec l’API publique `window.binksyGuide`.

Il existe en deux endroits, toujours identiques : `AGENTS.md` (Markdown, français puis anglais) et la page `agent/index.html`. Résumé court : `llms.txt`.

Points d’entrée découvrables sans JavaScript : `index.html` et `docs.html` déclarent `<link rel="alternate" type="text/markdown" href="AGENTS.md">`, `<link rel="help" href="agent/">` et `<link rel="alternate" type="text/plain" href="llms.txt">`, contiennent un commentaire HTML et un `<noscript>` qui renvoient ici. `robots.txt` autorise tout et cite `llms.txt` et `AGENTS.md`.

---

## 1. Règle fondamentale

Le générateur met en page ce que le graphiste fournit. Ce n’est **pas** un générateur de logos ni de contenus de marque.

* **N’invente jamais** un logo, un symbole, une icône, une couleur de marque, une police ou un texte de marque (nom, slogan, présentation, valeurs). Ce que l’utilisateur fournit (fichiers `.binksy`, SVG, couleurs, polices, textes, assets) **fait foi**.
* Ne redessine pas un SVG fourni et ne modifie pas sa géométrie. Ne remplace pas un fichier manquant par un fichier « approchant ».
* Un titre ou une introduction que tu proposes reste une **suggestion** : fais-la valider avant de l’enregistrer avec `setOptions`.
* **Aucune clé API, aucun service en ligne.** Le générateur n’embarque aucune IA ; c’est toi qui décides et vérifies. Ne demande jamais de clé à l’utilisateur.
* **Tout est local** : lecture des fichiers, calculs et ZIP sont faits dans le navigateur. Rien n’est envoyé à un serveur. Les réglages sont mémorisés dans le stockage du navigateur.

Après chaque action : relis `await binksyGuide.state()`. **Un appel ou un clic n’est pas une preuve de réussite.**

---

## 2. Démarrer

### Lancer le générateur

Le site déployé (dossier `dist/`, produit par `python3 build.py`) contient : `index.html` (accueil), `app.html` (le générateur), `docs.html` (documentation), `agent/index.html` (cette page), `AGENTS.md`, `llms.txt`, `robots.txt`, `demo/` (fichiers de démonstration) et `assets/`.

```bash
cd dist && python3 -m http.server 8000
# puis ouvrir http://127.0.0.1:8000/app.html
```

* **http:// recommandé** : les fichiers de démo et les URL relatives (`demo/avora.binksy`) se chargent par `fetch`.
* **file:// marche aussi** (ouvrir `app.html` directement), mais les navigateurs bloquent `fetch` sur `file://` : passe alors les fichiers en `File`/`Blob`, `ArrayBuffer`, `Uint8Array`, data URL ou base64, pas en URL.
* N’importe quel hébergement statique convient (Netlify, Vercel, GitHub Pages…).
* Utilise un profil de navigateur dédié aux tests : les réglages (style, crédits…) sont mémorisés pour l’origine.

### Deux façons de piloter

1. **API JavaScript** (recommandé) : `window.binksyGuide`, dans `app.html`.
2. **Clics** : les commandes clés portent un attribut stable `data-agent` (voir § 9).

```js
await binksyGuide.ready();
binksyGuide.version;   // "2.0.0"
binksyGuide.help();    // [{name, signature, description}]
await binksyGuide.state();
```

---

## 3. Conventions de l’API

* Toutes les méthodes sont **async** (sauf `version`, `help()` et `styles()`) et renvoient du **JSON** simple.
* **Fichiers acceptés** : `File` / `Blob`, `ArrayBuffer`, `Uint8Array`, data URL, base64, URL relative ou absolue (chargée par `fetch`) et, pour les SVG, le texte SVG lui-même.
* Une erreur rejette la promesse avec un message explicite : lis-le, corrige l’appel, puis relis `state()`.
* Toujours commencer par `await binksyGuide.ready()`.

---

## 4. Référence de `window.binksyGuide`

```js
await binksyGuide.ready();
binksyGuide.version;                 // "2.0.0"
binksyGuide.help();                  // [{name, signature, description}]
await binksyGuide.state();           // {brand, sources, versions, palette, fonts, missingFonts, assets, options, style, credits, sections, files}
await binksyGuide.importBinksy(file, 'logokit' | 'multiformats');  // kind facultatif : détecté sinon
await binksyGuide.startBlank({ brand: 'Nom' });                     // partir de zéro
await binksyGuide.addLogo(svg, { name: 'Horizontal', kind: 'horizontal'|'vertical'|'icon'|'wordmark'|'monogram'|'other' });
await binksyGuide.addColor({ hex: '#ff5500', name: 'Orange', role: 'accent' });   // rôles : primary, secondary, accent, text, background, surface, neutral, light, dark
await binksyGuide.removeColor('#ff5500');
await binksyGuide.addFont(file, { family: 'Inter', weight: 400, italic: false, licence: 'OFL', free: true });
await binksyGuide.addAsset(file, { category: 'mockup'|'social'|'print'|'web'|'other', title: 'Affiche', caption: '' }); // → {id}
await binksyGuide.removeAsset(id);
await binksyGuide.setOptions({ lang: 'fr'|'en', brand, title, intro, sections: { logo: true, formats: false, ... } });
await binksyGuide.setBrandText({ tagline, description, history, mission, vision, goals, values, audience, positioning, personality, tone, voice, voiceDont, keywords, activity });
binksyGuide.styles();                // [{id, name, description}]
await binksyGuide.setStyle('suisse');
await binksyGuide.setCredits({ name: 'Binks Design', email, instagram, website, phone, links: [{ label, url }], show: true });
await binksyGuide.exportSite({ download: true, base64: false });   // → {name, size, count, base64?}
await binksyGuide.reset();
```

| Méthode | Rôle |
| --- | --- |
| `ready()` | Attend que le générateur soit prêt. |
| `version` / `help()` | Version de l’API, liste des méthodes. |
| `state()` | État complet : marque, sources, versions du logo, palette, polices, `missingFonts` (polices de la hiérarchie sans fichier), assets, options, style, crédits, sections, fichiers. |
| `importBinksy(file, kind?)` | Importe un `.binksy` Binksy LogoKit (v2 ZIP ou ancien JSON V5) ou un `.binksynext` Binksy LogoNext (type détecté d’après le contenu : manifest + modules). Deux appels = fusion. |
| `startBlank({brand})` | Part de zéro avec un nom de marque. |
| `addLogo(svg, {name, kind})` | Ajoute un logo SVG fourni par l’utilisateur. |
| `addColor({hex, name, role})` / `removeColor(hex)` | Ajoute / retire une couleur. |
| `addFont(file, {family, weight, italic, licence, free})` | Ajoute une police (TTF, OTF, WOFF, WOFF2). `free: true` seulement si la licence est libre. Sert aussi à fournir une police listée dans `missingFonts`. |
| `addAsset(file, {category, title, caption})` / `removeAsset(id)` | Ajoute / retire un asset supplémentaire. |
| `setOptions({lang, brand, title, intro, sections})` | Langue du site, nom, titre, intro, sections affichées. |
| `setBrandText({...})` | Textes de la plateforme de marque (voir § 7 bis). |
| `styles()` / `setStyle(id)` | Liste des 10 styles / choix du style. |
| `setCredits({...})` | Crédits du graphiste. |
| `exportSite({download, base64})` | Exporte le site en ZIP. |
| `reset()` | Remet le générateur à zéro. |

---

## 5. Sources et fusion

Sources possibles, combinables :

1. **`.binksy` LogoKit** (v2 ZIP ou ancien JSON V5) : versions du logo, règles (zone de protection, tailles minimales), palette, dégradés, typographies (fichiers + licences), hiérarchie, mauvais usages, brief et textes des pages du guide (plateforme de marque), mockups.
2. **`.binksynext` Binksy LogoNext** (`modules.multiformats`, même format interne qu’un `.binksy` v2 ; type détecté d’après le contenu, l’extension ne départage que les cas ambigus) : versions, palette et rôles, QR code, formats coupés / actifs, fond.
3. **Sans `.binksy`** (« Partir de zéro ») : nom de marque + logos SVG importés à la main + couleurs (HEX, nom, rôle) + polices (TTF / OTF / WOFF / WOFF2, avec licence libre ou non).
4. **Assets supplémentaires** créés ailleurs : images (PNG, JPG, WebP, SVG, GIF) ou fichiers (PDF, etc.) rangés dans une catégorie : `mockup` (Applications), `social` (Réseaux sociaux), `print` (Imprimés), `web`, `other` (Autres). Titre et légende modifiables.

**Site exporté** : chaque déclinaison de couleur du logo en SVG, PNG (2000 px, transparent), PDF (vectoriel) et JPG (fond plein), rangée dans `telechargements/logos/<version>/<svg|png|pdf|jpg>/` ; chaque fichier du site (formats, favicons, fichiers de palette, mockups, assets) a un aperçu, un bouton Copier (image ou code / texte) et un bouton Télécharger.

**Fusion** : LogoKit prioritaire (logo, typographies, guide, mockups) ; LogoNext ajoute ses formats et le QR code ; les ajouts manuels s’ajoutent à tout.

Démonstration (marque fictive AVORA) : `demo/avora.binksy` (LogoKit), `demo/avora-ancien-logokit.binksy` (ancien format LogoKit), `demo/avora-logonext.binksynext` (LogoNext). N’utilise la démo que pour tester ou si l’utilisateur la demande.

---

## 6. Les 10 styles

| id | Nom | En bref |
| --- | --- | --- |
| `editorial` | Éditorial | grands titres, filets fins, colonnes |
| `suisse` | Suisse | grille stricte, fer à gauche |
| `minimal` | Minimal | beaucoup de blanc, un seul accent |
| `noir` | Noir | fond sombre, contrastes forts |
| `magazine` | Magazine | images en grand, titres superposés |
| `brutal` | Brutaliste | bordures épaisses, ombres franches |
| `doux` | Doux | coins arrondis, teintes douces |
| `technique` | Technique | grille de plan, cotes, monospace |
| `aplat` | Aplat | couleur de marque en grands aplats |
| `classique` | Classique | luxe, serif |

`binksyGuide.styles()` fait foi pour les descriptions. Le style change la mise en page, pas l’identité.

---

## 7. Sections du site

`intro`, `marque`, `ton`, `logo`, `declinaisons`, `protection`, `tailles`, `interdits`, `couleurs`, `typographie`, `applications`, `assets`, `formats`, `ressources`. Active ou désactive-les avec `setOptions({ sections: { formats: false } })`. La section des bons et mauvais usages a pour id `interdits` (pas `usages`).

### 7 bis. Plateforme de marque (`setBrandText`)

Textes affichés dans **Introduction** (signature `tagline`, présentation `description`, activité `activity`), **La marque** (`marque` : `history` histoire, `mission`, `vision`, `goals` objectifs, `values` valeurs, `audience` public, `positioning` positionnement) et **Ton et voix** (`ton` : `personality` personnalité, `tone` ton, `keywords` mots-clés, `voice` « on dit », `voiceDont` « on évite »). Panneau « Plateforme de marque » dans l’interface (`data-agent="brand-text"`).

```js
await binksyGuide.setBrandText({
  tagline: 'Texte fourni par l’utilisateur',
  values: 'Valeur 1\nValeur 2\nValeur 3',   // un élément par ligne : values, goals, keywords, voice, voiceDont
  vision: '',                                // chaîne vide = bloc masqué
  mission: null,                             // null / undefined = texte du .binksy rétabli
});
```

* Les textes viennent du brief et des pages du guide d’un `.binksy` LogoKit, ou de l’utilisateur. **Chaque bloc n’apparaît que s’il contient du texte.**
* **N’écris jamais un texte de marque de ton cru** : ne passe à `setBrandText` que des textes fournis ou validés par l’utilisateur. Pour masquer un texte, passe `''`.

### 7 ter. Polices à importer (`missingFonts`)

Si la hiérarchie typographique utilise une police sans fichier, l’interface affiche « Polices à importer » (un bouton par famille) et `state().missingFonts` les liste. Demande le fichier à l’utilisateur puis appelle `addFont(file, { family: 'Nom exact de la famille' })`. Ne remplace jamais une police manquante par une autre.

---

## 8. Exemple complet

### Dans la page (JavaScript)

```js
const g = window.binksyGuide;
await g.ready();
await g.reset();

// 1. Sources : la démo AVORA (LogoKit + LogoNext = fusion)
await g.importBinksy('demo/avora.binksy', 'logokit');
await g.importBinksy('demo/avora-logonext.binksynext', 'multiformats');
let s = await g.state();
console.assert(s.brand && s.versions.length > 0, 'aucune version de logo');

// 2. Un asset fourni par l’utilisateur (ici en data URL)
const { id } = await g.addAsset(posterDataUrl, { category: 'print', title: 'Affiche de lancement', caption: '' });

// 3. Réglages, style et crédits
await g.setOptions({ lang: 'fr', sections: { formats: true, ressources: true } });
await g.setStyle('suisse');
await g.setCredits({
  name: 'Binks Design',
  email: 'graphistedubinks@gmail.com',
  instagram: 'https://www.instagram.com/graphiste.binks/',
  show: true,
});

// 4. Vérifier
s = await g.state();
console.assert(s.style === 'suisse' || s.style?.id === 'suisse');
console.assert(s.assets.some((a) => a.id === id));

// 5. Exporter sans boîte de téléchargement
const out = await g.exportSite({ base64: true });   // {name, size, count, base64}
```

### Depuis Playwright (Python)

```python
import base64, pathlib
from playwright.sync_api import sync_playwright

poster = 'data:image/png;base64,' + base64.b64encode(pathlib.Path('affiche.png').read_bytes()).decode()  # fichier de l’utilisateur

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()
    errors = []
    page.on('pageerror', lambda e: errors.append(str(e)))
    page.goto('http://127.0.0.1:8000/app.html')
    page.wait_for_function('window.binksyGuide')
    out = page.evaluate('''async (poster) => {
      const g = window.binksyGuide;
      await g.ready(); await g.reset();
      await g.importBinksy('demo/avora.binksy', 'logokit');
      await g.importBinksy('demo/avora-logonext.binksynext', 'multiformats');
      await g.addAsset(poster, { category: 'print', title: 'Affiche de lancement', caption: '' });
      await g.setStyle('suisse');
      await g.setCredits({ name: 'Binks Design', email: 'graphistedubinks@gmail.com',
                           instagram: 'https://www.instagram.com/graphiste.binks/', show: true });
      const state = await g.state();
      const zip = await g.exportSite({ base64: true });
      return { state, zip };
    }''', poster)
    pathlib.Path(out['zip']['name']).write_bytes(base64.b64decode(out['zip']['base64']))
    print(out['zip']['count'], 'fichiers,', out['zip']['size'], 'octets', '| erreurs :', errors)
    browser.close()
```

### Partir de zéro

```js
await g.reset();
await g.startBlank({ brand: 'Nom fourni par l’utilisateur' });
await g.addLogo(svgTexteFourni, { name: 'Horizontal', kind: 'horizontal' });
await g.addColor({ hex: '#1d1d1b', name: 'Noir', role: 'primary' });
await g.addFont(fontBytes, { family: 'Inter', weight: 400, italic: false, licence: 'OFL', free: true });
```

---

## 9. Repères `data-agent`

| Attribut | Commande |
| --- | --- |
| `import-logokit` | Importer un `.binksy` LogoKit |
| `import-multiformats` | Importer un `.binksynext` LogoNext |
| `start-blank` | Partir de zéro |
| `add-logo` | Ajouter un logo SVG |
| `add-color` | Ajouter une couleur |
| `add-font` | Ajouter une police |
| `add-asset` | Ajouter un asset supplémentaire |
| `brand-text` | Panneau « Plateforme de marque » (textes du site) |
| `style-<id>` | Choisir un style (`style-suisse`, `style-noir`…) |
| `export` | Exporter le site |

Exemple Playwright : `page.click('[data-agent="style-suisse"]')`, `page.set_input_files('[data-agent="import-logokit"] input[type=file], input[data-agent="import-logokit"]', 'avora.binksy')` (selon que l’attribut est sur le bouton ou sur le champ fichier — vérifie dans le DOM).

---

## 10. Vérifications avant de rendre le travail

1. `state().brand` est le nom fourni par l’utilisateur (aucune faute, rien d’inventé).
2. `state().sources` liste les sources attendues ; `versions` contient chaque version du logo fournie.
3. `palette` : chaque couleur a un nom et un rôle corrects.
4. `fonts` : `free: true` seulement pour une licence réellement libre (OFL, Apache…). Une police commerciale est affichée dans le site mais jamais téléchargeable.
5. `assets` : bonne catégorie, titre et légende relus.
6. `options` : langue, titre, intro (validés par l’utilisateur), sections. Textes de marque : uniquement ceux du `.binksy` ou de l’utilisateur ; `missingFonts` vide (ou signalé à l’utilisateur).
7. `style` et `credits` comme demandé.
8. `exportSite({ base64: true })` renvoie `count > 0` et `size > 0`. Décompresse le ZIP, ouvre `index.html` (http et `file://`) : aucune erreur console, logos visibles, chaque version de couleur présente dans `telechargements/logos/<version>/{svg,png,pdf,jpg}/`.
9. Capture d’écran de l’aperçu ou du site exporté, en bureau et en mobile.

---

## 11. Limites et erreurs

* **Formats non produits ici** : tickets thermiques, étiquettes Zebra, vidéo, EPS, jeux complets d’icônes Android / macOS → ils restent dans Binksy LogoNext.
* **CMJN** : conversion simple, sans profil ICC.
* **Polices non libres** : affichées (intégrées) mais pas téléchargeables.
* **`fetch` impossible en `file://`** : passe les fichiers en octets, data URL ou base64.
* **Style inconnu** : `setStyle` refuse un id hors de `styles()`.
* **Catégorie d’asset inconnue** : seules `mockup`, `social`, `print`, `web`, `other` sont acceptées.
* **Fichier qui n’est pas un `.binksy`** : l’import échoue ; demande le bon fichier à l’utilisateur au lieu de reconstruire la marque.
* **Téléchargement bloqué** (navigateur sans interface) : utilise `exportSite({ base64: true })`.

Si le comportement réel diffère de ce document, décris l’écart, fie-toi à `binksyGuide.help()` et à `state()`, et adapte-toi sans contourner les protections.

---

## 12. Logique à suivre pour chaque tâche

1. Comprendre la demande et ce que l’utilisateur a déjà fourni (ne redemande jamais une information donnée).
2. `await binksyGuide.ready()` puis `state()`.
3. Poser en un seul message les questions vraiment nécessaires (fichiers manquants, style, crédits) ; ne jamais combler un manque en inventant.
4. Agir par l’API (ou les repères `data-agent`).
5. Vérifier `state()`, puis le rendu (aperçu, export).
6. Exporter et rendre le ZIP avec un résumé : sources, style, sections, nombre de fichiers.


### 7 ter. Sections : ordre, textes, blocs et sections personnalisées

Chaque section (intégrée, reprise d’une page du guide LogoKit ou créée à la main) peut être renommée, recevoir un chapeau, être masquée, déplacée et enrichie de blocs. Les pages LogoKit sans équivalent (photographie, iconographie, réseaux sociaux, contact, construction, page vierge…) arrivent comme sections `.binksy`.

```js
binksyGuide.sections();                                   // [{id, title, lead, enabled, builtin, from, blocks:[…]}]
await binksyGuide.setSection('logo', { title: 'Notre logo', lead: '…', enabled: true, index: 2 });
await binksyGuide.setSection('logo', { reset: true });     // textes et blocs d’origine
const s = await binksyGuide.addSection({ title: 'Motion', lead: '…', after: 'logo', blocks: [
  { type: 'video', file: videoFile, caption: 'Animation' },
  { type: 'embed', url: 'https://vimeo.com/…', caption: 'Making-of' },
  { type: 'text', title: 'Durée', text: '2 secondes, jamais plus.' }
]});
await binksyGuide.addBlock('couleurs', { type: 'image', file: imageFile, caption: 'Ambiance' });
await binksyGuide.updateBlock(s.id, blockId, { caption: '…' });
await binksyGuide.removeBlock(s.id, blockId);
await binksyGuide.moveSection(s.id, 0);
await binksyGuide.removeSection(s.id);                    // sections personnalisées seulement
```

* Types de blocs : `text` (intertitre + texte, paragraphes séparés par une ligne vide), `image` (PNG, JPG, WebP, GIF, SVG, AVIF), `video` (MP4, WebM, MOV), `embed` (lien YouTube ou Vimeo, lu en ligne).
* Images et vidéos sont copiées **octet pour octet** : téléchargement en résolution d’origine dans `telechargements/sections/<section>/`. Les mises en situation et assets ajoutés restent aussi à leur résolution d’origine.
* Une section personnalisée sans chapeau ni bloc n’apparaît pas. Même règle : n’invente aucun texte.

<!-- EN -->

# BINKS BRAND GUIDE — AI AGENT GUIDE

This document is for AI agents (Claude Code, Codex, or any agent driving a browser through Playwright / CDP, or running JavaScript in the page). It explains how to use the **brand guide generator** (`app.html`): import one or two `.binksy` files, start from scratch, add assets, pick a style, set the designer credits and export the website, through the public `window.binksyGuide` API.

It lives in two places, always identical: `AGENTS.md` (Markdown, French then English) and the `agent/index.html` page. Short summary: `llms.txt`.

Entry points discoverable without JavaScript: `index.html` and `docs.html` declare `<link rel="alternate" type="text/markdown" href="AGENTS.md">`, `<link rel="help" href="agent/">` and `<link rel="alternate" type="text/plain" href="llms.txt">`, and carry an HTML comment and a `<noscript>` pointing here. `robots.txt` allows everything and cites `llms.txt` and `AGENTS.md`.

---

## 1. Golden rule

The generator lays out what the designer provides. It is **not** a logo or brand-copy generator.

* **Never invent** a logo, symbol, icon, brand colour, typeface or brand text (name, tagline, description, values). What the user provides (`.binksy` files, SVGs, colours, fonts, copy, assets) **is the source of truth**.
* Do not redraw a provided SVG or alter its geometry. Do not replace a missing file with a “close enough” one.
* A title or intro you propose is a **suggestion**: get it approved before saving it with `setOptions`.
* **No API key, no online service.** The generator embeds no AI; you decide and verify. Never ask the user for a key.
* **Everything is local**: file reading, processing and ZIP creation happen in the browser. Nothing is sent to a server. Settings are remembered in browser storage.

After every action: re-read `await binksyGuide.state()`. **A call or a click is not proof of success.**

---

## 2. Getting started

### Run the generator

The deployed site (`dist/` folder, produced by `python3 build.py`) contains: `index.html` (home), `app.html` (the generator), `docs.html` (documentation), `agent/index.html` (this page), `AGENTS.md`, `llms.txt`, `robots.txt`, `demo/` (demo files) and `assets/`.

```bash
cd dist && python3 -m http.server 8000
# then open http://127.0.0.1:8000/app.html
```

* **http:// preferred**: demo files and relative URLs (`demo/avora.binksy`) are loaded with `fetch`.
* **file:// also works** (open `app.html` directly), but browsers block `fetch` on `file://`: pass files as `File`/`Blob`, `ArrayBuffer`, `Uint8Array`, data URL or base64, not as URLs.
* Any static host works (Netlify, Vercel, GitHub Pages…).
* Use a dedicated browser profile for tests: settings (style, credits…) are remembered per origin.

### Two ways to drive it

1. **JavaScript API** (recommended): `window.binksyGuide`, in `app.html`.
2. **Clicks**: key controls carry a stable `data-agent` attribute (see § 9).

```js
await binksyGuide.ready();
binksyGuide.version;   // "2.0.0"
binksyGuide.help();    // [{name, signature, description}]
await binksyGuide.state();
```

---

## 3. API conventions

* Every method is **async** (except `version`, `help()` and `styles()`) and returns plain **JSON**.
* **Accepted files**: `File` / `Blob`, `ArrayBuffer`, `Uint8Array`, data URL, base64, relative or absolute URL (loaded with `fetch`) and, for SVGs, the SVG text itself.
* An error rejects the promise with an explicit message: read it, fix the call, then re-read `state()`.
* Always start with `await binksyGuide.ready()`.

---

## 4. `window.binksyGuide` reference

```js
await binksyGuide.ready();
binksyGuide.version;                 // "2.0.0"
binksyGuide.help();                  // [{name, signature, description}]
await binksyGuide.state();           // {brand, sources, versions, palette, fonts, missingFonts, assets, options, style, credits, sections, files}
await binksyGuide.importBinksy(file, 'logokit' | 'multiformats');  // kind optional: detected otherwise
await binksyGuide.startBlank({ brand: 'Name' });                    // start from scratch
await binksyGuide.addLogo(svg, { name: 'Horizontal', kind: 'horizontal'|'vertical'|'icon'|'wordmark'|'monogram'|'other' });
await binksyGuide.addColor({ hex: '#ff5500', name: 'Orange', role: 'accent' });   // roles: primary, secondary, accent, text, background, surface, neutral, light, dark
await binksyGuide.removeColor('#ff5500');
await binksyGuide.addFont(file, { family: 'Inter', weight: 400, italic: false, licence: 'OFL', free: true });
await binksyGuide.addAsset(file, { category: 'mockup'|'social'|'print'|'web'|'other', title: 'Poster', caption: '' }); // → {id}
await binksyGuide.removeAsset(id);
await binksyGuide.setOptions({ lang: 'fr'|'en', brand, title, intro, sections: { logo: true, formats: false, ... } });
await binksyGuide.setBrandText({ tagline, description, history, mission, vision, goals, values, audience, positioning, personality, tone, voice, voiceDont, keywords, activity });
binksyGuide.styles();                // [{id, name, description}]
await binksyGuide.setStyle('suisse');
await binksyGuide.setCredits({ name: 'Binks Design', email, instagram, website, phone, links: [{ label, url }], show: true });
await binksyGuide.exportSite({ download: true, base64: false });   // → {name, size, count, base64?}
await binksyGuide.reset();
```

| Method | Purpose |
| --- | --- |
| `ready()` | Waits until the generator is ready. |
| `version` / `help()` | API version, list of methods. |
| `state()` | Full state: brand, sources, logo versions, palette, fonts, `missingFonts` (hierarchy fonts with no file), assets, options, style, credits, sections, files. |
| `importBinksy(file, kind?)` | Imports a LogoKit `.binksy` (v2 ZIP or legacy JSON V5) or a Binksy LogoNext `.binksynext` (type detected from the content: manifest + modules). Two calls = merge. |
| `startBlank({brand})` | Starts from scratch with a brand name. |
| `addLogo(svg, {name, kind})` | Adds a logo SVG provided by the user. |
| `addColor({hex, name, role})` / `removeColor(hex)` | Adds / removes a colour. |
| `addFont(file, {family, weight, italic, licence, free})` | Adds a font (TTF, OTF, WOFF, WOFF2). `free: true` only for a genuinely free licence. Also fills a font listed in `missingFonts`. |
| `addAsset(file, {category, title, caption})` / `removeAsset(id)` | Adds / removes an extra asset. |
| `setOptions({lang, brand, title, intro, sections})` | Site language, name, title, intro, visible sections. |
| `setBrandText({...})` | Brand platform copy (see § 7 bis). |
| `styles()` / `setStyle(id)` | List of the 10 styles / pick one. |
| `setCredits({...})` | Designer credits. |
| `exportSite({download, base64})` | Exports the website as a ZIP. |
| `reset()` | Resets the generator. |

---

## 5. Sources and merging

Possible sources, combinable:

1. **LogoKit `.binksy`** (v2 ZIP or legacy JSON V5): logo versions, rules (clear space, minimum sizes), palette, gradients, typefaces (files + licences), hierarchy, misuses, brief and guide-page copy (brand platform), mockups.
2. **Binksy LogoNext `.binksynext`** (`modules.multiformats`, same internal format as `.binksy` v2): versions, palette and roles, QR code, cut / active formats, background.
3. **No `.binksy`** (“Start from scratch”): brand name + hand-imported SVG logos + colours (HEX, name, role) + fonts (TTF / OTF / WOFF / WOFF2, free licence or not).
4. **Extra assets** made elsewhere: images (PNG, JPG, WebP, SVG, GIF) or files (PDF, etc.) filed in a category: `mockup` (Applications), `social` (Social media), `print` (Print), `web`, `other` (Other). Editable title and caption.

**Exported site**: every logo colour version in SVG, PNG (2000 px, transparent), PDF (vector) and JPG (solid background), sorted in `telechargements/logos/<version>/<svg|png|pdf|jpg>/`; every file in the site (formats, favicons, palette files, mockups, assets) has a preview, a Copy button (image or code / text) and a Download button.

**Merge**: LogoKit wins (logo, typefaces, guide, mockups); LogoNext adds its formats and the QR code; manual additions are added on top of everything.

Demo (fictional brand AVORA): `demo/avora.binksy` (LogoKit), `demo/avora-ancien-logokit.binksy` (legacy Binksy LogoKit format), `demo/avora-logonext.binksynext` (LogoNext). Use the demo only for testing or when the user asks for it.

---

## 6. The 10 styles

| id | Name | In short |
| --- | --- | --- |
| `editorial` | Éditorial (Editorial) | big headlines, hairlines, columns |
| `suisse` | Suisse (Swiss) | strict grid, flush left |
| `minimal` | Minimal | lots of white, one accent |
| `noir` | Noir (Dark) | dark background, strong contrast |
| `magazine` | Magazine | large images, overlapping titles |
| `brutal` | Brutaliste (Brutalist) | thick borders, hard shadows |
| `doux` | Doux (Soft) | rounded corners, soft tints |
| `technique` | Technique (Technical) | blueprint grid, dimensions, monospace |
| `aplat` | Aplat (Flat colour) | brand colour in large flat areas |
| `classique` | Classique (Classic) | luxury, serif |

`binksyGuide.styles()` is authoritative for descriptions. A style changes the layout, not the identity.

---

## 7. Site sections

`intro`, `marque` (the brand), `ton` (tone of voice), `logo`, `declinaisons` (colour variants), `protection` (clear space), `tailles` (minimum sizes), `interdits` (do’s and don’ts), `couleurs` (colours), `typographie`, `applications`, `assets`, `formats`, `ressources` (downloads). Toggle them with `setOptions({ sections: { formats: false } })`. The do’s and don’ts section id is `interdits` (not `usages`).

### 7 bis. Brand platform (`setBrandText`)

Copy shown in **Introduction** (`tagline`, `description`, `activity`), **The brand** (`marque`: `history`, `mission`, `vision`, `goals`, `values`, `audience`, `positioning`) and **Tone of voice** (`ton`: `personality`, `tone`, `keywords`, `voice` “we say”, `voiceDont` “we avoid”). “Plateforme de marque” panel in the UI (`data-agent="brand-text"`).

```js
await binksyGuide.setBrandText({
  tagline: 'Text provided by the user',
  values: 'Value 1\nValue 2\nValue 3',   // one item per line: values, goals, keywords, voice, voiceDont
  vision: '',                             // empty string = block hidden
  mission: null,                          // null / undefined = .binksy text restored
});
```

* Copy comes from the brief and guide pages of a LogoKit `.binksy`, or from the user. **Each block only appears if it has text.**
* **Never write brand copy yourself**: only pass text the user provided or approved to `setBrandText`. To hide a text, pass `''`.

### 7 ter. Fonts to import (`missingFonts`)

If the type hierarchy uses a font with no file, the UI shows “Polices à importer” (one button per family) and `state().missingFonts` lists them. Ask the user for the file, then call `addFont(file, { family: 'Exact family name' })`. Never substitute another font.

---

## 8. Complete example

### In the page (JavaScript)

```js
const g = window.binksyGuide;
await g.ready();
await g.reset();

// 1. Sources: the AVORA demo (LogoKit + LogoNext = merge)
await g.importBinksy('demo/avora.binksy', 'logokit');
await g.importBinksy('demo/avora-logonext.binksynext', 'multiformats');
let s = await g.state();
console.assert(s.brand && s.versions.length > 0, 'no logo version');

// 2. An asset provided by the user (here as a data URL)
const { id } = await g.addAsset(posterDataUrl, { category: 'print', title: 'Launch poster', caption: '' });

// 3. Settings, style and credits
await g.setOptions({ lang: 'en', sections: { formats: true, ressources: true } });
await g.setStyle('suisse');
await g.setCredits({
  name: 'Binks Design',
  email: 'graphistedubinks@gmail.com',
  instagram: 'https://www.instagram.com/graphiste.binks/',
  show: true,
});

// 4. Verify
s = await g.state();
console.assert(s.style === 'suisse' || s.style?.id === 'suisse');
console.assert(s.assets.some((a) => a.id === id));

// 5. Export without a download dialog
const out = await g.exportSite({ base64: true });   // {name, size, count, base64}
```

### From Playwright (Python)

```python
import base64, pathlib
from playwright.sync_api import sync_playwright

poster = 'data:image/png;base64,' + base64.b64encode(pathlib.Path('poster.png').read_bytes()).decode()  # the user's file

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()
    errors = []
    page.on('pageerror', lambda e: errors.append(str(e)))
    page.goto('http://127.0.0.1:8000/app.html')
    page.wait_for_function('window.binksyGuide')
    out = page.evaluate('''async (poster) => {
      const g = window.binksyGuide;
      await g.ready(); await g.reset();
      await g.importBinksy('demo/avora.binksy', 'logokit');
      await g.importBinksy('demo/avora-logonext.binksynext', 'multiformats');
      await g.addAsset(poster, { category: 'print', title: 'Launch poster', caption: '' });
      await g.setStyle('suisse');
      await g.setCredits({ name: 'Binks Design', email: 'graphistedubinks@gmail.com',
                           instagram: 'https://www.instagram.com/graphiste.binks/', show: true });
      const state = await g.state();
      const zip = await g.exportSite({ base64: true });
      return { state, zip };
    }''', poster)
    pathlib.Path(out['zip']['name']).write_bytes(base64.b64decode(out['zip']['base64']))
    print(out['zip']['count'], 'files,', out['zip']['size'], 'bytes', '| errors:', errors)
    browser.close()
```

### Starting from scratch

```js
await g.reset();
await g.startBlank({ brand: 'Name given by the user' });
await g.addLogo(providedSvgText, { name: 'Horizontal', kind: 'horizontal' });
await g.addColor({ hex: '#1d1d1b', name: 'Black', role: 'primary' });
await g.addFont(fontBytes, { family: 'Inter', weight: 400, italic: false, licence: 'OFL', free: true });
```

---

## 9. `data-agent` hooks

| Attribute | Control |
| --- | --- |
| `import-logokit` | Import a LogoKit `.binksy` |
| `import-multiformats` | Import a LogoNext `.binksynext` |
| `start-blank` | Start from scratch |
| `add-logo` | Add an SVG logo |
| `add-color` | Add a colour |
| `add-font` | Add a font |
| `add-asset` | Add an extra asset |
| `brand-text` | “Plateforme de marque” panel (site copy) |
| `style-<id>` | Pick a style (`style-suisse`, `style-noir`…) |
| `export` | Export the website |

Playwright example: `page.click('[data-agent="style-suisse"]')`, `page.set_input_files('[data-agent="import-logokit"] input[type=file], input[data-agent="import-logokit"]', 'avora.binksy')` (depending on whether the attribute sits on the button or on the file input — check the DOM).

---

## 10. Checks before handing back

1. `state().brand` is the name the user gave (no typo, nothing invented).
2. `state().sources` lists the expected sources; `versions` holds every logo version provided.
3. `palette`: every colour has a correct name and role.
4. `fonts`: `free: true` only for a genuinely free licence (OFL, Apache…). A commercial font is displayed on the site but never downloadable.
5. `assets`: right category, title and caption proof-read.
6. `options`: language, title, intro (approved by the user), sections. Brand copy: only from the `.binksy` or the user; `missingFonts` empty (or reported to the user).
7. `style` and `credits` as requested.
8. `exportSite({ base64: true })` returns `count > 0` and `size > 0`. Unzip, open `index.html` (http and `file://`): no console error, logos visible, every colour version present in `telechargements/logos/<version>/{svg,png,pdf,jpg}/`.
9. Screenshot of the preview or of the exported site, desktop and mobile.

---

## 11. Limits and errors

* **Formats not produced here**: thermal receipts, Zebra labels, video, EPS, full Android / macOS icon sets → they stay in Binksy LogoNext.
* **CMYK**: simple conversion, no ICC profile.
* **Non-free fonts**: displayed (embedded) but not downloadable.
* **`fetch` impossible on `file://`**: pass files as bytes, data URL or base64.
* **Unknown style**: `setStyle` rejects an id not listed by `styles()`.
* **Unknown asset category**: only `mockup`, `social`, `print`, `web`, `other` are accepted.
* **File that is not a `.binksy`**: the import fails; ask the user for the right file instead of rebuilding the brand.
* **Download blocked** (headless browser): use `exportSite({ base64: true })`.

If the actual behaviour differs from this document, describe the gap, trust `binksyGuide.help()` and `state()`, and adapt without bypassing safeguards.

---

## 12. Logic for every task

1. Understand the request and what the user already provided (never ask again for information already given).
2. `await binksyGuide.ready()` then `state()`.
3. Ask, in a single message, only the questions that matter (missing files, style, credits); never fill a gap by inventing.
4. Act through the API (or the `data-agent` hooks).
5. Check `state()`, then the rendering (preview, export).
6. Export and hand back the ZIP with a summary: sources, style, sections, file count.

### 7 ter. Sections: order, copy, blocks and custom sections

Every section (built-in, imported from a LogoKit guide page, or created by hand) can be renamed, given a lead, hidden, moved and extended with blocks. LogoKit pages with no built-in match (photography, icons, social, contact, construction, blank page…) arrive as `.binksy` sections.

```js
binksyGuide.sections();
await binksyGuide.setSection('logo', { title, lead, enabled, index, reset });
await binksyGuide.addSection({ title, lead, after, blocks: [{ type: 'text' | 'image' | 'video' | 'embed', title, text, file, url, caption }] });
await binksyGuide.addBlock(sectionId, { type, file, caption, index });
await binksyGuide.updateBlock(sectionId, blockId, { ... });
await binksyGuide.removeBlock(sectionId, blockId);
await binksyGuide.moveSection(sectionId, index);
await binksyGuide.removeSection(sectionId);               // custom sections only
```

* Images and videos are copied byte for byte and downloadable at their original resolution from `telechargements/sections/<section>/`.
* A custom section with neither lead nor block is not shown. Never invent copy.

