diff --git a/docs/ui-modernisation/visual-correction/DISCREPANCIES.md b/docs/ui-modernisation/visual-correction/DISCREPANCIES.md
index 8247fb40..8821e39b 100644
--- a/docs/ui-modernisation/visual-correction/DISCREPANCIES.md
+++ b/docs/ui-modernisation/visual-correction/DISCREPANCIES.md
@@ -12,6 +12,10 @@ Owner correction: 3 November 2026. This register extends IM10 and the original 3
| V06 | Remaining routes | Whole-application modernisation has not received current route-by-route visual review | OPEN; all 34 routes explicitly tracked in ROUTE_COVERAGE.json | Review actual screens against A3 and resolve material differences; do not infer completion from shared styles |
| V07 | Connected workflows | Existing accepted journeys predate this visual correction | OPEN | Discovery→review→track; CV create/import→edit→preview→export; application→status→follow-up; Career Profile→document reuse; desktop/mobile |
| V08 | Final acceptance | Automated checks cannot establish owner visual approval or human/device/native-language accessibility | OPEN | Consolidated final visual review and accurately recorded outstanding qualifications |
+| V09 | Secondary views | Duplicate Offer Comparison heading and Operations section card around task cards | Implemented bounded correction; 61 component tests passed; 36-configuration successor matrix in progress | Inspect successor phone/tablet/desktop captures and record full result before qualification |
+| V10 | Account | Continuous account navigation/profile/password sections retain full surrounding surface cards | CONFIRMED by source plus accepted IM09 account capture; relevant ProfilePage/security/session/theme sources unchanged from773c9431; current shared shell differs and is qualified separately | Review and flatten continuous sections in next eligible account package, preserving draft/jump/security/session/credential boundaries; fresh affected rendered qualification |
+| V11 | Offer localisation | Literal date-only values are formatted through local-time Date conversion | REPRODUCED for supported string fixture input: 2026-12-01 formats as12/1/2026 in Europe/Oslo and11/30/2026 in America/Los_Angeles. Actual API model also permits timestamp values, whose precision/timezone must not be guessed | Next bounded fix must preserve literal calendar dates while retaining existing local formatting for genuine timestamps; require timezone regression proof. No claim that deployed API records use date-only serialization |
+| V12 | Applications | Current nine-width capture set still shows a collection border around mobile job cards and a large outlined application-detail header; passing axe does not establish A3 visual parity | CONFIRMED by actual `evidence/applications/table-en-light-320.png` and `detail-en-light-1440.png`; full36 journey/144 scan matrix passes functional/accessibility scope | Next core-workflow visual package: flatten collection/header hierarchy and improve density, retain column preferences, selection/bulk actions, every action, query/return state, revisions and right-side context. Compare approved B1/A3 before changing interactions |
The owner-approved design foundation remains original A1 dashboard, D1 visual system, B1 tracking, editor left / preview right, application context right, desktop sidebar and hybrid mobile navigation. No controls, imports, revisions, permissions or provider rights may be removed to obtain visual simplicity.
diff --git a/docs/ui-modernisation/visual-correction/SECONDARY_REVIEW.md b/docs/ui-modernisation/visual-correction/SECONDARY_REVIEW.md
new file mode 100644
index 00000000..15b238ae
--- /dev/null
+++ b/docs/ui-modernisation/visual-correction/SECONDARY_REVIEW.md
@@ -0,0 +1,16 @@
+# Secondary route visual review — in progress
+
+4 November 2026. Runtime starts at published/verified `2218659ecf692e8e377dd33a9ce6415b01e43032`. The initial visual run was deliberately interrupted after 35 baseline captures when material discrepancies were identified. These images are observations, not a complete passing matrix; see `evidence/secondary/BEFORE_INTERRUPTED.json`.
+
+| Route | Actual phone review | Implementation / preservation |
+|---|---|---|
+| /companies | One card per company, relationship actions retained; no outer card around the collection | Retain meaningful company object boundary; desktop table and relationship paths remain |
+| /offers | Page heading repeated below the shell heading | Removed redundant local heading; retained explanation, All Offers action, named results region, unknown facts and individual offer objects; object headings now h2 under page h1 |
+| /operations | Open task cards were enclosed within another section card | Removed outer section surfaces, retaining named sections, individual notification/task objects, progress, permission-dependent actions, polling and focus/error/committed-write protections |
+| /analytics | Independent chart/widget surfaces and explicit period/source explanations | Retain distinct data objects; do not remove necessary scope qualifications to save space or imply historical conversion |
+| /trash | Existing filters and selection/pagination within results boundary; no additional nested collection card | Empty fixture only; real restore/delete and populated selection remain separately qualified |
+| /cv/:slug | Public document route owns its paper and download link | Retain document boundary; no app sidebar/identity fetch introduced; synthetic preview is not physical PDF or deployed anonymous-access acceptance |
+
+PASSED:61 selected component tests/five suites, export build and full lint after bounded Offer Comparison / Operations changes. The 36-configuration successor run writes to `evidence/secondary-after` and is **IN PROGRESS**. Do not claim216 passing captures until its final report succeeds. Source census and independent source review must be reconciled with the successor before publication.
+
+Remaining screenshots and desktop/tablet layouts require inspection. These six routes do not cover all 34 routes or their loading/error/populated variants. Application Add Job/table/board/detail visual matrix is prepared as an opt-in extension of the existing verifier. Its **one-configuration pilot passed** four scans/captures and the controlled search→board→precise status→detail/context→return journey plus Add Job open/Escape and three alias URL checks, with one intercepted fixture status write. The complete36-configuration matrix remains **NOT RUN** until recorded otherwise. A pilot is not full qualification. No new application provider, model, migration or live action is included.
diff --git a/docs/ui-modernisation/visual-correction/SECONDARY_TESTS.json b/docs/ui-modernisation/visual-correction/SECONDARY_TESTS.json
new file mode 100644
index 00000000..51da04fa
--- /dev/null
+++ b/docs/ui-modernisation/visual-correction/SECONDARY_TESTS.json
@@ -0,0 +1 @@
+{"numFailedTestSuites":0,"numFailedTests":0,"numPassedTestSuites":5,"numPassedTests":61,"numPendingTestSuites":0,"numPendingTests":0,"numRuntimeErrorTestSuites":0,"numTodoTests":0,"numTotalTestSuites":5,"numTotalTests":61,"openHandles":[],"snapshot":{"added":0,"didUpdate":false,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0},"startTime":1793748328085,"success":true,"testResults":[{"assertionResults":[{"ancestorTitles":[],"duration":273,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"shows persistent operation and notification states with accessible actions","invocations":1,"location":null,"numPassingAsserts":11,"retryReasons":[],"startAt":1793748331584,"status":"passed","title":"shows persistent operation and notification states with accessible actions"},{"ancestorTitles":[],"duration":61,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"shows honest empty and failure states","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793748331857,"status":"passed","title":"shows honest empty and failure states"},{"ancestorTitles":[],"duration":34,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"localizes operation controls and empty states in Norwegian Bokmål","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793748331918,"status":"passed","title":"localizes operation controls and empty states in Norwegian Bokmål"},{"ancestorTitles":[],"duration":66,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-08 operations] initial failure offers single-flight GET Retry without false empty or writes","invocations":1,"location":null,"numPassingAsserts":12,"retryReasons":[],"startAt":1793748331952,"status":"passed","title":"[P7-08 operations] initial failure offers single-flight GET Retry without false empty or writes"},{"ancestorTitles":[],"duration":88,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-08 operations] cached refresh failure preserves known rows and does not fabricate empty results","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793748332018,"status":"passed","title":"[P7-08 operations] cached refresh failure preserves known rows and does not fabricate empty results"},{"ancestorTitles":[],"duration":72,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-08 operations] same-render duplicate Cancel reserves exactly one mutation and blocks competing work","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793748332107,"status":"passed","title":"[P7-08 operations] same-render duplicate Cancel reserves exactly one mutation and blocks competing work"},{"ancestorTitles":[],"duration":93,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-08 operations] same-render duplicate Retry reserves exactly one mutation and blocks competing work","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793748332179,"status":"passed","title":"[P7-08 operations] same-render duplicate Retry reserves exactly one mutation and blocks competing work"},{"ancestorTitles":[],"duration":92,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-08 operations] same-render duplicate Mark read reserves exactly one mutation and blocks competing work","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793748332272,"status":"passed","title":"[P7-08 operations] same-render duplicate Mark read reserves exactly one mutation and blocks competing work"},{"ancestorTitles":[],"duration":93,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-08 operations] same-render duplicate Dismiss reserves exactly one mutation and blocks competing work","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793748332364,"status":"passed","title":"[P7-08 operations] same-render duplicate Dismiss reserves exactly one mutation and blocks competing work"},{"ancestorTitles":[],"duration":63,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-08 operations] language and ordinary rerenders reformat cached data without refetch or polling restart","invocations":1,"location":null,"numPassingAsserts":9,"retryReasons":[],"startAt":1793748332457,"status":"passed","title":"[P7-08 operations] language and ordinary rerenders reformat cached data without refetch or polling restart"},{"ancestorTitles":[],"duration":46,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-08 operations] 15-second polling is single-flight and resumes after the held pair completes","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793748332520,"status":"passed","title":"[P7-08 operations] 15-second polling is single-flight and resumes after the held pair completes"},{"ancestorTitles":[],"duration":78,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-08 operations] mutation invalidates a held old poll before adopting the returned same-ID DTO","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793748332566,"status":"passed","title":"[P7-08 operations] mutation invalidates a held old poll before adopting the returned same-ID DTO"},{"ancestorTitles":[],"duration":157,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-08 operations] committed cancel plus failed refresh announces truth and GET Retry cannot replay or unlock stale writes","invocations":1,"location":null,"numPassingAsserts":10,"retryReasons":[],"startAt":1793748332644,"status":"passed","title":"[P7-08 operations] committed cancel plus failed refresh announces truth and GET Retry cannot replay or unlock stale writes"},{"ancestorTitles":[],"duration":99,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-08 operations] committed204 Mark read updates notices locally even when refresh fails without completing a task","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793748332801,"status":"passed","title":"[P7-08 operations] committed204 Mark read updates notices locally even when refresh fails without completing a task"},{"ancestorTitles":[],"duration":85,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-08 operations] committed204 Dismiss updates notices locally even when refresh fails without completing a task","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793748332900,"status":"passed","title":"[P7-08 operations] committed204 Dismiss updates notices locally even when refresh fails without completing a task"},{"ancestorTitles":[],"duration":117,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-08 operations] failed POST preserves eligibility and current-locale recovery without false commit or new GET","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793748332985,"status":"passed","title":"[P7-08 operations] failed POST preserves eligibility and current-locale recovery without false commit or new GET"},{"ancestorTitles":[],"duration":65,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-08 operations] server action admission overrides displayed status and unknown metadata remains honest","invocations":1,"location":null,"numPassingAsserts":12,"retryReasons":[],"startAt":1793748333102,"status":"passed","title":"[P7-08 operations] server action admission overrides displayed status and unknown metadata remains honest"},{"ancestorTitles":[],"duration":7,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-08 operations] unmount aborts a pending read and late completion cannot fetch or publish","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793748333167,"status":"passed","title":"[P7-08 operations] unmount aborts a pending read and late completion cannot fetch or publish"},{"ancestorTitles":[],"duration":51,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-08 operations] unmount during a write prevents its late follow-up GET, event and focus effects","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793748333174,"status":"passed","title":"[P7-08 operations] unmount during a write prevents its late follow-up GET, event and focus effects"},{"ancestorTitles":[],"duration":112,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-08 operations] removing the initiating control restores meaningful focus while deliberate focus elsewhere stays put","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793748333225,"status":"passed","title":"[P7-08 operations] removing the initiating control restores meaningful focus while deliberate focus elsewhere stays put"},{"ancestorTitles":[],"duration":99,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-08 operations] accepted background polls preserve surviving and deliberate focus and recover removed focused controls","invocations":1,"location":null,"numPassingAsserts":12,"retryReasons":[],"startAt":1793748333337,"status":"passed","title":"[P7-08 operations] accepted background polls preserve surviving and deliberate focus and recover removed focused controls"}],"endTime":1793748333437,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\operations-page.test.tsx","startTime":1793748329054,"status":"passed","summary":""},{"assertionResults":[{"ancestorTitles":[],"duration":136,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"opens one company relationship view with applications and recent contact","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793748334256,"status":"passed","title":"opens one company relationship view with applications and recent contact"},{"ancestorTitles":[],"duration":46,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"company contact dates follow the en UI locale","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793748334393,"status":"passed","title":"company contact dates follow the en UI locale"},{"ancestorTitles":[],"duration":45,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"company contact dates follow the nb UI locale","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793748334439,"status":"passed","title":"company contact dates follow the nb UI locale"},{"ancestorTitles":[],"duration":546,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-J08 E08] en company save failure remains inside the dialog with the draft available for retry","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793748334484,"status":"passed","title":"[PRES-J08 E08] en company save failure remains inside the dialog with the draft available for retry"},{"ancestorTitles":[],"duration":542,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-J08 E08] nb company save failure remains inside the dialog with the draft available for retry","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793748335030,"status":"passed","title":"[PRES-J08 E08] nb company save failure remains inside the dialog with the draft available for retry"},{"ancestorTitles":[],"duration":439,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a held company save locks all nine fields and cancellation, submits one exact snapshot, and retains a failed draft","invocations":1,"location":null,"numPassingAsserts":27,"retryReasons":[],"startAt":1793748335572,"status":"passed","title":"a held company save locks all nine fields and cancellation, submits one exact snapshot, and retains a failed draft"},{"ancestorTitles":[],"duration":240,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a locale change translates an existing company-save failure without refetching or replacing its draft","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793748336011,"status":"passed","title":"a locale change translates an existing company-save failure without refetching or replacing its draft"},{"ancestorTitles":[],"duration":72,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"retrying a failed relationship read loads the same company without reloading the company list","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793748336251,"status":"passed","title":"retrying a failed relationship read loads the same company without reloading the company list"},{"ancestorTitles":[],"duration":388,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a late success for relationship A cannot replace the opened relationship B","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793748336323,"status":"passed","title":"a late success for relationship A cannot replace the opened relationship B"},{"ancestorTitles":[],"duration":404,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a late failure for relationship A cannot replace the opened relationship B","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793748336711,"status":"passed","title":"a late failure for relationship A cannot replace the opened relationship B"},{"ancestorTitles":[],"duration":401,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a relationship response received after closing stays closed and does not populate another held relationship","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793748337116,"status":"passed","title":"a relationship response received after closing stays closed and does not populate another held relationship"},{"ancestorTitles":[],"duration":436,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"the company edit deep link removes only edit while preserving query, hash, state and replace navigation","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793748337517,"status":"passed","title":"the company edit deep link removes only edit while preserving query, hash, state and replace navigation"}],"endTime":1793748337954,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\companies-relationship.test.tsx","startTime":1793748333447,"status":"passed","summary":""},{"assertionResults":[{"ancestorTitles":[],"duration":200,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01 S02] all existing reports have exact values independent of hidden Dashboard flags","invocations":1,"location":null,"numPassingAsserts":14,"retryReasons":[],"startAt":1793748339041,"status":"passed","title":"[PRES-S01 S02] all existing reports have exact values independent of hidden Dashboard flags"},{"ancestorTitles":[],"duration":502,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01 E07] period is direct-loadable and browser Back restores the period without refetching snapshot","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793748339242,"status":"passed","title":"[PRES-S01 E07] period is direct-loadable and browser Back restores the period without refetching snapshot"},{"ancestorTitles":[],"duration":220,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] failed snapshot remains an error while trends and retry stay available","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793748339744,"status":"passed","title":"[PRES-S01] failed snapshot remains an error while trends and retry stay available"},{"ancestorTitles":[],"duration":33,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S01] empty cohorts do not invent a denominator, and unsupported URL periods default safely","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793748339964,"status":"passed","title":"[PRES-S01] empty cohorts do not invent a denominator, and unsupported URL periods default safely"},{"ancestorTitles":[],"duration":113,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-J05 J09] drill-down retains supported raw status and company identities","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793748339997,"status":"passed","title":"[PRES-J05 J09] drill-down retains supported raw status and company identities"}],"endTime":1793748340111,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\analytics-presentation.test.tsx","startTime":1793748337961,"status":"passed","summary":""},{"assertionResults":[{"ancestorTitles":[],"duration":62,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"compares recorded offer facts without filling missing values","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793748340994,"status":"passed","title":"compares recorded offer facts without filling missing values"},{"ancestorTitles":[],"duration":28,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"shows an honest empty state when there are no offers","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793748341056,"status":"passed","title":"shows an honest empty state when there are no offers"},{"ancestorTitles":[],"duration":336,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"shows the first 25 of the real total in server application-date order without inventing the remaining offers","invocations":1,"location":null,"numPassingAsserts":10,"retryReasons":[],"startAt":1793748341084,"status":"passed","title":"shows the first 25 of the real total in server application-date order without inventing the remaining offers"},{"ancestorTitles":[],"duration":68,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"opens the full recorded offer source with the same supported filter and ordering","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793748341421,"status":"passed","title":"opens the full recorded offer source with the same supported filter and ordering"},{"ancestorTitles":[],"duration":56,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"distinguishes held and failed reads from empty results and recovers through one explicit identical retry","invocations":1,"location":null,"numPassingAsserts":16,"retryReasons":[],"startAt":1793748341489,"status":"passed","title":"distinguishes held and failed reads from empty results and recovers through one explicit identical retry"},{"ancestorTitles":[],"duration":53,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a live locale change translates loaded offer chrome and preserves recorded facts without another GET","invocations":1,"location":null,"numPassingAsserts":9,"retryReasons":[],"startAt":1793748341545,"status":"passed","title":"a live locale change translates loaded offer chrome and preserves recorded facts without another GET"},{"ancestorTitles":[],"duration":78,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"an existing read failure translates without refetching and Retry retains the same query","invocations":1,"location":null,"numPassingAsserts":9,"retryReasons":[],"startAt":1793748341598,"status":"passed","title":"an existing read failure translates without refetching and Retry retains the same query"},{"ancestorTitles":[],"duration":207,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"structured salaries follow UI locale rather than host locale while preserving zero, bounds, currency, unknown periods and free text","invocations":1,"location":null,"numPassingAsserts":18,"retryReasons":[],"startAt":1793748341676,"status":"passed","title":"structured salaries follow UI locale rather than host locale while preserving zero, bounds, currency, unknown periods and free text"},{"ancestorTitles":[],"duration":73,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"uses recorded applied/deadline/contact dates, keeps absent applied dates unknown, and opens the exact current application","invocations":1,"location":null,"numPassingAsserts":11,"retryReasons":[],"startAt":1793748341883,"status":"passed","title":"uses recorded applied/deadline/contact dates, keeps absent applied dates unknown, and opens the exact current application"}],"endTime":1793748341957,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\offer-comparison.test.tsx","startTime":1793748340119,"status":"passed","summary":""},{"assertionResults":[{"ancestorTitles":[],"duration":47,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"public CV exposes the rendered CV and PDF download","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793748342834,"status":"passed","title":"public CV exposes the rendered CV and PDF download"},{"ancestorTitles":[],"duration":29,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a public slug containing path and URL delimiters stays one encoded API and PDF path segment","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793748342882,"status":"passed","title":"a public slug containing path and URL delimiters stays one encoded API and PDF path segment"},{"ancestorTitles":[],"duration":28,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a held public CV read has a named page and loading announcement without a document or download","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793748342911,"status":"passed","title":"a held public CV read has a named page and loading announcement without a document or download"},{"ancestorTitles":[],"duration":36,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a revoked or missing public slug shows an unavailable state without retry or download","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793748342939,"status":"passed","title":"a revoked or missing public slug shows an unavailable state without retry or download"},{"ancestorTitles":[],"duration":75,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a transient failure retries only when requested and keeps focus on the page heading during its held read","invocations":1,"location":null,"numPassingAsserts":9,"retryReasons":[],"startAt":1793748342975,"status":"passed","title":"a transient failure retries only when requested and keeps focus on the page heading during its held read"},{"ancestorTitles":[],"duration":63,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"changing public slugs immediately hides the previous document, name and PDF and resets its measured height","invocations":1,"location":null,"numPassingAsserts":10,"retryReasons":[],"startAt":1793748343050,"status":"passed","title":"changing public slugs immediately hides the previous document, name and PDF and resets its measured height"},{"ancestorTitles":[],"duration":56,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a failed public slug cannot hide a subsequently loaded different public CV","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793748343114,"status":"passed","title":"a failed public slug cannot hide a subsequently loaded different public CV"},{"ancestorTitles":[],"duration":36,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a late success from the previous slug cannot replace the current CV or its title","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793748343170,"status":"passed","title":"a late success from the previous slug cannot replace the current CV or its title"},{"ancestorTitles":[],"duration":32,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a late failure from the previous slug cannot replace the current CV or its title","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793748343206,"status":"passed","title":"a late failure from the previous slug cannot replace the current CV or its title"},{"ancestorTitles":[],"duration":79,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"language changes translate loading, failure and loaded chrome without refetching anonymous CV content","invocations":1,"location":null,"numPassingAsserts":10,"retryReasons":[],"startAt":1793748343238,"status":"passed","title":"language changes translate loading, failure and loaded chrome without refetching anonymous CV content"},{"ancestorTitles":[],"duration":9,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"the public page removes only its own noindex metadata and ignores a response after unmount","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793748343317,"status":"passed","title":"the public page removes only its own noindex metadata and ignores a response after unmount"},{"ancestorTitles":[],"duration":20,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"leaving a loaded public CV restores the prior page title when its candidate title is still current","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793748343326,"status":"passed","title":"leaving a loaded public CV restores the prior page title when its candidate title is still current"},{"ancestorTitles":[],"duration":20,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"leaving a loaded public CV preserves a page title already replaced by another owner","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793748343346,"status":"passed","title":"leaving a loaded public CV preserves a page title already replaced by another owner"},{"ancestorTitles":[],"duration":58,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"returning from A through B to A ignores both earlier held reads until the current A response arrives","invocations":1,"location":null,"numPassingAsserts":10,"retryReasons":[],"startAt":1793748343366,"status":"passed","title":"returning from A through B to A ignores both earlier held reads until the current A response arrives"}],"endTime":1793748343425,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\public-cv-page.test.tsx","startTime":1793748341966,"status":"passed","summary":""}],"wasInterrupted":false}
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/BEFORE_INTERRUPTED.json b/docs/ui-modernisation/visual-correction/evidence/secondary/BEFORE_INTERRUPTED.json
new file mode 100644
index 00000000..3ecc1a62
--- /dev/null
+++ b/docs/ui-modernisation/visual-correction/evidence/secondary/BEFORE_INTERRUPTED.json
@@ -0,0 +1,46 @@
+{
+ "status": "INTERRUPTED",
+ "runtimeCommit": "2218659ecf692e8e377dd33a9ce6415b01e43032",
+ "reason": "Material duplicate Offer Comparison heading and nested Operations surfaces reproduced. Baseline run interrupted before unnecessary full matrix; implement corrections then qualify successor.",
+ "captures": [
+ "analytics-en-dark-320.png",
+ "analytics-en-dark-360.png",
+ "analytics-en-light-320.png",
+ "analytics-en-light-360.png",
+ "analytics-nb-dark-320.png",
+ "analytics-nb-light-320.png",
+ "companies-en-dark-320.png",
+ "companies-en-dark-360.png",
+ "companies-en-light-320.png",
+ "companies-en-light-360.png",
+ "companies-nb-dark-320.png",
+ "companies-nb-light-320.png",
+ "offers-en-dark-320.png",
+ "offers-en-dark-360.png",
+ "offers-en-light-320.png",
+ "offers-en-light-360.png",
+ "offers-nb-dark-320.png",
+ "offers-nb-light-320.png",
+ "operations-en-dark-320.png",
+ "operations-en-dark-360.png",
+ "operations-en-light-320.png",
+ "operations-en-light-360.png",
+ "operations-nb-dark-320.png",
+ "operations-nb-light-320.png",
+ "public-cv-en-dark-320.png",
+ "public-cv-en-light-320.png",
+ "public-cv-en-light-360.png",
+ "public-cv-nb-dark-320.png",
+ "public-cv-nb-light-320.png",
+ "trash-empty-en-dark-320.png",
+ "trash-empty-en-dark-360.png",
+ "trash-empty-en-light-320.png",
+ "trash-empty-en-light-360.png",
+ "trash-empty-nb-dark-320.png",
+ "trash-empty-nb-light-320.png"
+ ],
+ "limits": [
+ "Saved PNGs are visual baseline observations, not a complete passing regression or 216-state qualification.",
+ "Ctrl-C ended the owned browser verifier. No app service or provider was stopped."
+ ]
+}
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/analytics-en-dark-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/analytics-en-dark-320.png
new file mode 100644
index 00000000..30922e65
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/analytics-en-dark-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/analytics-en-dark-360.png b/docs/ui-modernisation/visual-correction/evidence/secondary/analytics-en-dark-360.png
new file mode 100644
index 00000000..9d3d09b1
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/analytics-en-dark-360.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/analytics-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/analytics-en-light-320.png
new file mode 100644
index 00000000..3d385518
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/analytics-en-light-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/analytics-en-light-360.png b/docs/ui-modernisation/visual-correction/evidence/secondary/analytics-en-light-360.png
new file mode 100644
index 00000000..01120cda
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/analytics-en-light-360.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/analytics-nb-dark-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/analytics-nb-dark-320.png
new file mode 100644
index 00000000..8b867a9b
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/analytics-nb-dark-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/analytics-nb-light-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/analytics-nb-light-320.png
new file mode 100644
index 00000000..f544328d
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/analytics-nb-light-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/companies-en-dark-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/companies-en-dark-320.png
new file mode 100644
index 00000000..9b5e0293
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/companies-en-dark-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/companies-en-dark-360.png b/docs/ui-modernisation/visual-correction/evidence/secondary/companies-en-dark-360.png
new file mode 100644
index 00000000..a879af07
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/companies-en-dark-360.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/companies-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/companies-en-light-320.png
new file mode 100644
index 00000000..965d91a2
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/companies-en-light-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/companies-en-light-360.png b/docs/ui-modernisation/visual-correction/evidence/secondary/companies-en-light-360.png
new file mode 100644
index 00000000..8e1ede9a
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/companies-en-light-360.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/companies-nb-dark-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/companies-nb-dark-320.png
new file mode 100644
index 00000000..b683b5fc
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/companies-nb-dark-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/companies-nb-light-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/companies-nb-light-320.png
new file mode 100644
index 00000000..cec3bd25
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/companies-nb-light-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/offers-en-dark-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/offers-en-dark-320.png
new file mode 100644
index 00000000..8343f9c5
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/offers-en-dark-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/offers-en-dark-360.png b/docs/ui-modernisation/visual-correction/evidence/secondary/offers-en-dark-360.png
new file mode 100644
index 00000000..a28e8164
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/offers-en-dark-360.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/offers-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/offers-en-light-320.png
new file mode 100644
index 00000000..a1ff6cbe
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/offers-en-light-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/offers-en-light-360.png b/docs/ui-modernisation/visual-correction/evidence/secondary/offers-en-light-360.png
new file mode 100644
index 00000000..21512551
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/offers-en-light-360.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/offers-nb-dark-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/offers-nb-dark-320.png
new file mode 100644
index 00000000..8fcccf4c
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/offers-nb-dark-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/offers-nb-light-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/offers-nb-light-320.png
new file mode 100644
index 00000000..5bf8d42b
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/offers-nb-light-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/operations-en-dark-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/operations-en-dark-320.png
new file mode 100644
index 00000000..022612bd
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/operations-en-dark-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/operations-en-dark-360.png b/docs/ui-modernisation/visual-correction/evidence/secondary/operations-en-dark-360.png
new file mode 100644
index 00000000..77da194e
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/operations-en-dark-360.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/operations-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/operations-en-light-320.png
new file mode 100644
index 00000000..568798cc
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/operations-en-light-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/operations-en-light-360.png b/docs/ui-modernisation/visual-correction/evidence/secondary/operations-en-light-360.png
new file mode 100644
index 00000000..66956862
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/operations-en-light-360.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/operations-nb-dark-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/operations-nb-dark-320.png
new file mode 100644
index 00000000..e637b26c
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/operations-nb-dark-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/operations-nb-light-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/operations-nb-light-320.png
new file mode 100644
index 00000000..0f2cf3cc
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/operations-nb-light-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/public-cv-en-dark-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/public-cv-en-dark-320.png
new file mode 100644
index 00000000..3d1cfdce
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/public-cv-en-dark-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/public-cv-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/public-cv-en-light-320.png
new file mode 100644
index 00000000..3b633863
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/public-cv-en-light-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/public-cv-en-light-360.png b/docs/ui-modernisation/visual-correction/evidence/secondary/public-cv-en-light-360.png
new file mode 100644
index 00000000..2be293f4
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/public-cv-en-light-360.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/public-cv-nb-dark-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/public-cv-nb-dark-320.png
new file mode 100644
index 00000000..bdc343c3
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/public-cv-nb-dark-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/public-cv-nb-light-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/public-cv-nb-light-320.png
new file mode 100644
index 00000000..5710ae88
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/public-cv-nb-light-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/trash-empty-en-dark-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/trash-empty-en-dark-320.png
new file mode 100644
index 00000000..5aafeb42
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/trash-empty-en-dark-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/trash-empty-en-dark-360.png b/docs/ui-modernisation/visual-correction/evidence/secondary/trash-empty-en-dark-360.png
new file mode 100644
index 00000000..9d9bd2b4
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/trash-empty-en-dark-360.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/trash-empty-en-light-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/trash-empty-en-light-320.png
new file mode 100644
index 00000000..7af64cca
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/trash-empty-en-light-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/trash-empty-en-light-360.png b/docs/ui-modernisation/visual-correction/evidence/secondary/trash-empty-en-light-360.png
new file mode 100644
index 00000000..219301f0
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/trash-empty-en-light-360.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/trash-empty-nb-dark-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/trash-empty-nb-dark-320.png
new file mode 100644
index 00000000..49cf3039
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/trash-empty-nb-dark-320.png differ
diff --git a/docs/ui-modernisation/visual-correction/evidence/secondary/trash-empty-nb-light-320.png b/docs/ui-modernisation/visual-correction/evidence/secondary/trash-empty-nb-light-320.png
new file mode 100644
index 00000000..52249d34
Binary files /dev/null and b/docs/ui-modernisation/visual-correction/evidence/secondary/trash-empty-nb-light-320.png differ
diff --git a/job-tracker-ui/scripts/verify-a3-secondary.mjs b/job-tracker-ui/scripts/verify-a3-secondary.mjs
index 1918d304..4bad0212 100644
--- a/job-tracker-ui/scripts/verify-a3-secondary.mjs
+++ b/job-tracker-ui/scripts/verify-a3-secondary.mjs
@@ -7,12 +7,13 @@ import fs from 'node:fs';
import path from 'node:path';
const compiled = ts.transpileModule(fs.readFileSync(new URL('../src/i18n/translations.ts', import.meta.url), 'utf8'), { compilerOptions: { module: ts.ModuleKind.ESNext } }).outputText;
const { translations } = await import(`data:text/javascript;base64,${Buffer.from(compiled).toString('base64')}`);
-const out = path.resolve(import.meta.dirname, '../../docs/ui-modernisation/evidence/secondary/browser');
+const visualMatrix = Boolean(process.env.A3_VISUAL_MATRIX);
+const out = path.resolve(import.meta.dirname, visualMatrix ? '../../docs/ui-modernisation/visual-correction/evidence/secondary-after' : '../../docs/ui-modernisation/evidence/secondary/browser');
fs.mkdirSync(out, { recursive: true });
const report = { screens: [], journeys: [], errors: [], writes: [], unavailableReads: [], blockedExternal: [] };
const browser = await chromium.launch();
try {
- const configurations = process.env.A3_PILOT ? [[320, 'en', 'light']] : [320, 390, 768, 1440].flatMap(width => ['en', 'nb'].flatMap(language => ['light', 'dark'].map(mode => [width, language, mode])));
+ const configurations = process.env.A3_PILOT ? [[320, 'en', 'light']] : (visualMatrix ? [320, 360, 390, 430, 768, 1024, 1280, 1440, 1920] : [320, 390, 768, 1440]).flatMap(width => ['en', 'nb'].flatMap(language => ['light', 'dark'].map(mode => [width, language, mode])));
for (const [width, language, mode] of configurations) {
console.log(`Checking ${width}/${language}/${mode}`);
const t = key => { assert.ok(translations[language][key], key); return translations[language][key]; };
diff --git a/job-tracker-ui/src/offer-comparison.test.tsx b/job-tracker-ui/src/offer-comparison.test.tsx
index 8afff433..ddaf9ec0 100644
--- a/job-tracker-ui/src/offer-comparison.test.tsx
+++ b/job-tracker-ui/src/offer-comparison.test.tsx
@@ -102,7 +102,7 @@ test("shows the first 25 of the real total in server application-date order with
expect(await screen.findByText(copy("offerComparisonSampleCount", "en", { shown: 25, total: 61 }))).toBeInTheDocument();
expect(screen.getByText(copy("offerComparisonSampleHelp"))).toBeInTheDocument();
expect(screen.getAllByRole("article")).toHaveLength(25);
- expect(screen.getAllByRole("heading", { level: 3 }).map((heading) => heading.textContent)).toEqual(items.map((item) => item.jobTitle));
+ expect(screen.getAllByRole("heading", { level: 2 }).map((heading) => heading.textContent)).toEqual(items.map((item) => item.jobTitle));
expect(screen.queryByText("Offer role 26")).not.toBeInTheDocument();
expect(mockedApi.get).toHaveBeenCalledTimes(1);
expect(mockedApi.get).toHaveBeenCalledWith("/jobapplications", request);
@@ -150,7 +150,7 @@ test("a live locale change translates loaded offer chrome and preserves recorded
renderPage();
expect(await screen.findByText(notes)).toBeInTheDocument();
changeLanguage("nb");
- expect(screen.getByRole("heading", { level: 2, name: translations.nb.offerComparisonTitle })).toBeInTheDocument();
+ expect(screen.getByRole("region", { name: translations.nb.offerComparisonTitle })).toBeInTheDocument();
const article = screen.getByRole("article", { name: "English source title" });
expect(within(article).getByText(notes)).toBeInTheDocument();
expect(within(article).getByText("GBP package as recorded")).toBeInTheDocument();
diff --git a/job-tracker-ui/src/views/OfferComparisonPage.tsx b/job-tracker-ui/src/views/OfferComparisonPage.tsx
index 9768b0e4..99a36774 100644
--- a/job-tracker-ui/src/views/OfferComparisonPage.tsx
+++ b/job-tracker-ui/src/views/OfferComparisonPage.tsx
@@ -5,7 +5,6 @@ import { api } from "../api";
import { useViewResource } from "../hooks/useViewResource";
import { useI18n } from "../i18n/I18nProvider";
import ViewStateNotice from "../components/ViewStateNotice";
-import { PageHeader } from "../layout/PageLayout";
import type { JobApplication } from "../types";
type PagedJobs = { items: JobApplication[]; total: number; page: number; pageSize: number };
@@ -24,8 +23,10 @@ export default function OfferComparisonPage() {
return (
- 0 ? : null} />
+
+ {t("offerComparisonHelp")}
+ {!loading && !resource.error && resource.data.items.length > 0 ? : null}
+
{!loading && !resource.error && resource.data.items.length === 0 ? (
- {t("offerComparisonEmpty")}
+ {t("offerComparisonEmpty")}{t("offerComparisonEmptyHelp")}
@@ -50,7 +51,7 @@ export default function OfferComparisonPage() {
{resource.data.items.map((offer) => (
- {offer.jobTitle}{offer.company?.name}
+ {offer.jobTitle}{offer.company?.name}
diff --git a/job-tracker-ui/src/views/OperationsPage.tsx b/job-tracker-ui/src/views/OperationsPage.tsx
index bc9d72c3..c3b3309c 100644
--- a/job-tracker-ui/src/views/OperationsPage.tsx
+++ b/job-tracker-ui/src/views/OperationsPage.tsx
@@ -137,7 +137,7 @@ export default function OperationsPage() {
{errorDetail !== errorTitle ? {errorDetail} : null}
: null}
{loading ? : null}
-
+
{hasLoaded && notifications.length === 0 ? {t("operationsNoNotifications")} : null}
{notifications.map(notification => {notification.title}
@@ -150,7 +150,7 @@ export default function OperationsPage() {
)}
-
+
{hasLoaded && operations.length === 0 ? {t("operationsEmpty")} : null}
{operations.map(operation =>