1. make the +/-/[x] buttons work
   - RESOLVED. BoardPageController's board/random/search and QdbExperience
     now load /assets/thread_reactions.js, so the +/- buttons bind and
     update the score in place. Routine vote status ("Saving tag...") is
     overlaid in theme-qdb.css so it no longer grows the card; errors still
     expand it.
   - make them look more like qdb.us buttons
     - reference: https://web.archive.org/web/20100210200238/http://qdb.us/latest/5
   - confirmed (live, thread-20260526032027-014695b3): a quote's score can
     read nonsensical things like "(5/0)" - 5 in the numerator, 0 votes in
     the denominator. Cause: the quote's permalink page (/threads/<id>)
     doesn't render quote_card.php at all - it's the generic forum
     thread.php/thread_root_card.php template, which shows a plain
     "Like"/"Flag" button pair instead of qdb's upvote/downvote/flag.
     Liking it there adds +1 to score_total (TagScore counts 'like' as
     scored) but vote_count only counts 'upvote'/'downvote' tags
     (TagScore::voteTags()), so a like moves the numerator without ever
     touching the denominator. Two different tag families are both
     feeding one quote's score through two different buttons on two
     different pages.
   - likely real fix: give qdb permalink pages their own thread template
     with upvote/downvote/flag (matching quote_card.php) instead of
     reusing the generic Like/Flag one, so there's exactly one voting
     mechanism per quote. (Narrower patch - just adding 'like' to
     TagScore::voteTags() - would fix the ratio math but leaves two
     buttons able to move a qdb quote's score, which doesn't feel right
     for the classic-qdb experience.) Needs a decision before implementing.

2. figure out how to do quote number headers
   - RESOLVED (docs/plans/qdb_quote_numbering/qdb_quote_numbering_step1_solution_assessment.md
     through step4_implementation_summary.md). New qdb submissions now
     mint a `thread-<timestamp>-qdb-<N>` ID (N = one past the highest
     qdb-numbered quote so far, imported or live) from both real
     submission paths (plain createThread and the signed
     prepareThread/createPreparedPost flow), so quote_card.php's existing
     `#<N>` display picks them up with no template changes.

3. anon voting / why vote counts are all 0
   - just a note for now, not fixing on this branch.
   - score_total is gated on approved identity
     (LocalWriteService::isApprovedIdentity(), enforced in
     ReadModelBuilder/IncrementalReadModelUpdater's score loops) - a
     drive-by anon voter's vote never moves the score. Already assessed in
     docs/plans/anonymous_reaction_scoring/anonymous_reaction_scoring_step1_solution_assessment.md,
     recommendation is Option B (count upvote/downvote toward score
     regardless of approval, leave Like/Flag and the write path alone) -
     scoped to its own plan/branch, not this one.
   - vote_count (the denominator) is NOT approval-gated in the code as
     written - it should increment for any real vote regardless of
     approval. If it's still reading 0 once item 1's wiring bug is fixed
     and real clicks are happening, that's a separate thing worth
     rechecking rather than assuming it's the same approval-gating issue.
   - user wants to go further than the Option B assessment: auto-create
     a guest keypair for every visitor on first page load (behind a
     proof-of-work challenge), and let any identity's vote count toward
     the total immediately, persisting across reloads. Captured as its
     own future feature, not scoped to this branch -
     docs/plans/guest_identity_proof_of_work_voting.md. Sequencing:
     after the qdb archive importer, not before - see that plan's
     "Sequencing decision".


remove the theme selector so it doesn't detract from the look?