Synthesize
The post-session sense-making tool: drop in transcripts, board photos, and recordings, and get back themes, decisions, and action items you would actually send. Reflect phase.
How to use this page
Synthesize has no ws_* helpers of its own. Every screen is built from JavaScript template strings in synthesize/synthesize.js, so the real contract is the class names in synthesize/synthesize.css. This page renders those classes against that stylesheet — change the CSS and these specimens move with the app.
synthesize.css..synth scope
synthesize.css is scoped under .synth. Specimens here are wrapped accordingly. If a block on this page renders unstyled, it is missing its wrapper. The three exceptions — .synth-modal, .synth-overlay, .synth-topbar — are top-level and marked where they appear.App Identity
--phase-learn
Red Unification: Synthesize sits in the Reflect phase by name only. Like every app on the platform it renders in the Learn red family — the legacy Reflect emerald is retired.
Token Aliases
synthesize.css opens with a block of short local variables aliased onto platform tokens, so the app's rules stay terse. Anything without a platform token behind it is listed as such — those are the honest gaps, not drift.
| Local | Resolves to | Notes |
|---|---|---|
--accent / --accentD / --accentVL | --phase-learn / -dark / -light | Unified brand red |
--accentL | none — #FCA5A5 | Red-300 step between light and main; no platform token exists |
--ink / --ink2 / --muted / --faint | --color-ink* ramp | Warm ink |
--hair | none — #C4BCB0 | Hairline between faint ink and border |
--bgPage / --surface / --subtle | --bg-page / --bg-surface / --bg-subtle | |
--border / --inner | none — #E8E3DC / #F0EBE4 | Warm borders; the platform only defines a cool border token |
--ok | none — #047857 | Decision green — the one non-red semantic in the app |
--sticky | none — #FEF9C3 | Rendered sticky-note yellow; functional, mirrors the source board |
--rCard / --rModal / --rBadge | --radius-xl / -2xl / -sm |
App Frame
Every working screen wears the same frame: sources rail, working surface, and the AI panel when open. All three render inside #synth-app so the app's single delegated click/drag handler keeps working.
--bg-page, so page and surface read as one sheet. Grid cell and line colour both come from platform tokens.Anatomy
.sy-app — the grid. Two columns; a third 340px column appears above 1440px when the AI panel opens..sy-rail — sticky sources rail, --app-rail-width (320px)..sy-main — working surface. Drafting grid, hairline border, --rCard radius..sy-main__bar — row one: where you are (back link or tabs) + how to add material..sy-tools / .sy-tool — 38px icon rail; labels live in .sy-tool__tip on hover..sy-main__foot — hint on the left, committing action on the right.Responsive behaviour
| Width | Frame | AI panel |
|---|---|---|
| ≥ 1440px | Three columns | Grid column, 340px |
| 1081–1439px | Two columns | Fixed sheet on the right edge, under the chrome via --synth-stage-top |
| ≤ 1080px | Single column, rail last | Bottom sheet, 78vh |
Sources
What you gathered. The rail persists through the whole session, so after synthesis it is still how you get back to where a theme came from.
Themes
The clustering output — now rendered by the recap-first surface, not the specimens this section used to show.
.theme-card, .themes-grid, .theme-chev, .theme-top, .theme-dot, .theme-title, .theme-notes, .theme-badges, .theme-points, .themes-bar, the bare .evidence/.ev-card containers, the theme card's .menu/.menu-wrap overflow menu, and the "Alternate layouts" .kanban/.kan-* and .outline/.ol-* specimens that lived here were deleted from synthesize.css when Task 11 (recap-first redesign closeout) confirmed zero remaining callers.The current theme surface is
.recap-theme/.recap-theme__head/.recap-theme__title/.recap-evidence/.recap-evidence__toggle (the "View sources (N)" disclosure) — shared verbatim between the Recap document (synthesize/js/modules/recap.js) and Workspace's Themes tab (synthesize/js/modules/workspace.js). The inner evidence-row pieces (.ev-text/.ev-by/.ev-foot/.ev-quote/.src-chip) are unchanged and still live.
Priorities
Three lenses on the same themes. A theme sits in the tray until it has been placed under the current lens — an empty grid is honest about the fact that nobody has judged anything yet.
.dot-card is drag-positioned in percentages, so the grid stays responsive.Action Items & Decisions
The two facts that decide whether an action happens — who owns it and when it is due — now render inline in the recap document, not as the grid cards this section used to show.
.act-grid, .act-card (open/done/unassigned), .act-progress, .dec-grid, and .dec-card were deleted from synthesize.css when Task 11 confirmed zero remaining callers — the card-grid layout they demoed is gone, replaced by a one-document read (#recap-actions/#recap-decisions in synthesize/js/modules/recap.js).The reusable, non-hover-scoped inner pieces are unchanged and still live, just nested in the new
.recap-action/.recap-decision containers instead: .act-body, .act-text, .act-meta, .act-field, .act-check, .carried-chip, .dec-check, .dec-text.
Quick Summary
The first tab, and the first thing anyone reads. Numbered so it reads as an ordered take rather than a bag of bullets, and flagged as a draft in words rather than by implication.
- 1The group is hungry for clearer decisions — three separate threads all point at the same gap.
- 2Status meetings are the most-named drag on the team's time.
- 3The offsite-is-too-long tension is loud enough to flag up.
#synthTopbarRedraft), shown only while Quick Summary is the active tab and disabled while a rewrite is in flight. It runs over the board as it stands after your edits, not the material the first pass saw.Synthesizer AI
A chat grounded in the board you have open. It reads the themes, decisions, and actions; nothing it says is written into the synthesis unless you copy it there yourself.
.sy-ai (the fixed-position side panel, both its base rule and its <1080px sheet override) and its .ai-head/.ai-head__icon/.ai-title title-bar chrome were deleted from synthesize.css when Task 11 confirmed zero remaining callers — Synthesizer AI is now Workspace's own "Ask Synthesizer" tab, embedded inline in the content column (synthesize/js/modules/workspace.js) rather than a flyout, so it needs no fixed positioning or its own close-button header.Everything else below is unchanged and reused verbatim by that tab:
.ai-log, .ai-empty, .ai-suggestions/.ai-suggest, .ai-msg/.ai-avatar, .ai-bubble (including --typing and .is-error), .ai-composer. The specimen below still renders on the live stylesheet.
Review & Loading
The steps between dropping material and seeing a board.
- check_circleReading your sources
- radio_button_checkedClustering the notes
- radio_button_uncheckedNaming the themes
- radio_button_uncheckedPulling out decisions and actions
.loading wrapper, which centres the steps in a 56vh grid on the real screen.No decisions captured
Add one below if the group committed to something.
Navigation
components.js only auto-binds .ws-tabs on DOMContentLoaded, and Synthesize renders these later — so the active state stays the app's to manage, which it already did.ws_segmented.Adopted from the Platform
includes/header.php loads components.css on every page, so all 58 platform components were already present inside Synthesize — the app just re-implemented a handful of them privately. Those private copies are gone. The PHP ws_* helpers still can't be called from a client-rendered app, so synthesize.js emits the same class names directly; small mirrors of the templates live next to icon() in that file.
| Was | Now | What stayed local, and why |
|---|---|---|
.btn, --accent, --sm, --ghost | .ws-btn + --primary/--secondary/--ghost + sizes | Only the Material Symbols sizing — ws_button ships inline SVG, the app emits ligatures |
.sy-skel + its shimmer | .ws-skeleton + 9 variants | Nothing. Emitted as <div>: the component sets no display, so a span would collapse |
.guardrail | .ws-alert--warning / --error | .synth-alert--spaced, for an alert sitting inside a modal body |
.empty | .ws-empty-state | Nothing. Loading lines moved to .load-hint — they were never empty states |
.badge, .pill | .ws-badge, .ws-pill | .pri-badge and .cat-pill: both carry a colour set inline from app state, so they only make the text legible on top of it |
.tabrow / .tab | .ws-tabs--underline | Nothing. This also retired the reset Synthesize needed against the global .tab pill rule |
.synth-modal / .synth-overlay → .ws-modal: needs the focus trap in components.js checked against Synthesize's own handling first.(The
.sy-ai → .ws-modal--drawer row that used to sit here is moot: .sy-ai was deleted 2026-08-09 — Synthesizer AI is a Workspace tab now, not a panel to adopt a drawer shell for. See the Synthesizer AI section above.)
.seg. There is no ws_segmented to adopt, which makes it the strongest candidate for the next real component — not a swap, a promotion.Stays Local
These have exactly one consumer. Promoting them to ws_* would be speculative generality and would add drift-check burden for no reuse — so they stay in synthesize.css, documented here instead.
| Component | Classes | Why it stays |
|---|---|---|
| App frame | .sy-app, .sy-rail, .sy-main, .sy-tools | Planner and Facilitator have their own shells with different anatomy |
| Theme/evidence card | .recap-theme, .recap-evidence, .ev-text/.ev-by/.ev-foot/.ev-quote | Evidence-traced clustering output; nothing else on the platform produces it. (Renamed 2026-08-09 from .theme-card/.theme-points/.evidence/.ev-card, retired with the old five-tab board.) |
| Priority matrix | .matrix, .dot-card, .prio-tray, .prio-chip | Drag-positioned 2×2 tied to the framework definitions in synthesize.js |
| Source artifact card | .tray-item, .tray-item--session | Shape is dictated by the artifact types Synthesize ingests |
| AI panel | .ai-msg, .ai-bubble, .ai-composer | Chat is Synthesize-only today — revisit if a second app grows one. (No longer .sy-ai: that fixed side-panel container was retired 2026-08-09; these classes now live inline in Workspace's Ask Synthesizer tab.) |
| Summary card | .sum-card, .sum-num, .sum-flag | Numbered draft list; close to ws_checklist but semantically different |
Do / Don't
ws_app_bar. The canvas is pure workspace..res-head duplicated the doc-bar. A test asserts it never comes back.Shared Components Used
Two ws_* helpers are called from the PHP shell. The rest of the platform components reach the app as classes, emitted from synthesize.js — the helpers can't be called from a client-rendered body, but the CSS is already on the page either way.
| Component | How it gets there | Usage |
|---|---|---|
ws_app_bar() | PHP, synthesize/app.php | Session name, undo/redo, library, AI, Export, Save, New |
ws_header() | PHP, synthesize/app.php | Platform nav above the shell ($ws_header_section = 'synthesize') |
.ws-btn | Class, from JS | Every button in the app — 66 call sites |
.ws-tabs--underline | Class, from JS | The five results tabs |
.ws-badge / .ws-pill | Class, from JS (wsBadge, wsPill) | Priority bands, category tags, the muted marker |
.ws-alert | Class, from JS (wsAlert) | The material-size guardrail; board-import errors |
.ws-empty-state | Class, from JS (wsEmpty) | Empty summary, actions, decisions, workshop list, saved list, detached source |
.ws-skeleton | Class, from JS (wsSkel) | Boot and saved-list skeletons |
Files
| File | Purpose |
|---|---|
synthesize/app.php | Shell: header, app bar, #synth-app mount |
synthesize/synthesize.js | Every screen, rendered from template strings |
synthesize/synthesize.css | All app styles, scoped under .synth |
api/synthesize.php | Synthesis, persistence, imports, and the Synthesizer AI chat action |
synthesize/tests/synthesize.spec.js | Playwright E2E over the shell, tabs, priorities, and the AI panel |