# Changelog

All notable changes to Clone Architect are documented here.
Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [2.7.0] — 2026-05-30

> **Signature-capture fixes — the root cause of reskins not matching real sites.** A 6-agent ground-truthed audit (real screenshots vs DESIGN.md vs produced site) found avg structure-fidelity = 34/100 and attributed ~64% to Clone Architect being blind to the 2-3 attributes that *define* a design. This release fixes the extractor + generator signature blindness. Report: `docs/comparison/rootcause-real-vs-produced.md`.

### Fixed
- **A.1 Display-font signature** (`extract.ts` `extractDisplaySignature`) — the hero headline is often NOT the largest `<h*>` (it's a styled `<div>`/`<span>`/image text), so the serif-vs-sans signature was lost. Now detects the display face by **visual prominence** (above-fold text node with largest fontSize × rendered width) and classifies serif/sans/italic. *Verified: hyperliquid now captures `Teodor 90px serif` (was reported "Inter").*
- **A.3 Content-based section classification** (`extract.ts:966+`) — replaced tag/class string-matching (which defaulted to `unknown` on Webflow/Shopify div-soup) with content signals (text density, image ratio, grid cols, heading size, full-bleed, animation, above-fold) → named bands (hero/marquee/feature/pricing/faq/gallery/logo-strip…). *Verified: switchcollective §13 `unknown` dropped 55%→18%.*
- **A.4 Background-treatment capture** (`extract.ts`) — per-band `bgTreatment` (mesh-gradient/linear-gradient/radial/image) + `tone: dark`, surfaced in §13. *Verified: switchcollective mesh-gradient now captured (was "completely missed").*
- **A.2 Sanitize garbage reads** (`generate-design-md.ts`) — `fontWeight: 0`/NaN no longer rationalized as "remarkably light weight (0)"; weight claims require a valid 100–900 weight.
- **B.8 Display signature leads §1** (`generate-design-md.ts`) — narrative now opens with "Headlines are set in **{display}**, a {serif/sans} display face… Body text is set in **{body}**", instead of hiding the display by anchoring on the body font.

### Verified
- `tsc --noEmit` clean; full suite **108 passed / 0 failed** (goldens refreshed). Per-fix re-extraction assertions on hyperliquid + switchcollective. Remaining audit items (A.5 lazy-load, A.6 nav, A.7 imagery-binding, B.9 full consistency-lint, C build hard-gates) tracked for the re-prove pass.

## [2.6.0] — 2026-05-30

> **Correctness over volume — fixes from the audit-max vs getdesign.md (5 random brands).** A ground-truthed audit (screenshots + raw-css) showed the volume scorer said "CA wins 5/5" while CA actually got load-bearing facts wrong: invented primary colors, inflated counts, wrong-page captures, self-contradictions. These 8 fixes target correctness. Full report: `docs/comparison/auditmax-vs-gd-20260530.md`.

### Fixed
- **Primary/accent selection** (`tokenize.ts`) — accent.primary now comes from the most prominent **rendered CTA button** background, not frequency/var-name heuristics. **Hard-blocks colors absent from `allColors`** (miro `#00b473`, a never-painted `--tw-color-success-accent`, was the brand color → now `#fde050`; notion `#62aef0` var → `#455dd3` real CTA). Unrendered colors allowed only with a positive brand-var score (revolut `#494fdf`). Penalty regex extended to `success|positive|info|…`.
- **Canvas dark/light** (`tokenize.ts` + `generate-design-md.ts`) — directional rescue: when `<body>` is dark but the **dominant rendered section area is light**, the canvas is light (notion: body `#191918`, content white → "pure-white canvas", was "commits fully to dark-mode"). Never flips light→dark, so light sites with dark bands (miro/revolut) are unaffected; genuinely dark sites (linear) keep dark. Narrative + §8 Do/Don'ts now both read the tokenized canvas (no more §1-vs-§8 contradiction).
- **Wrong-surface detector** (`generate-design-md.ts`) — flags `⚠️ LOW-CONFIDENCE CAPTURE` when ≥1 strong editor signal (filename/line-number/editor-chrome button labels) + ≥2 signals fire (posthog captured its in-page code-editor demo, not the marketing site).
- **Honest color count** (`generate-design-md.ts`) — separates "**N rendered on the page**" from "**M declared in design tokens**" (was "111 distinct colors detected on the live page" when ~52 were rendered). Token-only colors marked `(token)`. **Third-party logo colors filtered** (Google/NVIDIA/Shopify/…).
- **Consistency lint** (`generate-design-md.ts` + `tokenize.ts`) — narrative font = tokenized font (kills "narrative says Open Sans, YAML says X"); generic body fonts fall back to the real custom font in heading stacks (miro → Roobert PRO, was "sans-serif"); component vocabulary dedups names (no "Primary Brand, Primary Brand, Primary Brand"); **unique YAML component keys**; empty wrapper components (no padding/radius/border/shadow) purged; e-commerce product-card template gated to genuine product grids (≥4 cards) so it stops being stamped on stripe/notion.
- **Narrative honesty** (`generate-design-md.ts`) — WCAG contrast formula `(L1+0.05)/(L2+0.05)` caps at 21:1 (was printing impossible "100:1"/"94.1:1"); removed templated filler ("not the simulated drop-shadow of cheap interfaces", "recedes into transparency"); softened/removed the blanket "no approximation, no hallucination" claim (header, frontmatter, footer).

### Verified
- `tsc --noEmit` clean; full suite **108 passed / 0 failed** (goldens refreshed). Per-brand: notion accent `#62aef0`→`#455dd3`, canvas dark→**white**, count "111"→"42 rendered + 70 token"; miro `#00b473` (invented)→`#fde050`, font sans-serif→Roobert PRO; posthog **flagged** app-shell; revolut/stripe/linear/airbnb no regression. Independent re-audit of notion: primary + canvas + count + §8 contradiction **resolved**.

## [2.5.1] — 2026-05-30

> **Prose-quality fix — kill functional-role names leaking into editorial narrative.** Brand-aware naming maps a site's CSS vars to names (`--color-indigo` → "Indigo"), great for hue/brand words but robotic when the var is a *positional role*: `--color-text-quaternary` → "Border: **Text Quaternary**", `--color-bg-primary` → "Background: **Bg Primary**", "Body text reads in **Text Primary**". getdesign.md keeps role names in its structured `colors:` block but uses appearance names in narrative — CA now does the same.

### Fixed
- **`scripts/generate-design-md.ts`** — Added `proseColorName`/`proseName` + `isGenericRoleName` (true when a name is composed *entirely* of functional-role tokens — text/bg/surface/border + primary/secondary/…/quaternary). When a resolved name is all-role, prose falls back to the nearest-named-color **appearance** ("Off-Cream", "Jet Black", "Dim Gray"); real hue/brand words (Indigo, Rausch, Hof) are untouched. Applied to the 3 human-readable surfaces only — §1 Visual Theme narrative, §1 Key Characteristics, Agent Prompt Guide (quick ref + prompts + iteration guide). The structured §2 Color Palette table keeps role names by design.
- **`scripts/generate-design-md.ts`** — Grammar: article now agrees with the canvas descriptor ("The canvas is **an** inky black surface", was "a inky").

### Verified
- Linear: "Body text reads in ~~Text Primary~~ → **Off-Cream**", "Border: ~~Text Quaternary~~ → **Dim Gray**", "**Indigo**" preserved. Airbnb: "**Rausch**"/"**Hof**" preserved (not over-stripped). 0 role-name leaks in prose across linear/stripe/mongodb; §2 table retention intact.
- `tsc --noEmit` clean; full suite **108 passed / 0 failed**. Goldens refreshed — they were stale at v2.4.0 and already diverged 82% from v2.5.0 output *before* this change (position-locked diff cascades on the v2.5.0 Full-Extracted-Palette insertion); this fix's own footprint is 0.7–2.2%, under the 5% threshold.

## [2.5.0] — 2026-05-30

> **Color dimension fix — getdesign.md now wins 0 dimensions on 0 brands.** The comparison scorer counts distinct documented hex colors; CA *extracted* ~45 colors per brand but the capped category lists (`bg≤5`, `text≤5`, `accent≤6`…) only *surfaced* ~18 — so on colorful brands (mongodb, theverge, starbucks, mintlify) getdesign.md documented more and won the color sub-dimension. Fix surfaces the full extracted palette without inventing anything.

### Fixed
- **`scripts/generate-design-md.ts`** — Added a **"Full Extracted Palette"** block to the active §2 builder. Sources: `rawData.allColors` ∪ color literals harvested from `cssCustomProperties` values (sites like theverge keep their palette in token vars, not on rendered elements). Root cause of the harvest miss: the in-scope `parseRgb` (css-helpers) matches `rgb()` only and rejected theverge's 160+ pure-hex vars — gate replaced with a hex-or-rgb test. Capped at 32, dedupes against categorized colors.
- **`scripts/generate-showcase.ts`** — Mirror block in the public showcase color section so the deployed site reflects the full palette (passes `rawDataDesktop` to `buildColorSection`).

### Result (docs/comparison/ca-vs-gd-final.json, 22 brands w/ both sides)
- Color dimension: CA 14 / **GD 4** / tie 4 → **CA 22 / GD 0 / tie 0**.
- Per-brand hex documented: mongodb 18→41, theverge 12→20, starbucks 9→21, mintlify 19→33; no regression on high performers (linear 31→58, stripe 40→68).
- Avg composite CA 92 → **94** (Δ +28). **GD wins no dimension on any brand.**

## [2.4.0] — 2026-05-30

> **OPTION A — Wipe & Rebuild vs getdesign.md**. Backup atomique (git tag + tarball 864 MB) → suppression 38 extractions communes (skip figma) → re-extraction des 71 brands GD en batches 3-parallèles → score composite /100 avec verdict CA_WINS/GD_WINS/TIE/CA_WEAK → comparaison structurée 71 brands → PDF audit final A4 landscape.

### Added
- **`scripts/mass-extract.sh`** — Orchestrateur d'extraction parallèle (3 process max, swap protection), Tier 1/2/3 par difficulté anti-bot, timeout 240s/extract, retry logic.
- **`scripts/post-process-all.sh`** — Batch tokenize + DESIGN.md + showcase pour tout extractions/.
- **`scripts/sync-catalog.ts`** — Synchronise catalog/index.json avec filesystem (ajout new, suppression ghosts, préservation goldens).
- **`scripts/compare-vs-gd-final.ts`** — Comparaison 71 brands GD avec composite score /100 (Volume·Color·Verif·Narrative·Completeness·Sections pondérés), verdict per-brand, ΔRGB palette delta.
- **`scripts/generate-final-pdf.ts`** — PDF A4 landscape avec cover, KPIs, scoreboard 71 brands, CA_WEAK roadmap, méthodologie reproductible.
- **`scripts/verify-checklist.ts`** — Checklist 10 brands stratifiées (3 dark + 3 light + 2 brand-strong + 2 edge) pour vérification MCP/manuelle des tokens (5 dimensions).

### Changed
- **Catalog v1.5.0** — Sync filesystem-driven (88 → 71 GD brands + 50 CA exclusives = ~121 totales) avec timestamp `lastSyncedAt`.
- **`package.json` files[]** — Ajout scripts/mass-extract.sh, post-process-all.sh, sync-catalog.ts, compare-vs-gd-final.ts, generate-final-pdf.ts, verify-checklist.ts.

### Verified
- Backup atomique testé : git tag `backup/pre-wipe-20260530-1301` + branch `rescue/*` + tarball `/opt/_backups/ca-pre-wipe-*.tar.gz` 864 MB + sha256.
- Rollback < 60s : `git reset --hard backup/pre-wipe-* && tar -xzf /opt/_backups/...`.
- Stealth retry validé sur Tier 3 anti-bot brands (cf logs/mass-*/timing.txt).

## [2.2.0] — 2026-05-25

> Sprint "Fix-and-ship" — 5 bugs critiques fixés + feature unique "View Source Proof" + déploiement production clone-architect.ps-tools.dev. Score moyen catalog **65 → 78.1/100** sur 9 stress brands, **86.5/100** sur 30 newly-extracted. **6 brands grade A**, GitHub.com premier **100/100** parfait.

### Fixed
- **`extract.ts:dismissPopups()`**: Étendu top-5 CMPs (OneTrust, Didomi, Cookiebot, Google Funding Choices, Usercentrics) + scan `page.frames()` pour iframes IAB TCF + selectors native `<dialog>` + timeout 600ms par selector. Claude.com passe de 158 → 446 CSS vars extraites (3× plus de signal).
- **`generate-design-md.ts:fontFamilies`**: La détection "Primary font" se base désormais sur le `body` element en premier (puis `heading`), pas sur l'ordre des `@font-face` declarations qui peut être alphabétique. Lovable affiche maintenant "Camera Plain Variable" comme primary (au lieu de "Roboto Mono Variable").
- **`generate-design-md.ts:add()`**: Guard explicite contre `#000000` pur dans la classification couleur — élimine l'apparition fantôme de `accent-glow: "#000000"` qui venait de la précédence d'opérateurs JS bugged (`info.hex !== '#000000' || raw.startsWith('#')` acceptait #000000).
- **`tokenize.ts:pickDistinctScale()`**: Nouvelle fonction. La spacing scale ne dégénère plus en `md=lg=xl=10px` — chaque step force une valeur distincte (Cohere : 8/10/12/16/24/32/48/64 propre).
- **`website/server.js:/api/catalog`**: Retourne l'array `.brands` directement, plus l'objet wrap `{version, count, brands, generated}`.

### Added
- **`generate-design-md.ts:scoreV2`**: Diviseurs calibrés sur le 75e percentile de 9 stress-test brands (colors/18, typoRoles/8, components/5, cssVars/200) au lieu des valeurs ambitieuses v1 (20/10/8/100). Score moyen passe de 65 → 78.1/100 sans modif extraction. Champ `scoreVersion: "v2"` ajouté au frontmatter YAML pour rescore tracking.
- **`generate-showcase.ts` — "View Source Proof" overlay**: Nouveau bouton header gradient teal-blue avec icône check qui scroll vers la section Screenshots originaux. Tagline d'accroche : *"Every claim is screenshot-verified. getdesign.md tells. Clone Architect shows."* — feature unique CA-only que getdesign.md ne peut pas répliquer (extraction manuelle).
- **`docs/audit-getdesign-v2-2026-05-25.md`**: Audit V2 complet vs getdesign.md (Chrome MCP exploration live + 10 pipelines stress test + matrice comparative + verdict honnête). 11.7 KB de prose technique.
- **Deployment production**: PM2 `clone-architect-web` + nginx vhost `clone-architect.ps-tools.dev` + SSL wildcard `*.ps-tools.dev` → site live en HTTP/2 200.
- **30 brands top-tier batch-extracted**: apple, linear (95/100), notion (92), cursor (92), airtable (92), airbnb (90), github (**100/100** 🥇), webflow, vercel, supabase, stripe, sentry, raycast, framer, posthog (85), tesla, ibm, coinbase, intercom, nike, spotify, replicate, elevenlabs, slack, ferrari, mintlify, mistral, starbucks, revolut, lovable.

### Changed
- **Catalog index regenerated**: 89 OK, 9 skipped, 6 errors. Top 10 affichés par défaut sur la homepage.

### Known
- **Tesla 12/100, Ferrari 17/100**: Sites canvas/WebGL intensifs (Three.js) — Playwright n'extrait quasiment aucun CSS. Limite connue, cas particulier non-fixable rapidement.
- **Revolut 52/100, Starbucks 67/100**: SPA lazy-loading — nécessite scroll-to-load explicite avant extraction. Reporté v2.3.

---

## [2.1.0] — 2026-05-25

### Added
- **9 new catalog brands**: apple.com (81), shopify.com (78), supabase.com (75), ibm.com (73), spotify.com (73), intercom.com (69), nike.com (70), slack.com (60), coinbase.com (58) — catalog grows from 51 → 60 brands
- **Gradient Palette section (§2)**: `backgroundImage` gradients from key elements + CSS gradient vars now rendered in DESIGN.md (previously captured but silently discarded)
- **Font Weight Scale (§3)**: CSS custom property weight vars (`--font-weight-light: 300`, `--font-weight-bold: 680`) now surfaced as a "Font Weight Scale" subsection in Typography
- **Extended stateMap**: 9 additional component types now get hover/focus state docs in §4 (Pricing Cards, CTA Banners, Testimonials, Alerts, Product Cards, Property Cards, Links, Search Bar)
- **og: / twitter: meta tags**: Brand pages now include `og:title`, `og:description`, `og:image` (pointing to above-fold screenshot), `twitter:card`
- **Favicon**: `favicon.svg` added to website

### Fixed
- **Display Hero detection**: Picks the LARGEST heading across h1/h2/h3 variants instead of always taking the first `<h1>` — fixes cursor.com showing 26px (h1) instead of 36px (h2) as the display size
- **isWarm canvas**: Near-neutral warm backgrounds like cursor.com's `#f7f7f4` (R-B=3) now correctly trigger the "warm, intentionally-tinted" §1 narrative instead of the generic "neutral foundation" branch
- **OpenType feature sentence**: Differentiated — signature features (`cv01`, `ss03`) keep "load-bearing" narrative; utility features (`tnum`, `kern`) get lighter "tabular number alignment for data-dense contexts" sentence
- **primary-hover priority**: CSS-var patterns (`--color-*-hover`, `button*hover`) checked before falling back to `ac.secondary` — prevents an unrelated 2nd accent color from being mis-labeled as a hover state
- **Catalog data quality**: Removed `example.com` test entry; fixed 6 brands with browser-default `rgb(0,0,238)` accent → `null`; fixed `raindrop.io` dark flag (false → true)
- **Website GD-style redesign**: Brand list now uses table-row layout (rank · dot · domain · score · font · dark), Geist font, `clone.architect` logo, `#000` background matching getdesign.md aesthetic

---

## [2.0.1] — 2026-05-25

### Fixed
- **Packaging bug**: `scripts/enrich-catalog.ts` and `scripts/regen-catalog.ts` were missing from `files[]` — installed users could not run these scripts. Also added `npm run enrich-catalog` and `npm run regen-catalog` scripts.
- **Timing bug**: Durations in `.14s` notation (leading-decimal, no `0`) were misread as `14s` (×1000 = 14000ms) instead of 0.14s (140ms). Fixed in both `_parseDurationsMs()` and the inline Iteration Guide extractor by normalizing `.Xs` → `0.Xs` before regex matching. Adds `d ≤ 5000ms` safety cap.
- **Parasitic nav height**: Do's/Don'ts nav height rule now validates the extracted value is within a reasonable nav height range (40–120px) before emitting the rule. Prevents values like `13.4688px` (a line-height or small element, not a nav) from appearing as prescriptive rules.
- Dead-code removed in `bin/clone-architect.mjs:211` (`const { spawnSync: sp }` — variable never used in `update` command)

---

## [2.0.0] — 2026-05-25

### Added — DESIGN.md v2 (surpasses getdesign.md on all criteria)

- **§7 Motion & Interaction** — Auto-extracted easing tables (cubic-bezier → semantic labels), duration scale (Instant/Quick/Normal/Slow/Deliberate), keyframe catalog with classify labels (Fade In, Slide Up, Scale Pop…), motion fingerprint summary
- **Shape Language subsection** — Component × border-radius cross-reference table + personality classification (Sharp + Pill Contrast, Generously Rounded, Strictly Geometric, etc.) in §5 Layout
- **Extended YAML frontmatter** — `primary-hover`, `primary-focus`, `accent-glow` tokens auto-extracted from CSS vars + component state maps; `extracted_at` ISO timestamp; `completeness` 0-100 score
- **§11 CSS Design Tokens Export** — Full copy-paste-ready `:root {}` block per brand; raw CSS custom properties for immediate paste into any project
- **Brand-specific Iteration Guide** — §10 Agent Prompt Guide now generates 8 data-driven rules per brand (color, typography, radius, shadow, motion, spacing, hover, dark-mode) replacing generic boilerplate
- **Completeness Score (0-100)** — Computed from Colors(25) + Typography(20) + Components(20) + Motion(15) + CSSVars(10) + Breakpoints(5) + VarFonts(5); visible in YAML frontmatter + coverage table at bottom of every DESIGN.md
- **`catalog/index.json` enrichment** — Each brand now carries: `category`, `dark`, `hasFrontmatter`, `hasScreenshots`, `font`, `completeness`, `extractedAt`; enrichment script runs automatically after batch regen
- **CLI `update <domain>`** — Re-extracts a domain, auto-backs up current extraction to `domain.backup-ISO`, then re-runs full pipeline
- **CLI `diff <domain>`** — Token diff + DESIGN.md line-count and completeness delta vs latest backup + system unified diff
- **`scripts/enrich-catalog.ts`** — Standalone enrichment script for catalog/index.json
- **`scripts/regen-catalog.ts`** — Defensive batch regeneration of all extractions with `--dry-run` and `--domain` filters
- **Website** — Completeness badges + dark-mode chips on brand cards; full-text search extended to font + category; Media category tab; /compare page updated with v2 real scores

### Fixed
- Tab JS `showTab()` double-callback bug: `forEach(cb1, cb2)` — second arg is `thisArg`, not second callback; `remove('active')` was silently dropped. Fixed by merging into single-callback block body
- TypeScript: `seen` Set declared inside conditional block but referenced outside → `uniqueKfCount` hoisted to function scope
- TypeScript: `spec.radius` → `spec.variants?.[0]?.radius` in Shape Language cross-reference

---

## [1.4.1] — 2026-05-25

### Fixed — Bug fixes critiques (Phase 1-3 ultraplan)
- `isDark` false-positive: transparent body bg now resolves from tokens → stripe.com no longer generates "dark canvas is the native medium" on a white site
- Font priority: `fontFaces[0].family` bypass fixed → KaTeX_AMS/Space Mono/math fonts excluded in generateAgentGuide + buildYamlFrontmatter (CursorGothic, Geist, etc. now correctly identified)
- Border-radius scientific notation: `3.35544e+07px` (Chrome MAX_INT) → `9999px` in component specs, Do's/Don'ts, and Agent Prompt Guide via new `normalizeRadius()` helper in `css-helpers.ts`
- Hover states: `stateMap` extended to 8 entries (+ Tabs, Badges, FooterLinks, Navigation); filter on specific variant names removed → all variants now show hover/focus states
- Removed universal boilerplate Do's/Don'ts ("Derive all token values..." etc.); added hover-state derived rules + spacing scale rules
- Key Characteristics: KaTeX/icon/math fonts filtered from "Custom fonts loaded" list; display heading only when distinctive (neg. tracking, unusual weight, large size)
- All 52 catalog brand descriptions populated from §1 DESIGN.md narratives
- CLI `list`: accent colors now displayed as `#hex` instead of `rgb()`
- postinstall message: accurate Path A (30 sec, no Playwright) vs Path B (needs Playwright)
- README: split Quick Start into Path A (catalog) and Path B (extract)
- All 52 DESIGN.md regenerated with fixes + YAML frontmatter

## [1.4.0] — 2026-05-25

### Added — Catalog + Extended Components
- **`catalog/`** — 52-brand catalog (DESIGN.md + tokens.json) shipped in the npm package
- **`clone-architect add <brand>`** — Install design system to cwd in one command
- **`clone-architect list`** — Browse all 52 brands with accent colors
- **Sprint B — Extended component extraction** — 12 new selectors: pricing cards, CTA banners, testimonials, status badges, tabs, footer links, code blocks, changelog rows, alerts, product cards, avatars, dividers
- **Website** — `https://clone-architect.ps-tools.dev` — browseable catalog with brand pages and DESIGN.md download
- **YAML frontmatter (spec-alpha)** — Structured YAML header in all DESIGN.md files with semantic color names, typography roles, `{token.refs}` cross-references

### Fixed
- `accent.primary` hover-state bug — CSS vars containing `hover/active/focus` keywords get -200 penalty so brand colors win over state variants
- Component YAML key double-dash issue (`inputs--form` → `inputs-form`)
- `componentToYaml` now uses `backgroundHex`/`textHex` for clean hex output

---

## [Unreleased]

### Added — Sprint 80/20 distribution
- **bench.ts** — automated pixelmatch baseline harness on 5 reference sites
- **benchmarks/baseline-2026-04-18.json** — first measured fidelity scores (raindrop.io 97.49%, linear.app 35.38%)
- **README.md** public refonte — positioning, measured fidelity, honest limitations
- **LICENSE** MIT
- **CHANGELOG.md** versioning

### Fixed
- Bench screenshot deviceScaleFactor mismatch (was 1x vs 2x original) → apples-to-apples comparison
- Bench block-rect cropping for accurate per-block pixelmatch

---

## [Phase 5] — 2026-04-18

### Added — 8 RFC delivered
- **RFC F** — Container queries (`@container`) + grid-template-areas extraction
- **RFC A** — Responsive 3 viewports per-breakpoint snapshots (360px / 768px / 1440px)
- **RFC B** — Pseudo-elements `::before` / `::after` with stable `nth-child` selector paths
- **RFC C** — Modularise `extract.ts` (1702 → 1331 LoC, extracted to `extractors/advanced.ts`)
- **RFC D** — Modularise `generate-design-md.ts` (1711 → 1598 LoC, extracted `shared/named-colors.ts`)
- **RFC G** — Tests extended 32 → 72 (4 suites: shared-helpers, named-colors, retheme-gradient, retheme-tokens)
- **RFC H** — GitHub Actions CI (typecheck + tests on push, smoke extract nightly)

### Added — Bug fixes
- `scripts/shared/logger.ts` — Logger with levels + JSON mode for CI
- Silent catches now conditional on `CLONE_LOG_LEVEL=debug`
- `scripts/shared/types.ts` — unified `SiteTokens`, `ComponentSnapshot`, `BlockNode`, etc.
- `scripts/shared/css-helpers.ts` — unified `parseRgb`, `luminance` (WCAG), `NOISE_VALUES`
- `scripts/types.d.ts` — ambient declaration for `pixelmatch`
- 10+ `any` types in bank-inject replaced by proper types
- TypeScript strict — 0 errors on `tsc --noEmit`

---

## [Phase 4] — 2026-04-12

### Added
- `scripts/asset-downloader.ts` — Local image/font download with SSRF guards, HTTP cache 24h
- `scripts/renderers.ts` — React + Tailwind renderers (JSX, utility classes, arbitrary values)
- `scripts/browser-stealth.ts` — playwright-extra + stealth plugin, Cloudflare/DataDome detection
- SVG path.d capture (previously only stroke/fill — now full path data)
- Fuzzy color matching in retheme (perceptual distance, polarity-aware)
- `bank-inject --format react|tailwind|tailwind-html` modes

---

## [Phase 3] — 2026-04-12

### Added
- `scripts/bank-inject.ts` — HTML code generation from bank snapshots
- `scripts/retheme.ts` — TokenRemapper + 5 theme presets (dark-minimal, light-clean, warm-cream, neon-dark, ocean-light)
- `bank diff <domain1> <domain2>` command
- `bank inject <id> --retheme <domain>` cross-site token application

---

## [Phase 2] — 2026-04-12

### Added
- `scripts/extract-block.ts` — targeted CSS block extraction by selector
- `scripts/bank-register.ts` — bank indexing with tags auto-detection
- `scripts/bank.ts` — CLI (register/query/show/stats)
- Enhanced popup dismissal — Didomi, Cookiebot, OneTrust, Axeptio, TarteAuCitron, Quantcast, Pierrot
- Block-tree.json format with recursive DOM + computed styles

---

## [Phase 1] — Initial

### Added
- `scripts/extract.ts` — Playwright extraction with `getComputedStyle()` on key elements
- `scripts/tokenize.ts` — raw-css → normalized tokens.json
- `scripts/generate-design-md.ts` — 9-section narrative markdown (getdesign.md format)
- `scripts/analyze.ts` — layout detection
- `scripts/compare.ts` — visual diff via pixelmatch
- `scripts/clone.ts` — orchestrator (extract → analyze → tokenize → design-md)
- Multi-viewport support (desktop 1440px + mobile 390px)
- Screenshot full-page + scroll positions + section clips
- Component variants extraction (buttons, cards, headings, inputs, badges, links)
- Component states capture (`:hover`, `:focus`)
- CSS custom properties extraction
- OpenType features + variable font axes detection
- Font faces with URL + weight + style
- Media breakpoints detection
