diff --git a/.cursor/rules/ui-interaction-standards.mdc b/.cursor/rules/ui-interaction-standards.mdc
new file mode 100644
index 0000000..4d9960f
--- /dev/null
+++ b/.cursor/rules/ui-interaction-standards.mdc
@@ -0,0 +1,25 @@
+---
+description: Gallery UI interaction and component standards for client screens
+globs: client/src/**/*.{tsx,css}
+alwaysApply: false
+---
+
+# UI interaction standards
+
+Follow [Documentation/ui-interaction-and-component-standards.md](Documentation/ui-interaction-and-component-standards.md) (MUST / SHOULD / MAY). Do not invent a second nav, toast, or CSS framework.
+
+## MUST
+
+- Drill-down via `HomePage` `View` state. Nested screens get an explicit Back; do not rely on the browser Back button.
+- **Back to Timeline** → `goToTimelineHome()` (clears hall, resets year range). **Back to Gallery** → same hall session (camera/wing kept).
+- Hide actions the user cannot `can()`; do not leave buttons that 403.
+- Visitor chrome strings in `locales/{en,ru}`. Movement colours via `utils/movementColor.ts`. Timeline zoom/pan via `utils/timelineView.ts`.
+- Loading: `GalleryLoadingMarker`. Errors/empty states: visible copy, not a blank canvas.
+- Modals: `role="dialog"`, visible close, Escape. No nested modals. Destructive actions confirm first.
+- Colocated CSS; museum gold `#c9a96e` / navy `/ Georgia`. No MUI/Ant Design.
+
+## SHOULD
+
+- Reuse `CatalogSearchBar`, `CuratorLoginModal`, `ArtistFilterModal`, `PaintingLightbox`, `DebugSearchResultsModal`, `DebugUploadButton`.
+- Curator filters above the table; expensive work behind an explicit button.
+- Update the standards doc when changing Back, search, layout query, or loading behaviour.
diff --git a/Documentation/basics.md b/Documentation/basics.md
index 52a025c..e301693 100644
--- a/Documentation/basics.md
+++ b/Documentation/basics.md
@@ -67,7 +67,8 @@ Gallery/
│ │ ├── utils/debugMode.ts # Debug mode + “Show more” localStorage prefs
│ │ ├── utils/timelineView.ts # Shared zoom/pan math for timeline + movements
│ │ ├── utils/movementColor.ts # Shared vivid/shade hex helpers for all movement charts
-│ │ └── utils/movementTree.ts # View-independent Tree of Art layout engine│ └── dist/ # Production build (served by API when present)
+│ │ └── utils/movementTree.ts # View-independent Tree of Art layout engine
+│ └── dist/ # Production build (served by API when present)
├── scripts/ # Seed, bios, catalog expansion, image fetch, checkup tools
│ ├── seed-wikipedia.js
│ ├── seed-catalog-data.js
@@ -546,3 +547,4 @@ See [API.md](API.md#authentication) and [data-and-images.md](data-and-images.md#
| [tours.md](tours.md) | Guided tours — editor, public popup, 3D tour hall |
| [i18n-russian.md](i18n-russian.md) | Russian UI + entity_translations |
| [data-and-images.md](data-and-images.md) | Image pipeline and seeding |
+| [ui-interaction-and-component-standards.md](ui-interaction-and-component-standards.md) | UI interaction, navigation, and component standards |
diff --git a/Documentation/ui-interaction-and-component-standards.md b/Documentation/ui-interaction-and-component-standards.md
new file mode 100644
index 0000000..8e5aa89
--- /dev/null
+++ b/Documentation/ui-interaction-and-component-standards.md
@@ -0,0 +1,527 @@
+# UI Interaction and Component Standards
+
+**Subject:** Virtual Art Gallery — screen and interaction guidance
+**Applies to:** Public visitor UI, 3D halls, curator tools
+**Companion rule:** `.cursor/rules/ui-interaction-standards.mdc`
+
+---
+
+## 0. Introduction
+
+The Gallery is a single React SPA (`HomePage.tsx` view union — no URL router except `?layout=`). Public visitors browse a museum-dark timeline and 3D halls; curators use the same shell for catalog tools (Checkup, Translations, Influences, Tours, Users, Activity). Work lands in the same header, palette, and back-stack, so a new screen that invents its own chrome, confirmations, or loading pattern fragments the experience.
+
+This document is the shared contract for **how the UI behaves**. Architecture, APIs, and 3D hall construction live in [basics.md](basics.md), [API.md](API.md), and [data-and-images.md](data-and-images.md). Tree geometry lives in [movement-tree.md](movement-tree.md). Locale strings live in [i18n-russian.md](i18n-russian.md).
+
+### 0.1 Purpose and audience
+
+- **Purpose:** Define common UI interaction, navigation, and component standards for the Gallery client.
+- **Primary audience:** Developers (and agents) adding or changing `client/src` UI; anyone writing curator-tool screens.
+- **Secondary audience:** QA, product, copy/i18n.
+
+### 0.2 Scope
+
+- **In scope:**
+ - Navigation and back-stack behaviour
+ - Timeline / search / 3D / detail / curator-tool interaction patterns
+ - Loading, empty, error, and confirmation behaviour
+ - Permission-based UI (`can()` / RBAC)
+ - Shared component and visual-language rules
+- **Out of scope:**
+ - REST contracts, retries, and image pipeline internals
+ - Three.js hall architecture, textures, and lighting (see [basics.md](basics.md))
+ - Pixel-perfect branding kit (no separate design-system package; follow existing CSS)
+
+### 0.3 Requirement levels
+
+- **MUST:** Mandatory for new work and for fixes that touch the same screen.
+- **SHOULD:** Recommended; deviate only with a short note in the PR or screen section.
+- **MAY:** Optional pattern when the screen specification calls for it.
+
+### 0.4 Surface map
+
+| Surface | Typical components | Visitors | Curators |
+|---------|--------------------|----------|----------|
+| Timeline home | `Timeline`, `VerticalTimeline`, `MovementBands`, `VerticalMovementBands`, `MovementTree`, `CatalogSearchBar` | yes | yes |
+| 3D hall | `VirtualGallery`, wing navigator, exit overlays | yes | yes |
+| Painting / bio | `PaintingDetail`, `PaintingLightbox`, `ArtistBio`, `PaintingAnnotations` | yes | + debug panel when `can('images')` |
+| Overlays | `CuratorLoginModal`, `ArtistFilterModal`, `ToursPopup`, `DebugSearchResultsModal` | some | all |
+| Curator tools | `CheckupPage`, `TranslationsPage`, `InfluencesPage`, `ToursPage`, `UsersPage`, `AuditPage` | no | permission-gated |
+
+---
+
+## 1. Design Goals and Principles
+
+### 1.1 Problem statement
+
+- Statement: Visitors and curators share one shell. Inconsistent back labels, ad-hoc modals, English-only captions, and one-off loading/error treatment make the museum feel like several apps glued together.
+ - Scope: All `client/src` screens and overlays.
+ - Rationale: The product is a gallery, not an admin console with a visitor skin. Predictable chrome is part of the exhibit.
+ - Verification: Compare a new screen’s header, back control, loading marker, and locale keys against this document.
+
+### 1.2 Functional design principles
+
+- Principle 1: One museum, two roles
+ - Statement: Public browse chrome MUST stay museum-dark (navy / gold / Georgia). Curator tools MAY be denser but MUST reuse the same header back pattern, gold accent, and permission hiding — they MUST NOT look like a separate product.
+ - Rationale: Curators enter from the same timeline; a visual cliff breaks trust.
+ - Verification: Side-by-side with timeline header and Checkup / Tours editor.
+
+- Principle 2: Predictable drill-down and return
+ - Statement: Navigation MUST follow Timeline → (movement picker) → hall → painting/bio, with an explicit in-app Back that restores the intended session — not the browser history stack.
+ - Rationale: `HomePage` owns view state; the browser Back button is not wired.
+ - Verification: Walk the [basics.md navigation flow](basics.md#user-navigation-flow) and the Back table in §2.4.
+
+- Principle 3: Clarity of actions and feedback
+ - Statement: Every user-initiated load, save, delete, or failed request MUST show a loading, success, or error state the user can see without opening the console.
+ - Rationale: 3D and image work is slow; silent failure looks like a broken hall.
+ - Verification: Trigger catalog load, hall open, form save, and a failed API call.
+
+- Principle 4: Minimize cognitive load
+ - Statement: Timeline charts MUST keep zoom/pan/click hints visible. Curator tables MUST put filter/search above the grid. Destructive actions MUST be confirmed.
+ - Rationale: Dense history data and catalog tables are easy to mis-click.
+ - Verification: Hint captions present; filters above tables; delete paths show a confirm.
+
+- Principle 5: Locale and permission are first-class
+ - Statement: New visitor-facing copy MUST go through `react-i18next` (`locales/{en,ru}`). Actions the user cannot perform MUST be hidden, not disabled-without-explanation.
+ - Rationale: EN/RU is a product requirement; exposing forbidden tools invites errors.
+ - Verification: Toggle EN|RU; log in as a curator without the relevant `can()` and confirm the control is absent.
+
+---
+
+## 2. Navigation Principles
+
+### 2.1 Application chrome (no left module rail)
+
+The Gallery has **no persistent left navigation menu**. Primary wayfinding is the **timeline header** on home, and an explicit **Back** control on every nested view.
+
+- Statement: The timeline home MUST keep layout switch, catalog search, locale switcher, and role-appropriate tools in the site header.
+ - Scope: `HomePage` when `view` is `timeline` / `timeline-vertical` / `timeline-tree`.
+ - Rationale: Visitors need search and layout without hunting; curators need tools without leaving the museum frame.
+ - Verification: Header remains usable at 100vh; search dropdown stacks above movement bands (`z-index` on `.site-header`).
+
+- Statement: Layout switch links MUST stay in the top-left (`.site-layout-switch`). The Tree of Art control SHOULD use `.site-layout-link-feature`.
+ - Scope: Timeline home.
+ - Rationale: Three layouts must stay discoverable; Tree is the featured alternative start page.
+ - Verification: Classic / Vertical / Tree links match [basics.md](basics.md#timeline-and-movement-flow).
+
+- Statement: Nested views (hall, painting, bio, curator pages) MUST NOT reintroduce a second global nav. They MUST show a single primary Back control in the page header.
+ - Scope: All non-home views.
+ - Rationale: Avoid competing menus; the drill-down is the nav.
+ - Verification: No duplicate “home” plus “modules” rails on curator pages.
+
+### 2.2 Page hierarchy
+
+- Statement: Screens MUST stay within this hierarchy (max four levels):
+
+ 1. Timeline home (classic / vertical / tree)
+ 2. Overlay or picker (artist filter, tours popup, login) **or** curator tool page
+ 3. 3D hall (artist / movement / tour)
+ 4. Painting detail or artist bio (optional lightbox on top of detail)
+
+ - Scope: All visitor and curator flows.
+ - Rationale: Matches the existing drill-down; deeper stacks become unrecoverable without a router.
+ - Verification: New views are added to the `View` union in `HomePage.tsx` with a defined parent and Back handler.
+
+- Statement: Curator tools MUST open as siblings of the timeline (replace the home canvas), not as a fifth level under a hall.
+ - Scope: Checkup, Translations, Influences, Tours editor, Users, Activity.
+ - Rationale: Tools operate on the catalog, not on a hall session.
+ - Verification: Opening Checkup from a hall is not required; from timeline header, Back returns to timeline home.
+
+### 2.3 Location awareness (no breadcrumbs)
+
+- Statement: The app MUST NOT add a breadcrumb trail unless a future router lands. Until then, the Back label MUST name the destination (`← Back to Timeline`, `← Back to Gallery`, `← Back`).
+ - Scope: All nested views.
+ - Rationale: There is no URL path to reflect; a fake breadcrumb would lie.
+ - Verification: Labels match §2.4; they are i18n keys (`backToTimeline`, `backToGallery`, …).
+
+- Statement: Timeline layout MUST be deep-linkable via `?layout=classic|vertical|tree` (omit param for classic). Other views MUST NOT pretend to be bookmarkable until a router exists.
+ - Scope: Timeline home.
+ - Rationale: Layout is the one shareable start-page choice; halls and tools are session state.
+ - Verification: Load `?layout=tree`, switch layouts, confirm `history.replaceState` updates the query.
+
+### 2.4 Back navigation
+
+| Control | MUST return to |
+|---------|----------------|
+| **← Back to Timeline** (hall header, movement Exit to Timeline, painting opened from search) | Home timeline via `goToTimelineHome()` — unmount hall, clear session, reset year window to full catalog bounds |
+| **← Back to Gallery** (painting from a hall) | Same hall session (camera / wing preserved) |
+| **← Back** (artist bio) | `returnTo` view (usually the hall that opened bio) |
+| Curator page Back | Timeline home |
+
+- Statement: Back MUST be an in-app control. The browser Back button MUST NOT be relied on (it is not wired to `View` state).
+ - Scope: All nested views.
+ - Rationale: `setView` is the router.
+ - Verification: From painting detail, in-app Back restores the hall; browser Back does not need to.
+
+- Statement: `goToTimelineHome()` MUST be the single implementation for “leave everything and show the timeline.” New exits MUST call it rather than duplicating reset logic.
+ - Scope: Halls, search-opened paintings, curator Back.
+ - Rationale: Year-range reset and session clear must stay consistent.
+ - Verification: After Exit to Timeline, `viewStart`/`viewEnd` equal catalog bounds.
+
+### 2.5 State preservation
+
+- Statement: Timeline pan/zoom (`viewStart` / `viewEnd`) MUST persist while the user stays on a timeline layout. Switching classic ↔ vertical ↔ tree MUST keep the same year window.
+ - Scope: Timeline home.
+ - Rationale: Layout is a lens, not a new dataset.
+ - Verification: Zoom, switch to Tree, confirm the year rail range is unchanged.
+
+- Statement: Returning to timeline via `goToTimelineHome()` MUST reset the year window to full catalog bounds.
+ - Scope: Hall / search / curator exits that call `goToTimelineHome()`.
+ - Rationale: Documented in [basics.md](basics.md#back-navigation); visitors expect a fresh overview, not a leftover zoom.
+ - Verification: Zoom in, enter a hall, Back to Timeline → full span.
+
+- Statement: Hall camera and wing MUST be preserved across **Back to Gallery** from painting detail. They MUST be discarded on **Back to Timeline**.
+ - Scope: Artist, movement, and tour halls.
+ - Rationale: Inspecting a painting is a detour; leaving the museum is not.
+ - Verification: Move in the hall, open a painting, Back to Gallery → same viewpoint.
+
+- Statement: Catalog search input MAY clear when the dropdown closes. It MUST NOT change timeline zoom by itself.
+ - Scope: `CatalogSearchBar`.
+ - Rationale: Search is a jump, not a filter on the chart.
+ - Verification: Type a query, Escape; year window unchanged.
+
+---
+
+## 3. Common UI Interaction Patterns
+
+### 3.1 Catalog search (visitor)
+
+- Statement: Timeline search MUST live in the header, require **2** trimmed characters, debounce **300 ms**, and group results into Artists, Movements, Paintings.
+ - Scope: `CatalogSearchBar.tsx`.
+ - Rationale: Documented product behaviour; keeps `/api/search` load reasonable.
+ - Verification: 1 character shows no fetch; 2+ after debounce shows groups.
+
+- Statement: Keyboard MUST support `↑`/`↓` highlight, `Enter` to open, `Escape` to close.
+ - Scope: Catalog search dropdown.
+ - Rationale: Timeline is pointer-heavy; search should still be keyboardable.
+ - Verification: Keyboard-only open of an artist, movement, and painting.
+
+- Statement: Opening a painting from search MUST set `returnTo` timeline so Back is **← Back to Timeline**, not Gallery.
+ - Scope: Search → painting detail.
+ - Rationale: There is no hall session.
+ - Verification: Search a title, open, Back → home timeline.
+
+### 3.2 Timeline charts (zoom, pan, click)
+
+- Statement: Scroll MUST zoom, drag MUST pan, click on a stream/limb/label MUST open the movement (artist filter → hall). All three layouts MUST use `zoomTimelineView` / `panTimelineView`.
+ - Scope: `MovementBands`, `VerticalMovementBands`, `MovementTree`, era rails.
+ - Rationale: Shared year window; one mental model.
+ - Verification: Same wheel/drag behaviour on classic, vertical, and tree.
+
+- Statement: Each chart MUST show a localised hint caption (`captionClassicTimeline`, `captionClassicFlow`, `captionVerticalTimeline`, `captionVerticalFlow`, `captionTreeFlow`).
+ - Scope: Timeline home.
+ - Rationale: First-time visitors cannot discover zoom/pan otherwise.
+ - Verification: EN and RU captions change with `LocaleSwitcher`.
+
+- Statement: Timeline layout shifts (lane packing, tree fit scale) SHOULD animate rather than snap.
+ - Scope: Movement charts.
+ - Rationale: Unexplained jumps look like bugs.
+ - Verification: Zoom/pan does not teleport streams.
+
+### 3.3 Filters and search (curator tables)
+
+- Statement: Filter/search controls MUST sit in a toolbar **above** the table, not in a column header hack or a page footer.
+ - Scope: Checkup, Translations, Influences, Tours editor, Users, Activity.
+ - Rationale: Matches Checkup (`checkup-toolbar`) and keeps the grid scannable.
+ - Verification: Filters remain visible while the table scrolls.
+
+- Statement: Simple text filters MAY apply as the user types. Expensive operations (image search, import, refetch) MUST require an explicit button (e.g. Checkup **Search visible**).
+ - Scope: Curator list screens.
+ - Rationale: Checkup search is rate-limited and slow; typing must not fire it.
+ - Verification: Typing in Checkup filter does not start image search.
+
+- Statement: When a filter hides rows, the toolbar SHOULD show how many rows are visible (e.g. `N shown`).
+ - Scope: Filtered tables.
+ - Rationale: Empty-looking tables need an explanation.
+ - Verification: Filter to zero rows → empty state plus count.
+
+### 3.4 Date selection
+
+- Statement: Year fields in the catalog and timeline MUST use numeric years (negative = BCE). They MUST NOT switch to locale-specific calendar widgets for historical BCE dates.
+ - Scope: Timeline bounds, artist lifespan, painting years, curator year filters.
+ - Rationale: The catalog spans −800 to the present; HTML date inputs cannot represent BCE.
+ - Verification: Ancient era still filters correctly.
+
+- Statement: If a future screen needs a civil date (e.g. audit log day), it SHOULD use ISO `YYYY-MM-DD` and validate start ≤ end for ranges.
+ - Scope: Activity / audit and any new timestamp filters.
+ - Rationale: Consistent with API timestamps; avoids DD/MM ambiguity.
+ - Verification: Invalid range shows a field-level message.
+
+### 3.5 Tables and lists (curator)
+
+- Statement: Structured curator datasets MUST use a labeled HTML table (or existing page table classes), one logical record per row.
+ - Scope: Checkup, Users, Influences worklists, Tours list, Translations worklist, Activity.
+ - Rationale: Comparison and row actions need columns, not cards.
+ - Verification: Column headers present; row click/action affects one record.
+
+- Statement: Visitor-facing catalog MUST NOT be presented as a spreadsheet. Timeline streams, tree limbs, and 3D hangs are the list metaphor.
+ - Scope: Public home and halls.
+ - Rationale: The product is a gallery, not a DAM table.
+ - Verification: No “all paintings” data grid on the public home.
+
+- Statement: Tables MAY omit pagination while the dataset is curator-sized and client-filtered. If a list grows past comfortable scrolling, it SHOULD paginate or virtualise rather than rendering thousands of DOM rows.
+ - Scope: Curator tools.
+ - Rationale: Checkup is already filter-then-scroll; unbounded paint is a future foot-gun.
+ - Verification: New tools with large lists have a documented paging or virtualisation plan.
+
+- Statement: Row actions MUST sit in a dedicated column or overflow control, not as random icons in every cell.
+ - Scope: Interactive curator tables.
+ - Rationale: Scanability.
+ - Verification: Action column or consistent button set per row.
+
+### 3.6 Multi-row and per-record actions
+
+- Statement: Bulk actions MUST use a leading checkbox column, Select All for **visible** rows only, and a confirmation that includes the affected count for destructive work.
+ - Scope: Any new bulk-enabled table. (Today: ArtistFilterModal multi-select is a picker, not a bulk delete.)
+ - Rationale: Same as the Logistics template; prevent silent mass edits.
+ - Verification: Select All does not imply “all matching in the database” unless explicitly labelled.
+
+- Statement: Artist filter before a movement hall MUST be a modal checklist with explicit proceed/cancel, not a bulk-edit of the catalog.
+ - Scope: `ArtistFilterModal`.
+ - Rationale: It only chooses who appears in the hall.
+ - Verification: Cancel leaves the user on the timeline; proceed opens the hall.
+
+- Statement: Primary row/object action SHOULD be the name/title (open painting, open user, open tour). Secondary actions SHOULD stay in the row’s action controls.
+ - Scope: Curator tables and search results.
+ - Rationale: Matches search-result click-to-open.
+ - Verification: Clicking a Checkup title opens the painting when that handler exists.
+
+- Statement: Unavailable-by-permission actions MUST be hidden. Unavailable-by-record-state SHOULD be disabled with a `title`/tooltip explaining why.
+ - Scope: All tools.
+ - Rationale: RBAC vs workflow are different signals.
+ - Verification: Non-admin does not see Users; a disabled Fix button states why.
+
+### 3.7 Forms
+
+- Statement: Short auth and picker flows MUST use a modal. Multi-section catalog editors (Users create/edit, Tours editor, Influences wizard, Translations worklist) MUST be full-page (or the existing page layout), not nested modals.
+ - Scope: All forms.
+ - Rationale: Halls and timeline need to stay the “place”; heavy edit needs space.
+ - Verification: Login is modal; Users is a page.
+
+- Statement: Forms SHOULD be a single column. Related fields MAY group under a heading when there are more than five inputs.
+ - Scope: Curator forms.
+ - Rationale: Scanning beats dense multi-column on museum-width pages.
+ - Verification: Users create form remains vertically grouped.
+
+- Statement: Required fields MUST use the native `required` attribute and/or a visible marker; validation errors MUST appear next to the field or as a form-level error the submit control does not obscure.
+ - Scope: Login, Users, Tours, Influences, Translations.
+ - Rationale: Silent submit-disable is not enough.
+ - Verification: Submit empty login → field or form error, not a blank modal.
+
+- Statement: After successful save, the system MUST show an inline success message (or equivalent) and keep the user on the tool unless the spec says to return to timeline.
+ - Scope: Curator mutations.
+ - Rationale: Users page already uses `message` / `error` banners.
+ - Verification: Save permissions → success text; failed save → error text.
+
+- Statement: Unsaved-change guards SHOULD be added when a form is long enough that accidental Back would lose work (Tours editor, Translations). Login and tiny pickers MAY skip this.
+ - Scope: Heavy editors.
+ - Rationale: `window.confirm` on delete already exists; abandon-edit is the remaining hole.
+ - Verification: Dirty Tours editor + Back prompts or discards explicitly.
+
+### 3.8 Edit interaction pattern selection
+
+| Pattern | Use when |
+|---------|----------|
+| **Modal** | Login, artist filter, tours list popup, debug image picker, lightbox, hall exit/wing overlays |
+| **Inline** | Checkup flags, debug **Checked** / **Fix it** on painting detail — small, reversible |
+| **Full page** | Curator tools, painting detail, artist bio, 3D hall |
+| **Drawer** | MUST NOT be introduced unless a spec adds a shared drawer component |
+
+- Statement: Nested modals MUST NOT be used (no modal opened from another modal). The debug “More” picker MAY stack on painting detail because detail is a full page, not a modal.
+ - Scope: All overlays.
+ - Rationale: Focus traps and Back labels break.
+ - Verification: Login does not open another dialog.
+
+### 3.9 Modals and overlays
+
+- Statement: Modals MUST use `role="dialog"` and `aria-modal="true"`, a visible close/cancel, and **Escape** to dismiss unless a submit is in flight.
+ - Scope: `CuratorLoginModal`, `ArtistFilterModal`, `ToursPopup`, `DebugSearchResultsModal`, `PaintingLightbox`, hall exit overlays.
+ - Rationale: Accessibility and parity with search.
+ - Verification: Esc closes lightbox and debug picker; backdrop click matches existing login behaviour.
+
+- Statement: Backdrop click MAY close pickers and login. It MUST NOT close a modal that is applying a destructive or long-running action.
+ - Scope: Overlays.
+ - Rationale: Accidental dismiss during Fix/upload is costly.
+ - Verification: Click outside login closes; do not dismiss mid-upload.
+
+### 3.10 Loading, empty, and error states
+
+- Statement: Catalog, portrait, and hall loads MUST use `GalleryLoadingMarker` (overlay or banner), not an ad-hoc spinner per screen unless the marker cannot cover the region.
+ - Scope: Home, halls, painting/bio image work.
+ - Rationale: One recognisable “the museum is fetching” treatment.
+ - Verification: First visit shows “Loading art history…”; hall open uses the same marker family.
+
+- Statement: Loading SHOULD be scoped to the affected region. Full-viewport overlay MUST be used only when the user cannot usefully interact (first catalog load, hall WebGL init).
+ - Scope: All loads.
+ - Rationale: Portrait banner vs full-page overlay already follows this.
+ - Verification: Timeline remains visible while “Loading portraits…” banners.
+
+- Statement: Empty datasets MUST explain themselves (e.g. vertical flow: no movements in range; search: no matches; Checkup: no rows for filter).
+ - Scope: Charts, search, tables.
+ - Rationale: Blank gold-on-navy reads as a crash.
+ - Verification: Zoom to a year with no movements; search a nonsense string.
+
+- Statement: Failed loads MUST set a visible error string (`error-banner`, form error, or page error) — never `console.error` alone.
+ - Scope: All data-fetching views.
+ - Rationale: Visitors have no console.
+ - Verification: Stop the API and confirm home shows a load failure message.
+
+### 3.11 3D hall interaction
+
+- Statement: Halls MUST keep **← Back to Timeline**, pointer-lock / click-to-move as already implemented, and **E** (or documented key) for exit/wing navigation. New hall UI MUST not steal those keys without updating this section.
+ - Scope: `VirtualGallery`.
+ - Rationale: Muscle memory across artist, movement, and tour halls.
+ - Verification: Same Back label and exit overlay pattern in all three hall kinds.
+
+- Statement: Clicking a framed painting MUST open painting detail with `returnTo` the current hall. Missing images MUST show the draped-canvas placeholder, not a broken ``.
+ - Scope: Halls.
+ - Rationale: Documented in [basics.md](basics.md).
+ - Verification: Work without a file still shows a frame cover.
+
+---
+
+## 4. Behavioral Standards (Screen UX)
+
+### 4.1 Permission-based rendering (RBAC)
+
+- Statement: Header tools and debug controls MUST render only when `can('')` (and login for curator). Anonymous visitors MUST see browse + Tours popup + locale, not Checkup/Users/etc.
+ - Scope: `HomePage` header, painting/bio debug panel.
+ - Rationale: Security UX: hide, don’t tease.
+ - Verification: Logged-out home; curator without `users` cannot open Users.
+
+- Statement: Route-like views that require a role MUST show the existing “Curator access required” panel with login and back-to-gallery actions — they MUST NOT render an empty privileged page.
+ - Scope: Checkup, Translations, Influences, Tours editor, Users, Activity.
+ - Rationale: Deep view state can still be set; the gate must hold.
+ - Verification: Set view to Users while logged out → curator required copy.
+
+- Statement: The same permission MUST hide the same action on every surface (header, painting debug, API). Do not leave a visible button that 403s.
+ - Scope: All mutations.
+ - Rationale: Predictable roles.
+ - Verification: `can('images')` off → no debug panel and no Fix buttons.
+
+### 4.2 Notifications and user feedback
+
+- Statement: Curator mutations MUST show success or error text in the page’s existing banner/message area. The app has no global toast system; new screens MUST NOT invent a third notification widget without replacing this standard.
+ - Scope: Curator pages.
+ - Rationale: Users/Influences already use inline `message` / `error`.
+ - Verification: One visual treatment per page, consistent placement under the header.
+
+- Statement: Visitor-facing destructive actions (painting **Remove entry** in debug) MUST confirm and then show failure inline if the API rejects.
+ - Scope: Debug painting tools.
+ - Rationale: Catalog deletes are irreversible.
+ - Verification: Cancel confirm → no delete.
+
+- Statement: Copy MUST be concise and, for errors, actionable (“Is the server running?”, “Sign in as a curator…”).
+ - Scope: All user-visible strings.
+ - Rationale: Support load.
+ - Verification: Home catalog failure string remains understandable.
+
+### 4.3 Confirmation and cancellation
+
+- Statement: Destructive curator actions (delete influence, delete tour, remove painting, deactivate user if offered) MUST confirm before the request. `window.confirm` with an i18n string is the current standard (`confirmDelete`); a shared modal MAY replace it later but MUST stay one pattern.
+ - Scope: Influences, Tours, painting remove, Users.
+ - Rationale: Accidental clicks on dense tables.
+ - Verification: Delete tour → confirm; Cancel → no API call.
+
+- Statement: Cancel on a confirm MUST leave filters, selection, and unsaved fields unchanged.
+ - Scope: All confirms.
+ - Rationale: Context preservation.
+ - Verification: Filtered Checkup, cancel a destructive action → filter still applied.
+
+---
+
+## 5. Component Consistency Rules
+
+### 5.1 Shared interaction pattern usage
+
+- Statement: New UI MUST reuse existing components before creating parallels: `GalleryLoadingMarker`, `CatalogSearchBar`, `LocaleSwitcher`, `CuratorLoginModal`, `ArtistFilterModal`, `PaintingLightbox`, `DebugSearchResultsModal`, `DebugUploadButton`.
+ - Scope: `client/src`.
+ - Rationale: Duplicate spinners and dialogs already caused drift.
+ - Verification: PR does not add a second login modal or loading overlay.
+
+- Statement: Movement colours MUST go through `utils/movementColor.ts` (`vividMovementColor`, `shadeMovementColor`). Hex parsing MUST tolerate `#rgb` / `#rrggbb` and keep the first six digits of longer values.
+ - Scope: Timeline charts and any movement swatch.
+ - Rationale: Classic, vertical, and tree already share this util.
+ - Verification: No local `parseHexColor` copies in components.
+
+- Statement: Timeline zoom/pan MUST use `utils/timelineView.ts`. Tree horizontal layout MUST use `utils/movementTree.ts` (view-independent structure).
+ - Scope: Timeline surfaces.
+ - Rationale: Layouts share one year window.
+ - Verification: No one-off wheel handlers that bypass the util.
+
+### 5.2 Component and style selection
+
+- Statement: There is **no Ant Design / MUI**. UI MUST be React + colocated CSS (`ComponentName.tsx` + `ComponentName.css`). Global museum tokens SHOULD reuse existing values:
+
+ | Token | Typical value | Use |
+ |-------|----------------|-----|
+ | Gold | `#c9a96e` / `rgba(201, 169, 110, …)` | Accents, borders, links |
+ | Ink / navy | `#0f0f1a`, `#1a1a2e`, `#16213e` | Page background |
+ | Type | Georgia, serif | Titles, captions, layout links |
+ | Viewport | `100vh`, `overflow: hidden` on home | No document scroll for the museum shell |
+
+ - Scope: All client UI.
+ - Rationale: The look *is* the design system.
+ - Verification: New CSS does not introduce a bright Bootstrap theme.
+
+- Statement: Prefer semantic `