Sentiment & churn @switch (activePanel()) { @case ('signals') { @if (voiceBlocked() > 0) { {{ voiceBlocked() }} voice signal(s) are disabled: scoring a live audio stream needs an LLM that can run on it, and none is selected. Text scoring is unaffected. } } @case ('escalation') { Evaluated top down; the first match wins. Every target is a defined tier — “senior” has to mean something specific before a rule can route to it.
    @for (trigger of triggers; track trigger.id) {
  1. }
} @case ('urgency') { Impact against time-sensitivity, decided once as policy instead of judged per conversation. {{ interruptVolume() }} conversations a week land in the cell that interrupts the queue — that is the cost of the top-left rule.
impact ↓ / urgency → @for (time of impacts; track time) { {{ time }} } @for (impact of impacts; track impact) { {{ impact }} @for (time of impacts; track time) { @if (cell(impact, time); as item) { } } }
} @case ('memory') { {{ crossChannelCount() }} of {{ memory.length }} memory kinds follow the customer across channels. That is the whole point: nobody should have to say the same thing twice because they switched app. Worked example
    @for (step of memoryExample; track step.step) {
  1. {{ step.step }} {{ step.carried }} @if (step.avoided === '—') { — } @else { avoided: {{ step.avoided }} }
  2. }
} @case ('intent-map') { Volume is the bar, sentiment is the colour. A step can be busy, well understood, and still be where people give up angry — that is what the overlay is for. } } @switch (activePanel()) { @case ('signals') { @if (selectedSignal(); as signal) {

{{ signal.name }}

{{ signal.channels }}

Signal

{{ signal.description }}

@if (signal.caveat) {
Caveat {{ signal.caveat }}
}
Composite

Scores are normalised over the active weight, so disabling a signal re-weights the rest rather than lowering every score.

} @else { Select a signal. } } @case ('escalation') { @if (selectedTrigger(); as trigger) {

{{ trigger.condition }}

→ {{ tierName(trigger.targetTierId) }}

Trigger

{{ trigger.note }}

@if (tierOf(trigger.targetTierId); as tier) {
Target tier

{{ tier.definition }}

} Edit routing and groups } @else { Select a trigger. } } @case ('urgency') { @if (selectedCell(); as item) {

{{ item.impact }} impact · {{ item.timeSensitivity }} urgency

{{ urgencyBandLabels[item.band] }}

Cell
Typical case {{ item.example }}
Cost of interrupting

Everything promoted here pushes something else back. Widening the “interrupt” band is a staffing decision, not a settings change.

} @else { Select a cell. } } @case ('memory') { @if (selectedMemory(); as kind) {

{{ kind.kind }}

{{ kind.scope }}

Policy

{{ kind.why }}

Identity caveat

Cross-channel memory is only as good as the identity match. Once WhatsApp usernames roll out, a phone number stops being a reliable key — ask for an email or mobile on first contact instead.

} @else { Select a memory kind. } } @case ('intent-map') { @if (selectedNode(); as node) {

{{ node.label }}

{{ node.stage }}

Node
@if (hotspot(); as hot) {
Worst hotspot {{ hot.label }} — {{ hot.dropOff }}% stop here at sentiment {{ hot.sentiment }}. Losing people who are already unhappy is the most expensive drop-off there is.
} See intent → outcome by flow } @else { Select a node. } } }