For sales teams of 5 to 50
Change your pipeline today, not next quarter
Brindle is a hosted sales workspace for small B2B teams. Whoever runs sales can add a stage, a field or a view in a few clicks, and the team sees it at once.
Pipeline
A sales pipeline in four stages, lead, qualified, proposal and won. A won deal is closed and leaves the board, then the top card of each stage moves on to the next one, its stage dots filling as it lands, and a new lead comes in at the bottom.
19.5 || lr < -0.5 || lr > 4.5) return 0.0;
float a = 0.2 + 0.1 * lift;
if (lr < 0.5 && lc > 0.5 && lc < 11.5) a = st > 2.5 ? 1.05 : 0.72; // a won deal's title is marked
else if (lr > 0.5 && lr < 1.5 && lc > 0.5 && lc < 15.5) a = 0.42;
else if (lr > 1.5 && lr < 2.5 && lc > 0.5 && lc < 9.5) a = 0.42;
else if (lr > 2.5 && lr < 3.5) {
float d = (lc - 1.0) / 2.0;
if (lc > 0.5 && lc < 7.5 && abs(d - floor(d + 0.5)) < 0.1) a = floor(d + 0.5) < st + 0.5 ? 1.05 : 0.25;
if (lc > 12.5 && lc < 18.5) a = 0.72;
}
return a;
}
float closedLine(float lc, float lr) {
if (abs(lr) > 0.5 || lc < -0.5 || lc > 19.5) return 0.0;
if (lc < 2.5) return 0.25;
if (lc > 3.5 && lc < 11.5) return 0.42;
if (lc > 14.5) return 0.72;
return 0.0;
}
float field(vec2 p, float t) {
if (p.x < 24.0 || p.x > 696.0 || p.y < 24.0 || p.y > 436.0) return 0.0;
float c = floor((p.x - 4.0) / 7.0 + 0.5);
float k = floor(p.y / 9.4);
float v = 0.0;
{
float lc = c - 5.0;
if (abs(k - 7.0) < 0.5 && lc > -0.5 && lc < 19.5) v = max(v, lc < 3.5 ? 0.72 : 0.22);
if (t < 2.8) v = max(v, card(lc, k - 9.0, 0.0, 0.0));
float up = floor(6.0 * ease((t - 3.5) / 0.4) + 0.5);
for (int i = 1; i < 5; i++) v = max(v, card(lc, k - 9.0 - 6.0 * float(i) + up, 0.0, 0.0));
v = max(v, sm(3.9, 4.5, t) * card(lc, k - 33.0, 0.0, 0.0));
if (k > 40.5 && k < 42.5 && lc > -0.5 && lc < 19.5) { float cnt = 5.0 - step(2.8, t) + step(4.2, t); v = max(v, lc < 3.0 * cnt - 0.5 ? 0.72 : 0.2); }
}
{
float lc = c - 28.0;
if (abs(k - 7.0) < 0.5 && lc > -0.5 && lc < 19.5) v = max(v, lc < 3.5 ? 0.72 : 0.22);
if (t < 1.8) v = max(v, card(lc, k - 9.0, 1.0, 0.0));
float up = floor(6.0 * ease((t - 2.5) / 0.4) + 0.5);
for (int i = 1; i < 4; i++) v = max(v, card(lc, k - 9.0 - 6.0 * float(i) + up, 1.0, 0.0));
if (t >= 3.6) v = max(v, card(lc, k - 27.0, 1.0, 0.0));
if (k > 40.5 && k < 42.5 && lc > -0.5 && lc < 19.5) { float cnt = 4.0 - step(1.8, t) + step(3.6, t); v = max(v, lc < 3.0 * cnt - 0.5 ? 0.72 : 0.2); }
}
{
float lc = c - 51.0;
if (abs(k - 7.0) < 0.5 && lc > -0.5 && lc < 19.5) v = max(v, lc < 3.5 ? 0.72 : 0.22);
if (t < 0.8) v = max(v, card(lc, k - 9.0, 2.0, 0.0));
float up = floor(6.0 * ease((t - 1.5) / 0.4) + 0.5);
for (int i = 1; i < 3; i++) v = max(v, card(lc, k - 9.0 - 6.0 * float(i) + up, 2.0, 0.0));
if (t >= 2.6) v = max(v, card(lc, k - 21.0, 2.0, 0.0));
if (k > 40.5 && k < 42.5 && lc > -0.5 && lc < 19.5) { float cnt = 3.0 - step(0.8, t) + step(2.6, t); v = max(v, lc < 3.0 * cnt - 0.5 ? 0.72 : 0.2); }
}
{
float lc = c - 74.0;
if (abs(k - 7.0) < 0.5 && lc > -0.5 && lc < 19.5) v = max(v, lc < 3.5 ? 0.72 : 0.22);
{ float u = sm(0.15, 0.6, t); v = max(v, (1.0 - u) * card(lc, k - 9.0 + floor(1.5 * u), 3.0, 0.0)); }
float up = floor(6.0 * ease((t - 0.5) / 0.4) + 0.5);
for (int i = 1; i < 2; i++) v = max(v, card(lc, k - 9.0 - 6.0 * float(i) + up, 3.0, 0.0));
if (t >= 1.6) v = max(v, card(lc, k - 15.0, 3.0, 0.0));
if (k > 40.5 && k < 42.5 && lc > -0.5 && lc < 19.5) { float cnt = 2.0 - step(0.15, t) + step(1.6, t); v = max(v, lc < 3.0 * cnt - 0.5 ? 0.72 : 0.2); }
}
{
float lc = c - 74.0;
float e = ease((t - 0.3) / 0.5);
float dn = floor(2.0 * e + 0.5);
v = max(v, sm(0.3, 0.7, t) * closedLine(lc, k - 23.0 - dn));
for (int j = 0; j < 6; j++) v = max(v, closedLine(lc, k - 25.0 - 2.0 * float(j) - dn));
v = max(v, (1.0 - sm(0.3, 0.7, t)) * closedLine(lc, k - 37.0 - dn));
}
if (t >= 2.8 && t < 3.6) {
float u = ease((t - 2.8) / 0.8);
float cc = floor(5.0 + 23.0 * u + 0.5);
float kk = floor(9.0 + 18.0 * u - 1.0 * sin(3.14159 * u) + 0.5);
float m = card(c - cc, k - kk, 0.0, sin(3.14159 * u));
if (m > 0.0) v = m; // carried over the other cards, so it hides them
}
if (t >= 1.8 && t < 2.6) {
float u = ease((t - 1.8) / 0.8);
float cc = floor(28.0 + 23.0 * u + 0.5);
float kk = floor(9.0 + 12.0 * u - 1.0 * sin(3.14159 * u) + 0.5);
float m = card(c - cc, k - kk, 1.0, sin(3.14159 * u));
if (m > 0.0) v = m; // carried over the other cards, so it hides them
}
if (t >= 0.8 && t < 1.6) {
float u = ease((t - 0.8) / 0.8);
float cc = floor(51.0 + 23.0 * u + 0.5);
float kk = floor(9.0 + 6.0 * u - 1.0 * sin(3.14159 * u) + 0.5);
float m = card(c - cc, k - kk, 2.0, sin(3.14159 * u));
if (m > 0.0) v = m; // carried over the other cards, so it hides them
}
{ float g = t - 1.6;
if (g > 0.0 && g < 0.6) v += 0.55 * (1.0 - g / 0.6) * exp(-pow(length((p - vec2(592.0, 163.0)) / vec2(1.0, 0.55)) - 70.0 - 90.0 * g, 2.0) / 40.0);
}
return v;
}]]>
No code Stages, fields and views change by point and click.
Nothing to install We host it. Sign in and your workspace is ready.
Set up in a day Bring a spreadsheet in the morning, sell from Brindle after lunch.
Small teams change how they sell every month. Most CRMs change once a year.
Change backlog
Change requests arrive one after another and pile up in a basin with a shut gate in its floor; only at release does the gate open, some of the requests run out into shipped, and the rest stay behind to wait for the next one.
696.0 || p.y < 24.0 || p.y > 436.0) return 0.0;
float c = floor((p.x - 4.0) / 7.0 + 0.5);
float k = floor(p.y / 9.4);
float v = 0.0;
float open = sm(3.5, 3.6, t) * (1.0 - sm(4.4, 4.5, t));
// how many rows wait: rising while the gate is shut, run down to three while it is open
float level = 8.0 + 10.0 * clamp(t / 3.55, 0.0, 1.0) - 10.0 * ease((t - 3.6) / 0.8);
// the lane the requests come in on, dotted
if (abs(k - 8.0) < 0.5 && c > 3.5 && c < 24.5 && mod(c, 2.0) < 0.5) v = max(v, 0.2);
// the basin: two walls, the floor, and the gate in the floor, which slides aside to open
if ((abs(c - 18.0) < 0.5 || abs(c - 82.0) < 0.5) && k > 13.5 && k < 37.5) v = max(v, 0.72);
if (abs(k - 37.0) < 0.5 && c > 17.5 && c < 82.5) {
if (c > 45.5 && c < 54.5) { if (c - 46.0 >= 9.0 * open - 0.5) v = max(v, 1.05); }
else v = max(v, 0.72);
}
// the waiting requests, stacked like bricks, the oldest at the bottom densest
if (c > 18.5 && c < 81.5 && k < 36.5) {
float j = 36.0 - k;
float top = floor(level + 0.5);
// a request is a brick three cells long on every other row; the oldest row is densest, the newest row lit
if (j < top - 0.5 && mod(j, 2.0) < 0.5) {
float x = c - 19.0 + (mod(j, 4.0) < 0.5 ? 0.0 : 2.0);
if (mod(x, 5.0) < 3.5) v = max(v, j < 0.5 ? 1.05 : (j > top - 2.5 ? 0.72 : 0.42));
}
}
// the requests arriving: along the lane, then down onto the pile
for (int i = 0; i < 10; i++) {
float tt = mod(t - 0.5 * float(i), 5.0);
if (tt < 1.0) {
float u = ease(clamp(tt / 0.6, 0.0, 1.0));
float d = clamp((tt - 0.6) / 0.4, 0.0, 1.0);
float x = mix(39.0, 172.0 + 7.0 * mod(float(i) * 13.0, 50.0), u);
float ys = 338.4 - 9.4 * (level - 1.0) - 6.0;
float y = mix(79.9, ys, d * d);
v += blob(p, vec2(x, y), 9.0, 1.1 * sm(0.0, 0.08, tt) * (1.0 - sm(0.9, 1.0, tt)));
}
}
// the release: a stream out of the open gate into SHIPPED, which flashes as the batch lands
if (c > 47.5 && c < 52.5 && k > 37.5 && k < 40.5)
v = max(v, open * sm(3.6, 3.7, t) * (1.0 - sm(4.3, 4.4, t)) * (mod(k - t * 24.0, 2.0) < 1.0 ? 1.05 : 0.42));
if (abs(k - 41.0) < 0.5 && c > 37.5 && c < 62.5)
v = max(v, 0.25 + 0.8 * sm(3.75, 3.9, t) * (1.0 - sm(3.9, 4.8, t)));
// the wait ruler on the right wall: a tick every two rows
if (abs(c - 85.0) < 0.5 && k > 13.5 && k < 36.5 && mod(36.0 - k, 3.0) < 0.5) v = max(v, 0.25);
return v;
}]]>
Waiting on the vendor
Requests go into a queue and sit there until the next release. A few make it out. The rest wait another round.
Waiting on a person
Or only an admin or an outside consultant can touch the setup, and their calendar is full until next month.
Your records, your board and your rules stay in step
Save a deal in the table and its card is redrawn on the board, then any rule you set runs by itself.
Building blocks
Three blocks joined by live links: a data model table, a board view and a four step workflow. A record is saved in the table, its card on the board is redrawn, and the workflow runs its trigger, condition and two actions in order.
696.0 || p.y < 24.0 || p.y > 436.0) return 0.0;
float c = floor((p.x - 4.0) / 7.0 + 0.5);
float k = floor(p.y / 9.4);
float v = 0.0;
// the links between the blocks, and down the workflow: dashes that keep flowing
float flow = mod(c - 8.0 * t / 4.8, 2.0) < 0.5 ? 0.25 : 0.0;
if (abs(k - 26.0) < 0.5 && ((c > 30.5 && c < 35.5) || (c > 62.5 && c < 67.5))) v = max(v, flow);
// the change travelling: to the view, then to the workflow
{ float u = clamp((t - 0.75) / 0.4, 0.0, 1.0); if (u > 0.0 && u < 1.0) v += blob(p, vec2(mix(214.0, 263.0, ease(u)), 249.1), 8.0, 1.05); }
{ float u = clamp((t - 1.3) / 0.4, 0.0, 1.0); if (u > 0.0 && u < 1.0) v += blob(p, vec2(mix(438.0, 487.0, ease(u)), 249.1), 8.0, 1.05); }
// DATA MODEL: a header and eleven records; the saved one is row 26
if (c > 4.5 && c < 29.5 && k > 13.5 && k < 40.5) {
float lc = c - 5.0;
float r = k - 14.0;
float a = 0.0;
float h = hash(vec2(k, 3.0));
if (r < 0.5) a = lc < 2.5 ? 1.05 : (abs(lc - 3.5) < 0.6 || abs(lc - 12.5) < 0.6 || abs(lc - 18.5) < 0.6 ? 0.0 : 0.72);
else if (mod(r, 2.0) < 0.5) {
if (lc < 2.5) a = 0.72;
else if (lc > 3.5 && lc < 5.5 + floor(h * 6.0)) a = 0.42;
else if (lc > 12.5 && lc < 17.5) a = 0.25;
else if (lc > 18.5 && lc > 23.5 - 2.0 - floor(h * 4.0)) a = 0.42;
if (abs(k - 26.0) < 0.5 && a > 0.0) {
float tw = 0.2 + 0.5 * lc / 24.0;
a += 0.6 * step(tw, t) * (1.0 - sm(tw, tw + 1.0, t));
if (t > 0.2 && t < 0.7 && abs(p.x - mix(39.0, 207.0, (t - 0.2) / 0.5)) < 4.0) a = 1.15;
}
}
v = max(v, a);
}
// VIEW: a board, two lists of cards; the record's card is the left list's third
if (c > 36.5 && c < 61.5 && k > 13.5 && k < 40.5) {
float lc = c - 37.0;
float side = lc < 11.5 ? 0.0 : 1.0;
float cc = lc - 13.0 * side;
float r = k - 14.0;
float a = 0.0;
if (r < 0.5) a = cc < 3.5 ? 0.72 : (cc < 10.5 ? 0.22 : 0.0);
else if (r > 1.5 && cc > -0.5 && cc < 10.5) {
float slot = floor((r - 2.0) / 4.0);
float cr = r - 2.0 - 4.0 * slot;
if (cr < 2.5 && slot < 5.5 - side) {
a = 0.2;
if (cr < 0.5 && cc > 0.5 && cc < 6.5 + mod(slot, 2.0) * 2.0) a = 0.72;
if (cr > 0.5 && cr < 1.5 && cc > 0.5 && cc < 9.5 - mod(slot + side, 3.0)) a = 0.42;
if (cr > 1.5 && cc > 7.5 && cc < 9.5) a = 0.42;
if (side < 0.5 && abs(slot - 2.0) < 0.5) a += 0.55 * step(1.15, t) * (1.0 - sm(1.15, 1.15 + 1.0, t));
}
}
v = max(v, a);
}
// WORKFLOW: trigger, condition and two actions, joined by a flowing link; each step lights as it runs
if (c > 68.5 && c < 93.5 && k > 13.5 && k < 40.5) {
float lc = c - 69.0;
float a = 0.0;
if (abs(lc - 12.0) < 0.5 && k > 18.5 && k < 33.5) a = mod(k - 8.0 * t / 4.8, 2.0) < 0.5 ? 0.25 : 0.0;
if (k > 15.5 && k < 18.5 && lc > 3.5 && lc < 20.5) {
float lit = step(1.7, t) * (1.0 - sm(1.7, 1.7 + 1.0, t));
float r = k - 16.0;
float n = 0.2 + 0.22 * lit;
if (lc < 5.5) n = 0.72 + 0.35 * lit;
else if (r < 0.5 && lc > 6.5 && lc < 13.5) n = 0.72 + 0.35 * lit;
else if (abs(r - 1.0) < 0.5 && lc > 6.5 && lc < 10.5) n = 0.42 + 0.3 * lit;
else if (r > 1.5 && lc > 16.5 && lc < 19.5) n = 0.42 + 0.3 * lit;
a = max(a, n);
}
if (k > 21.5 && k < 24.5 && lc > 3.5 && lc < 20.5) {
float lit = step(2.0, t) * (1.0 - sm(2.0, 2.0 + 1.0, t));
float r = k - 22.0;
float n = 0.2 + 0.22 * lit;
if (lc < 5.5) n = 0.72 + 0.35 * lit;
else if (r < 0.5 && lc > 6.5 && lc < 16.5) n = 0.72 + 0.35 * lit;
else if (abs(r - 1.0) < 0.5 && lc > 6.5 && lc < 15.5) n = 0.42 + 0.3 * lit;
else if (r > 1.5 && lc > 16.5 && lc < 19.5) n = 0.42 + 0.3 * lit;
a = max(a, n);
}
if (k > 27.5 && k < 30.5 && lc > 3.5 && lc < 20.5) {
float lit = step(2.3, t) * (1.0 - sm(2.3, 2.3 + 1.0, t));
float r = k - 28.0;
float n = 0.2 + 0.22 * lit;
if (lc < 5.5) n = 0.72 + 0.35 * lit;
else if (r < 0.5 && lc > 6.5 && lc < 13.5) n = 0.72 + 0.35 * lit;
else if (abs(r - 1.0) < 0.5 && lc > 6.5 && lc < 13.5) n = 0.42 + 0.3 * lit;
else if (r > 1.5 && lc > 16.5 && lc < 19.5) n = 0.42 + 0.3 * lit;
a = max(a, n);
}
if (k > 33.5 && k < 36.5 && lc > 3.5 && lc < 20.5) {
float lit = step(2.6, t) * (1.0 - sm(2.6, 2.6 + 1.0, t));
float r = k - 34.0;
float n = 0.2 + 0.22 * lit;
if (lc < 5.5) n = 0.72 + 0.35 * lit;
else if (r < 0.5 && lc > 6.5 && lc < 16.5) n = 0.72 + 0.35 * lit;
else if (abs(r - 1.0) < 0.5 && lc > 6.5 && lc < 11.5) n = 0.42 + 0.3 * lit;
else if (r > 1.5 && lc > 16.5 && lc < 19.5) n = 0.42 + 0.3 * lit;
a = max(a, n);
}
v = max(v, a);
}
return v;
}]]>
1
Add the fields you need
Make a field for contract length or seat count in a minute. It shows up on every deal at once.
2
Lay out your board
Pick the stages, their order and what each card shows. Every rep can keep a saved view of their own.
3
Set simple rules
When a deal reaches proposal, assign a reviewer and send a reminder. You pick the trigger, the condition and the actions from lists.
What teams changed in their first month
Stages that match the sale
A coffee roaster that sells to cafés split its one trial stage into sample sent and tasting booked on its second day.
Sales lead, wholesale coffee roaster
No consultant on retainer
An office furniture dealer ended a monthly support contract once its sales manager could add fields herself.
Sales manager, office furniture dealer
A view for a new hire
A solar installer built a view for its first partner manager in an hour, on the day she started.
Founder, commercial solar installer
Made for the people who sell every day
Table filter
A table of ten deals with name, company, stage, owner, value and close date. A filter on the stage column is clicked, the seven rows that are not at proposal fade back and three remain; then the filter is cleared and every row returns.
696.0 || p.y < 24.0 || p.y > 436.0) return 0.0;
float c = floor((p.x - 4.0) / 7.0 + 0.5);
float k = floor(p.y / 9.4);
float lc = c - 6.0;
float v = 0.0;
if (lc < -0.5 || lc > 87.5) return 0.0;
float on = sm(0.45, 0.85, t) * (1.0 - sm(3.25, 3.65, t));
// the filter control above the stage column: a funnel tag and a short name, flashing when clicked
if (abs(k - 6.0) < 0.5 && lc > 35.5 && lc < 46.5) {
float flash = sm(0.3, 0.35, t) * (1.0 - sm(0.35, 0.7, t))
+ sm(3.1, 3.15, t) * (1.0 - sm(3.15, 3.5, t));
float a = lc < 37.5 ? 0.72 + 0.35 * on : (lc > 38.5 && lc < 44.5 ? 0.42 + 0.3 * on : 0.2);
v = max(v, a + 0.5 * flash);
}
// the header
if (abs(k - 10.0) < 0.5) {
if (lc > -0.5 && lc < 6.5) v = max(v, 0.72);
if (lc > 17.5 && lc < 24.5) v = max(v, 0.72);
if (lc > 35.5 && lc < 42.5) v = max(v, 0.72 + 0.35 * on);
if (lc > 50.5 && lc < 57.5) v = max(v, 0.72);
if (lc > 60.5 && lc < 67.5) v = max(v, 0.72);
if (lc > 75.5 && lc < 82.5) v = max(v, 0.72);
}
// the hover band: a faint lift behind one row at a time, drifting down and back up
float hover = 13.0 + 13.5 + 13.5 * cos(TAU * t / 4.8);
// the rows
float r = k - 13.0;
if (r > -0.5 && r < 27.5 && mod(r, 3.0) < 0.5) {
float i = floor(r / 3.0 + 0.5);
float st = i < 0.5 ? 0.0 : i < 1.5 ? 2.0 : i < 2.5 ? 1.0 : i < 3.5 ? 2.0 : i < 4.5 ? 3.0 : i < 5.5 ? 0.0 : i < 6.5 ? 1.0 : i < 7.5 ? 2.0 : i < 8.5 ? 3.0 : i < 9.5 ? 1.0 : 0.0;
float h = hash(vec2(i, 9.0));
float a = 0.0;
if (lc > -0.5 && lc < 14.5) a = lc - 0.0 < 6.0 + floor(h * 8.0) ? 0.72 : 0.0;
if (lc > 17.5 && lc < 32.5) a = lc - 18.0 < 4.0 + floor(hash(vec2(i, 2.0)) * 10.0) ? 0.42 : 0.0;
if (lc > 35.5 && lc < 47.5) { float w = st < 0.5 ? 4.0 : (st < 1.5 ? 9.0 : (st < 2.5 ? 8.0 : 3.0)); a = lc - 36.0 < w ? (lc - 36.0 < 0.5 ? 1.05 : 0.42) : 0.0; }
if (lc > 50.5 && lc < 57.5) a = lc < 52.5 ? 0.72 : (lc > 53.5 && lc < 55.5 ? 0.25 : 0.0);
if (lc > 60.5 && lc < 72.5) a = 72.0 - lc < 3.0 + floor(hash(vec2(i, 5.0)) * 7.0) ? 0.42 : 0.0;
if (lc > 75.5 && lc < 87.5) a = lc - 76.0 < 9.5 ? 0.25 : 0.0;
float match = abs(st - 2.0) < 0.5 ? 1.0 : 0.0;
if (match > 0.5 && lc > 35.5 && lc < 47.5 && a > 0.0) a += 0.35 * on;
a = mix(a, min(a, 0.15), on * (1.0 - match));
float lift = 0.2 * (1.0 - sm(0.6, 1.6, abs(k - hover)));
v = max(v, max(a, lift));
}
// the rules under the header and under the last row
if ((abs(k - 11.0) < 0.5 || abs(k - 42.0) < 0.5) && mod(lc, 2.0) < 0.5) v = max(v, 0.2);
return v;
}]]>
Find the right deals fast
Filter a column with one click and a list of ten becomes the three at proposal. Clear it and every row comes back.
Live sync
One field of a record is typed in; the change goes out from a sync hub to five screens that have the record open, and the same row on each screen is rewritten as its copy arrives.
696.0 || p.y < 24.0 || p.y > 436.0) return 0.0;
float c = floor((p.x - 4.0) / 7.0 + 0.5);
float k = floor(p.y / 9.4);
float v = 0.0;
// the record: a title bar, then a label and a value on each field row, a faint body behind
if (c > 4.5 && c < 26.5 && k > 14.5 && k < 33.5) {
float lc = c - 5.0;
float a = 0.2;
if (abs(k - 15.0) < 0.5) a = lc < 2.5 ? 1.05 : (lc < 12.5 ? 0.72 : 0.2);
for (int i = 0; i < 5; i++) {
float rk = 19.0 + 3.0 * float(i);
if (abs(k - rk) < 0.5) {
if (lc > 0.5 && lc < 6.5) a = 0.25;
float len = 6.0 + floor(9.0 * hash(vec2(float(i), 4.0)));
if (lc > 8.5 && lc < 9.0 + len) a = 0.42;
}
}
// the edited field: written by a hot head, fresh cells cooling; the caret blinks after its last letter
if (abs(k - 25.0) < 0.5 && lc > 8.5 && lc < 20.5) {
float x = 102.0 + 77.0 * (t - 0.2) / 0.6;
float tw = 0.2 + 0.6 * (lc - 9.0) / 11.0;
if (lc < 19.5) {
a = 0.42 + 0.63 * step(tw, t) * (1.0 - sm(tw, tw + 1.2, t));
if (t > 0.2 && t < 0.8 && abs(p.x - x) < 4.0) a = 1.15;
} else if (abs(lc - 20.0) < 0.5 && (t < 0.2 || t > 0.8)) {
a = mod(t + 0.22, 0.88) < 0.44 ? 1.05 : 0.0; // on across the seam
}
}
v = max(v, a);
}
// the hub: a dense core in a thin rim, a ring as it sends
{
vec2 H = vec2(270.0, 239.7);
float d = length(p - H);
v += 0.42 * exp(-pow(d - 22.0, 2.0) / 10.0) + blob(p, H, 11.0, 0.95 + 0.35 * sm(0.85, 0.95, t) * (1.0 - sm(0.95, 1.45, t)));
float g = t - 0.85;
if (g > 0.0 && g < 0.55) v += 0.6 * (1.0 - g / 0.55) * exp(-pow(d - 24.0 - 80.0 * g, 2.0) / 40.0);
// the edit's link into the hub
v += seg(p, vec2(194.0, 239.7), H - vec2(24.0, 0.0), 3.0, 0.22);
}
// the screens, each joined to the hub by a faint link that its copy travels
{
vec2 A = vec2(294.0, 239.7);
vec2 B = vec2(442.0, 89.3);
v += seg(p, A, B, 3.0, 0.2);
float u = clamp((t - 0.95) / 1.0, 0.0, 1.0);
if (u > 0.0 && u < 1.0) v += blob(p, mix(A, B, ease(u)), 8.0, 1.0 * sm(0.0, 0.1, u));
if (c > 63.5 && c < 92.5 && k > 5.5 && k < 11.5) {
float lc = c - 64.0;
float r = k - 6.0;
float a = 0.17;
if (r < 0.5) a = lc < 4.5 ? (mod(lc, 2.0) < 0.5 ? 0.72 : 0.0) : 0.25;
else if (r > 1.5 && r < 2.5 && lc > 0.5 && lc < 18.5) a = lc > 15.5 ? 0.0 : 0.42;
else if (r > 2.5 && r < 3.5 && lc > 0.5 && lc < 26.5 && (lc < 16.5 || lc > 21.5)) {
float tw = 1.95 + 0.25 * (lc - 1.0) / 26.0;
a = (lc > 21.5 ? 0.72 : 0.42) + 0.5 * step(tw, t) * (1.0 - sm(tw, tw + 1.2, t));
}
else if (r > 3.5 && r < 4.5 && lc > 0.5 && lc < 9.0) a = 0.42;
v = max(v, a);
}
}
{
vec2 A = vec2(294.0, 239.7);
vec2 B = vec2(442.0, 164.5);
v += seg(p, A, B, 3.0, 0.2);
float u = clamp((t - 0.95) / 0.8, 0.0, 1.0);
if (u > 0.0 && u < 1.0) v += blob(p, mix(A, B, ease(u)), 8.0, 1.0 * sm(0.0, 0.1, u));
if (c > 63.5 && c < 92.5 && k > 13.5 && k < 19.5) {
float lc = c - 64.0;
float r = k - 14.0;
float a = 0.17;
if (r < 0.5) a = lc < 4.5 ? (mod(lc, 2.0) < 0.5 ? 0.72 : 0.0) : 0.25;
else if (r > 1.5 && r < 2.5 && lc > 0.5 && lc < 18.5) a = lc > 15.5 ? 0.0 : 0.42;
else if (r > 2.5 && r < 3.5 && lc > 0.5 && lc < 26.5 && (lc < 16.5 || lc > 21.5)) {
float tw = 1.75 + 0.25 * (lc - 1.0) / 26.0;
a = (lc > 21.5 ? 0.72 : 0.42) + 0.5 * step(tw, t) * (1.0 - sm(tw, tw + 1.2, t));
}
else if (r > 3.5 && r < 4.5 && lc > 0.5 && lc < 14.0) a = 0.42;
v = max(v, a);
}
}
{
vec2 A = vec2(294.0, 239.7);
vec2 B = vec2(442.0, 239.7);
v += seg(p, A, B, 3.0, 0.2);
float u = clamp((t - 0.95) / 0.6, 0.0, 1.0);
if (u > 0.0 && u < 1.0) v += blob(p, mix(A, B, ease(u)), 8.0, 1.0 * sm(0.0, 0.1, u));
if (c > 63.5 && c < 92.5 && k > 21.5 && k < 27.5) {
float lc = c - 64.0;
float r = k - 22.0;
float a = 0.17;
if (r < 0.5) a = lc < 4.5 ? (mod(lc, 2.0) < 0.5 ? 0.72 : 0.0) : 0.25;
else if (r > 1.5 && r < 2.5 && lc > 0.5 && lc < 18.5) a = lc > 15.5 ? 0.0 : 0.42;
else if (r > 2.5 && r < 3.5 && lc > 0.5 && lc < 26.5 && (lc < 16.5 || lc > 21.5)) {
float tw = 1.55 + 0.25 * (lc - 1.0) / 26.0;
a = (lc > 21.5 ? 0.72 : 0.42) + 0.5 * step(tw, t) * (1.0 - sm(tw, tw + 1.2, t));
}
else if (r > 3.5 && r < 4.5 && lc > 0.5 && lc < 19.0) a = 0.42;
v = max(v, a);
}
}
{
vec2 A = vec2(294.0, 239.7);
vec2 B = vec2(442.0, 314.9);
v += seg(p, A, B, 3.0, 0.2);
float u = clamp((t - 0.95) / 0.8, 0.0, 1.0);
if (u > 0.0 && u < 1.0) v += blob(p, mix(A, B, ease(u)), 8.0, 1.0 * sm(0.0, 0.1, u));
if (c > 63.5 && c < 92.5 && k > 29.5 && k < 35.5) {
float lc = c - 64.0;
float r = k - 30.0;
float a = 0.17;
if (r < 0.5) a = lc < 4.5 ? (mod(lc, 2.0) < 0.5 ? 0.72 : 0.0) : 0.25;
else if (r > 1.5 && r < 2.5 && lc > 0.5 && lc < 18.5) a = lc > 15.5 ? 0.0 : 0.42;
else if (r > 2.5 && r < 3.5 && lc > 0.5 && lc < 26.5 && (lc < 16.5 || lc > 21.5)) {
float tw = 1.75 + 0.25 * (lc - 1.0) / 26.0;
a = (lc > 21.5 ? 0.72 : 0.42) + 0.5 * step(tw, t) * (1.0 - sm(tw, tw + 1.2, t));
}
else if (r > 3.5 && r < 4.5 && lc > 0.5 && lc < 13.0) a = 0.42;
v = max(v, a);
}
}
{
vec2 A = vec2(294.0, 239.7);
vec2 B = vec2(442.0, 390.1);
v += seg(p, A, B, 3.0, 0.2);
float u = clamp((t - 0.95) / 1.0, 0.0, 1.0);
if (u > 0.0 && u < 1.0) v += blob(p, mix(A, B, ease(u)), 8.0, 1.0 * sm(0.0, 0.1, u));
if (c > 63.5 && c < 92.5 && k > 37.5 && k < 43.5) {
float lc = c - 64.0;
float r = k - 38.0;
float a = 0.17;
if (r < 0.5) a = lc < 4.5 ? (mod(lc, 2.0) < 0.5 ? 0.72 : 0.0) : 0.25;
else if (r > 1.5 && r < 2.5 && lc > 0.5 && lc < 18.5) a = lc > 15.5 ? 0.0 : 0.42;
else if (r > 2.5 && r < 3.5 && lc > 0.5 && lc < 26.5 && (lc < 16.5 || lc > 21.5)) {
float tw = 1.95 + 0.25 * (lc - 1.0) / 26.0;
a = (lc > 21.5 ? 0.72 : 0.42) + 0.5 * step(tw, t) * (1.0 - sm(tw, tw + 1.2, t));
}
else if (r > 3.5 && r < 4.5 && lc > 0.5 && lc < 18.0) a = 0.42;
v = max(v, a);
}
}
return v;
}]]>
The whole team sees each edit
Change a deal and every open screen shows it within a second. Nobody refreshes, and nobody works from an old copy.
Command bar
Letters are typed into a command field and the list of actions below narrows to two; the top match is picked and run, and the owner field on the record beside it is written. Then the field clears and the list fills again.
0.5 && lc < 2.5) return 0.72;
if (lc > 3.5 && lc < 9.5 + floor(h * 18.0)) return 0.42;
if (lc > 44.5 && lc < 46.5) return 0.25;
if (lc > 47.5 && lc < 49.5) return 0.25;
return 0.0;
}
float field(vec2 p, float t) {
if (p.x < 24.0 || p.x > 696.0 || p.y < 24.0 || p.y > 436.0) return 0.0;
float c = floor((p.x - 4.0) / 7.0 + 0.5);
float k = floor(p.y / 9.4);
float v = 0.0;
float lc = c - 7.0;
float cleared = sm(3.4, 3.5, t);
// the command field: a search tag, the typed letters, the caret after the last one
if (abs(k - 12.0) < 0.5 && lc > -0.5 && lc < 51.5) {
float a = lc < 1.5 ? 0.72 : 0.2;
float n = step(0.3, t) + step(0.5, t) + step(0.7, t) + step(0.9, t);
n *= 1.0 - cleared;
float slot = (lc - 3.0) / 2.0;
if (lc > 2.5 && abs(slot - floor(slot + 0.5)) < 0.1 && floor(slot + 0.5) < n - 0.5) a = 1.05;
if (abs(lc - 3.0 - 2.0 * n) < 0.5 && mod(t + 0.24, 0.96) < 0.48) a = 1.05;
v = max(v, a);
}
if (abs(k - 14.0) < 0.5 && lc > -0.5 && lc < 51.5 && mod(lc, 2.0) < 0.5) v = max(v, 0.2);
// the list
{
float rk = 16.0 + 3.0 * floor((0.0 + 0.0 * ease((t - 0.55) / 0.2) + 0.0 * ease((t - 0.95) / 0.2) + 0.0 * ease((t - 3.5) / 0.4)) + 0.5);
if (abs(k - rk) < 0.5) {
float a = actionRow(lc, 0.0) * (1.0 - sm(0.55, 0.7, t) * (1.0 - sm(3.5, 3.9, t)));
v = max(v, a);
}
}
{
float rk = 16.0 + 3.0 * floor((1.0 + -1.0 * ease((t - 0.55) / 0.2) + 0.0 * ease((t - 0.95) / 0.2) + 1.0 * ease((t - 3.5) / 0.4)) + 0.5);
if (abs(k - rk) < 0.5) {
float a = actionRow(lc, 1.0) * 1.0;
float picked = sm(1.25, 1.35, t) * (1.0 - sm(3.4, 3.5, t));
if (lc > -0.5 && lc < 51.5) a = max(a + 0.3 * picked, 0.22 * picked);
a += 0.4 * sm(1.55, 1.6, t) * (1.0 - sm(1.6, 2.05, t)) * step(0.1, a);
v = max(v, a);
}
}
{
float rk = 16.0 + 3.0 * floor((2.0 + 0.0 * ease((t - 0.55) / 0.2) + 0.0 * ease((t - 0.95) / 0.2) + 0.0 * ease((t - 3.5) / 0.4)) + 0.5);
if (abs(k - rk) < 0.5) {
float a = actionRow(lc, 2.0) * (1.0 - sm(0.55, 0.7, t) * (1.0 - sm(3.5, 3.9, t)));
v = max(v, a);
}
}
{
float rk = 16.0 + 3.0 * floor((3.0 + -2.0 * ease((t - 0.55) / 0.2) + 0.0 * ease((t - 0.95) / 0.2) + 2.0 * ease((t - 3.5) / 0.4)) + 0.5);
if (abs(k - rk) < 0.5) {
float a = actionRow(lc, 3.0) * (1.0 - sm(0.95, 1.1, t) * (1.0 - sm(3.5, 3.9, t)));
v = max(v, a);
}
}
{
float rk = 16.0 + 3.0 * floor((4.0 + -2.0 * ease((t - 0.55) / 0.2) + -1.0 * ease((t - 0.95) / 0.2) + 3.0 * ease((t - 3.5) / 0.4)) + 0.5);
if (abs(k - rk) < 0.5) {
float a = actionRow(lc, 4.0) * 1.0;
v = max(v, a);
}
}
{
float rk = 16.0 + 3.0 * floor((5.0 + 0.0 * ease((t - 0.55) / 0.2) + 0.0 * ease((t - 0.95) / 0.2) + 0.0 * ease((t - 3.5) / 0.4)) + 0.5);
if (abs(k - rk) < 0.5) {
float a = actionRow(lc, 5.0) * (1.0 - sm(0.55, 0.7, t) * (1.0 - sm(3.5, 3.9, t)));
v = max(v, a);
}
}
{
float rk = 16.0 + 3.0 * floor((6.0 + -3.0 * ease((t - 0.55) / 0.2) + 0.0 * ease((t - 0.95) / 0.2) + 3.0 * ease((t - 3.5) / 0.4)) + 0.5);
if (abs(k - rk) < 0.5) {
float a = actionRow(lc, 6.0) * (1.0 - sm(0.95, 1.1, t) * (1.0 - sm(3.5, 3.9, t)));
v = max(v, a);
}
}
{
float rk = 16.0 + 3.0 * floor((7.0 + 0.0 * ease((t - 0.55) / 0.2) + 0.0 * ease((t - 0.95) / 0.2) + 0.0 * ease((t - 3.5) / 0.4)) + 0.5);
if (abs(k - rk) < 0.5) {
float a = actionRow(lc, 7.0) * (1.0 - sm(0.55, 0.7, t) * (1.0 - sm(3.5, 3.9, t)));
v = max(v, a);
}
}
// the change, from the picked row to the record
{ float u = clamp((t - 1.6) / 0.4, 0.0, 1.0);
if (u > 0.0 && u < 1.0) v += blob(p, mix(vec2(414.0, 155.1), vec2(460.0, 258.5), ease(u)), 8.0, 1.05); }
// the record: a title, five fields; the owner field is written by the action
if (c > 65.5 && c < 93.5 && k > 13.5 && k < 33.5) {
float rc = c - 66.0;
float r = k - 14.0;
float a = 0.18;
if (r < 0.5) a = rc < 2.5 ? 1.05 : (rc < 14.5 ? 0.72 : 0.18);
else if (r > 3.5 && mod(r - 4.0, 3.0) < 0.5) {
if (rc > 0.5 && rc < 7.5) a = 0.25;
float len = 5.0 + floor(10.0 * hash(vec2(r, 8.0)));
if (rc > 9.5 && rc < 10.0 + len) a = 0.42;
if (abs(k - 27.0) < 0.5 && rc > 9.5 && rc < 23.5) {
float tw = 2.0 + 0.4 * (rc - 10.0) / 13.0;
a = (rc < 11.5 ? 1.05 : 0.42) + 0.5 * step(tw, t) * (1.0 - sm(tw, tw + 1.2, t));
if (t > 2.0 && t < 2.4 && abs(p.x - mix(536.0, 627.0, (t - 2.0) / 0.4)) < 4.0) a = 1.15;
}
}
v = max(v, a);
}
return v;
}]]>
Type, pick, done
Start typing an action, pick the match and it runs. Hand a deal to a new owner without leaving the record.
Small teams that sell on Brindle
Wholesale coffee roaster
Twelve reps, one board
The roaster tracks café accounts from first sample to standing order, on stages its sales lead rewrote twice in week one.
“I used to send our old vendor a request and hear back in spring. Now I change it before lunch and tell the team at standup.”
Odile Marchetti Head of sales
Read the story
Office furniture dealer
Fields for every fit-out
The dealer added fields for floor size, move date and building access, and stopped paying a consultant each month.
“Nobody here writes code. We still rebuilt our whole pipeline in one afternoon, between calls.”
Kasper Lindqvist Sales manager
Read the story
Commercial solar installer
A view for each partner
Each channel partner gets a saved view of the deals it shares, set up by the founder in a few minutes.
“Our partners see their own deals and nothing else. I set that up between two site visits.”
Rhea Tannenberg Founder
Read the story
Before you move your team over
Do I need anyone technical to set it up? No. Stages, fields, views and rules are all point and click. If you can edit a spreadsheet, you can change Brindle.
Who can change the pipeline? Anyone you make an editor. Most teams give that to whoever runs sales, and everyone else keeps selling.
Will a change get in my team's way? No. A new field or stage shows up for everyone at once, and older deals keep their history.
How do we bring our deals in? Upload a CSV or a spreadsheet export. Brindle suggests which column goes where, and you check it before anything is saved.
Is there anything to install? No. We host Brindle for you. It runs in the browser, and there are apps for phones.
What size of team is it for? Teams of five to fifty who sell to other businesses. Below five, a spreadsheet often does the job.
What does Brindle cost? A flat price per seat each month, after a free fourteen day trial. People who only read are free.
Shape your pipeline today. Sell on it tomorrow.