# Marketing Desk — handover notes

Marketing automation: twelve destinations in three labelled groups, six record
screens, seven settings screens plus a personal profile, and the whole auth set,
in an inset-pane sidebar shell. Full detail in
`templates/marketing-desk/README.md`; this file records only the calls AGENTS leaves to
the builder.

## Calls taken

- **Creation lives where the thing lives, sized to what it needs.** A journey, a
  draft's ladder and an audience get their own staged screens; a broadcast, a
  template, a domain and an invitation get staged dialogs. The dividing line is
  whether the thing needs a preview or a repeatable sub-editor — not how
  important it is.
- **Step editing is a route, not a mode.** The journey record commits instantly
  (publish, pause, stop) and §3 refuses to mix an instant screen with staged
  controls, so `/journeys/:id/steps` is its own staged screen. Only a draft can
  reach it — a live journey has people standing in it.
- **The audience builder's preview runs the rules.** `SEGMENT_RULES` carries each
  rule's own predicate, and `evaluateSegment` runs them over a forty-contact
  sample; the screen states that the matched count is exact for the sample and
  the size is an extrapolation, rounded to ten because forty contacts cannot
  support more precision. The saved audience carries `pendingEvaluation` until
  the engine does the full list.
- **An invited teammate is not an approver.** They hold a role but cannot use it
  until they accept, so the team screen counts them separately and says so.
- **One predicate for "can this step send".** `stepSendProblem` in `enums.js` is
  read by the journey's Publish button and by the step editor's problem list, so
  the steps screen can never call a ladder finished while the record refuses to
  publish it. A step whose template is a draft, archived or deleted still shows
  that template in the editor, labelled unsendable — the control shows the
  record's truth rather than an empty field over a set value.
- **An estimated audience says so everywhere it appears** — the list block, the
  record's figures and its facts all carry "about" and name the sample until the
  engine has evaluated the full list.

- **Shell.** The request named it, and twelve destinations put it past §6.3's
  5–9 for one group, so the **sidebar** carries three labelled groups: what is
  being sent, who it goes to, and who is doing it. Recorded at the top of
  `nav.js`, with the routes that are registered but deliberately off the rail.
- **Separation.** The frame separates by tint and gap; inside the pane the
  product spends exactly one further axis — the card. A tile and a `Band` take
  the same surface, the same hairline and the same single shadow (`--shadow-xs`),
  and nothing takes a second inset or a heavier elevation. A `Band` also
  redefines `--gutter-page` to the card gutter within itself, which is how the
  record screens joined the card language without being rewritten.
- **State is never a border.** Tiles carry no warning or danger tint: a coloured
  edge reads as a permanent property of the container, and every one of these
  containers is a queue that is empty most days. State lives inside — the badge,
  the chip, the countdown, the figure's own tone.
- **Curves carry the data.** A donut decomposes one total whose slices sum to
  its centre, an arc gauge shows a rate against a stated ceiling, a smoothed
  spark shows a series, a pill meter a proportion whose track is a named number.
  Straight bars are kept for the one job that needs them: comparing days.
- **Cascade trap.** `ds-base.js` appends the design system's stylesheets at
  runtime, so they land after `kit.css` whatever the link order. Anything here
  overriding a bare `.o-*` element rule must out-specify it — see the table
  block, every selector of which carries `.o-table` for that reason.
- **Recession predicate** (§5.4): a record with no available transition. Journeys
  — draft and stopped. Broadcasts — sending and sent. Templates — archived. One
  rule, three entities.
- **Act-ability predicate**: `BROADCAST_FLAGS.isApprovableBy(broadcast, viewer)`
  — in review, not submitted by the viewer, and the viewer's role entitled to
  approve. Nav badge, overview section, row button, bulk bar and the team screen's
  own figure all call it, so a role change on `/settings/team` moves all of them.
- **Roles buy entitlements, not copy**: `ROLE_CAN_APPROVE` and `ROLE_CAN_EDIT` in
  `enums.js` are what the product reads; the role is only what somebody is given.
  The team screen shows both columns beside the role select, and refuses to save a
  state with no approver left.
- **Content usage is derived** from the journeys and broadcasts that name a
  template, so archiving one still in use is refused with the count rather than
  hidden.
- **Deliverability is deliberately not time-scoped**, because journey step counts
  are cumulative since publication and dating them would be a lie. The screen
  says so in words where a period control would otherwise sit.
- **Audience counts are materialised**, not computed from a contact fixture,
  because a segment count is a materialised number in a real product. Made
  checkable by the rule in words plus a five-member sample on the audience record
  rather than by drill-through — there is no contact directory in this set, and
  the record says so.
- **Journey step arithmetic** is the product's spine: `entered = completed + at +
  failed + exited` on every step, `entered` on a step equal to `completed` on the
  one above. Every journey figure and every meter reads it, and the stop commit
  preserves it.
- **Delivery series**: journey sends come from a daily log; broadcast sends come
  from the broadcasts themselves, added on the day each went out. Nothing is
  duplicated between the two, so the overview and the broadcasts table cannot
  disagree.
- **Failure threshold**: one number, `FAILURE_THRESHOLD` in `enums.js`, compared
  through `isOverFailureThreshold`, which compares at the precision the screens
  display. The journey step badge, the deliverability tone and the footnote count
  all read it, so a rate rendered as "2.0%" cannot be toned on one screen and not
  another, and a count of flagged rows always matches the rows on screen.
- **A send in flight is not a failure.** `isPartial` requires a settled send;
  what a running broadcast has not handed off yet is pending. Only `sent` and
  `failed` contribute to the failure figures.
- **Period options** are 7/14/30 days, because thirty days is how much daily
  delivery history the fixture holds. No 90-day option offering sixty empty days.
- **Auth**: sign-in plus the whole password reset flow — request, sent
  confirmation, set-new behind the token, expired token, outcome — because §7.5
  wants all of them and three would leave a user holding a dead link. Password
  strategy, no providers (no marks in this product's folder, and §7.4 refuses
  hand-drawn ones). One `AuthShell` frames all six so they read as siblings.
  `PASSWORD_RULES` is one list read by both the checklist and the submit gate.
  Signup, MFA and lockout are still unbuilt and unlinked.

## Not built

Signup · MFA and OTP · account lockout · verify-email · accept-invite · a contact
directory · a template composer · per-record screens for templates and domains.
Nothing links to any of them.
