Bug Fixes and convergence testing

@adamhooper This PR addresses the bugs you caught last time (e.g. it uses a better font stack, clearer names, etc). Re the most substantive issue—how to stream from the server—it sends a blob of data every time the server processes 100 documents. This is roughly analogous to having the server send new data every x milliseconds. But doing it after a certain number of documents, rather than after a certain number of milliseconds, was more convenient: first, because timers are a pain, but also because it simplified my implementation of convergence testing (by simplifying the logic for predicting when the doc set will converge).

Re the convergence testing: obviously, this isn't statistically valid until the docs are coming back in a random order. But I figured I'd get the code ready anyway. On the Pitchfork doc set @jstray gave me, the wordcloud converged (meaning that the cosine similarity between wordcloud n and wordcloud n+1 was > .9999) after only 2,800 docs. (It's a 13,200 document set.) As a result, the server was able to return the "final" data in ~6 seconds, as opposed to ~17 seconds before. So we got a 3x speedup, even though we're only processing 1/5 as many docs. We didn't get a bigger bump because we're processing the token data (i.e. finding the top tokens and, now, computing computing the cosine similarity) more often; now we're doing it once per 100 docs until convergence, whereas before we were only doing it the 5 times we were responding. It'll be interesting to see how we can set `incrementSize` to minimize total response time (i.e. balance fewer times processing the tokens against converging after reading fewer docs).
So, `incrementSize` isn't just a proxy for time. It's also saying with what data to recalculate the cosine similarity (namely, with 100 more docs). 

The current convergence logic says to assume convergence as soon as we get wordcloud[n] and wordcloud[n+1] within some similarity to each other. In other words, we're saying "if adding x random docs didn't change the picture much, assume that adding the rest of the docs wouldn't either". That assumption only makes sense if the x docs themselves are reasonably representative of the remaining ones. That's why I'm using 100 docs—and, really, I should probably be setting `incrementSize` as some percentage of the docCount, maybe with an absolute min and max. 

If we recalculated the similarity after y time, we might end up doing so with only 2 new docs (as you point out) and, if those two docs are unrepresentatively similar to the prior ones we've processed, we could report convergence prematurely.

So, re your points about how the current model can break:
- If the docs stream faster than expected, the client can just ignore the extra incoming data
- If the docs stream slower than expected, we couldn't send the progress faster anyway because, as discussed above, we need to wait for x more docs to come in before recalculating the similarity and convergence rate.

Another solution to all this would be to complicate the convergence testing logic (e.g. look at cosine similarity changes over a larger window), which could probably be set up to work no matter how many new docs we processed in y time. But I was hoping to avoid that complexity. And, also, as I'll discuss in my response to Jonathan, I'm not sure that consistently-timed responses is really what we want anyway.