Your 5 pieces of feedback, sorted into already fixed (live on branch design/quick-filter-polish) and needs your call. Tap the demos; flip the toggles. Reply with your picks from your phone and I'll wire them up.
✓ Fixed in code◆ Needs your decision
1 Recipient picker presentation Decide
"The popover has a handle as though it's a mobile card when it isn't… it shouldn't be a drawer on mobile either. It should probably be a popover itself… a search field can feel more inline."
Agreed. The "Add recipients" picker is currently a bottom drawer with a grab handle — borrowed from mobile sheets, but it's really just "zoom into a search field." Three ways to fix it. Tap “+ add” in the demo to see each:
Centered dialog — no fake handle
Filter donorsClear all
Donated to
Any slate candidate ✕+ add
🔍 Search a candidate
Candidates
Samantha Kattan AD37 · slate+
Quick groups
Any slate candidate (18)Any opponent (21)
Tags
Slate FunderBoth Sides
Add recipientsDone
🔍 Search a candidate
Quick groups
Any slate candidate (18)Any opponent (21)
Candidates
Samantha Kattan AD37 · slate+
Johanna Ardila AD37 · opponent+
↑ Tap “+ add” to open · current toggle: Modal
Inline (C) — recommended
No overlay, no handle — reads as "expand the search"
You keep seeing the filter you're editing
Same on phone & desktop
Trade-off
Long candidate lists need an internal scroll cap
Slightly more layout shift in the panel
My pick: C (inline) for the quick-filter "Donated to", with B (modal) as the shared fallback the full query-builder still uses. Drop the grab handle everywhere it isn't a true swipe-to-dismiss sheet. If you'd rather keep it a popover that floats from the “+ add” pill, that's a close cousin of B — say the word.
2 Group-pill heights Fixed
"When the quick groups are on multiple lines they're different heights."
Cause: a pill alone on its row kept its natural height, while a pill sharing a row with the taller + add button got stretched to match it. Fixed by pinning the row alignment and the pill's font/line-height so every pill — span or button — computes the same box.
Before
Any slate candidate ✕
Any opponent ✕+ add
After
Any slate candidate ✕
Any opponent ✕+ add
3 Tags ↔ the query builder Decide
"Slate funders / anti-slate funders are really queries themselves — people who donated to ≥2 candidates in a category — but we have no way to surface that in the query builder."
Exactly right — and the good news: the engine already speaks this language. Every tag is a one-line condition over columns the query API already has:
Tag
Really means
As a query condition
Slate Funder
gave to >1 slate candidate
slate_count ≥ 2
Anti-Slate Funder
gave to >1 opponent
opponent_count ≥ 2
Both Sides
≥1 slate AND ≥1 opponent
both_sides flag
So the only thing missing is the UI bridge between the named chip and the editable query. Three options (they stack):
A "Edit as query" on a tag
Each tag chip gets a small ⋯ → "Edit as query", which opens the builder pre-filled with the equivalent condition. Teaches what the tag is, and lets you tweak it (e.g. bump "≥2" to "≥3", or add "…AND $1,000+").
⚑ Slate Funder ⋯
↓ Edit as query
Giving behavior
Gave toat least2 ▾slate candidates
B Behavior as a first-class condition
The builder's "Giving behavior" add-condition (already stubbed) offers these directly — so you can compose them from scratch: "gave to ≥2 slate candidates AND in AD-37 AND $250+". The tag and the condition become the same thing.
Add condition → Giving behavior
Gave to≥2 ▾slateopponent
Gave to both sideson / off
C Save a query as a segment — later
The reverse: build a query → "Save as segment" → it becomes a named chip you can reuse, like your saved Universes. Powerful, but overlaps the Universes feature you already have — so I'd defer it.
My recommendation: B then A (a hybrid). Make "Giving behavior" real (B) so the logic is first-class and composable, then point the tag chips' "Edit as query" (A) at the same conditions — so a tag and its query are one object at two zoom levels (the Linear / Amplitude-cohort model). Both are small because the columns already exist. Defer C until you're actually saving these. Which do you want — A, B, both (recommended), or all three?
4 "Build a query" button Fixed + pick icon
"Normal button height like the export buttons, and the icon shouldn't be an emoji."
Done: dropped the 🎛 emoji and the 40px height → now ~34px (export-button height) with a real stroke icon. I shipped the sliders + icon as the default; tap to compare and tell me if you prefer another.
"You had a sliding-toggle selector for geography in the mockups, but it's not reflected. Reference the originals, compare, and bring discrepancies in line."
I audited the original mockup against the shipped UI. Most segmented "sliding toggles" are implemented (Match ALL/ANY, Inside/Outside, Assembly/Senate/Congress). The gaps:
Element
Mockup
Shipped
Action
Geography active segment
solid-fill sliding pill
was a soft tint
Fixed
Geography options
All / In-district / NYC / US (4)
All / In-District / Out-of-State (3)
Your call
Build-a-query button
compact, icon
was 40px + emoji
Fixed
Location → area type (ZIP / City)
shown, "coming soon"
omitted
Defer
Match ALL/ANY · Inside/Outside · chambers
sliding toggles
implemented ✓
OK
Operator picker
inline tap
bottom-sheet list
OK (safer)
The one real question — Geography buckets. The mockup imagined 4 buckets; the browser shipped 3. Compare:
Now (shipped)
AllIn-DistrictOut-of-State
Mockup (4-way)
AllIn-districtNYCUS
Adding NYC (in NYC, outside the district) and US (rest of country) as their own buckets is a small data-pipeline change (every donor already has a location). Worth it only if you'd actually slice by "NYC-but-not-district" vs "out-of-state." Keep the simple 3, or restore the 4?
My take: keep 3 for now (All / In-District / Out-of-State is the 80% need) and restore the 4-way only if you want NYC-vs-rest-of-state cuts. Easy to add later.