<?php
/**
 * KooKoa Folders — Commercial funnel surfaces.
 *
 * Owns every upsell/lifecycle touch-point that is NOT the structural admin
 * menu: the phase-aware CTA submenu (FREE TRIAL / Upgrade to PRO), the hidden
 * in-plugin Upgrade pricing page (registration + render + the redirect/hide/
 * highlight machinery that keeps it reachable), and the three dashboard
 * notices (trial invite, TRIAL20 discount, 5-star review request).
 *
 * Instantiated lazily and dispatched from KookoaWPF, which keeps the hook
 * registrations (so external references and registered callbacks never change).
 *
 * @package KookoaWPF
 * @license GPL-2.0-or-later
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Renders the commercial menu entries, upgrade page and dashboard notices.
 */
class KookoaWPF_Funnel {

	/**
	 * Singleton instance.
	 *
	 * @var self|null
	 */
	private static $instance = null;

	/**
	 * Return the singleton instance.
	 *
	 * @return self
	 */
	public static function instance(): self {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Constructor. Hook registration lives on KookoaWPF (init_hooks), so this
	 * collaborator registers nothing of its own.
	 */
	private function __construct() {
		// Registration is owned by KookoaWPF (the hook surface).
	}

	/**
	 * Register the commercial menu entries under the KooKoa Folders top-level
	 * menu: the hidden "Upgrade to PRO" pricing sub-page, the external
	 * "Contact Us" link and the phase-aware CTA submenu. Called by
	 * KookoaWPF::add_admin_menu() AFTER it registers the structural Settings
	 * menu, so the submenu registration order is preserved exactly.
	 */
	public function register_menu_entries(): void {
		// Hidden sub-page: the in-plugin "Upgrade to PRO" pricing page. Register
		// it under the real parent slug so WordPress can resolve it through the
		// menu tree (get_admin_page_title() needs a valid parent). The visible
		// sidebar entry is removed later, on admin_head — after the capability /
		// access check on the page has run, but before the sidebar is rendered.
		add_submenu_page(
			'kookoa-folders',
			'KooKoa Folders — Upgrade to PRO',
			'Upgrade to PRO',
			'manage_options',
			'kookoa-folders-upgrade',
			array( $this, 'render_upgrade_page' )
		);

		// External link to the marketing contact page. WordPress renders any
		// submenu whose slug starts with http(s) as an outbound link, so we
		// don't need a callback or a registered page.
		add_submenu_page(
			'kookoa-folders',
			'',
			__( 'Contact Us', 'kookoa-folders' ),
			'manage_options',
			'https://www.kookoa.com/contact'
		);

		// Phase-aware CTA submenu, last entry under KK>_ Folders.
		// Fase 1 (trial never used) → "FREE TRIAL".
		// Fase 3 from FREE side (trial used + PRO not active) → "Upgrade to PRO".
		// Fase 2 (PRO active) → handled by the PRO plugin; we skip here because
		// trial_used is set and the PRO bootstrap function exists.
		// FASE 4 + FREE-only (paid customer, PRO currently uninstalled) →
		// "Upgrade to PRO" as a discovery path. The upgrade page renders a
		// "you're already a PRO customer, reinstall + activate" message in
		// this state — the submenu is the only way the user discovers
		// that informational page from the sidebar.
		// FASE 4 + PRO active → handled by the PRO plugin, nothing here.
		$trial_used = (bool) get_option( 'kookoawpf_trial_used' );
		$pro_active = function_exists( 'kookoawpf_pro_bootstrap' );
		$fase4      = (bool) get_option( 'kookoawpf_paid_license_activated' );
		if ( ! $pro_active ) {
			if ( $fase4 ) {
				add_submenu_page(
					'kookoa-folders',
					'',
					'<span class="kookoawpf-upgrade-menu">' . esc_html__( 'Upgrade to PRO', 'kookoa-folders' ) . '</span>',
					'manage_options',
					admin_url( 'admin.php?page=kookoa-folders-upgrade' )
				);
			} elseif ( ! $trial_used ) {
				add_submenu_page(
					'kookoa-folders',
					'',
					'<span class="kookoawpf-free-trial-menu">' . esc_html__( 'FREE TRIAL', 'kookoa-folders' ) . '</span>',
					'manage_options',
					admin_url( 'admin.php?page=kookoa-folders-upgrade' )
				);
			} else {
				add_submenu_page(
					'kookoa-folders',
					'',
					'<span class="kookoawpf-upgrade-menu">' . esc_html__( 'Upgrade to PRO', 'kookoa-folders' ) . '</span>',
					'manage_options',
					admin_url( 'admin.php?page=kookoa-folders-upgrade' )
				);
			}
		}
	}

	/**
	 * Redirect away from the Upgrade page when it isn't actually
	 * registered in the submenu tree.
	 *
	 * Why this exists: the PRO bootstrap's "no active PRO access" branch
	 * wipes `$submenu['kookoa-folders']` clean to force the user toward a
	 * single next step (entering a license / starting a trial). The FREE
	 * plugin still owns the `kookoa-folders-upgrade` URL but, because the
	 * page is no longer in the menu tree, wp-admin's capability resolver
	 * falls back to `do_not_allow` and serves the generic "Sorry, you are
	 * not allowed to access this page." wp_die screen. That's a dead end
	 * — the user has no obvious way to recover and the brand looks broken.
	 *
	 * Fix: intercept the request on `admin_init` (which fires after
	 * `admin_menu`, so `$submenu` is fully populated), check whether
	 * `kookoa-folders-upgrade` is actually present in the tree, and if
	 * not, redirect to the Freemius Account page — the only WP-admin page
	 * with an Activate License entry point. Falls back to the main
	 * Settings page if the Account page isn't registered either (e.g.
	 * the PRO plugin is deactivated mid-session).
	 *
	 * Non-admins are left alone: WP's standard "not allowed" screen is
	 * the correct response for a user that genuinely lacks the cap.
	 */
	public function maybe_redirect_blocked_upgrade(): void {
		// Read the page slug from `$_SERVER['QUERY_STRING']` rather than
		// `$_GET['page']` to avoid the NonceVerification sniff — this is a
		// read-only screen check that has nothing to act on, so a nonce
		// would be theatre. Same pattern as `highlight_parent_on_upgrade()`
		// below.
		$kookoawpf_query = isset( $_SERVER['QUERY_STRING'] )
			? sanitize_text_field( wp_unslash( $_SERVER['QUERY_STRING'] ) )
			: '';
		if ( '' === $kookoawpf_query ) {
			return;
		}
		parse_str( $kookoawpf_query, $kookoawpf_qs );
		if (
			! isset( $kookoawpf_qs['page'] )
			|| 'kookoa-folders-upgrade' !== $kookoawpf_qs['page']
		) {
			return;
		}
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}

		global $submenu;
		$kookoawpf_upgrade_registered = false;
		if ( isset( $submenu['kookoa-folders'] ) && is_array( $submenu['kookoa-folders'] ) ) {
			foreach ( $submenu['kookoa-folders'] as $kookoawpf_entry ) {
				if ( isset( $kookoawpf_entry[2] ) && 'kookoa-folders-upgrade' === $kookoawpf_entry[2] ) {
					$kookoawpf_upgrade_registered = true;
					break;
				}
			}
		}

		if ( $kookoawpf_upgrade_registered ) {
			return;
		}

		// Redirect to the main Settings page — that's where the FREE
		// plugin renders the "Activate License" entry point.
		wp_safe_redirect( admin_url( 'admin.php?page=kookoa-folders' ) );
		exit;
	}

	/**
	 * Drop the hidden "Upgrade to PRO" submenu link from the visible sidebar.
	 *
	 * Runs on admin_head so the capability/access check in wp-admin/admin.php
	 * has already accepted the page (the sub-page is still in $submenu when
	 * that check fires). After this, _wp_menu_output() renders the sidebar
	 * without the entry, while the page itself remains reachable by URL.
	 */
	public function hide_upgrade_submenu_link(): void {
		remove_submenu_page( 'kookoa-folders', 'kookoa-folders-upgrade' );

		// Force the "Contact Us" submenu link (external marketing URL) to
		// open in a new tab. WordPress doesn't accept a target attribute
		// in add_submenu_page(), so we patch the anchor at render time.
		echo '<script>
			document.addEventListener("DOMContentLoaded",function(){
				var a=document.querySelector(\'#adminmenu #toplevel_page_kookoa-folders .wp-submenu a[href="https://www.kookoa.com/contact"]\');
				if(a){a.target="_blank";a.rel="noopener noreferrer";}
			});
		</script>';

		// Same gating as add_admin_menu(): emit the orange-CTA styles only
		// when the matching submenu entry was actually registered.
		$trial_used = (bool) get_option( 'kookoawpf_trial_used' );
		$pro_active = function_exists( 'kookoawpf_pro_bootstrap' );
		$fase4      = (bool) get_option( 'kookoawpf_paid_license_activated' );
		if ( ! $pro_active ) {
			if ( $fase4 || $trial_used ) {
				// FASE 4 + FREE-only (discovery path to "you're already PRO")
				// OR Fase 3 (trial consumed, PRO not active) — both use the
				// "Upgrade to PRO" label, same orange CTA styling.
				echo '<style>
					#adminmenu .kookoawpf-upgrade-li > a { color: #FF9500 !important; font-weight: 700; }
					#adminmenu .kookoawpf-upgrade-li > a:hover { color: #fff !important; }
				</style>
				<script>
					document.addEventListener("DOMContentLoaded",function(){
						var s=document.querySelector(".kookoawpf-upgrade-menu");
						if(s){s.closest("li").classList.add("kookoawpf-upgrade-li");}
					});
				</script>';
			} else {
				// Fase 1 — FREE TRIAL.
				echo '<style>
					#adminmenu .kookoawpf-trial-li > a { color: #FF9500 !important; font-weight: 700; }
					#adminmenu .kookoawpf-trial-li > a:hover { color: #fff !important; }
				</style>
				<script>
					document.addEventListener("DOMContentLoaded",function(){
						var s=document.querySelector(".kookoawpf-free-trial-menu");
						if(s){s.closest("li").classList.add("kookoawpf-trial-li");}
					});
				</script>';
			}
		}
	}

	/**
	 * Keep the KooKoa Folders top-level menu highlighted while the user is on
	 * the hidden Upgrade sub-page. The sub-page is removed from the visible
	 * submenu (see add_admin_menu()), so without this filter WordPress would
	 * lose the parent association and unhighlight the top-level entry.
	 *
	 * @param string $parent_file The current parent file slug.
	 * @return string
	 */
	public function highlight_parent_on_upgrade( $parent_file ) {
		$query = isset( $_SERVER['QUERY_STRING'] ) ? sanitize_text_field( wp_unslash( $_SERVER['QUERY_STRING'] ) ) : '';
		if ( false !== strpos( $query, 'page=kookoa-folders-upgrade' ) ) {
			return 'kookoa-folders';
		}
		return $parent_file;
	}

	/**
	 * Render the in-plugin upgrade page.
	 */
	public function render_upgrade_page(): void {
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}
		include KOOKOAWPF_PLUGIN_DIR . 'templates/admin/upgrade.php';
	}

	/**
	 * Render KooKoa Folders' dashboard-only admin notices.
	 *
	 * Two notices are planned (each independently dismissible):
	 *   - Notice 1: trial invitation, only when the user has never
	 *     activated a trial on this site (`kookoawpf_trial_used` empty).
	 *   - Notice 2: TRIAL20 discount reminder, only when the pricing
	 *     page is in Block 3 (trial used and not active).
	 *
	 * Both render only on the WordPress Dashboard (`screen->id === 'dashboard'`)
	 * — the plugin stays silent on every other admin screen so the user
	 * isn't interrupted while editing posts, managing media, etc. Once
	 * the per-user dismiss flag is written, the notice is gone for that
	 * user forever (set-only).
	 *
	 * Style: black background, green accent, retro dark — the same vibe
	 * as the in-plugin CTAs, capped at the same width as our settings
	 * blocks so the notice never spans the full admin width.
	 */
	public function render_dashboard_notices(): void {
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}
		$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
		if ( ! $screen || 'dashboard' !== $screen->id ) {
			return;
		}

		$user_id = get_current_user_id();
		if ( ! $user_id ) {
			return;
		}

		// `kookoawpf_install_time` is normally written from
		// KookoaWPF_Database::activate() on the activation hook. If a
		// user installed the plugin before that line existed (or before
		// activate() ever ran in their setup), backfill it with
		// `time()` here — they'll start the post-install timers from
		// today, which is the safest fallback.
		$install_time = (int) get_option( 'kookoawpf_install_time', 0 );
		if ( $install_time <= 0 ) {
			$install_time = time();
			update_option( 'kookoawpf_install_time', $install_time );
		}

		// FASE 4 — paid license ever activated on this site. The two
		// commercial notices below (trial invite + discount reminder)
		// are suppressed for FASE 4 customers (they're already a
		// customer, no upsell needed). The review notice further down
		// is INTENTIONALLY still allowed: long-time paying customers
		// are the strongest review pool we have.
		$kookoawpf_fase4 = (bool) get_option( 'kookoawpf_paid_license_activated' );

		// ── Notice 1: trial invitation ──
		// Visible only after 14 days from install, when the user has
		// never started a trial and hasn't dismissed the notice. Hidden
		// in FASE 4 (the customer has converted past the trial stage).
		$trial_used       = (bool) get_option( 'kookoawpf_trial_used', false );
		$trial_active     = (bool) get_option( 'kookoawpf_trial_active', false );
		$invite_dismissed = (bool) get_user_meta( $user_id, 'kookoawpf_notice_trial_invite_dismissed', true );
		$age_seconds      = time() - $install_time;
		$invite_unlocked  = $age_seconds >= ( 14 * DAY_IN_SECONDS );
		if ( ! $kookoawpf_fase4 && $invite_unlocked && ! $trial_used && ! $invite_dismissed ) {
			$this->render_notice_trial_invite();
			return;
		}

		// ── Notice 2: TRIAL20 discount reminder ──
		// Mirrors the pricing-page Block 3 condition: trial was used and
		// is no longer active. Shown once per user, dismissible. The
		// CTA points to the upgrade page where Block 3 surfaces the
		// real TRIAL20 code in its big monospace pill. Hidden in
		// FASE 4 — no discount nudge to a confirmed customer.
		// ALSO hidden while PRO is still loaded: in that state the
		// pricing page is hijacked by Freemius's license-entry form,
		// so the "Go to Pricing" CTA would land on a non-functional
		// page. The dedicated no-license banner under the license-
		// entry form handles the upsell while PRO is loaded; this
		// dashboard notice fires only once PRO has been deactivated
		// or uninstalled and the FREE pricing page is reachable again.
		$pro_loaded         = function_exists( 'kookoawpf_pro_bootstrap' );
		$discount_dismissed = (bool) get_user_meta( $user_id, 'kookoawpf_notice_trial_discount_dismissed', true );
		if ( ! $kookoawpf_fase4 && ! $pro_loaded && $trial_used && ! $trial_active && ! $discount_dismissed ) {
			$this->render_notice_trial_discount();
			return;
		}

		// ── Notice 3: review request (90 days post-install) ──
		// Independent of the Settings → Rate Us card the user may have
		// dismissed there: this is a dashboard-only ask that fires once
		// per user, after the user has been around long enough to have
		// formed an opinion of the plugin. 90 days is the sweet spot —
		// long enough to convert into reviewers, short enough not to
		// miss users who churn before the 1-year mark.
		$review_dash_dismissed = (bool) get_user_meta( $user_id, 'kookoawpf_notice_review_dashboard_dismissed', true );
		$review_unlocked       = $age_seconds >= ( 90 * DAY_IN_SECONDS );
		if ( $review_unlocked && ! $review_dash_dismissed ) {
			$this->render_notice_review_dashboard();
		}
	}

	/**
	 * Render the "Try PRO free for 14 days" dashboard notice.
	 *
	 * Self-contained: HTML + scoped CSS + dismiss JS all rendered inline
	 * here so the notice doesn't depend on any settings page enqueue.
	 * Click on the orange × dismisses for the current user via the
	 * `kookoawpf_dismiss_notice` AJAX endpoint.
	 */
	private function render_notice_trial_invite(): void {
		$trial_url = 'https://checkout.freemius.com/plugin/27000/plan/44695/licenses/1/?trial=free';
		$nonce     = wp_create_nonce( 'kookoawpf_nonce' );
		?>
		<div class="kookoawpf-dash-notice" data-notice="trial_invite">
			<button type="button" class="kookoawpf-dash-notice-dismiss" aria-label="<?php esc_attr_e( 'Dismiss', 'kookoa-folders' ); ?>" data-notice="trial_invite" data-nonce="<?php echo esc_attr( $nonce ); ?>">&times;</button>
			<div class="kookoawpf-dash-notice-title"><?php esc_html_e( 'Try KooKoa Folders PRO free for 14 days', 'kookoa-folders' ); ?></div>
			<div class="kookoawpf-dash-notice-text"><?php esc_html_e( 'No credit card required. Test every PRO feature on your own site.', 'kookoa-folders' ); ?></div>
			<a href="<?php echo esc_url( $trial_url ); ?>" class="kookoawpf-dash-notice-cta" target="_blank" rel="noopener noreferrer">
				<?php esc_html_e( 'START FREE TRIAL', 'kookoa-folders' ); ?>
			</a>
		</div>
		<?php
		$this->print_dashboard_notice_assets();
	}

	/**
	 * Render the "20% off — TRIAL20" dashboard notice.
	 *
	 * Same chrome as `render_notice_trial_invite()`: black background,
	 * green title + CTA, shared dismiss button + JS handler. The CTA
	 * sends the user to our custom pricing page (`kookoa-folders-upgrade`)
	 * — the upgrade template's "Block 3" branch already carries the
	 * actual TRIAL20 code in its big monospace pill, so the notice
	 * stays short and the code lives in one canonical place.
	 */
	private function render_notice_trial_discount(): void {
		$pricing_url = admin_url( 'admin.php?page=kookoa-folders-upgrade' );
		$nonce       = wp_create_nonce( 'kookoawpf_nonce' );
		?>
		<div class="kookoawpf-dash-notice kookoawpf-dash-notice-orange" data-notice="trial_discount">
			<button type="button" class="kookoawpf-dash-notice-dismiss" aria-label="<?php esc_attr_e( 'Dismiss', 'kookoa-folders' ); ?>" data-notice="trial_discount" data-nonce="<?php echo esc_attr( $nonce ); ?>">&times;</button>
			<div class="kookoawpf-dash-notice-title"><?php esc_html_e( 'Get 20% off KooKoa Folders PRO', 'kookoa-folders' ); ?></div>
			<div class="kookoawpf-dash-notice-text"><?php esc_html_e( 'Loved your free trial? Use the TRIAL20 code at checkout to take 20% off the lifetime license.', 'kookoa-folders' ); ?></div>
			<a href="<?php echo esc_url( $pricing_url ); ?>" class="kookoawpf-dash-notice-cta">
				<?php esc_html_e( 'GO TO PRICING', 'kookoa-folders' ); ?>
			</a>
		</div>
		<?php
		// Both dashboard notices share the same scoped CSS + JS — they
		// were emitted by render_notice_trial_invite() if it ran. To
		// keep this method self-contained when it runs alone (notice 2
		// fires when notice 1 is hidden), re-emit the same block here.
		// The selectors are id-less so duplicate <style>/<script> tags
		// across the two methods don't conflict — last one wins, which
		// is fine since the rules are identical.
		$this->print_dashboard_notice_assets();
	}

	/**
	 * Render the "Enjoying KooKoa Folders? Leave a 5-star review"
	 * dashboard notice. Mirrors the Rate Us card on the Settings tab
	 * but lives independently — even users who dismissed the Settings
	 * card see this once on the dashboard after a year, when their
	 * long-time-user status is the strongest social proof we can ask
	 * to leverage.
	 */
	private function render_notice_review_dashboard(): void {
		$review_url = 'https://wordpress.org/support/plugin/kookoa-folders/reviews/?rate=5#new-post';
		$nonce      = wp_create_nonce( 'kookoawpf_nonce' );
		?>
		<div class="kookoawpf-dash-notice kookoawpf-dash-notice-orange kookoawpf-dash-notice-review" data-notice="review_dashboard">
			<button type="button" class="kookoawpf-dash-notice-dismiss" aria-label="<?php esc_attr_e( 'Dismiss', 'kookoa-folders' ); ?>" data-notice="review_dashboard" data-nonce="<?php echo esc_attr( $nonce ); ?>">&times;</button>
			<div class="kookoawpf-dash-notice-review-stars">&#9733;&#9733;&#9733;&#9733;&#9733;</div>
			<div class="kookoawpf-dash-notice-review-body">
				<div class="kookoawpf-dash-notice-title"><?php esc_html_e( 'Enjoying KooKoa Folders?', 'kookoa-folders' ); ?></div>
				<div class="kookoawpf-dash-notice-text"><?php esc_html_e( 'Help other users discover this plugin — leave a 5-star review on WordPress.org. It only takes a minute and means a lot.', 'kookoa-folders' ); ?></div>
				<a href="<?php echo esc_url( $review_url ); ?>" class="kookoawpf-dash-notice-cta" target="_blank" rel="noopener noreferrer">
					<?php esc_html_e( 'RATE THIS PLUGIN', 'kookoa-folders' ); ?>
				</a>
			</div>
		</div>
		<?php
		$this->print_dashboard_notice_assets();
	}

	/**
	 * Shared inline <style> + <script> for the dashboard notices. Either
	 * notice can call this safely; if both ever ran on the same request
	 * the assets would be duplicated but functionally identical.
	 */
	private function print_dashboard_notice_assets(): void {
		?>
		<style>
		.kookoawpf-dash-notice {
			max-width: 1130px;
			margin: 22px 22px 0 0;
			padding: 24px 32px 28px;
			background: #0d1117;
			color: #ffffff;
			font-family: 'Share Tech Mono', 'Courier New', monospace;
			position: relative;
			box-sizing: border-box;
		}
		.kookoawpf-dash-notice * { box-sizing: border-box; }
		.kookoawpf-dash-notice-title {
			font-size: 24px;
			color: #39FF14;
			text-shadow: 0 0 6px rgba(57, 255, 20, 0.4);
			letter-spacing: 0.04em;
			margin-bottom: 10px;
		}
		.kookoawpf-dash-notice-text {
			color: #ffffff;
			font-size: 15px;
			line-height: 1.55;
			margin-bottom: 18px;
		}
		.kookoawpf-dash-notice-cta {
			display: inline-block;
			padding: 11px 28px;
			background: #39FF14;
			color: #0d1117;
			font-family: 'Share Tech Mono', 'Courier New', monospace;
			font-size: 15px;
			font-weight: 700;
			letter-spacing: 0.08em;
			text-decoration: none;
			border: 2px solid #39FF14;
			transition: background 0.15s, color 0.15s, box-shadow 0.15s;
		}
		.kookoawpf-dash-notice-cta:hover,
		.kookoawpf-dash-notice-cta:focus {
			background: transparent;
			color: #39FF14;
			text-decoration: none;
			box-shadow: 0 0 14px rgba(57, 255, 20, 0.35);
		}
		.kookoawpf-dash-notice-dismiss {
			position: absolute;
			top: 6px;
			right: 12px;
			width: 48px;
			height: 48px;
			padding: 0;
			border: none;
			background: transparent;
			color: #ffffff;
			font-size: 44px;
			line-height: 1;
			cursor: pointer;
			opacity: 0.85;
			transition: opacity 0.15s, color 0.15s;
		}
		.kookoawpf-dash-notice-dismiss:hover,
		.kookoawpf-dash-notice-dismiss:focus {
			opacity: 1;
			color: #39FF14;
			outline: none;
		}
		/* Orange variant — applied to notices that point at the upgrade
			page (the in-plugin retro accent for "PRO/upgrade" things). */
		.kookoawpf-dash-notice-orange .kookoawpf-dash-notice-title {
			color: #FF9500;
			text-shadow: 0 0 6px rgba(255, 149, 0, 0.4);
		}
		.kookoawpf-dash-notice-orange .kookoawpf-dash-notice-cta {
			background: #FF9500;
			border-color: #FF9500;
			color: #0d1117;
		}
		.kookoawpf-dash-notice-orange .kookoawpf-dash-notice-cta:hover,
		.kookoawpf-dash-notice-orange .kookoawpf-dash-notice-cta:focus {
			background: transparent;
			color: #FF9500;
			box-shadow: 0 0 14px rgba(255, 149, 0, 0.35);
		}
		.kookoawpf-dash-notice-orange .kookoawpf-dash-notice-dismiss:hover,
		.kookoawpf-dash-notice-orange .kookoawpf-dash-notice-dismiss:focus {
			color: #FF9500;
		}
		/* Review variant — adds the stars column next to the standard body. */
		.kookoawpf-dash-notice-review {
			display: flex;
			align-items: center;
			gap: 28px;
		}
		.kookoawpf-dash-notice-review-stars {
			font-size: 36px;
			color: #FF9500;
			text-shadow: 0 0 8px rgba(255, 149, 0, 0.45);
			letter-spacing: 3px;
			flex-shrink: 0;
			line-height: 1;
		}
		.kookoawpf-dash-notice-review-body {
			flex: 1;
			min-width: 0;
		}
		</style>
		<script>
		(function() {
			if ( window.kookoawpfDashNoticeBound ) return;
			window.kookoawpfDashNoticeBound = true;
			document.addEventListener('click', function(e) {
				var btn = e.target.closest && e.target.closest('.kookoawpf-dash-notice-dismiss');
				if ( ! btn ) return;
				var notice = btn.getAttribute('data-notice') || '';
				var nonce  = btn.getAttribute('data-nonce') || '';
				var box    = btn.closest('.kookoawpf-dash-notice');
				if ( box ) {
					box.style.transition = 'opacity 0.25s';
					box.style.opacity = '0';
					setTimeout(function() { if ( box.parentNode ) box.parentNode.removeChild(box); }, 260);
				}
				var data = new FormData();
				data.append('action', 'kookoawpf_dismiss_notice');
				data.append('notice', notice);
				data.append('nonce', nonce);
				fetch(ajaxurl, { method: 'POST', credentials: 'same-origin', body: data });
			});
		})();
		</script>
		<?php
	}
}
