diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md
index f908184eeed..0d68a71e753 100644
--- a/.claude/CLAUDE.md
+++ b/.claude/CLAUDE.md
@@ -186,3 +186,5 @@ npm run i18n:pot # Generate translations
- Test files mirror source structure
- PHP tests require Docker - ensure it's running before executing tests
- Use `npm run test:php` to run all tests or edit the command to pass PHPUnit filters
+- When pushing, always push only the current branch: `git push origin HEAD` (not `git push` which tries to push all configured branches)
+- When pulling, always pull only the current branch: `git pull origin $(git branch --show-current)` or `git pull --rebase origin HEAD`
diff --git a/.claude/PM_PROMOTIONS.md b/.claude/PM_PROMOTIONS.md
new file mode 100644
index 00000000000..f1c6ab80aa7
--- /dev/null
+++ b/.claude/PM_PROMOTIONS.md
@@ -0,0 +1,536 @@
+# Payment Method (PM) Promotions
+
+## Overview
+
+PM Promotions display promotional offers for payment methods that merchants haven't yet enabled. The system uses a **flat data structure** where each promotion is a standalone object with a `type` field indicating its display context (spotlight, badge).
+
+## Data Flow
+
+```
+Transact Platform API → WC_Payments_PM_Promotions_Service → REST API → Redux Store → Components
+ ↓
+ validate → filter → normalize
+```
+
+**Server-side (backend) responsibilities:**
+- Fetch promotions from WooPayments API (with store context)
+- Validate promotion structure
+- Filter by: dismissals, PM validity, enabled status, **active discounts**
+- Normalize data (apply fallbacks, derive titles)
+- Cache results with context-aware invalidation
+
+**Client-side (frontend) responsibilities:**
+- Validate promotion structure (type guards, defense in depth)
+- Filter dismissed promotions (defense in depth)
+- Render appropriate UI based on `type`
+- Track analytics events
+
+## Data Structures
+
+### Promotion (TypeScript)
+
+```typescript
+// client/data/pm-promotions/types.d.ts
+
+type PmPromotionType = 'spotlight' | 'badge';
+
+interface PmPromotion {
+ id: string; // Globally unique promotion variation ID (e.g., "campaign-name-promo__spotlight__blabla"). A campaign can have multiple variations.
+ promo_id: string; // Campaign identifier e.g., "campaign-name-promo"
+ payment_method: string; // PM ID from Payment_Method constants e.g., "klarna"
+ payment_method_title: string; // Human-readable payment method title e.g., "Klarna"
+ type: PmPromotionType; // Display context: 'spotlight' | 'badge'
+ title: string; // Promotion headline
+ badge_text?: string; // Optional badge text (for spotlight type)
+ badge_type?: ChipType; // Optional badge visual style
+ description: string; // Promotion body text
+ cta_label: string; // Primary button text (fallback: "Enable {payment_method_title}")
+ tc_url: string; // Terms & conditions URL (required)
+ tc_label: string; // Terms link text (fallback: "See terms")
+ footnote?: string; // Optional footnote text to be displayed below main content
+ image?: string; // Optional image URL (mostly for spotlight type)
+}
+```
+
+### Redux State
+
+```typescript
+interface PmPromotionsState {
+ pmPromotions?: PmPromotion[];
+ pmPromotionsError?: ApiError;
+}
+```
+
+### Dismissals (Server-side storage)
+
+```php
+// Flat structure: [id => timestamp]
+[
+ 'klarna-2026-promo__spotlight' => 1733123456,
+ 'klarna-2026-promo__badge' => 1733123789,
+]
+```
+
+## Key Files
+
+### Client (Frontend)
+
+| File | Purpose |
+|------|---------|
+| `client/data/pm-promotions/types.d.ts` | TypeScript interfaces |
+| `client/data/pm-promotions/hooks.ts` | `usePmPromotions`, `usePmPromotionActions` hooks |
+| `client/data/pm-promotions/selectors.ts` | Redux selectors (`getPmPromotions`, `getPmPromotionsError`) |
+| `client/data/pm-promotions/actions.ts` | `activatePmPromotion`, `dismissPmPromotion` |
+| `client/data/pm-promotions/resolvers.ts` | API fetch with type guards |
+| `client/promotions/spotlight/index.tsx` | Spotlight promotion component |
+| `client/components/promotional-badge/index.tsx` | Badge promotion component with T&C tooltip |
+| `client/settings/payment-methods-list/payment-method.tsx` | PM settings item (uses PromotionalBadge) |
+
+### Server (Backend)
+
+| File | Purpose |
+|------|---------|
+| `includes/class-wc-payments-pm-promotions-service.php` | Main service: fetch, filter, normalize promotions |
+| `includes/admin/class-wc-rest-payments-pm-promotions-controller.php` | REST API controller |
+| `tests/unit/admin/test-class-wc-payments-pm-promotions-service.php` | Service unit tests |
+
+## Hooks API
+
+### usePmPromotions
+
+```typescript
+const { pmPromotions, isLoading, pmPromotionsError } = usePmPromotions();
+// Returns: { pmPromotions: PmPromotion[], isLoading: boolean, pmPromotionsError?: ApiError }
+```
+
+### usePmPromotionActions
+
+```typescript
+const { activatePmPromotion, dismissPmPromotion } = usePmPromotionActions();
+
+// Activate a promotion (enables the payment method)
+activatePmPromotion(id: string); // e.g., "klarna-2026-promo__spotlight"
+
+// Dismiss a promotion
+dismissPmPromotion(id: string); // e.g., "klarna-2026-promo__spotlight"
+```
+
+## Selectors
+
+```typescript
+// Get all promotions
+getPmPromotions(state): PmPromotion[]
+
+// Get promotions error
+getPmPromotionsError(state): ApiError | undefined
+```
+
+## Component Implementation Pattern
+
+### SpotlightPromotion Example
+
+```tsx
+// client/promotions/spotlight/index.tsx
+
+const SpotlightPromotion: React.FC = () => {
+ const { pmPromotions, isLoading } = usePmPromotions();
+ const { activatePmPromotion, dismissPmPromotion } = usePmPromotionActions();
+
+ // Don't render if data is still loading
+ if (isLoading) return null;
+
+ // Don't render if no promotions available
+ if (!pmPromotions || pmPromotions.length === 0) return null;
+
+ // Find spotlight promotion
+ const spotlightPromotion = pmPromotions.find(p => p.type === 'spotlight');
+ if (!spotlightPromotion) return null;
+
+ // Common event properties for tracking
+ const getEventProperties = () => ({
+ promo_id: spotlightPromotion.promo_id,
+ payment_method: spotlightPromotion.payment_method,
+ display_context: 'spotlight',
+ source: getPageSource(), // Helper that returns page identifier
+ path: window.location.pathname + window.location.search,
+ });
+
+ // Track when promotion becomes visible
+ const handleView = () => {
+ recordEvent('wcpay_payment_method_promotion_view', getEventProperties());
+ };
+
+ // Activate promotion and enable payment method
+ const handlePrimaryClick = () => {
+ recordEvent('wcpay_payment_method_promotion_activate_click', getEventProperties());
+ activatePmPromotion(spotlightPromotion.id);
+ };
+
+ // Open terms and conditions link
+ const handleSecondaryClick = () => {
+ recordEvent('wcpay_payment_method_promotion_link_click', {
+ ...getEventProperties(),
+ link_type: 'terms',
+ });
+ if (spotlightPromotion.tc_url) {
+ window.open(spotlightPromotion.tc_url, '_blank', 'noopener,noreferrer');
+ }
+ };
+
+ // Dismiss the promotion
+ const handleDismiss = () => {
+ recordEvent('wcpay_payment_method_promotion_dismiss_click', getEventProperties());
+ dismissPmPromotion(spotlightPromotion.id);
+ };
+
+ return (
+
+ { __( + 'Please enter a valid mobile phone number.', + 'woocommerce-payments' + ) } +
+ ) } - { isBlocksCheckout && ( -- { __( - 'Please enter a valid mobile phone number.', - 'woocommerce-payments' - ) } -
- ) }