diff --git a/Context.md b/Context.md
index de3bf58..d3330af 100644
--- a/Context.md
+++ b/Context.md
@@ -173,8 +173,9 @@ Get-ChildItem tests\test_*.py | ForEach-Object { "$($_.Name): $((python $_.FullN
this machine, with ODBC Driver 18 for SQL Server.
- Tests run offline against fake HTML pages in `tests/fixtures/` and fake DB
connections; they never touch eCW or SQL Server (`test_cdp_browser.py`
- starts a local Chromium on a debug port). As of 2026-10-23: 71 test
- files, **1211 tests**, all passed in the full run (earlier flaky, each
+ starts a local Chromium on a debug port). As of 2026-10-26: 71 test
+ files, **1215 tests** (full run 2026-10-23: 1211/1211; the 4 new Portal
+ tests and every Portal test file passed 2026-10-26) (earlier flaky, each
failed once and passed on rerun: test_verify_no_live_writes,
depression_dx_pointer, progress_note_wait — watch them) (about 5 minutes for all of them).
@@ -428,6 +429,21 @@ noted in Comments (Pass); Billing Notes unreadable or sliding fee UNKNOWN →
### Results database + web page (since 2026-10-08)
The same Charge Summary rows also go to SQL Server, and a local page shows
them. Spec: `docs/superpowers/specs/2026-10-08-results-db-and-web-page-design.md`.
+**Portal look since `a436489` (2026-10-26, operator: "konjam attractive,
+content add, grid correct, header footer align"):** header, breadcrumb,
+page and footer share one 1180px column; header has a "CV" mark,
+tab-style links with the current page marked (`aria-current`), a user
+avatar; a footer at the bottom of every page (brand, PHI note, mode).
+Dashboard adds the pass rate on Checked today, a **Last 7 days** Pass /
+Fail / Review chart (a day links to its page), **Today's Fail and Review
+reasons** (comments grouped with counts; the extra `day_results` query
+runs only when today has claims) and three equal cards (Claim status,
+Runs, Find a claim). Motion is movement only, never opacity (a tab the
+browser holds in the background must not stay faded), and off under
+`prefers-reduced-motion`; still no inline `style=` attributes (a test).
+Checked in the Browser pane against a made-up-data preview (port 8765,
+a scratchpad script; no SQL, no real users), desktop and phone width.
+The running Portal shows it after `run_web.bat` is restarted.
- Server `MECARE331\MECARE331`, database `Charge_Validation`, table
`dbo.CV_ChargeSummary` (every table of this project starts with **`CV_`**,
operator 2026-10-12): the format's 24 columns + `run_at`, `run_mode`
@@ -586,7 +602,9 @@ them. Spec: `docs/superpowers/specs/2026-10-08-results-db-and-web-page-design.md
History: `git log --oneline`.
-Last code commits: `8fea168` (2026-10-23, "Chrome open" box before the
+Last code commits: `a436489` (2026-10-26, Portal look: aligned header /
+footer, 7-day chart, today's reasons, pass rate — §5), `2d4d4fc`
+(2026-10-23, tracked files de-identified — §7), `8fea168` (2026-10-23, "Chrome open" box before the
login and the empty first tab closed, in a Chrome the run opened — §2),
`1c7eb55` (2026-10-23, #25: G0468 → Review when the preventive visit
history is not checked — the Cigna plans), `ac25f83` (2026-10-23, #25 `medicare_pps_code` — PPS
@@ -671,7 +689,7 @@ Status lives in Jira; the open work is in §6 and §10.
| EEWA-15 | bh_h_code_modifier (#12 H codes) | Done (closed 2026-10-20) |
| EEWA-16 | medicare_billing_screening (#14; rulings pending) | Done (closed 2026-10-20) |
| EEWA-17 | Results database and web page | Done (closed 2026-10-20) |
-| EEWA-18 | Claim-by-claim run stability (clean page, stacked dialogs, ghost form) | In Progress |
+| EEWA-18 | Claim-by-claim run stability (clean page, stacked dialogs, ghost form) | Done (closed 2026-10-26: 50/50 runs 22/10 and 23/10) |
| EEWA-19 | medicaid_uncheck_cpts (#43 15851/15852, #44 73521; Medicaid only, not RAE — ruled 2026-10-14) | Done (closed 2026-10-20) |
| EEWA-20 | dental_dummy_codes (#48 DPIMP on Dental payers) | In Progress |
| EEWA-21 | injection_admin_code (#20 add 96372; only the operator's 41 codes — ruled 2026-10-14) | In Progress |
@@ -684,9 +702,9 @@ Status lives in Jira; the open work is in §6 and §10.
| EEWA-28 | medicare_vaccine_a6 (#19 G0008/G0009 on Medicare → condition code A6; UB done, HCFA Review — no mapping needed, Medicare = UB) | To Do |
| EEWA-30 | medicaid_pregnancy_th (#23: TH modifier on a pregnancy visit on Medicaid) | To Do |
| EEWA-29 | medicare_wellness_history (#27/#28/#29: G0402 / G0438 / G0439 billed too often → Review) | To Do |
-| EEWA-31 | run_events + Runs page (Stage 1) | In Progress (built 2026-10-22, `751a177`; seen on four live runs that day, incl. a full 50-claim Portal run) |
+| EEWA-31 | run_events + Runs page (Stage 1) | Done (closed 2026-10-26; built `751a177`, seen on every live run since 2026-10-22) |
| EEWA-32 | portal sign-in (Stage 2: Portal Users, Roles, Started By / Stopped By); blocked by EEWA-31 | In Progress (built 2026-10-22, `c230a8e`; live Sign-in and a Portal Start with Started By 2026-10-22; Viewer not yet tried) |
-| EEWA-33 | portal run alerts (Stage 3: Run Error banner, `started_by` column); blocked by EEWA-32 | In Progress (built 2026-10-22, `ecbbf3a`; `started_by` seen on all 50 rows of the 18:47 Portal run; banner not yet seen live) |
+| EEWA-33 | portal run alerts (Stage 3: Run Error banner, `started_by` column); blocked by EEWA-32 | Done (closed 2026-10-26; built `ecbbf3a`; banner seen after the 19:42 run, `started_by` on the Portal runs' rows) |
| EEWA-34 | medicare_pps_code (#25) — spec | To Do (parent of the work below) |
| EEWA-35 | #25 PPS Code check — Review when missing | In Progress (built 2026-10-23, `ac25f83`; not run live) |
| EEWA-36 | #25 add the PPS Code in LIVE-WRITE; blocked by EEWA-35 | In Progress (built 2026-10-23, `ac25f83`; not run live) |
diff --git a/tests/test_web_app.py b/tests/test_web_app.py
index 35e4ff0..750ba7c 100644
--- a/tests/test_web_app.py
+++ b/tests/test_web_app.py
@@ -9,6 +9,7 @@ Claim numbers here are made up.
"""
import io
+import re
import sys
import json
import pathlib
@@ -227,6 +228,63 @@ def the_dashboard_tiles_are_todays_counts():
in html
+@case
+def the_dashboard_shows_todays_pass_rate():
+ today = date.today()
+ html = _client(FakeRepo(days=[_day(today, 30, 2, 24, 5, 1)]))[0] \
+ .get("/").text
+ assert '80% pass' in html
+ html = _client(FakeRepo(days=[]))[0].get("/").text
+ assert 'class="rate"' not in html, "no claims today: no rate"
+
+
+@case
+def the_dashboard_shows_the_last_seven_days_oldest_first():
+ today = date.today()
+ days = [_day(today - timedelta(days=i), 10 + i, 1, 8, 2, i % 2)
+ for i in range(9)] # newest first, as repo
+ html = _client(FakeRepo(days=days))[0].get("/").text
+ assert 'id="week"' in html
+ shown = re.findall(r'data-day="([\d-]+)"', html)
+ assert shown == [(today - timedelta(days=i)).isoformat()
+ for i in range(6, -1, -1)], shown
+ assert f'href="/day/{(today - timedelta(days=3)).isoformat()}"' in html
+ assert 'data-n="13"' in html, "a day's claims count"
+
+
+@case
+def the_dashboard_lists_todays_top_fail_and_review_reasons():
+ run = datetime.combine(date.today(), datetime.min.time()) \
+ + timedelta(hours=10)
+ tele = "Telehealth (POS 02, modifier 93 on 99213)"
+ rows = ([_row(f"90000{i}", "Fail", i, run, comments=tele)
+ for i in range(1, 4)]
+ + [_row("9000005", "Fail", 5, run, comments="ICD "),
+ _row("9000006", "Review", 6, run, comments="Could not search"),
+ _row("9000007", "Pass", 7, run, comments="Clean claims")])
+ html = _client(FakeRepo(days=[_day(date.today(), 6, 1, 1, 4, 1)],
+ rows=rows))[0].get("/").text
+ box = html.split('id="reasons"', 1)[1].split("", 1)[0]
+ assert box.index(tele) < box.index("ICD <pointer>") \
+ < box.index("Could not search"), box
+ assert '3' in box, "grouped, with its count"
+ assert "Clean claims" not in box, "Pass is not a reason"
+ html = _client(FakeRepo(rows=[]))[0].get("/").text
+ assert "No Fail or Review today" in html
+
+
+@case
+def every_page_has_the_footer_and_marks_where_you_are():
+ client, _ = _client()
+ for path in _PAGES:
+ html = client.get(path).text
+ foot = html.split('