Decisions
2026-08-04 — pipeline re-run (v2)
Context: the 2026-05 build predates most of the current suite (copy deck, draft-first mode, legal pages,
multilingual, SEO/GEO, dossier surface). Operator asked to re-run the pipeline on the same business.
- Fresh Places dossier.
place_id ChIJLfiRVX4KkEcRrAPWj5ejdA0, exported tocontent/sources/google-places.md.
Rating (4.9/150), hours, phone and address are unchanged since May — no fact drift. The *review sample*
rotated completely; both samples are now kept in content/reviews.md.
- Locales: German primary, English second. The salon is a walk-in hairdresser in Zürich-Oerlikon and all
five current Google reviews are German; the previous site was English-only, which was wrong for local search.
DE is canonical for local SEO and lives at the root; EN is prefixed /en/. Same slugs across locales.
- Full rebuild on the suite's
astro-starterrather than retrofitting the old single-file Vite/React page.
Gets tokens→CSS-vars generation, hreflang plumbing, schema helpers with the policy bans, draft ribbon +
/dossier-*/ transparency surface, sitemap, robots/llms.
- Brand tokens unchanged (v1.0.0). The 2026-05 brand DNA (warm paper, dark ink, brass accent, editorial
serif, squared-off UI) passed distinctiveness and there was no cue to redesign. The rebuild is a
structure/language change, not a restyle. Fonts stay system stacks — no webfont request, so no
Google-Fonts transfer to disclose in the privacy policy.
- Draft-first.
DRAFT=true: visible[TBD: …]placeholders for facts we don't have (services, prices,
walk-in policy, legal entity), draft ribbon, noindex. Nothing invented to fill a gap.
- New Cloudflare project
andre-coiffeur-v2. The existingandre-coiffeur.pages.devpreview is left
untouched, per the suite's never-overwrite-a-live-preview rule.
- Sitemap deviates from the hairdresser category spec — see
build/category-mapping.md.
Copy-deck parsing convention
The deck's section delimiter is ## <lowercase-slug>; a ## line that is prose (capitals, spaces) is
a *visible heading inside* the section, not a delimiter. This lets legal pages keep normal ##
subheadings inside a single ## body section. [TBD: …] lines are parsed as a separate structural
kind so they render in their own marked block and can never be mistaken for copy
(site/src/lib/copy.mjs).
Imagery — the Places photo set is not trustworthy as a whole
Pulling all 10 profile photos and reviewing them individually turned up that photos 7–10 are
user-uploaded and two of them show a different salon ("Coiffeur Rudi", different phone and hours).
Photos 6, 9 and 10 show identifiable people without consent. Only owner-uploaded photos 1–5 are used,
and places-05 (the salon interior) became the hero. Full detail and the reporting action are in
content/cautions.md and TODO.md T20. The v1 site's gallery alt text was wrong on every image
because it trusted the filenames; files are now named for what they contain.
Images moved to src/assets/ and go through astro:assets: 4.0 MB of JPEG originals now ship as
WebP, largest 253 kB, whole site 1.2 MB.
Distinctiveness review — 2026-08-04
- Token discipline: pass.
check-tokens.mjsclean, 12 files, 0 violations. Every component value
is a CSS custom property generated from brand/design-tokens.json.
- Hero layout: changed deliberately.
check-distinctiveness.mjsreported the last two sites both
used left-text-photo-right — and that is what this build had. Replaced with a text block over a
full-bleed photo strip: different composition, closer to the category spec's "bold hero", and it
keeps text off the photo so the styleguide's contrast rule holds. Registered as
text-block-fullbleed-strip.
- Palette warning accepted.
primary #1F1F1Dis within 24 RGB units of silvia-jayet's#2a302a.
Both are near-blacks; that is where the resemblance ends — this one is a warm ink with a brass
accent (#B98A56), the other a cool green-black. Different city, category and typography. Changing
a colour derived from this salon's own materials to avoid coincidental proximity to an unrelated
client's site would be the wrong trade.
- Source fidelity: pass. Every fact traces to
content/identity.mdor the Places dossier. Gaps
are visible [TBD:] placeholders, not invented values. No aggregateRating/Review markup.
- Preflight: 3 failures, all intentional draft artifacts (ribbon, noindex, dossier surface) that
the gate is designed to block from production. Everything else passes.
2026-05-28
- Created project at
~/Projects/andre-coiffeur. - Used the
hairdressercategory workflow because the operator requested it and Google Places classifies the business as a hair salon. - Built a single-page Vite + React website instead of a multi-page site because the supplied dossier has strong identity, hours, reviews, and photos but not enough team/service/pricing depth for full inner pages.
- Used WhatsApp as the primary contact path:
https://wa.me/41449742812. - Avoided current price claims. Older review mentions of CHF 23 are stored in
content/reviews.mdbut not presented as current pricing. - Chose a compact editorial hair-salon visual direction: warm paper background, dark ink, brass accents, real salon imagery, squared-off UI.
- Kept
/_styleguide/and/dossier/coiffeur-andre-dossier.mdavailable in the site.
Distinctiveness review — 2026-05-28
- Token discipline: pass — colors and component values are driven by design tokens/CSS variables.
- Visual distinctiveness: pass — the site uses a dark editorial salon treatment, real local images, compact practical sections, and avoids the generic pastel beauty/wellness look.
- Source fidelity: pass — all facts are from the supplied dossier and category workflow; missing service/pricing data is documented rather than invented.
- Reviewer: Hal
Claude implementation pass — 2026-05-28
- Ran the
coding-agentskill for the implementation pass, as required by the workspace workflow. - Claude Code could not write files because its tool permissions were blocked, but it returned a concrete implementation plan.
- Applied Claude's requested changes locally: richer metadata, HairSalon schema in
<head>, prefilled WhatsApp CTA, open/closed status pill, reviews anchor, review dates, directions CTA, stronger footer NAP, and removal of React-rendered JSON-LD. - The wrapper's Codex review step failed because the Codex CLI had no API authentication in that subprocess.
- Follow-up fix: moved token CSS variables onto
document.documentElementfor global CSS use, made theprimary-inklookup tolerate both token shapes, removed an unsourced "styling advice" service item, and made the opening-status pill time-aware instead of weekday-only. - Mobile screenshot review showed the hero headline clipping on a 390px viewport, so the type scale now uses explicit responsive breakpoints instead of viewport-scaled font sizing.
Website-builder pass — 2026-05-28
- Aligned
site/src/design-tokens.jsonto the brand token shape (kebab-caseprimary-ink) so the React code uses the same key the brand JSON exposes; removed the dual-shape fallback. - Replaced the React-internal
Styleguidecomponent.public/_styleguide/index.html(the brand-rendered styleguide) is the authoritative/_styleguide/route, so the duplicate React route was dead code. - Switched layout to proper landmarks:
<header>for sticky nav,<main id="top">for sections,<footer>outside. Each section has anaria-labelledbyheading and ascroll-margin-topto clear the sticky header on anchor jumps. - Added a small "How to book" three-card band sourced from the dossier: WhatsApp first, bring a reference photo (Tutto Family review), English is fine (Prashant Sharma review). No invented service or pricing facts.
- Reviews section now leads with the 4.9 / 150-rating callout and explicitly notes the 2018 CHF 23 figure is not current pricing.
- Visit panel: phone is a
tel:link, hour rows now use a closed-flag array (no string comparison to detect Saturday/Sunday), button labels say "Call 044 974 28 12" to make the destination obvious. index.html: added inline SVG favicon, hero image preload,og:locale=en_CH,robots=index, follow, andhasMap/areaServedon the HairSalon schema. JSON-LD stays static in the document head (not React-rendered) so crawlers see it on first byte.- Mobile sticky CTA now respects
env(safe-area-inset-bottom); body adds matching bottom padding on small viewports so the sticky CTA never covers footer content.
Distinctiveness review — 2026-05-28 (website-builder pass)
- Category compliance: pass — strong real-photo gallery, WhatsApp primary CTA, mobile sticky CTA, visible NAP/hours/reviews, HairSalon schema, and
/_styleguide/plus/dossier/coiffeur-andre-dossier.mdpreserved (verified indist/). - Source fidelity: pass — all copy (services, "how to book" steps, language note, review snippets, hours, closed days) is traceable to
content/identity.md,content/extraction.md, orcontent/reviews.md. Nothing invented; the CHF 23 figure is explicitly framed as historical. - Visual distinctiveness: pass — editorial serif headline, warm paper + dark ink + brass accent, squared-off UI, real Google Places imagery in the gallery, no pastel beauty-salon styling.
- Token discipline: pass — all colors, spacing, radii, and shadows come from the design-token CSS variables; the React tokens file matches the brand token key shape.
- Build verification:
npm run buildsucceeds;dist/_styleguide/index.html,dist/dossier/coiffeur-andre-dossier.md, anddist/images/*are all present. - Reviewer: Claude (website-builder pass)
Local preview fix — 2026-05-28
- Manual verification found that Vite dev served the React app fallback at
/_styleguide/even thoughpublic/_styleguide/index.htmlexisted. - Added
site/vite.config.jswith a small dev middleware that serves the brand-rendered styleguide HTML at/_styleguide/and/_styleguide/index.html. - This keeps the website-builder contract true in both local dev and production build output.