From 46d681c6b825d21b3f698d793c4e630c687d90ad Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Thu, 21 May 2026 21:41:53 +0000
Subject: [PATCH] =Major CustomBlocks.php overhaul, expanding block support and customization from the editor. theme.json should now be updated on new themes to set brand colours, etc. Also note: major change to .col vs .row alignment: simplifying it to .top .bottom vs the confusion of the differences for .col/.row .start and .a-start

---
 inc/managers/ReferralManager.php | 2469 ++++++++++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 2,035 insertions(+), 434 deletions(-)

diff --git a/inc/managers/ReferralManager.php b/inc/managers/ReferralManager.php
index 429ea99..c521c15 100644
--- a/inc/managers/ReferralManager.php
+++ b/inc/managers/ReferralManager.php
@@ -1,9 +1,10 @@
 <?php
 namespace JVBase\managers;
 
-use JVBase\managers\MagicLinkManager;
-use JVBase\integrations\Cloudflare;
-use JVBase\utility\Features;
+use JVBase\meta\Form;
+use JVBase\ui\CRUDSkeleton;
+use JVBase\ui\Tabs;
+use JVBase\base\Site;
 use WP_User;
 use WP_Error;
 
@@ -21,10 +22,19 @@
 {
 	protected $wpdb;
 	protected MagicLinkManager $magic_link;
-	protected CacheManager $cache;
+	protected Cache $cache;
+	protected Cache $requestCache;
+	protected Cache $statsCache;
 	protected string $referrals_table;
+	protected ?int $referralPage = null;
 	protected string $rewards_table;
 
+	protected CustomTable $referrals;
+	protected CustomTable $codes;
+	protected CustomTable $janeClients;
+	protected CustomTable $rewards;
+	protected CustomTable $treatments;
+
 	// Default reward settings
 	protected array $default_settings = [
 		'referrer_reward_applies_to' => 'per_user',  // 'per_user' or 'flat_total'
@@ -32,19 +42,38 @@
 		'referrer_reward_type'	=> 'fixed',
 		'referee_reward_type' => 'percentage',  // 'percentage' or 'fixed'
 		'referee_reward_amount' => 20,  // 20% or $20
-		'referee_reward_applies_to' => 'first_order'  // 'first_order' or 'all_orders'
+		'referee_reward_applies_to' => 'first_order',  // 'first_order' or 'all_orders'
 	];
 
+	protected string $role;
+
+	protected array $settings;
+
 	public function __construct()
 	{
+		$this->defineTables();
+		$this->role = Site::getDefaultReferralRole();
+		$this->default_settings['referral_role'] = $this->role;
 		global $wpdb;
 		$this->wpdb = $wpdb;
-		$this->cache = new CacheManager('referrals');
-		$this->referrals_table = BASE . 'referrals';
-		$this->rewards_table = BASE . 'referral_rewards';
-		$this->magic_link = new MagicLinkManager();
+		$this->cache = Cache::for('referrals', WEEK_IN_SECONDS);
+		$this->requestCache = Cache::for('referral_requests', WEEK_IN_SECONDS)->connect('referrals', true);
+		$this->statsCache = Cache::for('referral_stats', WEEK_IN_SECONDS)->connect('referrals', true);
+		if (JVB_TESTING) {
+			$this->cache->flush();
+			$this->requestCache->flush();
+			$this->statsCache->flush();
+		}
 
-		// Hook into user registration to track referrals
+		$this->referrals_table = $wpdb->prefix . BASE . 'referrals';
+		$this->rewards_table = $wpdb->prefix . BASE . 'referral_rewards';
+
+		$this->referralPage = $this->getReferralPageId();
+		$this->settings = $this->getRewardSettings();
+
+
+		add_action('jvbUserRegistered', [$this, 'processRegistrationToken'], 10, 3);
+		add_action('jvb_add_token_inputs', [$this, 'addLoginInputs'], 10, 1);
 		add_action('user_register', [$this, 'processReferral'], 10, 1);
 
 		// Add meta boxes for admin to manage referrals
@@ -55,14 +84,243 @@
 		add_action('personal_options_update', [$this, 'saveUserReferralCode']);
 		add_action('edit_user_profile_update', [$this, 'saveUserReferralCode']);
 
-		add_filter(BASE.'new_user_email_content', [$this, 'addReferralToWelcomeEmail'], 99, 2);
+		add_filter('jvbNewUserEmail', [$this, 'addReferralToWelcomeEmail'], 99, 2);
 
 
-		add_action('jvbAdditionalActions', [$this, 'outputShareWidget']);
+		add_filter('jvbAdditionalActions', [$this, 'outputShareWidget']);
 
 		add_action('wp_enqueue_scripts', [$this, 'enqueueScripts']);
 		// Schedule cron jobs for reports
 		$this->registerCronJobs();
+
+		// Add admin bar label for referral page
+		add_action('admin_bar_menu', [$this, 'addReferralPageLabel'], 999);
+
+		// Add admin notice to referral page edit screen
+		add_action('admin_notices', [$this, 'showReferralPageNotice']);
+
+
+		add_filter('jvbDashboardPage', [$this, 'renderDashPage'], 10, 2);
+
+		// Handle settings save
+		add_action('admin_init', [$this, 'registerSettings']);
+		// Handle admin page form submission
+		add_filter('jvb_admin_page_submission', [$this, 'handleAdminSubmission'], 10, 3);
+	}
+
+	protected function defineTables():void
+	{
+		$this->defineReferralsTable();
+		$this->defineCodeTable();
+		$this->defineJaneClientsTable();
+		$this->defineRewardsTable();
+		$this->defineTreatmentsTable();
+	}
+		protected function defineReferralsTable():void
+		{
+			$table = CustomTable::for('referrals');
+
+			$table->setColumns([
+				'id'		=> 'bigint(20) unsigned NOT NULL AUTO_INCREMENT',
+				'from_user'	=> "{$table->getUserIDType()} NOT NULL",
+				'to_user'	=> "{$table->getUserIDType()} NOT NULL",
+				'to_name'	=> 'varchar(255) NOT NULL',
+				'to_email'	=> 'varchar(255) NOT NULL',
+				'to_phone'	=> 'varchar(50) NOT NULL',
+				'referral_code'=> 'varchar(50) NOT NULL',
+				'status'	=> "ENUM('pending', 'consulted', 'treated', 'cancelled') DEFAULT 'pending'",
+				'created_at'=> 'datetime DEFAULT CURRENT_TIMESTAMP',
+				'consulted_at'=> 'datetime DEFAULT NULL',
+				'treated_at'=> 'datetime DEFAULT NULL',
+				'treatment_count' => 'int DEFAULT 0',
+				'notes'		=> 'text DEFAULT NULL',
+			]);
+
+			$table->setKeys([
+				['key' => 'PRIMARY', 'value' => 'id'],
+				['key' => 'UNIQUE', 'value' => '(`to_user`)'],
+				'from_user (`from_user`)',
+				'status (`status`)',
+				'code (`referral_code`)',
+				'date (`created_at`)',
+				'consult (`consulted_at`)'
+			]);
+
+			$base = BASE;
+			$table->setConstraints([
+				"CONSTRAINT `{$base}referral_from_user_fk` FOREIGN KEY (`from_user`)
+				REFERENCES `{$table->getUserTable()}` (`ID`) ON DELETE CASCADE",
+				"CONSTRAINT `{$base}referral_to_user_fk` FOREIGN KEY (`to_user`)
+				REFERENCES `{$table->getUserTable()}` (`ID`) ON DELETE CASCADE"
+			]);
+			$table->defineTable();
+			$this->referrals = $table;
+		}
+
+		protected function defineCodeTable():void
+		{
+			$table = CustomTable::for('referrals_codes');
+
+			$table->setColumns([
+				'id'		=> 'bigint(20) unsigned NOT NULL AUTO_INCREMENT',
+				'user_id'	=> "{$table->getUserIDType()} NOT NULL",
+				'code'		=> 'varchar(50) NOT NULL',
+				'created_at'=> 'datetime DEFAULT CURRENT_TIMESTAMP',
+			]);
+
+			$table->setKeys([
+				['key' => 'PRIMARY', 'value' => '(`id`)'],
+				['key' => 'UNIQUE', 'value' => '(`code`)'],
+				'user (`user_id`)',
+			]);
+
+			$base = BASE;
+			$table->setConstraints([
+				"CONSTRAINT `{$base}referral_code_user_fk` FOREIGN KEY (`user_id`)
+					REFERENCES `{$table->getUserTable()}` (`ID`) ON DELETE CASCADE",
+			]);
+			$table->defineTable();
+			$this->codes = $table;
+		}
+		protected function defineJaneClientsTable():void
+		{
+			$table = CustomTable::for('referrals_jane_clients');
+
+			$table->setColumns([
+				'id'			=> 'bigint(20) unsigned NOT NULL AUTO_INCREMENT',
+				'patient_guid'	=> 'varchar(50) NOT NULL',
+				'user_id'		=> "{$table->getUserIDType()} NOT NULL",
+				'first_name'	=> 'varchar(100) NOT NULL',
+				'last_name'		=> 'varchar(100) NOT NULL',
+				'email'			=> 'varchar(255) NOT NULL',
+				'imported_at'	=> 'datetime DEFAULT CURRENT_TIMESTAMP',
+				'updated_at'	=> 'datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP',
+			]);
+
+			$table->setKeys([
+				['key' => 'PRIMARY', 'value' => '(`id`)'],
+				['key' => 'UNIQUE', 'value' => '(`patient_guid`)'],
+				'user (`user_id`)',
+				'email (`email`)',
+			]);
+
+			$base = BASE;
+			$table->setConstraints([
+				"CONSTRAINT `{$base}jane_clients_user` FOREIGN KEY (`user_id`)
+				REFERENCES `{$table->getUserTable()}` (`ID`) ON DELETE CASCADE",
+			]);
+			$table->defineTable();
+			$this->janeClients = $table;
+		}
+		protected function defineRewardsTable():void
+		{
+			$table = CustomTable::for('referrals_rewards');
+
+			$table->setColumns([
+				'id'				=> 'bigint(20) unsigned NOT NULL AUTO_INCREMENT',
+				'referral_id'		=> 'bigint(20) unsigned NOT NULL',
+				'user_id'			=> "{$table->getUserIDType()} NOT NULL",
+				'reward_type'		=> "ENUM('referrer', 'referee') NOT NULL",
+				'amount'			=> 'decimal(10,2) NOT NULL',
+				'reward_calculation'=> "ENUM('percentage', 'fixed')",
+				'status'			=> "ENUM('available', 'redeemed', 'expired', 'cancelled') DEFAULT 'available'",
+				'created_at'		=> 'datetime NOT NULL DEFAULT CURRENT_TIMESTAMP',
+				'updated_at'		=> 'datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP',
+				'redeemed_at'		=> 'datetime DEFAULT NULL',
+				'expires_at'		=> 'datetime DEFAULT NULL',
+				'notes'				=> 'text DEFAULT NULL',
+			]);
+
+			$table->setKeys([
+				['key' => 'PRIMARY', 'value' => '(`id`)'],
+				'referral (`referral_id`)',
+				'user (`user_id`)',
+				'status (`status`)',
+				'type (`reward_type`)'
+			]);
+
+			$base = BASE;
+			$table->setConstraints([
+				"CONSTRAINT `{$base}reward_referral` FOREIGN KEY (`referral_id`)
+				REFERENCES `{$this->referrals->getFullTableName()}` (`id`) ON DELETE CASCADE",
+				"CONSTRAINT `{$base}reward_user` FOREIGN KEY (`user_id`)
+				REFERENCES `{$table->getUserTable()}` (`ID`) ON DELETE CASCADE"
+			]);
+			$table->defineTable();
+			$this->rewards = $table;
+		}
+		protected function defineTreatmentsTable():void
+		{
+			$table = CustomTable::for('referral_treatments');
+
+			$table->setColumns([
+				'id'			=> 'bigint(20) unsigned NOT NULL AUTO_INCREMENT',
+				'referral_id'	=> 'bigint(20) unsigned NOT NULL',
+				'user_id'		=> "{$table->getUserIDType()} NOT NULL",
+				'treatment_type'=> 'varchar(100) NOT NULL',		//Tier 1-6, Brows, etc
+				'treatment_date'=> 'datetime NOT NULL',
+				'invoice_number'=> 'varchar(50) DEFAULT NULL',
+				'amount'		=> 'decimal(10,2) DEFAULT NULL',
+				'status'		=> "ENUM('completed', 'no_show', 'cancelled') DEFAULT 'completed'",
+				'imported_at'	=> 'datetime DEFAULT CURRENT_TIMESTAMP',
+			]);
+
+			$table->setKeys([
+				['key' => 'PRIMARY', 'value' => '(`id`)'],
+				'referral (`referral_id`)',
+				'user (`user_id`)',
+				'date (`treatment_date`)',
+				'type (`treatment_type`)',
+			]);
+
+			$base = BASE;
+			$table->setConstraints([
+				"CONSTRAINT `{$base}treatment_referral` FOREIGN KEY (`referral_id`)
+				REFERENCES `{$this->referrals->getFullTableName()}` (`id`) ON DELETE CASCADE",
+				"CONSTRAINT `{$base}treatment_user` FOREIGN KEY (`user_id`)
+				REFERENCES `{$table->getUserTable()}` (`ID`) ON DELETE CASCADE"
+			]);
+			$table->defineTable();
+			$this->treatments = $table;
+		}
+
+	public function getSettings():array
+	{
+		return $this->settings;
+	}
+	public function getRole():string
+	{
+		return $this->role;
+	}
+	public function addLoginInputs(string $action):void
+	{
+		if (array_key_exists('ref', $_GET)) {
+			echo '<input type="hidden" name="referral_code" value="'.$_GET['ref'].'">';
+		}
+	}
+
+	public function modifyLoginText(array $defaults):array
+	{
+		if (!array_key_exists('ref', $_GET)){
+			return $defaults;
+		}
+
+		$code = $_GET['ref'];
+		$user = $this->getUserByReferralCode($code);
+
+		$desc = ($user) ? strtok($user->display_name, ' ') . ' invited you ' : 'You\'ve been invited ';
+
+		$defaults['title'] = 'Register your Account. Get Your Reward.';
+		$defaults['description'] = [
+			$desc.' to see the difference with us.',
+			'Oh, and you\'ll get 20% off your first treatment!',
+			'Finish this account creation (you\'ll still need to make an account on Jane App to book), or let us know when you come in for your appointment.'
+		];
+		$defaults['extra'] = [
+			'Once you\'re in, you\'ll get your own code you can share.',
+			'Get a $25 credit for <b>each</b> person you send our way who comes in for their first treatment!'
+		];
+		return $defaults;
 	}
 
 	public function enqueueScripts():void
@@ -72,10 +330,21 @@
 			'jvb-a11y',
 			'jvb-popup',
 			'jvb-tabs',
+			'jvb-data-store',
 		];
 
-		if (Features::hasIntegration('cloudflare') && JVB()->connect('cloudflare')->isSetUp()) {
-			$requirements[] = 'cloudflare-turnstile';
+		if (Site::hasIntegration('cloudflare') && JVB()->connect('cloudflare')->isSetUp()) {
+			JVB()->connect('cloudflare')->enqueueTurnstileScripts();
+		}
+		if (is_singular(BASE.'dash')) {
+			$requirements[] = 'jvb-form';
+			$requirements[] = 'jvb-view';
+
+			wp_enqueue_script('jvb-referral-admin',
+			JVB_URL.'assets/js/min/referralAdmin.min.js',
+			['jvb-referral'],
+			'1.0.0',
+			true);
 		}
 		wp_enqueue_script(
 			'jvb-referral',
@@ -104,6 +373,26 @@
 		}
 	}
 
+	public function createCode(int $user_id, string $code):string|false
+	{
+		$code = sanitize_title($code);
+		$existing = $this->codes->get(['code' => $code]);
+		if ($existing) {
+			if ($existing['user_id'] !== $user_id) {
+				return false;
+			}
+			return $code;
+		}
+		$success = $this->codes->insert([
+			'user_id' 	=> $user_id,
+			'code'		=> $code
+		]);
+		if ($success) {
+			return $code;
+		}
+		return false;
+	}
+
 	/**
 	 * Generate or get existing referral code for a user
 	 *
@@ -111,32 +400,35 @@
 	 * @param string|null $custom_code Optional custom code
 	 * @return string|WP_Error
 	 */
-	public function getUserReferralCode(int $user_id, ?string $custom_code = null)
+	public function getUserReferralCode(int $user_id, ?string $custom_code = null):string|false
 	{
-		$user = get_user_by('ID', $user_id);
+		$user = get_userdata($user_id);
 		if (!$user) {
-			return new WP_Error('invalid_user', 'User not found');
+			return false;
 		}
 
-		// Check if user already has a code
-		$existing_code = get_user_meta($user_id, BASE . 'referral_code', true);
-
-		if ($existing_code && !$custom_code) {
-			return $existing_code;
+		$existing = $this->codes->pluck('code', ['user_id' => $user_id],'created_at', 'DESC');
+		if (!empty($existing) && !$custom_code) {
+			return $existing[0];
+		}
+		if ($custom_code && !empty($existing) && !in_array($custom_code, $existing)) {
+			$test = $this->createCode($user_id, $custom_code);
+			if ($test) {
+				return $this->codes->pluck('code', ['user_id' => $user_id], 'created_at', 'DESC')[0];
+			} else {
+				return $existing[0];
+			}
 		}
 
 		// Generate new code if custom provided or none exists
-		$code = $custom_code ?: $this->generateReferralCode($user);
+		$code = $this->generateReferralCode($user);
 
-		// Validate uniqueness
-		if ($this->isCodeTaken($code, $user_id)) {
-			return new WP_Error('code_taken', 'This referral code is already in use');
+		$success = $this->createCode($user_id, $code);
+		if ($success) {
+			return $this->codes->pluck('code', ['user_id' => $user_id], 'created_at', 'DESC')[0];
 		}
 
-		// Save the code
-		update_user_meta($user_id, BASE . 'referral_code', $code);
-
-		return $code;
+		return false;
 	}
 
 	/**
@@ -169,65 +461,108 @@
 	 * Check if a referral code is already taken
 	 *
 	 * @param string $code
-	 * @param int|null $exclude_user_id
 	 * @return bool
 	 */
-	protected function isCodeTaken(string $code, ?int $exclude_user_id = null): bool
+	protected function isCodeTaken(string $code): bool
 	{
-		$args = [
-			'meta_key' => BASE . 'referral_code',
-			'meta_value' => $code,
-			'fields' => 'ID',
-			'number' => 1
-		];
-
-		if ($exclude_user_id) {
-			$args['exclude'] = [$exclude_user_id];
-		}
-
-		$users = get_users($args);
-		return !empty($users);
+		return (bool) $this->codes->get(['code' => $code]);
 	}
 
+	public function processRegistrationToken(int $user_id, string $email, array $data): void
+	{
+		// Check for referral code in data
+		$code = $data['referral_code'] ?? '';
+		if (empty($code)) {
+			return;
+		}
+
+		// Store in session/cookie for processReferral to pick up
+		if (session_status() === PHP_SESSION_NONE) {
+			session_start();
+		}
+		$_SESSION[BASE . 'referral_code'] = sanitize_text_field($code);
+		setcookie(
+			BASE . 'referral_code',
+			sanitize_text_field($code),
+			time() + (86400 * 30),
+			'/'
+		);
+	}
 	/**
 	 * Track a new referral when user registers
 	 *
 	 * @param int $user_id
+	 * @param array $userData
+	 * @return bool;
 	 */
-	public function processReferral(int $user_id): void
+	public function processReferral(int $user_id, array $userData): bool
 	{
-		// Check if user was created via referral magic link
-		$referral_code = get_user_meta($user_id, BASE . 'pending_referral_code', true);
+		$referral = $this->referrals->get(['to_user' => $user_id]);
 
-		if (!$referral_code) {
-			return;
+		if (empty($referral)) {
+			$referral = $this->referrals->get(['to_email' => $userData['email']]);
+		}
+		if (empty($referral)) {
+			// Check session/cookie if not in meta
+			if (session_status() === PHP_SESSION_NONE) {
+				session_start();
+			}
+			$referral_code = $_SESSION[BASE . 'referral_code'] ?? $_COOKIE[BASE . 'referral_code'] ?? '';
+			if (!empty ($referral_code)) {
+				$referral = [
+					'to_user'	=> $user_id,
+					'referral_code'	=> $referral_code,
+					'to_email'		=> $userData['user_email']
+				];
+			}
+		}
+
+		if (empty($referral)) {
+			return false; // No referral code - regular registration
 		}
 
 		// Find the referrer
-		$referrer = $this->getUserByReferralCode($referral_code);
+		$referrer = $this->codes->pluck('user_id', ['code' => $referral['referral_code']]);
+		if (empty($referrer)) {
+			//This should not happen, but whatever
+			return false;
+		}
+		$referrer = $referrer[0];
+		$record = $this->referrals->findOrCreate([
+			'to_user'	=> $user_id,
+			'referral_code'	=> $referral['referral_code'],
+		], [
+			'from_user'		=> $referrer,
+			'to_email'		=> $referral['to_email'],
+			'to_name'		=> $userData['first_name'],
+//			'to_phone'		=>
+			'status'		=> 'pending'
+		]);
 
-		if (!$referrer) {
-			delete_user_meta($user_id, BASE . 'pending_referral_code');
-			return;
+		if (!$record) {
+			error_log('[ReferralManager]::processReferral Could not update record for user: '.print_r($referral, true));
+			return false;
 		}
 
-		// Check for duplicates
-		$existing = $this->getReferralByReferee($user_id);
-		if ($existing) {
-			delete_user_meta($user_id, BASE . 'pending_referral_code');
-			return;
+
+		// Clean up temp data
+		delete_user_meta($user_id, BASE . 'pending_referral_code');
+		if (isset($_SESSION[BASE . 'referral_code'])) {
+			unset($_SESSION[BASE . 'referral_code']);
+		}
+		if (isset($_COOKIE[BASE . 'referral_code'])) {
+			setcookie(BASE . 'referral_code', '', time() - 3600, '/');
 		}
 
-		// Create referral record
-		$result = $this->createReferral($referrer->ID, $user_id, $referral_code);
+		// Clear caches
+		$this->cache->flush();
 
-		if ($result) {
-			// Clean up temp meta
-			delete_user_meta($user_id, BASE . 'pending_referral_code');
+		// Fire action for tracking
+		do_action('jvb_referral_processed', $user_id, $referrer->ID, $referral['referral_code']);
 
-			// Fire action for tracking
-			do_action('jvb_referral_processed', $user_id, $referrer->ID, $referral_code);
-		}
+		// Send notification to referrer
+		$this->sendReferrerNotification($referrer->ID, $userData['display_name']);
+		return true;
 	}
 
 	/**
@@ -238,7 +573,7 @@
 	 * @param string $code
 	 * @return int|false
 	 */
-	protected function createReferral(int $referrer_id, int $referee_id, string $code)
+	public function createReferral(int $referrer_id, int $referee_id, string $code)
 	{
 		$user = get_user_by('ID', $referee_id);
 
@@ -266,13 +601,17 @@
 	 */
 	public function getUserByReferralCode(string $code): ?WP_User
 	{
-		$users = get_users([
-			'meta_key' => BASE . 'referral_code',
-			'meta_value' => $code,
-			'number' => 1
-		]);
-
-		return $users[0] ?? null;
+		return $this->cache->remember(
+			$code,
+			function () use ($code) {
+				$users = get_users([
+					'meta_key' => BASE . 'referral_code',
+					'meta_value' => $code,
+					'number' => 1
+				]);
+				return $users[0] ?? null;
+			}
+		);
 	}
 
 	/**
@@ -337,8 +676,6 @@
 			return;
 		}
 
-		$settings = $this->getRewardSettings();
-
 		// Create referrer reward
 		$this->wpdb->insert(
 			$this->rewards_table,
@@ -346,7 +683,7 @@
 				'referral_id' => $referral_id,
 				'user_id' => $referral->referrer_id,
 				'reward_type' => 'referrer',
-				'amount' => $settings['referrer_reward_amount'],
+				'amount' => $this->settings['referrer_reward_amount'],
 				'status' => 'available',
 				'created_at' => current_time('mysql')
 			],
@@ -354,9 +691,9 @@
 		);
 
 		// Create referee reward
-		$referee_amount = $settings['referee_reward_type'] === 'percentage'
-			? $settings['referee_reward_amount']  // Store as percentage
-			: $settings['referee_reward_amount'];  // Store as fixed amount
+		$referee_amount = $this->settings['referee_reward_type'] === 'percentage'
+			? $this->settings['referee_reward_amount']  // Store as percentage
+			: $this->settings['referee_reward_amount'];  // Store as fixed amount
 
 		$this->wpdb->insert(
 			$this->rewards_table,
@@ -365,7 +702,7 @@
 				'user_id' => $referral->referee_id,
 				'reward_type' => 'referee',
 				'amount' => $referee_amount,
-				'reward_calculation' => $settings['referee_reward_type'],
+				'reward_calculation' => $this->settings['referee_reward_type'],
 				'status' => 'available',
 				'created_at' => current_time('mysql')
 			],
@@ -392,18 +729,42 @@
 
 		$args = wp_parse_args($args, $defaults);
 
-		$where = $this->wpdb->prepare("WHERE referrer_id = %d", $user_id);
+		return $this->requestCache->remember(
+			$this->requestCache->generateKey(array_merge(['user'=>$user_id], $args)),
+			function() use ($user_id, $args) {
+				$where = $this->wpdb->prepare("WHERE referrer_id = %d", $user_id);
 
-		if ($args['status'] !== 'all') {
-			$where .= $this->wpdb->prepare(" AND status = %s", $args['status']);
-		}
+				if ($args['status'] !== 'all') {
+					$where .= $this->wpdb->prepare(" AND status = %s", $args['status']);
+				}
 
-		$query = "SELECT * FROM {$this->referrals_table}
+				$query = "SELECT * FROM {$this->referrals_table}
                   {$where}
                   ORDER BY {$args['orderby']} {$args['order']}
                   LIMIT {$args['limit']} OFFSET {$args['offset']}";
 
-		return $this->wpdb->get_results($query);
+				$results =  $this->wpdb->get_results($query);
+
+				return array_map(function($referral) {
+					$last_invite = get_transient('referral_last_invite_' . md5($referral->referee_email));
+					$can_resend = !$last_invite || (time() - $last_invite) > WEEK_IN_SECONDS;
+					$status = match($referral->status) {
+						'consulted' => 'Awaiting Treatment',
+						'treated'	=> 'Rewarded!',
+						default => 'Pending',
+					};
+					return [
+						'id'			=> $referral->id,
+						'referee_name'	=> $referral->referee_name,
+						'referee_email'	=> $referral->referee_email,
+						'referred_at'	=> JVB()->routes('referral')->formatTimestamp($referral->referred_at),
+						'referral_status'=> $status,
+						'can_resend'	=> $can_resend
+					];
+				}, $results);
+			}
+		);
+
 	}
 
 	/**
@@ -414,38 +775,33 @@
 	 */
 	public function getUserStats(int $user_id): array
 	{
-		$cache_key = 'stats_' . $user_id;
-		$cached = $this->cache->get($cache_key);
+		return $this->statsCache->remember(
+			$user_id,
+			function() use ($user_id) {
+				$stats = $this->wpdb->get_row($this->wpdb->prepare(
+					"SELECT
+			COUNT(*) as code_used,
+			SUM(CASE WHEN status IN ('consulted', 'treated') THEN 1 ELSE 0 END) as consultations,
+			SUM(CASE WHEN status = 'treated' THEN 1 ELSE 0 END) as treatments,
+			SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending
+		FROM {$this->referrals_table}
+		WHERE referrer_id = %d",
+					$user_id
+				), ARRAY_A);
 
-		if ($cached !== false) {
-			return $cached;
-		}
+				// Get total rewards earned (available + redeemed)
+				$rewards = $this->wpdb->get_var($this->wpdb->prepare(
+					"SELECT SUM(amount)
+		FROM {$this->rewards_table}
+		WHERE user_id = %d AND reward_type = 'referrer'",
+					$user_id
+				));
 
-		$stats = $this->wpdb->get_row($this->wpdb->prepare(
-			"SELECT
-                COUNT(*) as total_referrals,
-                SUM(CASE WHEN status = 'treated' THEN 1 ELSE 0 END) as treated_count,
-                SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending_count
-            FROM {$this->referrals_table}
-            WHERE referrer_id = %d",
-			$user_id
-		), ARRAY_A);
-
-		// Get total rewards
-		$rewards = $this->wpdb->get_row($this->wpdb->prepare(
-			"SELECT
-                SUM(CASE WHEN status = 'available' THEN amount ELSE 0 END) as available_rewards,
-                SUM(CASE WHEN status = 'redeemed' THEN amount ELSE 0 END) as redeemed_rewards
-            FROM {$this->rewards_table}
-            WHERE user_id = %d AND reward_type = 'referrer'",
-			$user_id
-		), ARRAY_A);
-
-		$stats = array_merge($stats, $rewards);
-
-		$this->cache->set($cache_key, $stats, HOUR_IN_SECONDS);
-
-		return $stats;
+				$stats['total_rewards'] = floatval($rewards ?? 0);
+				$stats['user_id'] = $user_id;
+				return $stats;
+			}
+		);
 	}
 
 	/**
@@ -457,20 +813,23 @@
 	 */
 	public function getTopReferrers(int $limit = 10, string $period = 'all'): array
 	{
-		$where = '';
+		return $this->statsCache->remember(
+			$this->statsCache->generateKey(['limit'=>$limit, 'period' => $period]),
+			function() use ($limit, $period) {
+				$where = '';
 
-		if ($period !== 'all') {
-			$date_where = match($period) {
-				'day' => "referred_at >= DATE_SUB(NOW(), INTERVAL 1 DAY)",
-				'week' => "referred_at >= DATE_SUB(NOW(), INTERVAL 1 WEEK)",
-				'month' => "referred_at >= DATE_SUB(NOW(), INTERVAL 1 MONTH)",
-				default => "1=1"
-			};
+				if ($period !== 'all') {
+					$date_where = match($period) {
+						'day' => "referred_at >= DATE_SUB(NOW(), INTERVAL 1 DAY)",
+						'week' => "referred_at >= DATE_SUB(NOW(), INTERVAL 1 WEEK)",
+						'month' => "referred_at >= DATE_SUB(NOW(), INTERVAL 1 MONTH)",
+						default => "1=1"
+					};
 
-			$where = "WHERE {$date_where}";
-		}
+					$where = "WHERE {$date_where}";
+				}
 
-		$query = "SELECT
+				$query = "SELECT
                     referrer_id,
                     COUNT(*) as referral_count,
                     SUM(CASE WHEN status = 'treated' THEN 1 ELSE 0 END) as treated_count
@@ -480,16 +839,19 @@
                   ORDER BY referral_count DESC
                   LIMIT {$limit}";
 
-		$results = $this->wpdb->get_results($query);
+				$results = $this->wpdb->get_results($query);
 
-		// Enrich with user data
-		foreach ($results as &$result) {
-			$user = get_user_by('ID', $result->referrer_id);
-			$result->user_name = $user ? $user->display_name : 'Unknown';
-			$result->user_email = $user ? $user->user_email : '';
-		}
+				// Enrich with user data
+				foreach ($results as &$result) {
+					$user = get_user_by('ID', $result->referrer_id);
+					$result->user_name = $user ? $user->display_name : 'Unknown';
+					$result->user_email = $user ? $user->user_email : '';
+				}
 
-		return $results;
+				return $results;
+			}
+		);
+
 	}
 
 	/**
@@ -499,11 +861,8 @@
 	{
 		$yesterday = date('Y-m-d', strtotime('-1 day'));
 
-		// Get new referrals from yesterday
 		$new_referrals = $this->wpdb->get_results($this->wpdb->prepare(
-			"SELECT
-            r.*,
-            u.display_name as referrer_name
+			"SELECT r.*, u.display_name as referrer_name
         FROM {$this->referrals_table} r
         JOIN {$this->wpdb->users} u ON r.referrer_id = u.ID
         WHERE DATE(r.referred_at) = %s
@@ -511,48 +870,46 @@
 			$yesterday
 		));
 
-		// Only send if there's at least 1 new referral
 		if (empty($new_referrals)) {
 			return;
 		}
 
-		// Build email content
-		$content = '<h2>Daily Referral Report</h2>';
-		$content .= '<p><strong>' . count($new_referrals) . '</strong> new referral' .
-			(count($new_referrals) !== 1 ? 's' : '') . ' yesterday (' . $yesterday . ')</p>';
+		$content = JVB()->email()->h1('Daily Referral Report');
+		$content .= JVB()->email()->stat(
+			count($new_referrals),
+			count($new_referrals) === 1 ? 'New Referral' : 'New Referrals',
+			'From ' . $yesterday
+		);
 
-		$content .= '<table style="width:100%; border-collapse: collapse; margin: 20px 0;">';
-		$content .= '<thead><tr style="background: #f5f5f5;">';
-		$content .= '<th style="padding: 10px; text-align: left; border: 1px solid #ddd;">Referee</th>';
-		$content .= '<th style="padding: 10px; text-align: left; border: 1px solid #ddd;">Email</th>';
-		$content .= '<th style="padding: 10px; text-align: left; border: 1px solid #ddd;">Referrer</th>';
-		$content .= '<th style="padding: 10px; text-align: left; border: 1px solid #ddd;">Code</th>';
-		$content .= '</tr></thead><tbody>';
+		$content .= JVB()->email()->spacer(20);
+		$content .= JVB()->email()->h2('New Referrals');
 
+		// Build list of referrals
 		foreach ($new_referrals as $ref) {
-			$content .= '<tr>';
-			$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%s</td>',
-				esc_html($ref->referee_name));
-			$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%s</td>',
-				esc_html($ref->referee_email));
-			$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%s</td>',
-				esc_html($ref->referrer_name));
-			$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%s</td>',
-				esc_html($ref->referral_code));
-			$content .= '</tr>';
+			$cardContent = sprintf(
+				'<p><strong>%s</strong> (%s)</p>',
+				esc_html($ref->referee_name),
+				esc_html($ref->referee_email)
+			);
+			$cardContent .= sprintf(
+				'<p style="font-size:13px;color:%s;">Referred by: %s | Code: %s</p>',
+				JVB()->email()->colours['dark-200'],
+				esc_html($ref->referrer_name),
+				JVB()->email()->badge($ref->referral_code, 'info')
+			);
+
+			$content .= JVB()->email()->card($cardContent);
 		}
 
-		$content .= '</tbody></table>';
-
-		// Get admin email
 		$to = get_option('admin_email');
-		$subject = sprintf('[%s] %d New Referral%s',
+		$subject = sprintf(
+			'[%s] %d New Referral%s',
 			get_bloginfo('name'),
 			count($new_referrals),
-			count($new_referrals) !== 1 ? 's' : '');
+			count($new_referrals) !== 1 ? 's' : ''
+		);
 
-
-		jvbMail($to, $subject, $content);
+		JVB()->email()->sendEmail($to, $subject, $content, 'DAILY REPORT');
 	}
 
 	/**
@@ -563,19 +920,50 @@
 		$top_referrers = $this->getTopReferrers(10, 'week');
 		$total_referrals = $this->wpdb->get_var(
 			"SELECT COUNT(*) FROM {$this->referrals_table}
-             WHERE referred_at >= DATE_SUB(NOW(), INTERVAL 1 WEEK)"
+         WHERE referred_at >= DATE_SUB(NOW(), INTERVAL 1 WEEK)"
 		);
 
 		if ($total_referrals == 0) {
 			return;
 		}
 
+		$content = JVB()->email()->h1('Weekly Referral Summary');
+		$content .= JVB()->email()->stat(
+			$total_referrals,
+			'Total Referrals',
+			'This week'
+		);
+
+		$content .= JVB()->email()->spacer(30);
+		$content .= JVB()->email()->h2('Top 10 Referrers');
+
+		// Leaderboard style
+		$rank = 1;
+		foreach ($top_referrers as $referrer) {
+			$rankBadge = $rank <= 3
+				? JVB()->email()->badge('#' . $rank, $rank === 1 ? 'success' : 'info')
+				: '<span style="font-weight:600;color:' . JVB()->email()->colours['dark-200'] . ';">#' . $rank . '</span>';
+
+			$cardContent = sprintf(
+				'<p>%s <strong>%s</strong></p>',
+				$rankBadge,
+				esc_html($referrer->user_name)
+			);
+
+			$stats = [
+				JVB()->email()->stat($referrer->referral_count, 'Total Referrals'),
+				JVB()->email()->stat($referrer->treated_count, 'Treated')
+			];
+			$cardContent .= JVB()->email()->grid($stats, 2);
+
+			$content .= JVB()->email()->card($cardContent);
+			$rank++;
+		}
+
 		$to = get_option('admin_email');
 		$subject = '[' . get_bloginfo('name') . '] Weekly Referral Summary - ' . date('F j, Y');
 
-		$message = $this->generateWeeklyReportEmail($top_referrers, $total_referrals);
-
-		wp_mail($to, $subject, $message, ['Content-Type: text/html; charset=UTF-8']);
+		JVB()->email()->sendEmail($to, $subject, $content, 'WEEKLY SUMMARY');
 	}
 
 	/**
@@ -586,69 +974,30 @@
 	 */
 	protected function generateCSV(array $referrals): string
 	{
-		$csv = "Referred By,Referee Name,Referee Email,Referee Phone,Referral Code,Status,Referred At,Treated At\n";
+		$cache = Cache::for('referralCSV', HOUR_IN_SECONDS)->connect('referrals');
+		return $cache->remember(
+			'csv',
+			function () use ($referrals) {
+				$csv = "Referred By,Referee Name,Referee Email,Referee Phone,Referral Code,Status,Referred At,Treated At\n";
 
-		foreach ($referrals as $referral) {
-			$csv .= sprintf(
-				'"%s","%s","%s","%s","%s","%s","%s","%s"' . "\n",
-				$referral->referrer_name ?? 'Unknown',
-				$referral->referee_name,
-				$referral->referee_email,
-				$referral->referee_phone,
-				$referral->referral_code,
-				$referral->status,
-				$referral->referred_at,
-				$referral->treated_at ?? 'Not yet'
-			);
-		}
+				foreach ($referrals as $referral) {
+					$csv .= sprintf(
+						'"%s","%s","%s","%s","%s","%s","%s","%s"' . "\n",
+						$referral->referrer_name ?? 'Unknown',
+						$referral->referee_name,
+						$referral->referee_email,
+						$referral->referee_phone,
+						$referral->referral_code,
+						$referral->status,
+						$referral->referred_at,
+						$referral->treated_at ?? 'Not yet'
+					);
+				}
 
-		return $csv;
-	}
-
-	/**
-	 * Generate HTML email for daily report
-	 *
-	 * @param array $referrals
-	 * @param string $period
-	 * @return string
-	 */
-	protected function generateReportEmail(array $referrals, string $period): string
-	{
-		$count = count($referrals);
-
-		$content = sprintf('<p>You have <strong>%d new referral%s</strong> today.</p>',
-			$count,
-			$count !== 1 ? 's' : ''
+				return $csv;
+			}
 		);
 
-		$content .= '<table style="width:100%; border-collapse: collapse; margin: 20px 0;">';
-		$content .= '<thead><tr style="background: #f5f5f5; text-align: left;">';
-		$content .= '<th style="padding: 10px; border: 1px solid #ddd;">Referred By</th>';
-		$content .= '<th style="padding: 10px; border: 1px solid #ddd;">New User</th>';
-		$content .= '<th style="padding: 10px; border: 1px solid #ddd;">Email</th>';
-		$content .= '<th style="padding: 10px; border: 1px solid #ddd;">Status</th>';
-		$content .= '<th style="padding: 10px; border: 1px solid #ddd;">Time</th>';
-		$content .= '</tr></thead><tbody>';
-
-		foreach ($referrals as $referral) {
-			$content .= '<tr>';
-			$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%s</td>',
-				esc_html($referral->referrer_name ?? 'Unknown'));
-			$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%s</td>',
-				esc_html($referral->referee_name));
-			$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%s</td>',
-				esc_html($referral->referee_email));
-			$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%s</td>',
-				esc_html(ucfirst($referral->status)));
-			$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%s</td>',
-				esc_html(date('g:i A', strtotime($referral->referred_at))));
-			$content .= '</tr>';
-		}
-
-		$content .= '</tbody></table>';
-		$content .= '<p><small>See attached CSV for full details.</small></p>';
-
-		return jvbGetEmailTemplate($content, 'Daily Referral Report');
 	}
 
 	/**
@@ -666,31 +1015,22 @@
 			$total_referrals !== 1 ? 's' : ''
 		);
 
-		$content .= '<h3>Top 10 Referrers This Week</h3>';
-		$content .= '<table style="width:100%; border-collapse: collapse; margin: 20px 0;">';
-		$content .= '<thead><tr style="background: #f5f5f5; text-align: left;">';
-		$content .= '<th style="padding: 10px; border: 1px solid #ddd;">Rank</th>';
-		$content .= '<th style="padding: 10px; border: 1px solid #ddd;">User</th>';
-		$content .= '<th style="padding: 10px; border: 1px solid #ddd;">Total Referrals</th>';
-		$content .= '<th style="padding: 10px; border: 1px solid #ddd;">Treated</th>';
-		$content .= '</tr></thead><tbody>';
-
+		$referrers = [];
 		$rank = 1;
 		foreach ($top_referrers as $referrer) {
-			$content .= '<tr>';
-			$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%d</td>', $rank++);
-			$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%s</td>',
-				esc_html($referrer->user_name));
-			$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%d</td>',
-				$referrer->referral_count);
-			$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%d</td>',
-				$referrer->treated_count);
-			$content .= '</tr>';
+			$referrers[] = [
+				'label' => '#' . $rank++ . ' - ' . esc_html($referrer->user_name),
+				'value' => sprintf(
+					'<strong>Total Referrals:</strong> %d | <strong>Treated:</strong> %d',
+					$referrer->referral_count,
+					$referrer->treated_count
+				)
+			];
 		}
 
-		$content .= '</tbody></table>';
+		$content .= JVB()->email()->table($referrers, 'Top 10 Referrers This Week');
 
-		return jvbGetEmailTemplate($content, 'Weekly Referral Summary');
+		return $content;
 	}
 
 	/**
@@ -797,6 +1137,7 @@
 		</table>
 	<?php endif; ?>
 
+		<?php /**
 		<script>
 			function markReferralTreated(referralId) {
 				if (!confirm('Mark this referral as treated? This will create reward records.')) {
@@ -821,6 +1162,7 @@
 			}
 		</script>
 		<?php
+	 */
 	}
 
 	/**
@@ -877,17 +1219,16 @@
 	{
 
 		$user_id = get_current_user_id();
-		jvbDump($user_id);
-		$content = '<aside class="jvb-referral right">';
+		$content = '<aside class="main referral right">';
 		if (!$user_id) {
 			$content .= $this->getUnloggedInReferral();
 		} else {
 			$content .= $this->getLoggedInReferral($user_id);
 		}
-		$content .= '<button type="button" class="close">'.jvbIcon('close').'</button></aside>';
+		$content .= '</aside>';
 
 		$actions[] =[
-			'button' => '<button type="button" class="toggle-referral row" title="Your Referrals" data-action="toggle-referral" aria-label="Open Referral Sidebar" aria-controls="referral" aria-expanded="false">
+			'button' => '<button type="button" class="attn toggle-referral row" title="Your Referrals" data-action="toggle-referral" aria-label="Open Referral Sidebar" aria-controls="referral" aria-expanded="false">
 					'.jvbIcon('hand-heart').'<span class="screen-reader-text"></span>
 				</button>',
 			'content'	=> $content
@@ -896,101 +1237,142 @@
 		return $actions;
 	}
 
+	/**
+	 * Display referral sidebar for non-logged-in users
+	 */
 	function getUnloggedInReferral(): string
 	{
 		ob_start();
 		JVB()->connect('cloudflare')->renderTurnstile();
 		$turnstile = ob_get_clean();
-		$codeForm = '<form id="referral-code-form">
-					<div class="status" hidden>
-						<div class="spinner"></div>
-						<p class="message"></p>
-					</div>
-					<div class="field text">
-						<label for="referral-name">Your Name</label>
-						<input type="text"
-							   id="referral-name"
-							   name="name"
-							   placeholder="Mister Meeseeks"
-							   autocomplete="name"
-							   required>
-					</div>
 
-					<div class="field email">
-						<label for="referral-email">Your Email</label>
-						<input type="email"
-							   id="referral-email"
-							   name="email"
-							   placeholder="look@me.com"
-							   autocomplete="email"
-							   required>
-					</div>
+		$reward_text = $this->getRewardText(true);
 
-					<div class="field text">
-						<label for="referral-code-input">Referral Code</label>
-						<input type="text"
-							   id="referral-code-input"
-							   name="referral_code"
-							   placeholder="e.g., THISISFAKE1234"
-							   required
-							   pattern="[A-Za-z0-9]+"
-							   maxlength="20"
-							   autocomplete="off"
-							   style="text-transform: uppercase;">
-					</div>
+		// Pre-fill code if from referral link
+		$prefill_code = $_GET['ref'] ?? '';
+		$referrer_name = '';
+		if ($prefill_code) {
+			$referrer = $this->getUserByReferralCode($prefill_code);
+			$referrer_name = $referrer ? strtok($referrer->display_name, ' ') : '';
+		}
 
+		$header = sprintf(
+			'<header><h2>%sGet %s.</h2></header><h3>Have a code?</h3>%s<p>Enter your referral code to get started!</p>',
+			jvbIcon('confetti'),
+			esc_html($reward_text),
+			($referrer_name ? '<p>' . esc_html($referrer_name) . ' invited you to join us</p>' : '')
+		);
+
+		$codeForm = sprintf(
+			'<form id="referral-code-form">
+				%s
+   	 			<input type="hidden" name="user_select" value="%s">
+   	 			%s%s%s%s
+   	 			<div class="row">
+					<button type="button" class="button-secondary check-code-btn">
+						%s Verify Code
+					</button>
 					<button type="submit">
 						Get Started
 					</button>
+				</div>
 
-					<p class="helper-text">
-						We\'ll send you a link to complete your registration.
-					</p>
-					'.$turnstile.'
-				</form><div class="success-content" hidden>
-					<h3>Check Your Email!</h3>
-					<p>We\'ve sent you a magic link to complete your registration. Click the link to activate your account and claim your reward!</p>
-					<p class="hint">Can\'t find it? Check your spam folder.</p>
-				</div>';
+				<div class="code-status" hidden></div>
 
-		$loginForm = '<form id ="login-form">
-		<div class="status" hidden>
-			<div class="spinner"></div>
-			<p class="message"></p>
-		</div>
-		<div class="field email">
-			<label for="login-email">Your Email</label>
-			<input id="login-email" name="login-email" type="email" autocomplete="email">
-		</div>
-		'.$turnstile.'
-		<button type="submit">Login With Magic Link</button>
-</form>
-	<div class="success-content" hidden>
-		<h3>Check Your Email!</h3>
-		<p>We\'ve sent you a magic link to log in - no password required! Click the link in your email to log in.</p>
-		<p class="hint">Can\'t find it? Check your spam folder.</p>
-	</div>';
+				<p class="hint">
+					We\'ll send you a link to complete your registration.
+				</p>
+			</form>
+			<div class="success-content" hidden>
+				<h3>Check Your Email!</h3>
+				<p>We\'ve sent you a magic link to complete your registration. Click the link to activate your account and claim your reward!</p>
+				<p class="hint">Can\'t find it? Check your spam folder.</p>
+			</div>',
+			jvbFormStatus(),
+			esc_attr(get_option(BASE.'referral_role','client')),
+			Form::render('referral_name', '', [
+				'required'	=> true,
+				'type'		=> 'text',
+				'label'		=> 'Your Name',
+				'placeholder'=> 'Mister Meeseeks',
+				'autocomplete'=>'name'
+			]),
+			Form::render('referral_email', '', [
+				'required'	=> true,
+				'type'		=> 'email',
+				'label'		=> 'Your Email',
+				'placeholder'=> 'look@me.com',
+				'autocomplete'=> 'email'
+			]),
+			Form::render('referral_code', $prefill_code, [
+				'required'	=> true,
+				'type'		=> 'text',
+				'label'		=> 'Referral Code',
+				'pattern'	=> '[A-Za-z0-9]+',
+				'maxLength'	=> 20,
+				'autocomplete'=>'off',
+				'data-referrer' => $referrer_name
+			]),
+			$turnstile,
+			jvbIcon('check-circle')
+		);
 
+		$loginHeader = sprintf(
+			'<header><h2>%sLogin</h2></header><p>Already have an account?<br>Log in to see your rewards!</p>',
+			jvbIcon('sign-in')
+		);
+		$loginForm = sprintf(
+			'<form id="login-form">%s%s%s
+			<button type="submit">%sLogin With Magic Link</button>
+		</form>
+		<div class="success-content" hidden>
+			<h3>Check Your Email!</h3>
+			<p>We\'ve sent you a magic link to log in - no password required! Click the link in your email to log in.</p>
+			<p class="hint">Can\'t find it? Check your spam folder.</p>
+		</div>',
+			jvbFormStatus(),
+		Form::render('login_email', null, [
+			'required'	=> true,
+			'type'		=> 'email',
+			'label'		=> 'Your Email',
+			'autocomplete'=>'email'
+		]),
+		$turnstile,
+			jvbIcon('magic-wand')
+		);
+
+
+		$footer = sprintf(
+			'<p class="hint">
+			<a href="%s" class="text-link">Prefer to use a password?</a>
+	</p>',
+			wp_login_url()
+		);
 		$tabs = [
 			'enterCode' => [
 				'title'	=> 'Have a Code?',
 				'description'	=> [
-					'Enter the code given to you to get 20% off your first treatment!'
+					'Enter your referral code to get started'
 				],
+				'header' => $header,
 				'content'	=> $codeForm
 			],
 			'login'	=> [
+				'header'	=> $loginHeader,
 				'title'		=> 'Login',
 				'description'	=> [
-					'Login to see your rewards'
+					'Already have an account? Log in to see your rewards'
 				],
-				'content'	=> $loginForm
+				'content'	=> $loginForm.$footer
 			]
 		];
 
+
+
 		return jvbRenderTabs($tabs, true);
 	}
 
+
 	protected function getReferralSuccessMessage(string $code): string
 	{
 		$referrer = $this->getUserByReferralCode($code);
@@ -999,9 +1381,8 @@
 			return '';
 		}
 
-		$settings = $this->getRewardSettings();
-		$reward_amount = $settings['referee_reward_amount'] ?? 20;
-		$reward_type = $settings['referee_reward_type'] ?? 'percentage';
+		$reward_amount = $this->settings['referee_reward_amount'] ?? 20;
+		$reward_type = $this->settings['referee_reward_type'] ?? 'percentage';
 
 		$reward_text = $reward_type === 'percentage'
 			? $reward_amount . '% off'
@@ -1037,87 +1418,82 @@
 		return ob_get_clean();
 	}
 
-	function getLoggedInReferral(int $user_id):string
+	/**
+	 * Display referral sidebar for logged-in users
+	 */
+	public function getLoggedInReferral(int $user_id): string
 	{
-		// Logged-in user widget
-		$referral_code = get_user_meta($user_id, BASE . 'referral_code', true);
-
-		// Generate code if user doesn't have one
-		if (empty($referral_code)) {
-			$referral_code = $this->getUserReferralCode($user_id);
-			if (is_wp_error($referral_code)) {
-				return '';
-			}
+		$referral_code = $this->getUserReferralCode($user_id);
+		if (!$referral_code) {
+			return '';
 		}
 
-		$share_url = home_url('/?ref=' . $referral_code);
-
+		$share_url = $this->getShareURL($referral_code);
 		ob_start();
 		?>
-			<header>
-				<h3>Share the ♡</h3>
-				<p>Invite your friends.</p>
-				<p>Earn rewards when they book!</p>
-			</header>
+		<div class="wrap">
+		<header>
+			<h3>Share the ♡</h3>
+			<p>Invite friends. Earn rewards.</p>
+		</header>
 
-		<div class="row even share-buttons">
-			<a href="mailto:?subject=<?php echo urlencode('Check out ' . get_bloginfo('name')); ?>&body=<?php echo urlencode('I thought you might be interested: ' . $share_url); ?>"
-			   class="share-btn email-share">
-				<?php echo jvbIcon('envelope', ['size' => 20]); ?>
-				Email
-			</a>
-			<a href="https://www.facebook.com/sharer/sharer.php?u=<?php echo urlencode($share_url); ?>"
-			   target="_blank"
-			   rel="noopener noreferrer"
-			   class="share-btn facebook-share">
-				<?php echo jvbIcon('facebook', ['size' => 20]); ?>
-				Facebook
-			</a>
-			<a href="https://twitter.com/intent/tweet?url=<?php echo urlencode($share_url); ?>&text=<?php echo urlencode('Check this out!'); ?>"
-			   target="_blank"
-			   rel="noopener noreferrer"
-			   class="share-btn twitter-share">
-				<?php echo jvbIcon('twitter', ['size' => 20]); ?>
-				Twitter
-			</a>
-		</div>
+		<?php $this->getShareButtons($user_id); ?>
 
+		<section class="copy">
 			<h4>Your Referral Link</h4>
-			<div class="row btw">
-				<code id="your-referral-link"><?= esc_url($share_url)?></code>
-				<button type="button" class="copy" data-target="your-referral-link">
-					Copy Link
+			<div class="copy-group row x-btw nowrap">
+				<code id="referral-link" class="copy-target"><?= esc_url($share_url) ?></code>
+				<button type="button" class="copy-btn" data-target="referral-link" title="Copy referral link">
+					<?= jvbIcon('copy'); ?>
+					<?= jvbIcon('check-circle'); ?>
 				</button>
 			</div>
+			<p class="hint">Quickest and easiest: autofills your code.</p>
 
 
 			<h4>Your Code</h4>
-			<div class="row btw">
-				<code id="your-referral-code"><?=esc_html($referral_code)?></code>
-				<button type="button" class="copy" data-target="your-referral-code">
-					Copy Code
+			<div class="copy-group row x-btw nowrap">
+				<code id="referral-code" class="copy-target"><?= esc_html($referral_code) ?></code>
+				<button type="button" class="copy-btn" data-target="referral-code" title="Copy referral code">
+					<?= jvbIcon('copy'); ?>
+					<?= jvbIcon('check-circle'); ?>
 				</button>
 			</div>
+			<p class="hint">Manually copy and paste the code</p>
 
-			<div class="row btw referral-stats">
-				<div class="stat-item">
-					<span class="stat-value" data-stat="total">-</span>
-					<span class="stat-label">Total Referrals</span>
-				</div>
-				<div class="stat-item">
-					<span class="stat-value" data-stat="treated">-</span>
-					<span class="stat-label">Successful</span>
-				</div>
-				<div class="stat-item">
-					<span class="stat-value" data-stat="pending">-</span>
-					<span class="stat-label">Pending</span>
-				</div>
-				<div class="stat-item">
-					<span class="stat-value" data-stat="rewards">$0.00</span>
-					<span class="stat-label">Available Rewards</span>
-				</div>
+		</section>
+
+		<section class="recent-referrals">
+			<h4>Recent Referrals</h4>
+			<div class="recent-referrals-list" data-user-id="<?= $user_id ?>">
+				<div class="loading">Loading...</div>
 			</div>
+		</section>
 
+		<section class="stats-summary">
+			<div class="row x-btw">
+				<span class="stat-label">Total Referrals</span>
+				<span class="stat-value" data-stat="total">-</span>
+			</div>
+			<div class="row x-btw">
+				<span class="stat-label">Successful</span>
+				<span class="stat-value" data-stat="treated">-</span>
+			</div>
+			<div class="row x-btw">
+				<span class="stat-label">Pending</span>
+				<span class="stat-value" data-stat="pending">-</span>
+			</div>
+			<div class="row x-btw highlight">
+				<span class="stat-label">Available Rewards</span>
+				<span class="stat-value" data-stat="rewards">$0.00</span>
+			</div>
+		</section>
+
+		<a href="<?= get_home_url(null, '/dash/referrals')?>" class="view-dashboard-btn">
+			Dashboard <?= jvbIcon('arrow-right', ['size' => 16]); ?>
+		</a>
+		<p class="hint">Bulk-invite your friends via email - the link will pre-fill their name, email, and code!</p>
+		</div>
 		<?php
 		return ob_get_clean();
 	}
@@ -1130,27 +1506,51 @@
 			return $content;
 		}
 
-		$settings = get_option(BASE . 'referral_settings', []);
-		$reward_amount = $settings['referee_reward_amount'] ?? 20;
-		$reward_type = $settings['referee_reward_type'] ?? 'percentage';
+		$reward_amount = $this->settings['referee_reward_amount'] ?? 20;
+		$reward_type = $this->settings['referee_reward_type'] ?? 'percentage';
 
 		$reward_text = $reward_type === 'percentage'
 			? $reward_amount . '% off'
 			: '$' . number_format($reward_amount, 2) . ' off';
 
-		$bonus_content = '<div style="background: #e7f5ff; padding: 20px; border-radius: 8px; margin: 20px 0;">';
-		$bonus_content .= '<h3 style="margin-top: 0; color: #2271b1;">🎉 Welcome Bonus!</h3>';
-		$bonus_content .= '<p>Since you were referred by a friend, you\'ve earned <strong>' . $reward_text . '</strong> your first booking!</p>';
-		$bonus_content .= '<p>Your reward will be automatically applied when you book.</p>';
-		$bonus_content .= '</div>';
+		$bonus_content = sprintf(
+			'
+			<h3>Thanks for the ♡</h3>
+			<p>Since you were referred by a friend, you\'ve earned <strong>%s</strong> your first booking!</p>
+			<p>Your reward will be automatically applied when you book.</p>',
+			$reward_text,
+		);
 
-		// Insert bonus content after the first paragraph
-		$parts = explode('</p>', $content, 2);
-		if (count($parts) === 2) {
-			return $parts[0] . '</p>' . $bonus_content . $parts[1];
+
+		$code = $this->getUserReferralCode($user->ID);
+		$yourCode = '';
+		if ($code) {
+			$share_url = $this->getShareURL($code);
+			$yourCode = sprintf(
+				'<div class="callout">
+			<h3>Share the ♡ with Friends</h3>
+			<p>If you find you love what we can do for you, you can share your own code!</p>
+			<p>Your Referral Code: <strong>%s</strong></p>
+			<p>Or click the button below:</p>
+			%s
+			</div>',
+				JVB()->email()->link($code),
+				JVB()->email()->button($share_url, 'Share Your Code')
+			);
 		}
 
-		return $content . $bonus_content;
+
+		return $content . $bonus_content . $yourCode;
+	}
+
+	public function getShareURL(string $code):string
+	{
+		return add_query_arg(
+			[
+				'ref' => $code
+			],
+			get_home_url()
+		);
 	}
 
 	/**
@@ -1159,15 +1559,12 @@
 	 * @param int $user_id Referrer's user ID
 	 * @param string $invitee_email Email of person to invite
 	 * @param string $invitee_name Name of person to invite
+	 * @param string $subject
+	 * @param string $message
 	 * @return array|WP_Error Result with success/error
 	 */
-	public function sendReferralInvitation(int $user_id, string $invitee_email, string $invitee_name):array|WP_Error
+	public function sendReferralInvitation(int $user_id, string $invitee_email, string $invitee_name, string $subject, string $message):array|WP_Error
 	{
-		// Verify user exists
-		if (!$this->checkUser($user_id)) {
-			return new WP_Error('invalid_user', 'Invalid user ID');
-		}
-
 		// Check email rate limit (15/hour)
 		$rate_check = $this->checkEmailRateLimit($user_id);
 		if ($rate_check !== true) {
@@ -1180,11 +1577,7 @@
 			return new WP_Error('invalid_email', 'Invalid email address');
 		}
 
-		// Check if this email has already been invited or registered
-		if ($this->isEmailInvited($invitee_email)) {
-			return new WP_Error('already_invited', 'This person has already been invited');
-		}
-
+		// Check if already registered
 		if (email_exists($invitee_email)) {
 			return new WP_Error('user_exists', 'This person already has an account');
 		}
@@ -1193,34 +1586,60 @@
 		$referrer = get_user_by('ID', $user_id);
 		$referral_code = $this->getUserReferralCode($user_id);
 
-		if (is_wp_error($referral_code)) {
+		if ($referral_code) {
 			return $referral_code;
 		}
 
-		// Get reward text for email
-		$settings = $this->getRewardSettings();
-		$reward_text = $settings['referee_reward_type'] === 'percentage'
-			? "Get {$settings['referee_reward_amount']}% off your first treatment!"
-			: "Get \${$settings['referee_reward_amount']} off your first treatment!";
-
-		// Record the invitation attempt (for tracking)
+		// Record the invitation attempt (for rate limiting only)
 		$this->recordInvitationAttempt($user_id, $invitee_email, $invitee_name);
 
-		// Send magic link via MagicLinkManager
-		$result = $this->magic_link->sendMagicLink(
+		// Create registration URL with token (opens sidebar with prefilled form)
+		$token_data = [
+			'name' => sanitize_text_field($invitee_name),
+			'email' => $invitee_email,
+			'expires' => time() + (30 * DAY_IN_SECONDS)
+		];
+
+		// Encode the token
+		$token = base64_encode(json_encode($token_data));
+		$registration_url = add_query_arg([
+			'ref' => $referral_code,
+			'rname' => sanitize_text_field($invitee_name),
+			'remail'=> rawurlencode($invitee_email),
+		], home_url('/'));
+
+		// Get reward text for email
+		$reward_text = $this->settings['referee_reward_type'] === 'percentage'
+			? "{$this->settings['referee_reward_amount']}% off"
+			: "\${$this->settings['referee_reward_amount']} off";
+
+		// Build email content
+		$email_content =
+			sprintf(
+				'<h2>%s invited you to %s!</h2>
+			<p>%s</p>
+			<div class="callout">
+				<h3>Get %s your first treatment!</h3>
+			</div>
+			<p>Click the button below to register and claim your reward:</p>
+			%s
+			<p><small>This invitation expires in 30 days.</small></p>',
+				esc_html($referrer->display_name),
+				esc_html(get_bloginfo('name')),
+				nl2br(esc_html($message)),
+				esc_html($reward_text),
+				JVB()->email()->button($registration_url, 'Register & Get Your Reward')
+			);
+
+		// Send email
+		$sent = JVB()->email()->sendEmail(
 			$invitee_email,
-			MagicLinkManager::TYPE_REFERRAL,
-			[
-				'name' => sanitize_text_field($invitee_name),
-				'referral_code' => $referral_code,
-				'referrer_id' => $user_id,
-				'referrer_name' => $referrer->display_name,
-				'reward_text' => $reward_text
-			]
+			$subject,
+			$email_content
 		);
 
-		if (is_wp_error($result)) {
-			return $result;
+		if (!$sent) {
+			return new WP_Error('email_failed', 'Failed to send invitation email');
 		}
 
 		return [
@@ -1238,7 +1657,7 @@
 	 * @param array $invitations Array of ['email' => '', 'name' => '']
 	 * @return array Results with success/failed arrays
 	 */
-	public function sendBatchReferralInvitations(int $user_id, array $invitations): array
+	public function sendBatchReferralInvitations(int $user_id, array $invitations, string $subject, string $message): array
 	{
 		$results = [
 			'success' => [],
@@ -1258,7 +1677,7 @@
 				continue;
 			}
 
-			$result = $this->sendReferralInvitation($user_id, $email, $name);
+			$result = $this->sendReferralInvitation($user_id, $email, $name, $subject, $message);
 
 			if (is_wp_error($result)) {
 				$results['failed'][] = [
@@ -1279,7 +1698,7 @@
 
 		return [
 			'success' => !empty($results['success']),
-			'results' => $results,
+			'result' => $results,
 			'summary' => sprintf(
 				'Sent %d invitations, %d failed',
 				count($results['success']),
@@ -1463,5 +1882,1187 @@
 
 		return $csv_content;
 	}
+
+	/**
+	 * Add referral settings subpage to admin menu
+	 * Add referral settings subpage to admin menu
+	 *
+	 * @param array $subpages
+	 * @return array
+	 */
+	public static function addSubpage():void
+	{
+		$subpage = [
+			'page_title' => 'Referral System',
+			'menu_title' => 'Referrals',
+			'capability' => 'manage_options',
+			'menu_slug' => BASE . 'referral-admin',
+			'callback' => [self::class, 'renderAdminPageStatic']
+		];
+		AdminPages::addSubPage(BASE.'referral-admin', $subpage);
+	}
+
+	/**
+	 * Static wrapper for renderAdminPage
+	 * Called by WordPress when admin page is rendered
+	 */
+	public static function renderAdminPageStatic(): void
+	{
+		// Get the properly initialized instance from JVB singleton
+		JVB()->referrals()->renderAdminPage();
+	}
+
+	/**
+	 * Register settings
+	 */
+	public function registerSettings(): void
+	{
+		register_setting(
+			BASE . 'referral_settings',
+			BASE . 'referral_page_id',
+			[
+				'type' => 'integer',
+				'sanitize_callback' => 'absint',
+				'default' => 0
+			]
+		);
+
+		register_setting(
+			BASE . 'referral_settings',
+			BASE . 'referral_reward_settings',
+			[
+				'type' => 'array',
+				'sanitize_callback' => [$this, 'sanitizeRewardSettings'],
+				'default' => $this->default_settings
+			]
+		);
+	}
+
+	/**
+	 * Sanitize reward settings
+	 */
+	public function sanitizeRewardSettings(array $settings): array
+	{
+		return [
+			'referrer_reward_applies_to' => in_array($settings['referrer_reward_applies_to'] ?? '', ['per_user', 'flat_total'])
+				? $settings['referrer_reward_applies_to']
+				: 'per_user',
+			'referrer_reward_amount' => floatval($settings['referrer_reward_amount'] ?? 25.00),
+			'referrer_reward_type' => in_array($settings['referrer_reward_type'] ?? '', ['fixed', 'percentage'])
+				? $settings['referrer_reward_type']
+				: 'fixed',
+			'referee_reward_type' => in_array($settings['referee_reward_type'] ?? '', ['percentage', 'fixed'])
+				? $settings['referee_reward_type']
+				: 'percentage',
+			'referee_reward_amount' => floatval($settings['referee_reward_amount'] ?? 20),
+			'referee_reward_applies_to' => in_array($settings['referee_reward_applies_to'] ?? '', ['first_order', 'all_orders'])
+				? $settings['referee_reward_applies_to']
+				: 'first_order',
+		];
+	}
+
+	/**
+	 * Render the admin settings page
+	 */
+	public function renderAdminPage(): void
+	{
+		?>
+		<div class="wrap jvb-admin-wrap">
+			<h1>Referral System Management</h1>
+
+			<!-- CSV Upload Section -->
+			<div class="card">
+				<h2>Import Data from Jane App</h2>
+				<p>Upload your exported CSV files from Jane App to sync client and sales data.</p>
+
+				<div class="jvb-upload-section" style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 20px;">
+					<!-- Client List Upload -->
+					<div class="jvb-upload-box">
+						<h3>Client List</h3>
+						<form id="client-upload-form" enctype="multipart/form-data">
+							<input type="file"
+								   name="client_file"
+								   id="client_file"
+								   accept=".csv"
+								   required />
+							<button type="submit" class="button button-primary" style="margin-top: 10px;">
+								Upload Clients
+							</button>
+						</form>
+						<div id="client-upload-status" style="margin-top: 10px;"></div>
+					</div>
+
+					<!-- Sales Export Upload -->
+					<div class="jvb-upload-box">
+						<h3>Sales Export</h3>
+						<form id="sales-upload-form" enctype="multipart/form-data">
+							<input type="file"
+								   name="sales_file"
+								   id="sales_file"
+								   accept=".csv"
+								   required />
+							<button type="submit" class="button button-primary" style="margin-top: 10px;">
+								Upload Sales
+							</button>
+						</form>
+						<div id="sales-upload-status" style="margin-top: 10px;"></div>
+					</div>
+				</div>
+			</div>
+
+			<!-- Referrals Table -->
+			<div class="card" style="margin-top: 20px;">
+				<h2>Referrals Management</h2>
+
+				<div class="jvb-table-controls" style="margin-bottom: 15px; display: flex; gap: 10px; align-items: center;">
+					<label>
+						Filter by Status:
+						<select id="referral-status-filter">
+							<option value="">All Statuses</option>
+							<option value="pending">Pending</option>
+							<option value="consulted">Consulted</option>
+							<option value="treated">Treated</option>
+							<option value="cancelled">Cancelled</option>
+						</select>
+					</label>
+					<input type="text"
+						   id="referral-search"
+						   placeholder="Search by name or email..."
+						   style="min-width: 250px;" />
+					<button type="button" class="button" id="refresh-table">Refresh</button>
+				</div>
+
+				<div id="referrals-table-container">
+					<div class="jvb-loading">Loading referrals...</div>
+				</div>
+			</div>
+
+			<!-- Settings Section -->
+			<?= $this->renderAdminHTML() ?>
+		</div>
+<?php /**
+		<style>
+			.jvb-upload-box {
+				padding: 20px;
+				background: #f9f9f9;
+				border: 1px solid #ddd;
+				border-radius: 4px;
+			}
+			.jvb-upload-box h3 {
+				margin-top: 0;
+			}
+			.referrals-table {
+				width: 100%;
+				border-collapse: collapse;
+			}
+			.referrals-table th,
+			.referrals-table td {
+				padding: 12px;
+				text-align: left;
+				border-bottom: 1px solid #ddd;
+			}
+			.referrals-table th {
+				background: #f5f5f5;
+				font-weight: 600;
+			}
+			.referrals-table tr:hover {
+				background: #f9f9f9;
+			}
+			.referral-status {
+				padding: 4px 8px;
+				border-radius: 3px;
+				font-size: 12px;
+				font-weight: 500;
+			}
+			.referral-status.pending {
+				background: #fff3cd;
+				color: #856404;
+			}
+			.referral-status.consulted {
+				background: #d1ecf1;
+				color: #0c5460;
+			}
+			.referral-status.treated {
+				background: #d4edda;
+				color: #155724;
+			}
+			.referral-actions {
+				display: flex;
+				gap: 5px;
+			}
+			.notice.notice-success,
+			.notice.notice-error {
+				margin: 10px 0;
+			}
+		</style>
+*/
+		if (is_admin()) {
+?>
+		<script>
+			jQuery(document).ready(function($) {
+				// Client upload
+				$('#client-upload-form').on('submit', function(e) {
+					e.preventDefault();
+					const formData = new FormData(this);
+					formData.append('file', $('#client_file')[0].files[0]);
+
+					$('#client-upload-status').html('<span class="spinner is-active"></span> Uploading...');
+
+					$.ajax({
+						url: '<?= rest_url('jvb/v1/referrals/upload-clients') ?>',
+						method: 'POST',
+						data: formData,
+						processData: false,
+						contentType: false,
+						beforeSend: function(xhr) {
+							xhr.setRequestHeader('X-WP-Nonce', '<?= wp_create_nonce('wp_rest') ?>');
+						},
+						success: function(response) {
+							if (response.success) {
+								let message = '<div class="notice notice-success"><p>' + response.message;
+								message += '<br>Created: ' + (response.stats.created || 0);
+								message += ', Updated: ' + (response.stats.updated || 0);
+								message += ', Skipped: ' + (response.stats.skipped || 0) + '</p>';
+
+								// Show skipped details if any
+								if (response.stats.skipped_details && response.stats.skipped_details.length > 0) {
+									message += '<details style="margin-top: 10px;"><summary>View skipped records (' + response.stats.skipped_details.length + ')</summary>';
+									message += '<table class="widefat" style="margin-top: 10px;"><thead><tr>';
+									message += '<th>Line</th><th>Name</th><th>Email</th><th>GUID</th><th>Reason</th>';
+									message += '</tr></thead><tbody>';
+									response.stats.skipped_details.forEach(function(item) {
+										message += '<tr>';
+										message += '<td>' + (item.line || '-') + '</td>';
+										message += '<td>' + (item.name || '-') + '</td>';
+										message += '<td>' + (item.email || '-') + '</td>';
+										message += '<td>' + (item.guid || '-') + '</td>';
+										message += '<td>' + item.reason + '</td>';
+										message += '</tr>';
+									});
+									message += '</tbody></table></details>';
+								}
+
+								message += '</div>';
+
+								$('#client-upload-status').html(message);
+								$('#client-upload-form')[0].reset();
+								loadReferralsTable();
+							} else {
+								$('#client-upload-status').html(
+									'<div class="notice notice-error"><p>' + response.message + '</p></div>'
+								);
+							}
+						},
+						error: function(xhr) {
+							$('#client-upload-status').html(
+								'<div class="notice notice-error"><p>Upload failed</p></div>'
+							);
+						}
+					});
+				});
+
+				// Sales upload
+				$('#sales-upload-form').on('submit', function(e) {
+					e.preventDefault();
+					const formData = new FormData(this);
+					formData.append('file', $('#sales_file')[0].files[0]);
+
+					$('#sales-upload-status').html('<span class="spinner is-active"></span> Uploading...');
+
+					$.ajax({
+						url: '<?= rest_url('jvb/v1/referrals/upload-sales') ?>',
+						method: 'POST',
+						data: formData,
+						processData: false,
+						contentType: false,
+						beforeSend: function(xhr) {
+							xhr.setRequestHeader('X-WP-Nonce', '<?= wp_create_nonce('wp_rest') ?>');
+						},
+						success: function(response) {
+							if (response.success) {
+								$('#sales-upload-status').html(
+									'<div class="notice notice-success"><p>' + response.message +
+									'<br>Consultations: ' + response.stats.consultations +
+									', Treatments: ' + response.stats.treatments +
+									', Skipped: ' + response.stats.skipped + '</p></div>'
+								);
+								$('#sales-upload-form')[0].reset();
+								loadReferralsTable();
+							} else {
+								$('#sales-upload-status').html(
+									'<div class="notice notice-error"><p>' + response.message + '</p></div>'
+								);
+							}
+						},
+						error: function(xhr) {
+							$('#sales-upload-status').html(
+								'<div class="notice notice-error"><p>Upload failed</p></div>'
+							);
+						}
+					});
+				});
+
+				// Load referrals table
+				function loadReferralsTable(page = 1) {
+					const status = $('#referral-status-filter').val();
+					const search = $('#referral-search').val();
+
+					$.ajax({
+						url: '<?= rest_url('jvb/v1/referrals') ?>',
+						method: 'GET',
+						data: {
+							offset: page -1,
+							limit: 20,
+							status: status === '' ? 'all' : status,
+							search: search
+						},
+						beforeSend: function(xhr) {
+							xhr.setRequestHeader('X-WP-Nonce', '<?= wp_create_nonce('wp_rest') ?>');
+							$('#referrals-table-container').html('<div class="jvb-loading">Loading...</div>');
+						},
+						success: function(response) {
+							if (response.success) {
+								renderReferralsTable(response);
+							}
+						}
+					});
+				}
+
+				// Render table
+				function renderReferralsTable(data) {
+					let html = '<table class="referrals-table widefat">';
+					html += '<thead><tr>';
+					html += '<th>Referrer</th>';
+					html += '<th>Referee</th>';
+					html += '<th>Email</th>';
+					html += '<th>Status</th>';
+					html += '<th>Referred Date</th>';
+					html += '<th>Total Referrals</th>';
+					html += '<th>Actions</th>';
+					html += '</tr></thead><tbody>';
+
+					if (data.items.length === 0) {
+						html += '<tr><td colspan="7" style="text-align: center;">No referrals found</td></tr>';
+					} else {
+						data.items.forEach(function(ref) {
+							html += '<tr>';
+							html += '<td>' + (ref.referrer_name || 'Unknown') + '</td>';
+							html += '<td>' + (ref.referee_display_name || ref.referee_name) + '</td>';
+							html += '<td>' + (ref.referee_display_email || ref.referee_email) + '</td>';
+							html += '<td><span class="referral-status ' + ref.status + '">' + ref.status + '</span></td>';
+							html += '<td>' + new Date(ref.referred_at).toLocaleDateString() + '</td>';
+							html += '<td>' + (ref.referrer_total_referrals || 0) + '</td>';
+							html += '<td class="referral-actions">';
+
+							if (ref.status === 'pending') {
+								html += '<button class="button button-small mark-consulted" data-id="' + ref.id + '">Mark Consulted</button>';
+							}
+							if (ref.status !== 'treated') {
+								html += '<button class="button button-small mark-treated" data-id="' + ref.id + '">Mark Treated</button>';
+							}
+
+							html += '</td>';
+							html += '</tr>';
+						});
+					}
+
+					html += '</tbody></table>';
+
+					// Add pagination
+					if (data.total_pages > 1) {
+						html += '<div class="tablenav"><div class="tablenav-pages">';
+						for (let i = 1; i <= data.total_pages; i++) {
+							const active = i === data.page ? 'button-primary' : 'button';
+							html += '<button class="button ' + active + ' page-link" data-page="' + i + '">' + i + '</button> ';
+						}
+						html += '</div></div>';
+					}
+
+					$('#referrals-table-container').html(html);
+				}
+
+				// Event handlers for actions
+				$(document).on('click', '.mark-consulted', function() {
+					const id = $(this).data('id');
+					if (!confirm('Mark this referral as consulted? This will create the consultation reward.')) return;
+
+					$.ajax({
+						url: '<?= rest_url('jvb/v1/referrals') ?>', // Changed from /mark-consulted
+						method: 'POST',
+						data: JSON.stringify({
+							action: 'consulted',  // Added action parameter
+							referral_id: id
+						}),
+						contentType: 'application/json',
+						beforeSend: function(xhr) {
+							xhr.setRequestHeader('X-WP-Nonce', '<?= wp_create_nonce('wp_rest') ?>');
+						},
+						success: function(response) {
+							if (response.success) {
+								alert(response.message);
+								loadReferralsTable();
+							} else {
+								alert('Error: ' + response.message);
+							}
+						}
+					});
+				});
+
+				$(document).on('click', '.mark-treated', function() {
+					const id = $(this).data('id');
+					if (!confirm('Mark this referral as treated? This will create rewards for both parties.')) return;
+
+					$.ajax({
+						url: '<?= rest_url('jvb/v1/referrals') ?>', // Changed from /mark-treated
+						method: 'POST',
+						data: JSON.stringify({
+							action: 'treated',  // Added action parameter
+							referral_id: id
+						}),
+						contentType: 'application/json',
+						beforeSend: function(xhr) {
+							xhr.setRequestHeader('X-WP-Nonce', '<?= wp_create_nonce('wp_rest') ?>');
+						},
+						success: function(response) {
+							if (response.success) {
+								alert(response.message);
+								loadReferralsTable();
+							} else {
+								alert('Error: ' + response.message);
+							}
+						}
+					});
+				});
+
+				$(document).on('click', '.page-link', function() {
+					loadReferralsTable($(this).data('page'));
+				});
+
+				$('#referral-status-filter, #refresh-table').on('change click', function() {
+					loadReferralsTable();
+				});
+
+				// Search with debounce
+				let searchTimeout;
+				$('#referral-search').on('keyup', function() {
+					clearTimeout(searchTimeout);
+					searchTimeout = setTimeout(function() {
+						loadReferralsTable();
+					}, 500);
+				});
+
+				// Initial load
+				loadReferralsTable();
+			});
+		</script>
+		<?php
+		}
+	}
+
+	protected function renderAdminHTML():string
+	{
+		ob_start();
+		?>
+		<div class="wrap">
+			<h1>Referral Settings</h1>
+
+			<form method="post" action="">
+				<?php wp_nonce_field(BASE . 'admin_page_nonce'); ?>
+
+				<div class="card">
+					<h2>Referral Page</h2>
+					<p>Select the page where users can access their referral dashboard.</p>
+
+					<table class="form-table">
+						<tr>
+							<th scope="row">
+								<label for="<?= BASE ?>referral_page_id">Referral Page</label>
+							</th>
+							<td>
+								<?php
+								if (!$this->referralPage) {
+									$this->referralPage = $this->getReferralPageId();
+								}
+								wp_dropdown_pages([
+									'name' => BASE . 'referral_page_id',
+									'id' => BASE . 'referral_page_id',
+									'selected' => $this->referralPage,
+									'show_option_none' => __('— Select —', 'jvbase'),
+									'option_none_value' => '0'
+								]);
+								?>
+								<p class="description">
+									This page will show "Referral Page" in the admin bar when editing.
+								</p>
+							</td>
+						</tr>
+					</table>
+				</div>
+
+				<div class="card">
+					<h2>Reward Settings</h2>
+
+					<table class="form-table">
+						<tr>
+							<th colspan="2"><h3>Referrer Rewards</h3></th>
+						</tr>
+						<tr>
+							<th scope="row">
+								<label for="referrer_reward_type">Reward Type</label>
+							</th>
+							<td>
+								<select name="referrer_reward_type" id="referrer_reward_type">
+									<option value="fixed" <?php selected($this->settings['referrer_reward_type'], 'fixed'); ?>>Fixed Amount</option>
+									<option value="percentage" <?php selected($this->settings['referrer_reward_type'], 'percentage'); ?>>Percentage</option>
+								</select>
+							</td>
+						</tr>
+						<tr>
+							<th scope="row">
+								<label for="referrer_reward_amount">Reward Amount</label>
+							</th>
+							<td>
+								<input type="number"
+									   name="referrer_reward_amount"
+									   id="referrer_reward_amount"
+									   value="<?= esc_attr($this->settings['referrer_reward_amount']) ?>"
+									   step="0.01"
+									   min="0">
+								<p class="description">Amount in dollars or percentage</p>
+							</td>
+						</tr>
+						<tr>
+							<th scope="row">
+								<label for="referrer_reward_applies_to">Applies To</label>
+							</th>
+							<td>
+								<select name="referrer_reward_applies_to" id="referrer_reward_applies_to">
+									<option value="per_user" <?php selected($this->settings['referrer_reward_applies_to'], 'per_user'); ?>>Per User Referred</option>
+									<option value="flat_total" <?php selected($this->settings['referrer_reward_applies_to'], 'flat_total'); ?>>Flat Total</option>
+								</select>
+							</td>
+						</tr>
+
+						<tr>
+							<th colspan="2"><h3>Referee (New User) Rewards</h3></th>
+						</tr>
+						<tr>
+							<th scope="row">
+								<label for="referee_reward_type">Reward Type</label>
+							</th>
+							<td>
+								<select name="referee_reward_type" id="referee_reward_type">
+									<option value="percentage" <?php selected($this->settings['referee_reward_type'], 'percentage'); ?>>Percentage</option>
+									<option value="fixed" <?php selected($this->settings['referee_reward_type'], 'fixed'); ?>>Fixed Amount</option>
+								</select>
+							</td>
+						</tr>
+						<tr>
+							<th scope="row">
+								<label for="referee_reward_amount">Reward Amount</label>
+							</th>
+							<td>
+								<input type="number"
+									   name="referee_reward_amount"
+									   id="referee_reward_amount"
+									   value="<?= esc_attr($this->settings['referee_reward_amount']) ?>"
+									   step="0.01"
+									   min="0">
+								<p class="description">Amount in dollars or percentage</p>
+							</td>
+						</tr>
+						<tr>
+							<th scope="row">
+								<label for="referee_reward_applies_to">Applies To</label>
+							</th>
+							<td>
+								<select name="referee_reward_applies_to" id="referee_reward_applies_to">
+									<option value="first_order" <?php selected($this->settings['referee_reward_applies_to'], 'first_order'); ?>>First Order Only</option>
+									<option value="all_orders" <?php selected($this->settings['referee_reward_applies_to'], 'all_orders'); ?>>All Orders</option>
+								</select>
+							</td>
+						</tr>
+						<tr>
+							<th scope="row">
+								<label for="<?= BASE ?>referral_role">Client Import Role</label>
+							</th>
+							<td>
+								<?php
+								$selected_role = get_option(BASE . 'referral_role', '');
+								$roles = wp_roles()->get_names();
+								?>
+								<select name="<?= BASE ?>referral_role" id="<?= BASE ?>referral_role">
+									<?php foreach ($roles as $role_value => $role_name): ?>
+										<option value="<?= esc_attr($role_value) ?>" <?php selected($selected_role, $role_value); ?>>
+											<?= esc_html($role_name) ?>
+										</option>
+									<?php endforeach; ?>
+								</select>
+								<p class="description">
+									Role assigned to users imported from Jane App client list.
+								</p>
+							</td>
+						</tr>
+					</table>
+				</div>
+
+				<p class="submit">
+					<button type="submit" name="submit" class="button button-primary">Save Settings</button>
+				</p>
+			</form>
+
+			<?= $this->renderReferralStats(true) ?>
+		</div>
+		<?php
+		return ob_get_clean();
+	}
+
+	/**
+	 * Render referral statistics
+	 */
+	protected function renderReferralStats(bool $wrapCard = false): string
+	{
+		global $wpdb; // Use fresh global instead of stored reference
+
+		ob_start();
+
+		// Get fresh table name references
+		$referrals_table = $wpdb->prefix . BASE . 'referrals';
+
+		// Use proper WordPress prepare even for COUNT
+		$total_referrals = $wpdb->get_var(
+			$wpdb->prepare(
+				"SELECT COUNT(*) FROM `{$referrals_table}` WHERE 1=%d",
+				1
+			)
+		);
+
+		$pending_referrals = $wpdb->get_var(
+			$wpdb->prepare(
+				"SELECT COUNT(*) FROM `{$referrals_table}` WHERE status = %s",
+				'pending'
+			)
+		);
+
+		$treated_referrals = $wpdb->get_var(
+			$wpdb->prepare(
+				"SELECT COUNT(*) FROM `{$referrals_table}` WHERE status = %s",
+				'treated'
+			)
+		);
+
+		?>
+		<table class="widefat">
+			<tr>
+				<th>Total Referrals</th>
+				<td><?= esc_html($total_referrals ?? 0) ?></td>
+			</tr>
+			<tr>
+				<th>Pending</th>
+				<td><?= esc_html($pending_referrals ?? 0) ?></td>
+			</tr>
+			<tr>
+				<th>Treated</th>
+				<td><?= esc_html($treated_referrals ?? 0) ?></td>
+			</tr>
+		</table>
+		<?php
+		$table = ob_get_clean();
+
+		if ($wrapCard) {
+			$table = '<div class="card">
+            <h2>Referral Statistics</h2>
+            ' . $table . '
+        </div>';
+		}
+
+		return $table;
+	}
+
+	/**
+	 * Add "Referral Page" label to admin bar
+	 *
+	 * @param WP_Admin_Bar $wp_admin_bar
+	 */
+	public function addReferralPageLabel($wp_admin_bar): void
+	{
+		if (!is_admin()) {
+			return;
+		}
+
+		if (!$this->referralPage) {
+			$this->referralPage = $this->getReferralPageId();
+		}
+		if (!$this->referralPage) {
+			return;
+		}
+
+		global $pagenow, $post;
+
+		// Check if we're editing the referral page
+		if ('post.php' === $pagenow && $post && $post->ID === $this->referralPage) {
+			$wp_admin_bar->add_node([
+				'id' => 'referral-page',
+				'parent' => 'top-secondary',
+				'title' => __('Referral Page', 'jvbase'),
+				'meta' => [
+					'class' => 'referral-page-notice'
+				]
+			]);
+		}
+	}
+
+	/**
+	 * Get the referral page ID
+	 *
+	 * @return int|null
+	 */
+	public function getReferralPageId(): ?int
+	{
+		$page_id = get_option(BASE . 'referral_page_id');
+		return $page_id ? (int) $page_id : null;
+	}
+
+	/**
+	 * Show admin notice on referral page edit screen
+	 */
+	public function showReferralPageNotice(): void
+	{
+		global $pagenow, $post;
+
+		if ('post.php' !== $pagenow || !$post) {
+			return;
+		}
+		if (!$this->referralPage) {
+			$this->referralPage = $this->getReferralPageId();
+		}
+		if ($post->ID === $this->referralPage) {
+			echo '<div class="notice notice-info">';
+			echo '<p>' . __('This page is designated as the <strong>Referral Page</strong>.', 'jvbase') . '</p>';
+			echo '</div>';
+		}
+	}
+
+	public function renderDashPage(string $content, string $page): string
+	{
+		if ($page !== 'Referrals') {
+			return $content;
+		}
+
+		// Regular users get their referral dashboard
+		$user_id = get_current_user_id();
+
+		$referral_code = $this->getUserReferralCode($user_id);
+		if (!$referral_code) {
+			$user = get_userdata($user_id);
+			$referral_code = $this->generateReferralCode($user);
+		}
+
+		$referrals = $this->getUserReferrals($user_id, ['limit' => 20]);
+
+		ob_start();
+
+		$tabs = new Tabs();
+		$tabs->addTab('share')
+			->title('Share')
+			->icon('share-fat')
+			->description('Share your code and earn rewards when your referrals complete their first treatment!')
+			->content($this->shareDashboard($user_id, $referral_code));
+		$tabs->addTab('referrals')
+			->title('Your Referrals')
+			->icon('hand-heart')
+			->content($this->referralCRUD($user_id));
+
+		?>
+		<div class="referral-dashboard">
+			<?= $tabs->render(true);?>
+		</div>
+
+		<?php
+		return ob_get_clean();
+	}
+
+	protected function shareDashboard(int $user_id, string $referral_code):string
+	{
+		ob_start();
+		?>
+		<?php $this->getShareButtons($user_id); ?>
+
+		<!-- Referral Code Card -->
+		<details open>
+			<summary>Your Code</summary>
+			<h3>Share Link</h3>
+			<div class="row x-btw nowrap">
+				<code id="referral-link" class="copy-target"><?= home_url('/?ref=' . $referral_code) ?></code>
+				<button type="button" class="copy-btn" data-target="referral-link" title="Copy referral link">
+					<?= jvbIcon('copy'); ?>
+					<?= jvbIcon('check-circle'); ?>
+				</button>
+			</div>
+			<h3>Share Code</h3>
+			<div class="row x-btw nowrap">
+				<code id="referral-code" class="copy-target"><?= esc_html($referral_code) ?></code>
+				<button type="button" class="copy-btn" data-target="referral-code" title="Copy referral code">
+					<?= jvbIcon('copy'); ?>
+					<?= jvbIcon('check-circle'); ?>
+				</button>
+			</div>
+		</details>
+		<form class="invite">
+			<h2>Invite your Friends</h2>
+			<p>Or, if you prefer, enter your friends name(s) and email(s), and we'll send off some emails.</p>
+			<p><small>(No data is stored. Your friends will get an email from our email.)</small></p>
+			<?php
+			$invite = [
+				'type' => 'taglist',
+				'label' => 'Invite Your Friends',
+				'hint' => 'Add friends to send them a referral link',
+				'add_label' => 'Add Invite',
+				'tag_format' => '{{name}} ({{email}})', // or 'first_field', 'all_fields', 'email', etc.
+				'fields' => [
+					'name' => [
+						'type' => 'text',
+						'label' => 'Name',
+						'placeholder' => 'Full Name',
+						'required' => true
+					],
+					'email' => [
+						'type' => 'email',
+						'label' => 'Email',
+						'placeholder' => 'email@example.com',
+						'required' => true
+					]
+				]
+			];
+			$fields = [
+				'subject'	=> [
+					'type'	=> 'text',
+					'label'	=> 'Email Subject',
+					'value'	=> 'Try Legacy for Tattoo Removal',
+				],
+				'message'	=> [
+					'type'		=> 'textarea',
+					'label'		=> 'Customize message',
+					'value'	=> 'I had a great experience at Legacy Tattoo Removal!
+
+If you click the link below, you can get 20% off your first treatment with them.',
+					'hint'		=> 'We\'ll add your code and a link automatically.'
+				]
+			];
+			echo Form::render('invite', '', $invite);
+			?>
+			<details>
+				<summary class="icon icon-caret-down">Customize Message</summary>
+				<?php
+				foreach ($fields as $fieldName => $field) {
+					$value = (array_key_exists('value', $field)) ? $field['value'] : [];
+					echo Form::render($fieldName, $value, $field);
+				}
+				?>
+			</details>
+
+			<button type="submit"><?=jvbIcon('envelope')?>Send Invites</button>
+		</form>
+		<?php
+		return ob_get_clean();
+	}
+
+	protected function referralCRUD(int $user_id):string
+	{
+		$stats = $this->getUserStats($user_id);
+		ob_start();
+		?>
+		<!-- Stats Grid with Updated Labels -->
+		<div class="item-grid stats">
+			<div class="card">
+				<h4>Code Used</h4>
+				<span class="stat-number" data-stat="code_used"><?= esc_html($stats['code_used'] ?? 0) ?></span>
+				<p class="hint">People who used your code</p>
+			</div>
+			<div class="card">
+				<h4>Treatments</h4>
+				<span class="stat-number" data-stat="treatments"><?= esc_html($stats['treatments'] ?? 0) ?></span>
+				<p class="hint">Completed first treatment</p>
+			</div>
+			<div class="card highlight">
+				<h4>Total Rewards</h4>
+				<span class="stat-number" data-stat="total_rewards">$<?= number_format($stats['total_rewards'] ?? 0, 2) ?></span>
+				<p class="hint">Earned from referrals</p>
+			</div>
+		</div>
+
+		<?php
+		// Configure CRUDSkeleton for referrals
+		$crud = new CRUDSkeleton();
+		$crud->title('Your Referrals', 'Track friends you\'ve invited and rewards earned')
+			->content('referral', 'Referral', 'Referrals')
+//			->initMeta('custom', 'referral')
+			->setFields([
+				'referee_name' => [
+					'label' => 'Name',
+					'type' => 'text',
+				],
+				'referee_email' => [
+					'label' => 'Email',
+					'type' => 'text',
+				],
+				'referred_at' => [
+					'label' => 'Code Used',
+					'type' => 'date',
+				],
+				'referral_status' => [
+					'label' => 'Status',
+					'type' => 'text',
+				]
+			])
+			->setStatuses(['all', 'unused', 'registered', 'consulted', 'completed'])
+			->addViews(['table', 'list'])
+			->defaultView('table')
+			->addCapabilities(['view'])
+			->addDateFilter('referred_at')
+			->showBulkControls(false)
+			->showFilters(false)
+			->useCRUDjs(false); // We'll use our custom Referral.js with DataStore
+
+		// Add custom template for actions column
+		$crud->addItemActions(['resend', 'trash']);
+		$crud->defineItemAction('resend', [
+			'title'	=> 'Resend Invitation',
+			'icon'	=> 'paper-plane-tilt'
+		]);
+		$crud->defineItemAction('trash', [
+			'title'	=> 'Remove from List'
+		]);
+
+		// Custom empty state
+		$crud->addTemplate('empty', '
+		<template class="emptyState">
+			<div class="empty-state">
+				<h3>' . jvbDashIcon('hand-heart') . 'Nothing Yet' . jvbDashIcon('hand-heart') . '</h3>
+				<p>Start sharing your referral code to earn rewards!</p>
+				<p><small><i>Share your code using the "Share" tab below.</i></small></p>
+			</div>
+		</template>
+	');
+
+		$crud->render();
+
+		return ob_get_clean();
+	}
+
+	/**
+	 * Handle admin page form submission
+	 *
+	 * @param mixed $result Previous result
+	 * @param string $page_slug Current page slug
+	 * @param array $post_data POST data
+	 * @return array|null Result array or null if not our page
+	 */
+	public function handleAdminSubmission($result, string $page_slug, array $post_data): ?array
+	{
+		// Only handle our page
+		if ($page_slug !== BASE . 'referral-admin') {
+			return $result;
+		}
+
+		try {
+			// Save referral page
+			$page_id = isset($post_data[BASE . 'referral_page_id']) ? absint($post_data[BASE . 'referral_page_id']) : 0;
+			update_option(BASE . 'referral_page_id', $page_id);
+
+			// Save client import role
+			$import_role = sanitize_text_field($post_data[BASE . 'referral_role'] ?? Site::getDefaultReferralRole());
+			update_option(BASE . 'referral_role', $import_role);
+
+			// Save reward settings
+			$settings = [
+				'referrer_reward_type' => sanitize_text_field($post_data['referrer_reward_type'] ?? 'fixed'),
+				'referrer_reward_amount' => floatval($post_data['referrer_reward_amount'] ?? 25.00),
+				'referrer_reward_applies_to' => sanitize_text_field($post_data['referrer_reward_applies_to'] ?? 'per_user'),
+				'referee_reward_type' => sanitize_text_field($post_data['referee_reward_type'] ?? 'percentage'),
+				'referee_reward_amount' => floatval($post_data['referee_reward_amount'] ?? 20),
+				'referee_reward_applies_to' => sanitize_text_field($post_data['referee_reward_applies_to'] ?? 'first_order')
+			];
+
+			update_option(BASE . 'referral_settings', $settings);
+
+			return [
+				'success' => true,
+				'message' => 'Referral settings saved successfully!'
+			];
+
+		} catch (\Exception $e) {
+			return [
+				'success' => false,
+				'message' => 'Failed to save settings: ' . $e->getMessage()
+			];
+		}
+	}
+
+	/**
+	 * Get formatted reward text for referee
+	 *
+	 * @param bool $full Include "off your first treatment" text
+	 * @return string
+	 */
+	public function getRewardText(bool $full = true): string
+	{
+		$reward_amount = $this->settings['referee_reward_amount'] ?? 20;
+		$reward_type = $this->settings['referee_reward_type'] ?? 'percentage';
+
+		$reward_text = $reward_type === 'percentage'
+			? $reward_amount . '% off'
+			: '$' . number_format($reward_amount, 2) . ' off';
+
+		if ($full) {
+			$reward_text .= ' your first treatment';
+		}
+
+		return $reward_text;
+	}
+
+	public function getShareButtons(int $user_id):void
+	{
+		$referral_code = $this->getUserReferralCode($user_id);
+		if (!$referral_code) {
+			return;
+		}
+
+		$share_url = $this->getShareURL($referral_code);
+		$referral_page_id = $this->getReferralPageId();
+
+		// SMS share text
+		$sms_text = urlencode("Check out " . get_bloginfo('name') . "! " . $share_url);
+
+		// Share message
+		$share_message = urlencode("I love " . get_bloginfo('name') . "! Thought you might want to check them out.");
+
+		?>
+		<nav class="share">
+			<h4>Quick Share</h4>
+			<ul>
+				<a href="mailto:?subject=<?php echo urlencode('Check out ' . get_bloginfo('name')); ?>&body=<?php echo urlencode($share_message . ' ' . $share_url); ?>"
+				   class="button" title="Email">
+					<?php echo jvbIcon('envelope'); ?>
+				</a>
+				<a href="sms:?&body=<?php echo $sms_text; ?>"
+				   class="button" title="Text">
+					<?php echo jvbIcon('chat'); ?>
+				</a>
+				<a href="https://www.facebook.com/sharer/sharer.php?u=<?php echo urlencode($share_url); ?>"
+				   target="_blank"
+				   rel="noopener noreferrer"
+				   class="button" title="Facebook">
+					<?php echo jvbIcon('facebook-logo'); ?>
+				</a>
+				<a href="https://twitter.com/intent/tweet?url=<?php echo urlencode($share_url); ?>&text=<?php echo urlencode($share_message); ?>"
+				   target="_blank"
+				   rel="noopener noreferrer"
+				   class="button" title="Twitter">
+					<?php echo jvbIcon('twitter-logo'); ?>
+				</a>
+				<a href="https://wa.me/?text=<?php echo $sms_text; ?>"
+				   target="_blank"
+				   rel="noopener noreferrer"
+				   class="button" title="WhatsApp">
+					<?php echo jvbIcon('whatsapp-logo'); ?>
+				</a>
+			</ul>
+		</nav>
+	<?php
+	}
+
+	/**
+	 * Send notification to referrer when someone registers
+	 *
+	 * @param int $referrer_id
+	 * @param string $referee_name
+	 */
+	protected function sendReferrerNotification(int $referrer_id, string $referee_name): void
+	{
+		$referrer = get_userdata($referrer_id);
+		if (!$referrer) {
+			return;
+		}
+
+		$subject = sprintf('%s signed up with your referral code!', $referee_name);
+		$message = sprintf(
+			"Great news! %s just signed up using your referral code.\n\n" .
+			"View your referrals: %s",
+			$referee_name,
+			home_url('/dash/referrals')
+		);
+
+		JVB()->email()->sendEmail(
+			$referrer->user_email,
+			$subject,
+			$message
+		);
+	}
+
+	/**
+	 * Get welcome message for newly referred user
+	 *
+	 * @param int $user_id
+	 * @return string HTML content for welcome message
+	 */
+	public function getReferralWelcomeMessage(int $user_id): string
+	{
+		// Check if user was referred
+		$referral = $this->getReferralByReferee($user_id);
+
+		if (!$referral || $referral->status !== 'pending') {
+			return '';
+		}
+
+		// Only show for recent registrations (within 7 days)
+		$registered_time = strtotime($referral->referred_at);
+		if ((time() - $registered_time) > (7 * DAY_IN_SECONDS)) {
+			return '';
+		}
+
+		// Get referrer name
+		$referrer = get_userdata($referral->referrer_id);
+		$referrer_first_name = $referrer ? strtok($referrer->display_name, ' ') : 'Your friend';
+
+		// Get reward text
+		$reward_text = $this->getRewardText(); // Just "20% off" or "$25 off"
+
+		$booking_url = apply_filters('jvb_referral_booking_url', home_url('/contact'));
+		$estimate_url = apply_filters('jvb_referral_estimate_url', home_url('/estimate'));
+
+		ob_start();
+		?>
+		<div class="welcome-banner referral-welcome">
+			<div class="banner-content">
+				<h3><?= jvbIcon('confetti') ?>Welcome! <small><b><?= esc_html($referrer_first_name) ?></b> invited you to save <b><?= esc_html($reward_text) ?></b>!</small></h3>
+				<p>But we're not done yet! Here's what happens next:</p>
+				<div class="callout">
+					<ol>
+						<li>Book your <b>free consultation</b></li>
+						<li>Come in and we'll assess your tattoo</li>
+						<li>Get <?= esc_html($reward_text) ?> your first treatment!</li>
+					</ol>
+				</div>
+				<p class="hint">
+					<strong>Important:</strong> If you book with a different email than
+					<strong><?= esc_html(wp_get_current_user()->user_email) ?></strong>,
+					please let us know so we can apply your reward!
+				</p>
+				<ul class="buttons">
+					<li><a href="<?= esc_url($estimate_url) ?>" class="button-secondary">
+						<?= jvbIcon('calculator') ?> Get an Estimate First
+					</a></li>
+					<li><a href="<?= esc_url($booking_url) ?>" class="button-primary">
+						<?= jvbIcon('calendar') ?> Book Free Consult
+					</a></li>
+				</ul>
+
+
+			</div>
+		</div>
+		<?php
+		return ob_get_clean();
+	}
 }
 

--
Gitblit v1.10.0