# CSS Reduction — Baseline (2026-06-23)

## Goal
Reduce `dist/assets/index-*.css` bytes without changing any tested screen.
One declaration/rule at a time, verified by pixel-identical screenshots
and project checks. This file is the baseline the iterative loop is
compared against.

## Build (the size we are trying to shrink)
| metric       | bytes  |
| ------------ | ------ |
| raw          | 60,695 |
| gzip         | 11,786 |
| brotli       |  9,875 |

`dist/assets/index-BznqBut5.css`, built with `npx vite build` (Vite 5.4.21,
8.9s).

## Test matrix (the screens we will hold pixel-identical)
- **Theme**: dark only (the app is `<html class="dark">` + `theme="dark"`
  for the Toaster; no light path exists in the design system).
- **Sizes**: 3 (desktop 1440×900, tablet 768×1024, mobile 375×812).
- **Routes**: 13 (`/`, `/upload`, `/inbox`, `/claims`, `/claims?status=denied`,
  `/remittances`, `/providers`, `/reconciliation`, `/acks`, `/batches`,
  `/batch-diff`, `/activity`, `/does-not-exist`).
- **Interactive states**: 6 (claim drawer open, remit drawer open, inbox
  row focused, search focused, keyboard cheatsheet open, upload dropzone
  focused) × 3 sizes = 18 captures.
- **Total**: 13 × 3 + 6 × 3 = **57 screenshots** per build.

Capture source: `audit-uiux-extended.mjs` (new, superset of
`audit-uiux.mjs`). Pixel-diff harness: `tools/css-screenshots-diff.mjs`
using `pixelmatch` (threshold 0).

## Project checks (the gates we hold green)
| check                       | baseline | used as gate? | reason                                  |
| --------------------------- | -------- | ------------- | --------------------------------------- |
| `npx vite build`            | passes   | **yes**       | produces the CSS bundle under reduction |
| `npx vitest run`            | 498 / 501 | **yes**       | pre-existing 3 failures, see "Known failures" below |
| `tsc -b` (in `npm run build`)| **fails**| no            | 15 type errors in test files + `Upload.tsx`, all pre-existing |
| `npm run typecheck`         | fails    | no            | same as above                           |
| `npm run lint`              | fails    | no            | `sh: eslint: command not found` (eslint not installed) |

### Known pre-existing failures (not caused by this work)
`vitest run` — 3 failing tests:
- `src/pages/Inbox.test.tsx > Inbox page > SP14: payer-rejected row count rolls up into the need-eyes header`
- `src/store/tail-store.test.ts > useTailStore > test_fifo_cap_evicts_oldest_when_over_10000`
- `src/components/inbox/InboxHeader.test.tsx > InboxHeader > renders the date and counts`
None touch CSS. Each candidate run will compare the new `vitest` exit
status and failure set to the baseline — a candidate is rejected if it
adds a new failure, even if the count stays at 3.

`tsc -b` / `npm run typecheck` — 15 errors in test files and
`src/pages/Upload.tsx`. Not exercised by the reduction loop.
`npm run lint` — `eslint` is not installed in `devDependencies`; the
script `npm run lint` exits 127. Not exercised.

## Interactive-state coverage
Out of 18 interactive captures, 15 succeed and 3 fail (the same 3 every
run):

- `remittances-drawer` × all sizes — the `/remittances` page renders with
  no table rows in the current backend state, so the row-click step finds
  nothing. The screenshot falls back to the static `/remittances` page
  with no drawer; pixel-identical comparison still works for that
  fallback, but the remittance-drawer CSS itself is not exercised. This
  is captured here as an **untested state** for the final report.

The other 15 interactions (`claims-drawer`, `inbox-row-focused`,
`search-focused`, `cheatsheet-open`, `upload-dropzone-focused` × 3 sizes)
all succeed. They exercise the CSS for the open claim drawer, the
keyboard cheatsheet overlay, the focused search button, the focused
upload dropzone, and the focus ring on inbox rows.

## Pre-existing noise (not regressions, not caused by this work)
From the baseline run (`baseline-report.json`):
- 6/57 flows have ≥1 console error. All from the `inbox` route and the
  `inbox-row-focused` state. The 4 interactive ones are the same
  source.
- 24/57 flows have ≥1 failed request. Likely the dashboard summary
  endpoint (`/api/dashboard/summary`) and a few analytics / SSE probes
  in the inbox.
- 0 page errors.

A candidate that does not increase these counts is fine; an increase
is informational, not a gate.

## Self-diff sanity check
`tools/css-screenshots-diff.mjs compare` with `BASE == CAND` returns
`PIXEL-IDENTICAL — 57 compared, 0 differing`. The harness works.

## What is in scope for the iterative loop
- Source: `src/index.css` (custom CSS + Tailwind base/components/utilities)
- Config: `tailwind.config.js`
- Component `.tsx` files (only if removing a Tailwind utility from a
  `className` produces a smaller bundle)

## What is out of scope
- `index.html` (Google Fonts preconnect, hard to prove pixel-identical
  after font source change)
- The JS bundle (separate goal)
- The backend (`backend/`)
- Any non-CSS build artifact
