B06: money back takes two people, corrections only cut what is owed (D55/D56)

Returning money that was already collected is the one flow where a single
person must never act alone, and correcting what is still owed must never
touch money that was already taken. Both are now built, server-enforced,
and walkable end to end (DEC23/DEC24).

- RefundService: anyone holding finance.refund (cashier, finance
  supervisor, finance manager) requests against one till transaction; the
  new refunds.approve door (finance supervisor and manager only)
  approves, and that approval performs the reversal atomically -
  requester and approver must differ, enforced in the service, not the
  UI. The reversal is append-only: a negative payment_point_transactions
  row sharing the original collection_reference, Invoice::reversePayment()
  decrementing paid_amount with status recomputed downward (never below
  zero), wallet-paid amounts credited back through WalletService.
  Partial refunds capped at the transaction less refunds already
  processed against it. States: requested -> processed | rejected; a
  decided request is immutable and keeps its reason.
- CreditNoteService: a note corrects what is still owed and only that -
  capped at the credit-aware balance (Invoice::creditTotal()), counting
  from the moment it is issued, voidable with a recorded reason. A fully
  paid invoice offers no note at all; the refund is the instrument for
  money already collected. Every balance consumer (invoice page, invoice
  list, till picker, dashboards) nets off issued notes.
- Surfaces: /refunds (review, approve/refuse, void notes) and the
  invoice page's Payments and adjustments section (till rows with live
  refundable, refund requests, credit notes, the note form capped at the
  true balance). Both carry the sandbox label - no gateway is contacted,
  no real money moves; reversals are recorded in this clinic's register.
- Permissions and navigation: refunds.approve in the catalogue and in the
  finance supervisor/manager templates; Refunds under Finance; foreign
  references in payloads still 404, attribution still from session/row.

Defects fixed along the way:

- Invoice::approval() inferred a non-existent approval_id column, so the
  B10 Approval request card could never render - the double-approval UI
  lived only in POST routes and tests. Now hasOne(InvoiceApproval::class,
  'invoice_id')->latestOfMany(); the full supervisor -> manager walk is
  captured in the evidence.
- The new tables were the only ones in the app without the house
  <div style="overflow-x:auto;"> wrapper and escaped their card at phone
  width; five tables wrapped, and the capture run now fails if any
  element paints past the viewport without a scrolling ancestor.
- The till empty state claimed nothing had been collected against an
  invoice that had just had a payment recorded on it; the copy now says
  what the section lists and where invoice-page payments appear.
- PaymentCollectionTest::test_the_till_screen_offers_the_settled_and_dra
  ft_invoices_nothing re-scoped, intent unchanged: the seeded till rows
  honestly name the settled invoice in the Recent collections log, so
  the assertion now covers the offering, not the history.

Tests: RefundsAndCreditNotesTest 16 tests / 85 assertions new; focused
rerun 69 tests / 248 assertions; post-inspection rerun of every
payment/invoice/refund class against the final views 124 tests / 477
assertions; full suite 719 tests / 3036 assertions green with 1
pre-existing skip (work/phpunit-final.txt). Evidence under
outputs/feature-evidence/B06-refunds-and-credit-notes/rev-20261016-001/
(22 screenshots including narrow-390 and tablet-768 passes,
verification.md, test outputs) captured through a headless-Edge CDP
driver after a fresh migrate:fresh --seed per demo set, so every figure
on screen is the seeded value the outline names. Outline entries D55/D56
at revision v4; DEC23/DEC24 register rows flipped to implemented;
CONTINUE.md gains rev-20261016-003.
