Personal portfolio site for Taylor Bosier. Static HTML, CSS and vanilla JS.
No build step, no dependencies. GitHub Pages serves it straight from main.
index.html Single page: hero, metrics, decision systems, about,
experience, work, skills, education, contact
cv.html Resume page with an inline PDF viewer
404.html Styled not-found page
about.html Redirect to index.html#about (old links)
portfolio.html Redirect to index.html#work (old links)
pre-ai/ The site as it stood in May 2025, kept verbatim and
shown behind the Pre-AI switch. Marked noindex.
robots.txt
sitemap.xml
assets/css/styles.css Design tokens, one locked palette, light and dark, all layout
assets/css/door.css The collapse sequence and the consent terminal
assets/js/main.js Every behavior; one module per feature, no dependencies
assets/js/door.js The door: kills the page, boots a tty, waits for a keystroke
brain/ Taylor's Brain. A separate page with its own design
system; see below. Marked noindex.
resume/ The LaTeX source the PDF is built from
assets/resume/ The published resume PDF
assets/images/ Portrait, favicons, social card
Five tools under one tabbed section, in this order. The first three compute. Agents and Contracts do not, and the page says so.
- Capacity (computes) is a two-stage stochastic program with recourse over 400 sampled demand scenarios. The capacity constraint binds only the first stage, so the second stage separates by lane and the Lagrangian decomposes: bisect on the scalar dual until the per-lane newsvendor quantiles sum to the fleet. Reports EVPI and VSS off the same scenario set. It leads the tabs because it is the strongest of the three that compute.
- Pricing (computes) is a three-level Normal hierarchy fitted by empirical Bayes. Within-lane sums of squares give sigma-squared; tau-squared comes from a weighted moment equation solved by bisection, not from the variance of the lane means, which would count thin lanes' sampling noise as real spread. The quote is the precision-weighted blend.
- Dispatch (computes) is a pickup-and-delivery problem. Candidate routes are built by randomized cheapest insertion under precedence, capacity and a range limit; a greedy pass then covers every order with a disjoint subset of them. It is a heuristic with no LP relaxation and therefore no optimality gap, and the panel copy says that outright. Do not call it set partitioning. It does not always cover all eleven orders (seeds 51, 62, 68 and 98 serve ten); the readout reports the shortfall and the copy says it can happen. Time windows, service times and hours-of-service are not modeled. Nothing that is not enforced may be displayed as though it were a constraint.
- Agents (does not compute) is a drawn walkthrough of one invented
deduction case worked twice on one timeline: on top by an analyst agent (a tool
call to a SQL data agent that reads a knowledge base, four checks as parallel
tool calls, a question to the associate manager on Teams through Azure Bot
Service, what it learned written to a knowledge base a vector index reads), underneath by a person in
Power BI, Excel and a Teams message. It then pulls back to a queue: one agent
with every open case in flight above eight analysts working one case each;
every case on both sides waits on the manager.
The customer, prices, elapsed times and queue counts at the top of
initAgentsare invented, and the panel says the agents have no measured results yet. Two rules keep it honest. The manager answers the person and the agent equally fast (seeHUMAN_CLOCKagainstAGENT_CLOCK): the gap is the manual work, not a slower manager. And no employer's customers, volumes or headcount belong on that canvas. - Contracts (does not compute) is a hand-authored six-beat walkthrough
of the shape of a contract retrieval pipeline. Every constant at the top of
initContracts(chunk count, token counts, eligible revenue, tier rate, the customer) is invented, and must stay invented. No employer's corpus size, customer, or commercial terms belong on that canvas or anywhere else on this site. The panel carries a disclosure saying the data is not real, and the section intro names it and Agents as the exceptions to the other three. If you change those numbers, or make this panel compute for real, update both.
Every number in the first three readouts is measured off the solution being drawn. If you change a model, check the readout still agrees with the picture.
The site serves two readers. index.html is Taylor as observed from outside:
ordered, concise, and enough for a hiring manager in forty-five seconds. At the
bottom of it is one marked exit, [ enter Taylor's brain ], and everything past
that door is allowed to be art.
The weirdness is opt-in, and the opt-in is real. Clicking the door does not
navigate. It desaturates the portfolio, removes its navigation, fades it to
black, and boots a terminal that runs a fake pacman sync and then asks
Continue? [y/N]:. There is no button. The visitor types y on a physical
keyboard, or on a phone taps the black once to raise the on-screen one, and
types it there. n and Escape both print reasonable. and put the portfolio
back exactly as it was. Anything else prints error: expected a decision. and
asks again — and a key pressed while that prompt is reprinting is held, not
dropped, with Escape always taking priority over anything queued.
The exit is never not available, and that is a load-bearing claim. Escape
aborts at any point in the roughly sixteen-second boot, not only once the
prompt appears; on a touch device a tap on the black does the same. A refusal
tags its own cleanup, so pressing the door again during the 1.5s restore does
not leave the new run's terminal hidden. And a pageshow handler puts the page
back if a browser restores it from the back/forward cache mid-sequence.
brain/ is a single page of eight rooms, one mounted at a time from
data-room on <html>, each an independently scrolling field with its own
ground, its own entrance and its own personality. It deliberately does not
share styles.css: a darkroom that followed your light-mode preference would
not be a darkroom.
-
Fig. 001, the map. A branching ink drawing that is a fresh draw on every visit. The spine of each limb is a discrete Brownian bridge pinned at the origin and at that limb's terminal, with excursions past ±105 user units clipped so an unlucky draw cannot leave the plate — which is why the caption says clipped and not just bridge. The side growth is a branching process with Poisson(λ) offspring and λ = 1.75 × 0.62 per generation. Note that this is not subcritical: the first two generations have mean 1.75 and 1.09, and only from the third does the mean fall below one. Termination is guaranteed by the four-generation cap and a segment budget, not by the criticality, and the caption says so. The seven terminals are not sampled — they are the navigation. The seed is printed under the plate, and
?seed=<n>reproduces a draw you liked. -
Uncertainty — a posterior that breathes. The pointer supplies the data: across is the sample mean, up is the sample size. Conjugate normal update with known variance: prior N(0,1), each observation carrying variance 1, so the posterior precision is 1+n and the mean is n·x̄/(1+n). The band is the central 89% interval, drawn at z = 1.5982, the 0.945 normal quantile. Four scraps carry real writing; a fifth is blank on purpose.
-
Photographs — a contact sheet, cursor replaced by a working loupe.
-
Machines — a schematic of a forecasting pipeline with components you can inspect. Every figure on that plate is invented and the plate says so. The shape is real; the numbers are not, and no employer's data belongs there.
-
Familiars — the cats, hung as a formal exhibition.
-
Current obsession — Rust, as a man page crossed with a photocopied zine. Replace this room when the obsession changes; that is the point of it.
-
Opinions nobody requested — one opinion, argued properly, with the paper that supports it and the paper that complicates it, and a live exhibit that demonstrates the claim rather than asserting it.
The claim is not "charts should have transitions". Bars that tween on a filter change are table stakes; Power BI and Tableau have shipped that for years, and the essay concedes it out loud as act one. The claim is that a dashboard should be able to explain any number it shows, and an explanation is a sequence — so act two lets you click any bar and watch it come apart into the four terms that produced it, one at a time, landing back on the height you started from. A BI tool will not do that, because its unit of composition is the chart and the thing being explained is one mark.
Two rules keep the exhibit honest. The series is generated from those four terms, not typed in — see
COMPandSEASONin the opinions module — so the waterfall cannot drift out of agreement with the bar; change a term and every total changes with it. And both charts in act one carry the hover readout and the drill-down, so the only difference between them is the transition. Giving the animated one exclusive access to exact values would stop the comparison isolating animation, which is the exact confound the Tversky review it cites is about. -
Things which currently have no purpose — a dead end, and a note to future Taylor kept in
localStorageand sent nowhere.
There is also a curated shell on ` or the $_ in the corner. cd math
and friends are real navigation. sudo rm -rf self-doubt asks for a password
and, whatever you type, reports that self-doubt remains installed.
Placeholder media. The contact sheet pulls from picsum.photos and the cats
from cataas.com, both at runtime, both with an onerror fallback that says so
on the plate. Swap the photographs for real negatives when they are scanned;
the captions are already the real thing.
Constraints that still apply in here. Reduced motion collapses every
entrance and every transition — including, pointedly, the one chart that is
arguing in favour of animation, which then says on the page that it has just
undercut itself. Nothing uses innerHTML for text. No employer's data, corpus,
customer or commercial term appears anywhere, exactly as on the front of the
site.
There is nothing to install. To preview locally:
python3 -m http.server 8000
# then open http://localhost:8000Open a file, edit it, refresh. That's the whole workflow.
The résumé is authored in LaTeX at resume/tbosier_resume2.tex, in this repo,
so the source and the published PDF version together. Build it and copy the
result to where Pages serves it:
cd resume && pdflatex -interaction=nonstopmode tbosier_resume2.tex && cd ..
cp resume/tbosier_resume2.pdf assets/resume/tbosier_resume2.pdfThe build artifacts pdflatex leaves in resume/ are gitignored; the PDF is
committed only at assets/resume/, so there is one published copy and no
second one drifting behind it.
It fits on one page, and the preamble is tuned so it only just does. Adding a bullet will push Education onto page two. Check the page count before committing:
pdfinfo assets/resume/tbosier_resume2.pdf | grep PagesThen bump the "Last updated" line in cv.html. Both the nav button and the
hero's download button point at assets/resume/tbosier_resume2.pdf.
-
Nothing confidential. No employer's corpus sizes, customer names, contract terms, or internal counts, on the page, in the résumé, or in a canvas constant. Describe the work and the method; leave the scale vague.
-
Colors, spacing, radii, easing are CSS custom properties at the top of
styles.css. Change a token, not a rule. -
Dark mode redefines only the tokens under
:root[data-theme="dark"]. An inline script in each page's<head>sets the attribute before first paint so the page never flashes the wrong theme. -
Animation is opt-in per element: add
class="reveal"and it fades up when scrolled into view. Wrap a group indata-stagger="80"to cascade its direct.revealchildren 80ms apart. -
Reduced motion is honored globally: every animation collapses to its finished state under
prefers-reduced-motion: reduce. Test it before adding anything new. -
Metrics count up from zero:
data-count="1200"with optionaldata-prefix,data-suffix, anddata-decimals. -
Palettes are token sets. Each one owns a light and a dark pair, written as
:root[data-palette="x"]:not([data-theme="dark"])and:root[data-palette="x"][data-theme="dark"]so both sit at the same specificity. Anything filled with--brandtakes its text color from--on-brand, never a hard-coded white: in dark palettes--brandis light. -
Canvas modules must survive being measured at zero size, because a panel inside a closed tab reports no dimensions. Defer first paint until a real measurement arrives rather than returning early.
Copy any <article class="card reveal reveal--scale"> block in the #work
section, swap the icon path, title, description, and .tag list. The grid
reflows on its own.
The page is sectioned so this drops in without restructuring: add a
<section class="section" id="photos"> following the pattern of #work, and
add a matching <li> to the nav list in both index.html and cv.html.
Active-section tracking picks up any nav link whose href starts with #
and points at a real element, so no JS changes are needed.