You are Worker E2 on the Bookshelf project (~/bookshelf). Wave 3, part 2:
LIBRARY + DETAIL + SCAN screens — the three the user actually looks at most.

FIRST, READ THESE — the contract. Follow exactly; do not invent alternative names,
do not restate them back to me:
  ~/bookshelf/docs/SPEC.md     (authoritative — see "Screens", "Design language")
  ~/bookshelf/docs/HANDOFF.md  (operational state, gotchas already paid for)

Waves 1-2 are DONE and verified green: theme + shared components (ui.theme,
ui.components), the whole data layer (data.local/remote/repo/prefs, AppContainer),
metadata lookup (data.metadata) and scanner plumbing (ui.scan: ScannerController,
IsbnBarcodeAnalyzer, ScanCodeFilter). REUSE them. Read `AppContainer.kt`, the
repository classes, and the existing ui.scan classes to learn the real API before
writing against them.

## Your scope
  ui.library  LibraryScreen: adaptive 2-3 column cover grid, search by title/author/
              ISBN, filter by bookcase/shelf, sort by title/author/added, empty state
              that invites the first scan, FAB -> scan, sync status line.
              Covers are the hero — let them carry the color.
  ui.detail   DetailScreen: big cover, title/subtitle/authors/publisher/year/pages/ISBN,
              collapsible description, editable notes, location picker, edit,
              soft-delete WITH UNDO.
  ui.scan     ScanScreen ONLY — the camera screen itself, built on the EXISTING
              ScannerController/IsbnBarcodeAnalyzer (do not rewrite them; extend only
              if genuinely necessary). Camera + reticle; on hit -> bottom sheet with
              the fetched book + shelf picker + Save/Skip. Duplicate-ISBN warning if
              already owned. CONTINUOUS mode: after a save, stay on camera for the next
              book, with a running "added this session" count. Handle permission denial,
              torch toggle, and a manual-ISBN-entry escape hatch.

## HARD BOUNDARIES — Worker E1 is running RIGHT NOW in this same repo
- DO NOT create or edit anything under ui.nav, ui.setup, ui.locations, ui.settings,
  or MainActivity.kt. E1 owns those. E1 will call your screens from the NavHost using
  exactly the signatures in the contract block below — match them precisely.
- DO NOT edit ui.theme or ui.components (wave 1B, verified green). Reuse, don't modify.
- DO NOT edit `app/build.gradle.kts` or `gradle/libs.versions.toml`. Everything needed
  is already declared and wired. If you think something is missing, report it, do not add it.
- Data layer: you MAY extend BookRepository / BookDao (e.g. sort-by-author, filter by
  bookcase) if a screen genuinely needs it. You may NOT touch LocationRepository,
  ShelfDao, or BookcaseDao — E1 owns those. If you need a location-side query, report it.

## SHARED SCREEN CONTRACT — fixed by the orchestrator, IDENTICAL in both wave-3
## prompts. Do NOT change these signatures. E1 writes BookshelfNavHost against them;
## E2 writes the screens to match. If you deviate, the other worker's code stops
## compiling and the wave fails.

Routes (string constants live in ui/nav/Routes.kt, owned by E1):
  "setup" | "library" | "library?shelfId={shelfId}" | "detail/{bookId}" |
  "scan" | "locations" | "settings"

Screen composable signatures:

  // E2 owns
  @Composable fun LibraryScreen(
      shelfIdFilter: String?,
      onBookClick: (String) -> Unit,
      onScanClick: () -> Unit,
      onLocationsClick: () -> Unit,
      onSettingsClick: () -> Unit,
      container: AppContainer,
  )
  @Composable fun DetailScreen(
      bookId: String,
      onBack: () -> Unit,
      container: AppContainer,
  )
  @Composable fun ScanScreen(
      onBack: () -> Unit,
      container: AppContainer,
  )

  // E1 owns
  @Composable fun SetupScreen(onSetupComplete: () -> Unit, container: AppContainer)
  @Composable fun LocationsScreen(
      onBack: () -> Unit,
      onShelfClick: (String) -> Unit,   // navigates to library?shelfId=...
      container: AppContainer,
  )
  @Composable fun SettingsScreen(
      onBack: () -> Unit,
      onSignedOut: () -> Unit,          // navigates back to "setup"
      container: AppContainer,
  )

All screens take `container: AppContainer` and construct their own ViewModel from it
(manual DI per SPEC — NO Hilt). Package = ui.<screen>, e.g. ui.library.LibraryScreen.

## Build/verify — CRITICAL
Never run `./gradlew`; E1 builds concurrently and you will corrupt each other's build.
ALWAYS use the serialized wrapper (it takes a lock and may block — wait for it):
    ~/bookshelf/tasks/gw assembleDebug
    ~/bookshelf/tasks/gw testDebugUnitTest

## Definition of done — verified by YOU, actually run, not assumed
1. `~/bookshelf/tasks/gw assembleDebug` exits 0.
2. `~/bookshelf/tasks/gw testDebugUnitTest` exits 0 with ZERO failures. There are 68
   existing passing tests — you must not break any of them.
3. Add real unit tests with real assertions for the logic you add (search/sort/filter
   selection, duplicate-ISBN detection, scan-session state). SPEC: "Do not write
   assertion-free tests." Do not test-by-screenshot only.
4. Offline-first per SPEC: every read comes from Room, no screen blocks on network,
   nothing crashes when the server is unreachable.
5. Every screen uses the wave-1B theme and components. Warm paper / mahogany / gold,
   Literata for titles. Do not introduce new colors or a second type scale.

## IMPORTANT — do not repeat Worker D's mistake
A previous worker ended its turn saying "tests are running in the background, I'll
report when done." That is a FAILURE. Run the commands, WAIT for them, and report real
exit codes you actually observed. A truthfully reported gap is worth more than a false
green; an unverified claim is worth less than nothing.

## Report back (short — read by a token-constrained orchestrator)
- real exit codes of the two gradle commands, and the test failure count
- files created, one line each
- anything in SPEC.md you could NOT satisfy, and why
