| name | designer |
| description | Read editor.md, editor.json, and analyst.json. Make data-driven creative visual decisions for every section — teaser, charts, images, videos, audio, maps, and interactive demos when they fit the data. Generate selected assets. No HTML. Outputs designer.json with des_xx IDs. |
| argument-hint | ["PROJECT_DIR"] |
| allowed-tools | Bash(*), Read, Write, Glob |
Designer
Your job is creative visual thinking. For every section of the blog, decide how to make the finding land in the most engaging, memorable way based on the data's actual properties. You do not write HTML — that is the Programmer's job.
Think like a creative director, not a developer. Your output is a precise visual brief that tells the Programmer exactly what to build. Do not satisfy a fixed media checklist. Let the data, story, and editorial rhythm determine whether each section needs a chart, image, video, audio, map, interactive, stat callout, instance, or text-only treatment.
Fast profile. When run_config.json says run_profile: "fast", design charts + static images only — no text2video / image2video / text2music generation, no animated hero (a static cover image is fine), and no cinematic scroll (those roles are skipped in fast). Charts plus a few license-clean / fetched (or a single generated) static images carry the page. The full five-channel multimedia brief below is the premium profile.
Setup
PROJECT_DIR = first argument
- Resolve
SKILL_DIR = the directory containing this SKILL.md (.../skills/data2story-pro/designer). Replace SKILL_DIR placeholders with the resolved, quoted path before running Bash. Do not hard-code machine-local paths.
- Read
PROJECT_DIR/editor.md, PROJECT_DIR/editor.json, PROJECT_DIR/analyst.json, and PROJECT_DIR/scout.json (if present) before doing anything
- Read the shared design system in
../../frontend-design-pro/ (SKILL.md + references/) and choose a theme + component vocabulary for this story
- Assets go in
PROJECT_DIR/assets/
- Output:
PROJECT_DIR/designer.json
How to read the input files
editor.md: the prose document with section structure. Each section has an edt_xx ID, lists its evidence (ana_xx) and context (det_xx), and contains the verbatim text.
editor.json: machine-readable sections. Each edt_xx has findings, chart_placeholder (which ana_xx drives the chart), a typed media_placeholder, and editorial_notes.
analyst.json: items keyed by ana_xx. Each has content, calculation, and crucially data_table (chart-ready data) — review it to understand what data is available for each chart.
Tools
Media tools route through OpenRouter (OPENROUTER_API_KEY must be set): text2image, text2video, image2video, text2music, and embeddings — co-located under SKILL_DIR/scripts/openrouter-*/. Default models and exact invocations are in references/tools.json; full per-tool docs are each tool's own SKILL.md under SKILL_DIR/scripts/openrouter-*/. Use image2video to animate a strong still you already generated; text2video when motion itself is the point. For the moving-hero / cinemagraph pipeline (still → animate → upscale), the OpenRouter video-model facts (models are listed only via GET /api/v1/videos/models; duration is only 4/6/8; Kling v3 std is the faces-safe model — Veo/Wan refuse real faces, seedance moves the subject), and the AI-upscale recipe (Real-ESRGAN x4plus 4×→downscale, avoid the 2× tile bug), read references/video_pipeline.json before animating anything.
Step 1: Design the Hero (REQUIRED on every run)
The hero is the first thing the reader sees — before the headline, before any prose. It must create curiosity on its own. A hero is REQUIRED on every run — never none. You do not "choose one teaser type" freely; you walk a recorded ladder and take the first rung that the data and your verified assets actually support, then record the chosen rung in a new content.hero_strategy field on the hero item (so the gate and the Critic can see which path was taken and why):
interactive — an interactive hero, used only when the centerpiece finding can be front-loaded (the reader produces the hook in the first screen: a guess-then-reveal, a mini live recompute, a pick-your-side). Prefer this when the story's one contestable number can open the page without spoiling the rest. This is often the best rung for a computational topic with no star entity — e.g. "guess this year's inflation rate before we reveal it", a swing-the-vote recompute on an election margin, or a slider that re-runs a public-health projection — letting a sober numbers story lead with its hook instead of hunting for a face.
real_still — a verified real-subject still, FETCHED not generated: the Scout's sct_ asset (license- + identity-checked in scout.json) or the Detective's ref_ asset. Use this when a specific real subject (a real person, trophy, stadium, landmark) is the natural face of the story. Never generate a recognizable real subject from scratch (photoreal or illustration) — but animating a FETCHED real photo into a subtle cinemagraph (faces held within the warp guard, with proportionate disclosure) is sanctioned; see the real-subject rule in Step 3 and topic_profile.ai_face_policy.
abstract_still — an abstract / atmospheric generated still (text2image) with no specific real referent (a mood scene, texture, metaphor). This was the gold blog's choice (an empty floodlit pitch) — it carries zero real-subject risk and is always available as the floor of the ladder when rungs 1–2 don't fit.
An animated cover is the DEFAULT; a static cover is only a recorded fallback. The Hero role (a Designer-team member, Stage 4.6 — see Team coordination) OWNS rendering the animated hero: it walks an auto-select source ladder (image2video cinemagraph on a verified still, Kling on a real face, a license-clean stock/atmospheric clip) and ships assets/teaser.webm + _web.mp4 + .jpg poster, keeping the still as the video poster so it degrades gracefully. You record the chosen rung in content.hero_strategy; the Hero renders it. A static <img> cover is shipped only when every animation rung degrades, with a recorded media_blocker. For the cinemagraph route, the model choice, the 4/6/8 duration constraint and the upscale step, follow references/video_pipeline.json. (Do NOT animate a real person/subject via image2video as a generic video — that is the Veo refusal in Step 3; the ONLY sanctioned animation of a real face is a held-subject cinemagraph via the faces-safe Kling v3 std model with honest disclosure — see video_pipeline.json. Veo/Wan hard-refuse real faces; otherwise animate only abstract/atmospheric stills or fetched-still posters that contain no recognizable real face.)
Record the rung you took in content.hero_strategy (one of interactive / real_still / abstract_still, with the optional note +image2video when you animated it) on the hero item, plus a one-line why for why higher rungs were not taken (e.g. "centerpiece cannot front-load; no freely-licensed real subject is the natural face → abstract atmospheric still").
The hero must help LAND THE STORY'S HOOK fast. It should signal the subject and surface — or immediately precede — the surprising finding/number/tension, so the reader meets the hook in the first beat, not after scrolling. A purely atmospheric "mood" hero (a generic texture/scene with no subject or number) is allowed only if the headline/hook lands immediately after it. This is a light rule — keep your creative freedom over form; just don't let a mood hero delay the hook. (Topic-agnostic: the "subject + surprise" can be a stat callout, a guess-first prompt, a one-line overlay, or the headline butting right up against the visual.)
Write the hero spec (rung + why; full interaction/prompt/mood description; how it lands the hook) and generate the asset if it is an image or video. Save to PROJECT_DIR/assets/teaser.* (the hero is the teaser section). If the hero would show a specific real subject (a real trophy, stadium, or person), use a fetched real image rather than generating one — see the real-subject rule in Step 3.
Step 2: Visual Decision per Section
For every edt_xx section in editor.json, decide the presentation. The full mode catalog — interactive/static charts, maps, timelines, scrollytelling, before/after sliders, card decks, quizzes, demos, generated image/video, image-to-video, stat callouts, audio, text-only — is in references/visual_modes.json. Default to a data-driven visual decision; text-only is valid when prose is genuinely stronger.
Before you spec any chart, pick the right form. Read ../../dataviz-craft/references/chart_chooser.json to choose the chart type that fits the data's shape and the point being made, and ../../dataviz-craft/references/encoding_craft.json to choose the encoding (what maps to position/length/color) before writing the spec — get the type + encoding right first, then describe it for the Programmer.
Purpose rule (every element): each visual must serve one explicit purpose — INFORM (carries data/information the prose cannot) or IMMERSE (sets mood/atmosphere that aids reading). Record it in the item's purpose field. An element that serves neither is decoration — do not create it, and never add media merely to use a channel.
Interactive centerpiece (Pudding-grade): for the finding the Editor designated as the centerpiece, design an interaction that makes the reader PRODUCE it, not just read it. Read ../../frontend-design-pro/references/interaction_playbook.json and choose from its recipes — prefer explorable_recompute / tune_the_assumption for a model/derived finding (they consume the Analyst's client_model), or guess_then_reveal / scored_quiz / personal_input_where_you_land / playable_game for a "you assume X but actually Y" or experiential claim; use scrollytelling when the argument is a sequence/funnel. Spec the manipulate→recompute→payoff loop precisely and place it at/before the reveal. Aim for the hero centerpiece + the approved supporting set the Editor curated — each supporting playground bound to a distinct finding and earning its place (see the earned_test in interaction_playbook.json centerpiece_doctrine), not a pile of widgets.
Aim for a multimedia-rich page by default. Apply the diversity rules in references/diversity_rules.json together with the presentation doctrine and per-dataset richness targets in ../../frontend-design-pro/references/media_presentation.json: every blog should use all five channels — chart, image, video, audio, interactive_or_map. Before you set any channel's used:false, you must first try its documented fallback (animate a strong still with image2video for video; the sourced-BGM now-playing card (a real Scout-found track, never AI) for audio; a guess-reveal/sortable/before-after for interactive; atmospheric or real fetched images for image). Skip a channel only when even the fallback would be fabricated or purely decorative, and record that data-grounded reason in meta.media_decisions. Avoid chart streaks and visual sameness across blogs.
Audio gets its own treatment — BGM is expected on every blog: the BGM is a real track presented in a self-hosted, cover-art "now-playing" card that starts on the reader's first click (the World Cup blog's form), never an AI-composed track, placed at the top of the article. (text2music is SFX only — an opt-in atmospheric sound_design bed for a genuinely un-findable sound — and is never the front BGM; see Step 3.) Every blog opens with a fitting real-sourced track, including sober topics — match the mood word to the tone: a sober / computational story (economics, elections, public-health stats, finance) gets a pensive / ambient / minimal / orchestral / nocturne track (a restrained real bed, NOT silence and NOT a generic upbeat loop), a celebratory / sport story gets epic / anthem, a somber story gets elegy / adagio. Judge the flavour off the shared classifier ../references/topic_profile.json (is_visual / tags), then walk the BGM ladder (a ladder to climb down, NOT a menu to pick from) to source the fitting track — never drop audio for lack of effort. FIT FIRST: pick the most recognizable best-FIT real track for the story; license-tier is a tiebreak only among comparably-fitting tracks. So when the story HAS a signature anthem/track (the official anthem, the profiled artist's song), prefer that real best-fit track (rung 2 below — self-hosted, demo-gated) over a generic unrelated license-clean CC0 loop; only when no recognizable track fits the story do you reach for a clean-but-generic mood track. (License-tier still breaks ties between two tracks that fit equally well — a clean fitting track beats a gated equally-fitting one.) The rungs:
sourced_bgm — license-clean real track (publishable): a re-hostable CC0/CC-BY/PD/royalty-free track the Scout found that fits the story's mood / place / era, referenced by media_ref → its sct_xx, self-hosted with its cover art so the now-playing card has a square cover (a representative license-clean image or a designed CSS cover — never an AI cover). Take this rung when a clean track genuinely fits; do not settle for a generic unrelated CC0 loop in preference to a recognizable signature track the story is about (rung 2).
- Copyrighted best-fit real track — self-hosted for the DEMO, publish-gated (PREFERRED when the story has a signature track): when the song the story is about (an official anthem, the profiled artist) is the most-fitting BGM, self-host that real track + its cover for the demo and register it as a Designer
des_ audio asset with publish_blocker: true, license: "All Rights Reserved — demo-only", and a real source_url. A publish_note is MANDATORY on this rung (the swap target the Auditor/Programmer surface) — validate.py Section 8 hard-errors a gated asset with no swap target. Because it is a des_ publish-blocker (NOT a clean sct_), it does not pass the validate.py license-allowlist gate as clean; the Auditor raises an advisory publish-blocker and the Programmer renders a "demo-only — must license or swap before publishing" credit line (the demo build is flagged, never blocked). This rung is the right default for a story with a recognizable signature track — fit beats license-tier; only fall to rung 1 when no signature track fits and a clean track does.
- Embed the official player: if you can neither source a clean track nor self-host the copyrighted one, surface the real song as an
embed (the official Spotify/YouTube player carries its own rights).
C. Classical-recording floor (the GUARANTEED terminal rung): when no topic-fitting real track (rung 1) and no signature track (rungs 2–3) land, the Scout sources a license-clean classical RECORDING (rung C — verify the recording's own license, not just the PD composition's) for the same top-of-article spinning-vinyl card. This rung always succeeds, so a clean BGM is always reachable.
There is NO audio.used:false / no-BGM outcome on any topic — BGM is mandatory on every blog, including sober / abstract / privacy_sensitive ones, because the rung-C classical-recording floor never fails. A sober economics / elections / public-health-stats / finance story gets a fitting restrained track (pensive / ambient / minimal / orchestral), which is the right BGM there, not "tonally-wrong filler"; a privacy-sensitive topic gets a quiet, non-intrusive classical recording (rung C), never silence. Whichever rung lands the track, never autoplay (start on the reader's first click/tap), and the page always works fully when muted. A copyrighted song the story references may also appear as an embed "listen ↗" link in context even when the BGM is a rung-1 track — that is separate from the BGM. Place the now-playing card at the very top of the article (set its section to the opening) so the reader can start it before reading. The card is the enriched signature_media_card — spec it with an eyebrow (e.g. "Official anthem") above the title and a scroll-to-start behaviour (it begins on the reader's first scroll/click, never autoplay), so the soundtrack reads as a deliberate signature element rather than a bare audio tag. The full ladder, the never-AI-BGM rule, the publish-gate, and the web-weight transcode are in references/audio_rules.json.
Respect the editor's media_placeholder hints unless you have a stronger creative reason. For each section, write mode, rationale, a precise spec/brief, and the asset file (if generated) into the corresponding des_xx item.
Figure captions are the Copywriter's, not yours. The Copywriter (Stage 3.5, just before you) writes the displayed <figcaption> for every des_xx into copywriter.json items[des_xx].caption (+ subtitle) as a takeaway-title (the chart's conclusion + metric subtitle; a photo's who/what/where + date, then why), and the Programmer renders that verbatim. So you don't author the on-page caption text — keep your des_xx label/highlight/spec as the visual's internal description (what to chart, what to annotate), and let the Copywriter's caption be the reader-facing line. Your annotation spec still drives the on-chart callout the caption's takeaway points at (pair with dataviz-craft/references/annotation_layers.json).
Live-status, if any, is a compact dated badge — never a list. If the Scout passed live_status[] (the "since the snapshot" freshness), present it as a compact, dated summary — e.g. a small "as of " badge with a count + the single latest result — display-only, alongside the data it relates to. Do not render a long list of many specific forward-dated results: that reads like new data and confuses the dataset snapshot the story is built on. (Shared with the Editor work-stream; topic-agnostic.)
When the blog is about a scientific paper, additional modes (PDF preview, paper anatomy, review scorecard, citation network, task demo, paper+review browser, etc.) are available in references/visual_modes.json → science_paper_modes.
Step 3: Generate Assets When Selected
Generated assets follow from the media decisions. Run the generation tool for every generated image/video/audio decision — do not just write the spec; the Programmer cannot generate media.
Real-subject rule — fetch, don't fake. NEVER AI-generate a specific, real, identifiable subject: a real trophy (e.g. the FIFA World Cup trophy), a named stadium or landmark, a real person, an official logo/crest. Generative models get recognizable details wrong (a fake trophy reads as obviously wrong). For any such subject use a REAL image — prefer the Scout's verified scout_* assets (already license- and identity-checked in scout.json), then the Detective's ref_*/flag/logo assets, or fetch one from Wikimedia/Commons via the Detective's helper (../detective/scripts/fetch_images.py). When you place a scout_* asset (or any asset that has an sct_xx record), set the item's media_ref to that sct_xx (see references/field_rules.json) so the Programmer tags data-sct and the contract gate can check its license + identity. If no freely-licensed real image exists, pick a different real subject or an honestly-abstract treatment — do NOT fake the specific object. Reserve text2image / text2video / image2video for genuinely ABSTRACT or ATMOSPHERIC visuals with no specific real referent (mood scenes, textures, metaphors), and caption them as illustration.
Generation fallbacks — the scripts degrade, never drop. The media-generation scripts no longer fail hard on an API refusal/timeout: they retry, swap to a fallback model, and print FALLBACK_USED=<rung>. For image2video, if every rung fails they write a static _poster still instead of a video and print VIDEO_UNUSED=1 + POSTER_PATH=<path> (rather than mislabel a still as .mp4). When you see these signals: reference the poster as the hero's data-des image (the hero is preserved as a still, not a video), set the video channel used:false with the reason, and transcribe any printed media_blocker: {…} line into meta.media_blockers. A fallback is a recorded outcome — never a silent missing asset.
Never feed a recognizable real public figure to Veo/Wan. google/veo-3.1-fast (image2video/text2video) and alibaba/wan-2.7 deterministically refuse to animate a recognizable real public figure (refusal code 15236754 — not a retriable, retry-clearable refusal). A real person in the hero (or anywhere) must be a FETCHED verified still (Scout sct_ / Detective ref_), never a GENERATED face (and never a faked one) — but animating that fetched real photo IS sanctioned when it goes through the faces-safe model: a subtle cinemagraph via Kling v3 std (face held enough to avoid warping, subtle natural motion allowed on top of moving atmosphere) with proportionate disclosure, plus a warp-quality guard (reject a distorting candidate → ffmpeg Ken-Burns on the same still). So motion on a real-person beat is fine via the held cinemagraph; what stays banned is routing the real face through Veo/Wan (they refuse) or generating the face. If no usable fetched photo of the person exists, do not generate the person — neither a photoreal fake nor an illustration of the person — take a theme-first NON-PERSON cover instead (real scene/object → data-as-cover → conceptual/typographic → abstract atmospheric, in that order; honor topic_profile.ai_face_policy.no_photo_fallback and ../hero/SKILL.md). (See the project memory note veo_celebrity_face_block: Veo hard-blocks celebrity faces; the documented fallback when Kling is unavailable/distorts is ffmpeg Ken-Burns on the same fetched still, not generating the face.) Verify each file (ls -la PROJECT_DIR/assets/). Match the richness targets in ../../frontend-design-pro/references/media_presentation.json (richness_targets) — these are soft targets, not quotas: e.g. a visual/place/sport story should aim for around 4-6 images (prefer the Detective's real fetched photos over generic AI fills), plus a video (a text2video scene or an image2video-animated still — at least animate the teaser), plus audio, on top of charts and an interactive/map. Because the cinematic scroll background is mandatory, every topic — including an abstract/computational one — sources a relevant real-image set to back it: an economics / elections / public-health story is backed by relevant real historical / archival / atmospheric photos (real factory / loom / worker / steam-engine / trading-floor photos for an industrial-revolution piece; polling-station / ballot-box archival photos for an elections piece — abundant in the public domain and on Commons). What stays banned is purely-decorative stock that says nothing about the story (a random smiling-businessperson stock photo on an inflation piece) — the Critic penalizes decorative stock — and AI-generating a specific real subject. A relevant historical/archival real photo is NOT decorative: it is exactly the real-image set the cinematic needs. On such an abstract/computational topic, also read the positive playbook ../../frontend-design-pro/references/abstract_excellence.json. Reuse any ref_* images the Detective downloaded — see also references/diversity_rules.json (asset_volume_by_dataset, before_generating).
Register every reused photo you intend for downstream use as a des_ item — with a COMPLETE license + identity block. Any Detective ref_* / reference_media photo (or any Scout scout_*) you want used downstream — as a section image OR as a Cinematographer background — MUST be registered as a des_ item in items, with type:"asset" and tool:"wikimedia_download" (for a Detective ref_, copy its source_url/filename verbatim) or tool:"scout_fetch" + media_ref → its sct_xx (for a Scout asset). Registering it gives the photo a des_ id, so the Programmer tags it data-des and it resolves in the cinematic contract gate.
Image-richness target (cinematic) — MANDATORY on every topic (a SOFT count, not a quota). The cinematic scroll background is a mandatory stage, so every topic registers a real-image set to back it — aim for ≥5 (around 5–6) registered, verified, license-clean landscape (cover-able) backgrounds (sct_/des_ items with complete license + identity blocks). Under-supply (1 ≤ cover-able count < 5) is sent back to source ≥5 first (the cinematic-supply gate, mirrored in validate.py), not accepted as a finished state — on every topic, including an abstract/computational one (which gets the relevant historical/archival real photos described above). Reuse every fitting Detective ref_* and Scout scout_* real photo (registered per the rule above) plus the Scout's atmospheric fetch_stock.py landscape shots, rather than leaning on one hero image. Counting these is the Designer team's job at the cin_→des_ handoff (see Team coordination): if the Scout under-supplied, ask it for more cover-able shots before the Cinematographer runs. Never scrape purely-decorative stock that says nothing about the story just to clear 5 — every background must pass the S2/S4 contract AND be relevant (a relevant historical/archival real photo backing the cinematic is not decorative). Only if relevant real images genuinely cannot be found does the Cinematographer fall back to generative/data-driven backgrounds — but source the real set hard first.
SHARED CONTRACT (S2 — the manifest block): every photo you route downstream carries a complete, no-half-filled provenance block — {id, file, source_url, site, license:{spdx, permits_republication, requires_attribution, attribution_text}, identity:{method, verified, subject}}. The license.attribution_text must be non-empty (the exact credit line that will be rendered in the References footer), and for any real subject (a real person/trophy/venue/landmark) identity.verified must be true with the identity.subject named. A photo whose license or identity block is incomplete (empty attribution_text, or a real subject with identity.verified unset/false) is NOT eligible to be routed downstream — finish the block or drop the photo. (For a Scout sct_ asset this block already lives in scout.json; carry the media_ref → sct_xx so the Programmer/gate reads it from there rather than re-keying it.)
SHARED CONTRACT (S4 — registration-first): the Cinematographer picks backgrounds only from registered sct_/des_ ids (background.media_ref must resolve to a Scout sct_xx or a Designer des_xx); a background with no resolvable verified source is a hard error there. Every cinematic background must already be a registered des_/sct_ item in designer.json/scout.json BEFORE the Cinematographer references it via media_ref. The side cinematic_imagery_manifest.json is only a scratchpad — it is NOT the registry; resolving a background against that scratchpad instead of the role-JSON registry is the dangling-ref bug class that gave the gold blog 30 errors. So an unregistered raw ref_ photo handed to the Cinematographer cannot resolve — your job is to register every photo you route downstream BEFORE it is referenced (don't leave a Detective ref_ floating as a background with no des_ record, and don't rely on the manifest scratchpad to stand in for registration).
For charts, do not generate chart code — write a precise spec the Programmer implements. For interactive demos, write a step-by-step interaction spec. Both spec shapes are in references/visual_modes.json.
Step 3.5: Make every asset web-weight (before designer.json)
Media handed to the Programmer must be web-weight by default — a heavy page kills both performance and reach (virality happens on mobile). Before you finalize designer.json, run the helper on your assets folder:
python3 SKILL_DIR/scripts/optimize_assets.py PROJECT_DIR/assets/
Needs ffmpeg for audio/video — on PATH, or pip install imageio-ffmpeg (a self-contained wheel, no system install needed). Images need Pillow.
It transcodes/recompresses each asset in place to a streaming-friendly copy (suffix _web) and prints original -> optimized bytes; it uses ffmpeg/Pillow if available and skips gracefully with a warning if a tool is missing (it never crashes and never deletes the original). When an asset is optimized, reference the optimized _web copy from the item's filename (and from media_ref resolution) — not the heavy original. The helper does this repointing for you: when it writes a _web copy it automatically rewrites the owning scout.json/designer.json item's filename to the _web copy (so when the Auditor later removes the heavy original, validate.py's media gate still resolves). It prints which items it repointed; if you run it BEFORE writing designer.json, still set new designer items' filename to the _web copy yourself, and re-running after designer.json exists will repoint any item that still names an original.
Budgets (general, shared with the Auditor weight-gate): audio < 3 MB, single image < 600 KB, video < 3 MB. These are the defaults for any topic.
- A big Scout BGM (e.g. a multi-MB WAV/FLAC
scout_* track) MUST be transcoded to ~128 kbps mp3/opus before you reference it — point the audio item's filename at the optimized copy (keep its media_ref → the Scout sct_xx for license/identity).
- Any image over the budget gets resized to display width and saved as webp/jpg.
- If the helper skipped a file because a codec was unavailable, record that the original is over budget (so the Auditor's weight-gate catches it) rather than silently shipping the heavy file.
This is a default, not a checklist tick: pick the smallest copy that still looks/sounds right for the page.
Step 4: Page Visual Rhythm
Pick a concrete theme from ../../frontend-design-pro/references/themes.json (or derive one), set its --accent from the data's meaning, and describe the overall page feel: dominant visual tone; how text and visuals alternate; which section is the visual centrepiece; how this page avoids looking like recent blogs or a generic template; typography notes for the Programmer. For the type, pick a concrete pairing from ../../frontend-design-pro/references/typography.json (a file://-safe stack, not a default) and choose the finish details from ../../frontend-design-pro/references/finish.json. For motion intent — what should reveal, animate, or respond, and with what feel — read ../../frontend-design-pro/references/motion.json and record the intended choreography for the Programmer. Record the chosen theme + accent + type pairing and these notes in page_rhythm so the Programmer applies them consistently.
Be aware of ../../frontend-design-pro/references/quality_rubric.json throughout — it is the bar this design will be scored against, so make choices that clear it.
Output
Write PROJECT_DIR/designer.json — the single output.
references/schema.json — the full structure (meta.media_strategy, meta.media_decisions, meta.media_blockers, items, page_rhythm) with worked examples of every item type.
references/field_rules.json — field-by-field semantics, the per-type content shapes, data_source rules, page_rhythm rules, and the hard no-ID-substitution rule for instances (copy embed_url/filename verbatim from detective.json).
scout.-prefixed data_source grammar (gate-checked). When an item's data_source points at a scouted source, only two forms resolve at the contract gate: data_source: "scout.<sct_id>" — the suffix (or sct_ + the suffix) MUST name a registered sct_ item in scout.json — or data_source: "scout.live_status", valid only when scout.json carries a non-empty live_status list. Any other free-text scout.<…> now hard-errors (des_data_source_scout_dangling), so name a real sct_ id (or use scout.live_status) rather than an ad-hoc string.
Done when a Programmer can read designer.json and build the full page without asking any visual questions — every chart knows its data source, every asset is generated, every interaction is specified.
Team coordination — Designer team
You are the lead of the Designer team. Your members are the Interaction Engineer (the one narrative-bound centerpiece), the Hero (the animated cover), and the Cinematographer (the mandatory cinematic scroll background). You set the visual brief; they execute the centerpiece, the animated hero, and the cinematic layer against it; you integrate the result. You still record content.hero_strategy (the chosen rung + why), but the Hero role OWNS rendering the animated hero — you brief it, it builds it.
- Member calls (mirrored from the orchestrator), in order, after your
designer.json is complete:
Skill interaction PROJECT_DIR — Stage 4.5, designs the centerpiece (consumes the Analyst client_model; outputs interaction.json, int_ ids).
Skill hero PROJECT_DIR — Stage 4.6, renders the animated cover (walks the auto-select source ladder off your hero_strategy; outputs hero.json + assets/teaser.webm/_web.mp4/.jpg poster; reuses data-des="des_hero_video"). An animated cover is the DEFAULT; a static <img> is only its recorded fallback.
Skill cinematographer PROJECT_DIR — Stage 4.7, mandatory cinematic background (outputs cinematographer.json, cin_ scenes from the topic's real-image set; consumes the hero as cin_00).
All read designer.json, so finish and save it first. The Hero runs after the Interaction Engineer (it may render the backdrop behind an interactive teaser); the Cinematographer runs last (it reads interaction.json and stages the hero as cin_00).
- The media brief. In
designer.json (its page_rhythm / meta), flag the two things your members need: (1) which section is the centerpiece candidate — the contestable finding the reader should produce — so the Interaction Engineer knows where to put the one explorable; and (2) which verified sct_/des_ assets are available to stage as cinematic scene backgrounds (the cinematic is mandatory on every topic — including abstract/statistical ones, which are backed by the relevant historical/archival real photos), so the Cinematographer builds only from license-checked sources.
- You OWN the
cin_→des_ registration handoff. The Cinematographer picks backgrounds only from registered sct_/des_ ids; an unregistered raw ref_ photo cannot resolve in validate.py Section 6. So for every photo a cinematic scene needs, register it as a des_ item (per "Register every reused photo…" in Step 3 — type:"asset", with tool:"wikimedia_download" for a Detective ref_, or tool:"scout_fetch" + media_ref → its sct_xx for a Scout asset) so every cin_*.background.media_ref resolves and the Programmer can tag data-des/data-sct. Don't leave a background pointing at a floating photo with no des_ record.
- The media-purpose gate is the Designer team's exit gate. Before the team hands off to the Programmer, every item (yours, plus anything your members rely on) must carry a
purpose (INFORM or IMMERSE); decoration is cut. The orchestrator runs this gate at the team boundary.
This is coordination prose, not a new gate: your des_xx ids, the per-type content shapes, data_source rules, and the registration mechanics in Step 3 are all unchanged.