HEX
Server: Apache/2.4.68 (Debian)
System: Linux as-cs-widget-demo-us-central1 6.1.0-44-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.164-1 (2026-03-09) x86_64
User: root (0)
PHP: 8.2.32
Disabled: NONE
Upload Files
File: /var/www/html/wp-content/plugins/allspice/includes/settings.php
<?php
// includes/settings.php
if (!defined('ABSPATH')) exit;

define('ALLSPICE_SETTINGS_PAGE', 'allspice-settings');
define('ALLSPICE_SETTINGS_GROUP', 'allspice_settings_group');
define('ALLSPICE_SYNC_BUTTONS_SETTINGS_ACTION', 'allspice_sync_buttons_settings');
define('ALLSPICE_SYNC_RECIPES_ACTION', 'allspice_sync_recipes');
define('ALLSPICE_SYNC_ALL_ACTION', 'allspice_sync_all');

$css = '
.allspice-admin{
  --asx-solid:#F9705F;
  --asx-g1:#F25B57;
  --asx-g2:#FF8266;
  --asx-text:#303030;
  --asx-text2:#606060;
  --asx-text3:#B0B0B0;
  --asx-bg:#FFFFFF;
  --asx-bg2:#F2F2F2;
  --asx-tint:#EAEAEA;
  --asx-blue:#0099FF;
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, "Apple Color Emoji","Segoe UI Emoji";
}
.allspice-admin .description{ color: var(--asx-text2); }
.allspice-admin__stack{
  max-width: 980px;
  margin: 14px 0 18px 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.allspice-admin__card{
  background: var(--asx-bg);
  border: 1px solid rgba(194,194,194,0.38);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 2px 10px rgba(0,0,0,0.06);
}
.allspice-admin__accent{
  height: 6px;
  background: linear-gradient(90deg, var(--asx-g1), var(--asx-g2));
}
.allspice-admin__body{ padding: 18px; }
.allspice-admin__sectionTitle{
  margin: 0 0 10px 0;
  color: var(--asx-text);
  font-weight: 900;
  font-size: 14px;
  letter-spacing: 0.2px;
}
.allspice-admin__kicker{
  margin: 0 0 10px 0;
  color: var(--asx-text3);
  font-weight: 900;
  letter-spacing: 0.10em;
  font-size: 11px;
}
.allspice-admin__divider{
  height: 1px;
  background: rgba(234,234,234,0.95);
  margin: 16px 0;
}
.allspice-admin .form-table th{
  color: var(--asx-text);
  font-weight: 900;
  padding-top: 12px;
}
.allspice-admin .form-table td{
  color: var(--asx-text2);
  padding-top: 12px;
}
.allspice-admin .form-table .description{
  margin: 6px 0 0 0;
  font-size: 12.5px;
  line-height: 1.45;
}
.allspice-admin input.regular-text,
.allspice-admin input.large-text,
.allspice-admin input[type="text"],
.allspice-admin input[type="password"]{
  border: 1px solid rgba(194,194,194,0.55);
  border-radius: 10px;
  padding: 8px 10px;
  box-shadow: none;
}
.allspice-admin input:focus{
  border-color: rgba(249,112,95,0.70);
  box-shadow: 0 0 0 3px rgba(249,112,95,0.18);
}
.allspice-admin .button,
.allspice-admin .button-secondary,
.allspice-admin .button-primary{
  border-radius: 10px !important;
  font-weight: 900 !important;
}
.allspice-admin .button-primary{
  background: linear-gradient(90deg, var(--asx-g1), var(--asx-g2)) !important;
  border: 0 !important;
  box-shadow: 0 6px 14px rgba(242,91,87,0.20) !important;
  text-shadow: none !important;
}
.allspice-admin .button-primary:hover{ filter: brightness(0.98); }
.allspice-admin .button-secondary{
  border-color: rgba(194,194,194,0.55) !important;
}
.allspice-admin .button:focus,
.allspice-admin .button-primary:focus,
.allspice-admin .button-secondary:focus{
  box-shadow: 0 0 0 3px rgba(249,112,95,0.18) !important;
  border-color: rgba(249,112,95,0.70) !important;
}
.allspice-admin__card .notice,
.allspice-admin__card .updated,
.allspice-admin__card .error,
.allspice-admin__card .wrap > .notice,
.allspice-admin__card .wrap > .updated,
.allspice-admin__card .wrap > .error{
  display:none !important;
}
.allspice-mkt{ max-width: 980px; margin: 0; }
.allspice-mkt__card{
  background: var(--asx-bg);
  border: 1px solid rgba(194,194,194,0.38);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 2px 10px rgba(0,0,0,0.06);
}
.allspice-mkt__accent{ height: 6px; background: linear-gradient(90deg, var(--asx-g1), var(--asx-g2)); }
.allspice-mkt__head{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:14px;
  padding: 14px 18px 0 18px;
}
.allspice-mkt__brand{
  display:flex;
  align-items:center;
  gap:12px;
  min-width: 0;
}
.allspice-mkt__brand img{ height: 28px; width:auto; display:block; }
.allspice-mkt__tag{
  color: var(--asx-text2);
  font-size: 12.5px;
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.allspice-mkt__ctaTop{
  display:flex;
  gap:10px;
  flex-wrap:wrap;
  justify-content:flex-end;
}
.allspice-mkt__btnPrimary{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  padding: 9px 12px;
  border-radius: 10px;
  border: 0;
  text-decoration:none;
  color:#fff !important;
  background: linear-gradient(90deg, var(--asx-g1), var(--asx-g2));
  font-weight: 900;
  font-size: 13px;
  box-shadow: 0 6px 14px rgba(242,91,87,0.22);
}
.allspice-mkt__btnPrimary:hover{ filter: brightness(0.98); transform: translateY(-0.5px); }
.allspice-mkt__btnSecondary{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  padding: 9px 12px;
  border-radius: 10px;
  border: 1px solid rgba(234,234,234,0.95);
  text-decoration:none;
  color: var(--asx-text) !important;
  background:#fff;
  font-weight: 900;
  font-size: 13px;
}
.allspice-mkt__btnSecondary:hover{ border-color: rgba(249,112,95,0.55); color: var(--asx-solid) !important; }
.allspice-mkt__inner{
  display: grid;
  grid-template-columns: 1.25fr 0.75fr;
  gap: 18px;
  padding: 18px;
}
@media (max-width: 900px){ .allspice-mkt__inner{ grid-template-columns: 1fr; } }
.allspice-mkt__leftTitle{
  margin:0;
  color: var(--asx-text3);
  font-weight: 900;
  letter-spacing: 0.10em;
  font-size: 11px;
}
.allspice-mkt__grid{
  margin-top: 10px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}
@media (max-width: 900px){ .allspice-mkt__grid{ grid-template-columns: 1fr; } }
.allspice-mkt__feature{
  background: rgba(242,242,242,0.55);
  border: 1px solid rgba(234,234,234,0.95);
  border-radius: 12px;
  padding: 10px 12px;
}
.allspice-mkt__featureTitle{
  margin:0;
  color: var(--asx-text);
  font-weight: 900;
  font-size: 13px;
  letter-spacing: 0.2px;
}
.allspice-mkt__featureBody{
  margin: 4px 0 0 0;
  color: var(--asx-text2);
  font-size: 13px;
  line-height: 1.55;
}
.allspice-mkt__right{
  border-left: 1px solid rgba(234,234,234,0.9);
  padding-left: 18px;
}
@media (max-width: 900px){
  .allspice-mkt__right{
    border-left: none;
    padding-left: 0;
    border-top: 1px solid rgba(234,234,234,0.9);
    padding-top: 14px;
  }
}
.allspice-mkt__stepsTitle{
  margin:0;
  color: var(--asx-text3);
  font-weight: 900;
  letter-spacing: 0.10em;
  font-size: 11px;
}
.allspice-mkt__steps{
  margin: 10px 0 0 0;
  padding-left: 18px;
  color: var(--asx-text2);
  font-size: 13px;
  line-height: 1.55;
}
.allspice-mkt__steps li{ margin: 6px 0; }
.allspice-mkt__steps strong{ color: var(--asx-text); font-weight: 900; }
.allspice-mkt__note{
  margin-top: 10px;
  padding: 10px 12px;
  background: rgba(242,242,242,0.60);
  border: 1px solid rgba(234,234,234,0.95);
  border-radius: 12px;
  color: var(--asx-text2);
  font-size: 12.5px;
  line-height: 1.5;
}
.allspice-mkt__note a{ color: var(--asx-blue); text-decoration:none; font-weight:900; }
.allspice-mkt__note a:hover{ text-decoration: underline; }
.allspice-premium-banner{
  margin: 16px 0 0 0;
  padding: 14px 16px;
  border-radius: 14px;
  border: 1px solid rgba(249,112,95,0.35);
  background: rgba(249,112,95,0.08);
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:14px;
}
.allspice-premium-banner__title{
  margin:0;
  color: var(--asx-text);
  font-weight:900;
  font-size:14px;
}
.allspice-premium-banner__body{
  margin:4px 0 0 0;
  color: var(--asx-text2);
  font-size:13px;
  line-height:1.45;
  max-width:700px;
}
.allspice-premium-banner__button{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  white-space:nowrap;
  padding:9px 12px;
  border-radius:10px;
  background:linear-gradient(90deg, var(--asx-g1), var(--asx-g2));
  color:#fff !important;
  text-decoration:none;
  font-weight:900;
  box-shadow:0 6px 14px rgba(242,91,87,0.18);
}
.allspice-premium-banner__button:hover{
  filter:brightness(0.98);
}
@media (max-width: 700px){
  .allspice-premium-banner{
    align-items:flex-start;
    flex-direction:column;
  }
}
.allspice-sync-actions{
  display:flex;
  flex-wrap:wrap;
  gap:10px;
  align-items:center;
  margin-top:12px;
}
.allspice-sync-actions form{
  margin:0;
}
';

add_action('admin_enqueue_scripts', function ($hook) use ($css) {
    if ($hook !== 'settings_page_' . ALLSPICE_SETTINGS_PAGE) return;
    wp_register_style('allspice-admin-settings', false, [], '1.0.0');
    wp_enqueue_style('allspice-admin-settings');
    wp_add_inline_style('allspice-admin-settings', $css);
});

add_action('current_screen', function ($screen) {
    if (!$screen || $screen->id !== 'settings_page_' . ALLSPICE_SETTINGS_PAGE) return;
    remove_all_actions('admin_notices');
    remove_all_actions('all_admin_notices');
    remove_all_actions('network_admin_notices');
    remove_all_actions('user_admin_notices');
    add_action('admin_notices', function () {
        $n = get_transient('allspice_admin_notice');
        if (!$n || empty($n['msg'])) return;
        delete_transient('allspice_admin_notice');
        $type = in_array(($n['type'] ?? ''), ['success','error','warning','info'], true)
            ? (string)$n['type']
            : 'info';
        echo '<div class="notice notice-' . esc_attr($type) . ' is-dismissible">';
        echo '<p>' . esc_html((string)$n['msg']) . '</p>';
        echo '</div>';
    }, 1);
});

function allspice_admin_card_open(string $kicker, string $title = '', bool $accent = false): void {
    echo '<div class="allspice-admin__card">';
    if ($accent) echo '<div class="allspice-admin__accent"></div>';
    echo '<div class="allspice-admin__body">';
    if ($kicker !== '') echo '<p class="allspice-admin__kicker">' . esc_html($kicker) . '</p>';
    if ($title !== '') echo '<h2 class="allspice-admin__sectionTitle">' . esc_html($title) . '</h2>';
}

function allspice_admin_card_close(): void {
    echo '</div></div>';
}

function allspice_render_marketing_section(array $s): void {
    $portal_base = 'https://www.allspicelabs.com';
    $login_url = $portal_base . '/login/';
    $register_url = $portal_base . '/signup/';
    $logo_url = 'https://firebasestorage.googleapis.com/v0/b/allspice-f3bfd.firebasestorage.app/o/hosted_assets%2Fallspice_combined_red_full_registered.png?alt=media';
    echo '<div class="allspice-mkt"><div class="allspice-mkt__card"><div class="allspice-mkt__accent"></div>';
    echo '<div class="allspice-mkt__head">';
    echo '<div class="allspice-mkt__brand"><img src="' . esc_url($logo_url) . '" alt="Allspice" loading="lazy"/><div class="allspice-mkt__tag">Tools that work with your existing site</div></div>';
    echo '<div class="allspice-mkt__ctaTop">';
    echo '<a class="allspice-mkt__btnPrimary" href="' . esc_url($login_url) . '" target="_blank" rel="noopener noreferrer">Manage in Publisher Portal</a>';
    echo '<a class="allspice-mkt__btnSecondary" href="' . esc_url($register_url) . '" target="_blank" rel="noopener noreferrer">Create account</a>';
    echo '</div></div>';
    echo '<div class="allspice-mkt__inner">';
    echo '<div><p class="allspice-mkt__leftTitle">HOW IT WORKS</p><div class="allspice-mkt__grid">';
    echo '<div class="allspice-mkt__feature"><p class="allspice-mkt__featureTitle">Save &amp; Organize Recipes</p><p class="allspice-mkt__featureBody">Save recipes across any site in our ecosystem. The only platform that lets readers build a collection from multiple recipe sites.</p></div>';
    echo '<div class="allspice-mkt__feature"><p class="allspice-mkt__featureTitle">Smart Grocery Lists</p><p class="allspice-mkt__featureBody">AI-powered grocery lists that understand ingredients, suggest substitutions, and integrate with shopping services.</p></div>';
    echo '<div class="allspice-mkt__feature"><p class="allspice-mkt__featureTitle">Create Meal Plans</p><p class="allspice-mkt__featureBody">Plan entire weeks of meals from your recipes. Readers organize dinners, get suggestions, and send lists to Amazon Fresh.</p></div>';
    echo '<div class="allspice-mkt__feature"><p class="allspice-mkt__featureTitle">Track Pantry</p><p class="allspice-mkt__featureBody">Check what readers already have, get substitution suggestions, and alerts for items running low or expiring soon.</p></div>';
    echo '<div class="allspice-mkt__feature"><p class="allspice-mkt__featureTitle">Cook Mode</p><p class="allspice-mkt__featureBody">Step-by-step view for phone-in-hand cooking. No more scrolling between ingredients and instructions.</p></div>';
    echo '<div class="allspice-mkt__feature"><p class="allspice-mkt__featureTitle">AI Chat Agent</p><p class="allspice-mkt__featureBody">Intelligent cooking assistant that helps customize recipes, troubleshoot issues, and answer questions in real-time.</p></div>';
    echo '</div></div>';
    echo '<div class="allspice-mkt__right"><p class="allspice-mkt__stepsTitle">SETUP</p><ol class="allspice-mkt__steps">';
    echo '<li>Log in to the Publisher Portal</li>';
    echo '<li>Add your site to get a <strong>Domain ID</strong></li>';
    echo '<li>Copy your <strong>Publisher ID</strong> + <strong>Webhook Token</strong> into this page</li>';
    echo '<li>Click <strong>Sync All</strong></li>';
    echo '</ol><div class="allspice-mkt__note">Don&#039;t have an account yet? Create one at <a href="' . esc_url($register_url) . '" target="_blank" rel="noopener noreferrer">https://www.allspicelabs.com/signup/</a> to get your Publisher ID.</div></div>';
    echo '</div></div></div>';
}

function allspice_render_premium_banner(array $s): void {
    $premium = (bool)($s['premium'] ?? true);
    if ($premium) return;
    $portal_url = 'https://portal.allspicelabs.com/login/';
    echo '<div class="allspice-premium-banner">';
    echo '<div>';
    echo '<p class="allspice-premium-banner__title">Premium subscription required</p>';
    echo '<p class="allspice-premium-banner__body">This site is connected to Allspice, but features are not active for this domain until billing setup is completed in the Publisher Portal.</p>';
    echo '</div>';
    echo '<a class="allspice-premium-banner__button" href="' . esc_url($portal_url) . '" target="_blank" rel="noopener noreferrer">Open Publisher Portal</a>';
    echo '</div>';
}

add_action('admin_menu', function () {
    $hook = add_options_page(
        'Allspice',
        'Allspice',
        'manage_options',
        ALLSPICE_SETTINGS_PAGE,
        'allspice_render_settings_page'
    );
    add_action('load-' . $hook, function () {
        nocache_headers();
    });
});

add_action('admin_init', function () {
    register_setting(
        ALLSPICE_SETTINGS_GROUP,
        ALLSPICE_SETTINGS_OPTION,
        ['sanitize_callback' => 'allspice_sanitize_settings']
    );
    add_settings_section(
        'allspice_main',
        '',
        function () {
            echo '<p class="description" style="max-width:700px;">Configure your Allspice integration. These settings control how buttons are linked and refreshed from Allspice.</p>';
        },
        ALLSPICE_SETTINGS_PAGE
    );
    add_settings_field('partner_id', 'Publisher ID', 'allspice_field_partner_id', ALLSPICE_SETTINGS_PAGE, 'allspice_main');
    add_settings_field('domain_id', 'Domain ID', 'allspice_field_domain_id', ALLSPICE_SETTINGS_PAGE, 'allspice_main');
    add_settings_field('webhook_token', 'Webhook Token', 'allspice_field_webhook_token', ALLSPICE_SETTINGS_PAGE, 'allspice_main');
});

function allspice_sanitize_settings($input): array {
    $cur = allspice_opt_get();
    $in = is_array($input) ? $input : [];
    $out = array_merge($cur, $in);
    if (isset($in['partner_id'])) $out['partner_id'] = sanitize_text_field((string)$in['partner_id']);
    if (isset($in['domain_id'])) $out['domain_id'] = sanitize_text_field((string)$in['domain_id']);
    if (isset($in['webhook_token'])) $out['webhook_token'] = sanitize_text_field((string)$in['webhook_token']);
    return $out;
}

function allspice_field_partner_id(): void {
    $s = allspice_opt_get();
    echo '<input type="text" class="regular-text" name="' . esc_attr(ALLSPICE_SETTINGS_OPTION) . '[partner_id]" value="' . esc_attr((string)$s['partner_id']) . '"/>';
    echo '<p class="description">Get your <strong>Publisher ID</strong> from the <a href="' . esc_url('https://www.allspicelabs.com/login/') . '" target="_blank" rel="noopener noreferrer">Allspice Publisher Portal</a>.</p>';
}

function allspice_field_domain_id(): void {
    $s = allspice_opt_get();
    echo '<input type="text" class="regular-text" name="' . esc_attr(ALLSPICE_SETTINGS_OPTION) . '[domain_id]" value="' . esc_attr((string)$s['domain_id']) . '"/>';
    echo '<p class="description">Identifies this WordPress site (from the Publisher Portal).</p>';
}

function allspice_field_webhook_token(): void {
    $s = allspice_opt_get();
    echo '<input type="password" class="regular-text" autocomplete="new-password" name="' . esc_attr(ALLSPICE_SETTINGS_OPTION) . '[webhook_token]" value="' . esc_attr((string)$s['webhook_token']) . '"/>';
    echo '<p class="description">Shared secret used for secure communication with Allspice.</p>';
}

function allspice_admin_debug_url(string $path = '/'): string {
    $nonce = wp_create_nonce(ALLSPICE_DEBUG_NONCE_ACTION);
    $base = home_url($path);
    return add_query_arg(
        [
            ALLSPICE_DEBUG_PARAM => '1',
            ALLSPICE_DEBUG_NONCE_PARAM => $nonce,
        ],
        $base
    );
}

function allspice_render_settings_page(): void {
    if (!current_user_can('manage_options')) return;
    $s = allspice_opt_get();
    echo '<div class="wrap allspice-admin">';
    echo '<div class="allspice-admin__stack">';
    allspice_render_marketing_section($s);
    allspice_admin_card_open('CONFIGURATION', 'Connect WordPress to Allspice', true);
    echo '<form method="post" action="options.php">';
    settings_fields(ALLSPICE_SETTINGS_GROUP);
    do_settings_sections(ALLSPICE_SETTINGS_PAGE);
    submit_button('Save Changes');
    echo '</form>';
    allspice_render_premium_banner($s);
    echo '<div class="allspice-admin__divider"></div>';
    echo '<p class="allspice-admin__sectionTitle" style="margin-bottom:6px;">Synchronization</p>';
    echo '<p class="description" style="max-width:700px;margin-top:0;">Manually sync buttons and recipes from Allspice.</p>';
    
    echo '<div class="allspice-sync-actions">';
    echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '">';
    wp_nonce_field(ALLSPICE_SYNC_BUTTONS_SETTINGS_ACTION);
    echo '<input type="hidden" name="action" value="' . esc_attr(ALLSPICE_SYNC_BUTTONS_SETTINGS_ACTION) . '"/>';
    submit_button('Sync Buttons & Settings', 'secondary', 'submit', false);
    echo '</form>';
    echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '">';
    wp_nonce_field(ALLSPICE_SYNC_RECIPES_ACTION);
    echo '<input type="hidden" name="action" value="' . esc_attr(ALLSPICE_SYNC_RECIPES_ACTION) . '"/>';
    submit_button('Sync Recipes', 'secondary', 'submit', false);
    echo '</form>';
    echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '">';
    wp_nonce_field(ALLSPICE_SYNC_ALL_ACTION);
    echo '<input type="hidden" name="action" value="' . esc_attr(ALLSPICE_SYNC_ALL_ACTION) . '"/>';
    submit_button('Sync All', 'primary', 'submit', false);
    echo '</form>';
    echo '</div>';
    
    allspice_admin_card_close();
    allspice_admin_card_open('ADVANCED', 'Status & Debug', false);
    $fmt = get_option('date_format') . ' ' . get_option('time_format');
    echo '<table class="widefat striped" style="margin-top:6px;">';
    echo '<tr><th>Linked Buttons</th><td>' . esc_html((string)($s['buttons_count'] ?? 0)) . '</td></tr>';
    echo '<tr><th>Last Manual Refresh</th><td>' . esc_html(!empty($s['manual_refreshed_at']) ? wp_date($fmt, (int)$s['manual_refreshed_at']) : 'Never') . '</td></tr>';
    echo '<tr><th>Last Cache Refresh</th><td>' . esc_html(!empty($s['cache_fetched_at']) ? wp_date($fmt, (int)$s['cache_fetched_at']) : 'Never') . '</td></tr>';
    echo '<tr><th>Cache TTL</th><td>' . esc_html(round(((int)($s['cache_ttl'] ?? 0)) / 3600, 2)) . ' hours</td></tr>';
    $recipes_count = (int)($s['recipes_count'] ?? 0);
    $recipes_synced_at = (int)($s['recipes_synced_at'] ?? 0);
    $recipes_last_error = (string)($s['recipes_last_error'] ?? '');
    echo '<tr><th>Synced Recipes</th><td>' . esc_html((string)$recipes_count) . '</td></tr>';
    echo '<tr><th>Last Recipe Sync</th><td>' . esc_html($recipes_synced_at ? wp_date($fmt, $recipes_synced_at) : 'Never') . '</td></tr>';
    $tp_synced_at = (int)($s['theme_policies_synced_at'] ?? 0);
    $tp_err = (string)($s['theme_policies_last_error'] ?? '');
    $tp = function_exists('allspice_theme_policies_cache_get') ? allspice_theme_policies_cache_get() : ['theme' => null, 'policy' => null];
    $theme_synced = ($tp_synced_at > 0) && ($tp['theme'] !== null);
    $policies_synced = ($tp_synced_at > 0) && ($tp['policy'] !== null);
    echo '<tr><th>Theme</th><td>' . ($theme_synced ? '<strong>Synced</strong>' : '<em>Not synced</em>') . '</td></tr>';
    echo '<tr><th>Policies</th><td>' . ($policies_synced ? '<strong>Synced</strong>' : '<em>Not synced</em>') . '</td></tr>';
    echo '<tr><th>Last Theme/Policies Sync</th><td>' . esc_html($tp_synced_at ? wp_date($fmt, (int)$tp_synced_at) : 'Never') . '</td></tr>';
    echo '<tr><th>Manual Error</th><td>' . (!empty($s['manual_last_error']) ? esc_html($s['manual_last_error']) : '<em>None</em>') . '</td></tr>';
    echo '<tr><th>Cache Error</th><td>' . (!empty($s['cache_last_error']) ? esc_html($s['cache_last_error']) : '<em>None</em>') . '</td></tr>';
    echo '<tr><th>Recipe Sync Error</th><td>' . (!empty($recipes_last_error) ? esc_html($recipes_last_error) : '<em>None</em>') . '</td></tr>';
    echo '<tr><th>Theme/Policies Sync Error</th><td>' . (!empty($tp_err) ? esc_html($tp_err) : '<em>None</em>') . '</td></tr>';
    $premium = (bool)($s['premium'] ?? true);
    $premium_checked_at = (int)($s['premium_checked_at'] ?? 0);
    echo '<tr><th>Premium</th><td>' . ($premium ? '<strong>Active</strong>' : '<strong style="color:#b42318;">Not active</strong>') . '</td></tr>';
    echo '<tr><th>Last Premium Check</th><td>' . esc_html($premium_checked_at ? wp_date($fmt, $premium_checked_at) : 'Never') . '</td></tr>';
    echo '</table>';
    echo '<div class="allspice-admin__divider"></div>';
    echo '<p class="allspice-admin__sectionTitle" style="margin-bottom:6px;">Debug</p>';
    echo '<p class="description" style="max-width:700px;margin-top:0;">Debug logging is restricted to administrators and requires a nonce. Use this link to enable debug mode on the front end.</p>';
    $debug_url = allspice_admin_debug_url('/');
    echo '<p style="margin:10px 0 8px 0;"><input type="text" class="large-text code" readonly value="' . esc_attr($debug_url) . '" /></p>';
    echo '<p style="margin:0;"><a class="button button-secondary" href="' . esc_url($debug_url) . '" target="_blank" rel="noopener noreferrer">Open Debug Mode</a></p>';
    allspice_admin_card_close();
    echo '</div>';
    echo '</div>';
}
    
function allspice_admin_post_sync_buttons_settings(): void {
    if (!current_user_can('manage_options')) wp_die('Forbidden', 403);
    check_admin_referer(ALLSPICE_SYNC_BUTTONS_SETTINGS_ACTION);
    $now = time();
    $rb = allspice_refresh_buttons_cache(true);
    if (empty($rb['ok'])) {
        allspice_opt_update(['manual_last_error' => (string)($rb['error'] ?? 'Unknown error')]);
        set_transient('allspice_admin_notice', [
            'msg' => 'Sync failed (buttons): ' . (string)($rb['error'] ?? 'Unknown error'),
            'type' => 'error'
        ], 30);
        wp_safe_redirect(admin_url('options-general.php?page=' . ALLSPICE_SETTINGS_PAGE));
        exit;
    }
    $rtp = allspice_refresh_theme_policies_cache(true);
    if (empty($rtp['ok'])) {
        allspice_opt_update([
            'manual_refreshed_at' => $now,
            'manual_last_error' => '',
            'theme_policies_last_error' => (string)($rtp['error'] ?? 'Unknown error'),
            'theme_policies_synced_at' => $now,
        ]);
        set_transient('allspice_admin_notice', [
            'msg' => 'Buttons synced, but settings sync failed: ' . (string)($rtp['error'] ?? 'Unknown error'),
            'type' => 'error'
        ], 30);
        wp_safe_redirect(admin_url('options-general.php?page=' . ALLSPICE_SETTINGS_PAGE));
        exit;
    }
    allspice_opt_update(['manual_refreshed_at' => $now, 'manual_last_error' => '']);
    $buttons_count = (int)($rb['count'] ?? (allspice_opt_get()['buttons_count'] ?? 0));
    $theme_ok = !empty($rtp['theme_ok']);
    $pol_ok = !empty($rtp['policy_ok']);
    set_transient('allspice_admin_notice', [
        'msg' => 'Buttons & settings synced successfully. Buttons: ' . $buttons_count .
                ' | Theme: ' . ($theme_ok ? 'synced' : 'missing') .
                ' | Policies: ' . ($pol_ok ? 'synced' : 'missing'),
        'type' => 'success'
    ], 30);
    wp_safe_redirect(admin_url('options-general.php?page=' . ALLSPICE_SETTINGS_PAGE));
    exit;
}
    
function allspice_admin_post_sync_recipes(): void {
    if (!current_user_can('manage_options')) wp_die('Forbidden', 403);
    check_admin_referer(ALLSPICE_SYNC_RECIPES_ACTION);
    $now = time();
    $rr = allspice_refresh_recipes_cache(true);
    if (empty($rr['ok'])) {
        allspice_opt_update([
            'recipes_last_error' => (string)($rr['error'] ?? 'Unknown error'),
            'recipes_synced_at'  => $now,
        ]);
        set_transient('allspice_admin_notice', [
            'msg' => 'Recipe sync failed: ' . (string)($rr['error'] ?? 'Unknown error'),
            'type' => 'error'
        ], 30);
        wp_safe_redirect(admin_url('options-general.php?page=' . ALLSPICE_SETTINGS_PAGE));
        exit;
    }
    $recipe_count = (int)($rr['count'] ?? 0);
    $premium = (bool)($rr['premium'] ?? true);
    set_transient('allspice_admin_notice', [
        'msg' => 'Recipes synced successfully. Recipes: ' . $recipe_count .
                ($premium ? '' : ' | Premium is not active for this domain.'),
        'type' => $premium ? 'success' : 'warning'
    ], 30);
    wp_safe_redirect(admin_url('options-general.php?page=' . ALLSPICE_SETTINGS_PAGE));
    exit;
}
    
function allspice_admin_post_sync_all(): void {
    if (!current_user_can('manage_options')) wp_die('Forbidden', 403);
    check_admin_referer(ALLSPICE_SYNC_ALL_ACTION);
    $now = time();
    $rb = allspice_refresh_buttons_cache(true);
    if (empty($rb['ok'])) {
        allspice_opt_update(['manual_last_error' => (string)($rb['error'] ?? 'Unknown error')]);
        set_transient('allspice_admin_notice', [
            'msg' => 'Sync failed (buttons): ' . (string)($rb['error'] ?? 'Unknown error'),
            'type' => 'error'
        ], 30);
        wp_safe_redirect(admin_url('options-general.php?page=' . ALLSPICE_SETTINGS_PAGE));
        exit;
    }
    allspice_opt_update(['manual_refreshed_at' => $now, 'manual_last_error' => '']);
    $rr = allspice_refresh_recipes_cache(true);
    if (empty($rr['ok'])) {
        allspice_opt_update([
            'recipes_last_error' => (string)($rr['error'] ?? 'Unknown error'),
            'recipes_synced_at'  => $now,
        ]);
        set_transient('allspice_admin_notice', [
            'msg' => 'Buttons synced, but recipe sync failed: ' . (string)($rr['error'] ?? 'Unknown error'),
                'type' => 'error'
        ], 30);
        wp_safe_redirect(admin_url('options-general.php?page=' . ALLSPICE_SETTINGS_PAGE));
        exit;
    }
    $rtp = allspice_refresh_theme_policies_cache(true);
    if (empty($rtp['ok'])) {
        allspice_opt_update([
            'theme_policies_last_error' => (string)($rtp['error'] ?? 'Unknown error'),
            'theme_policies_synced_at'  => $now,
        ]);
        set_transient('allspice_admin_notice', [
            'msg' => 'Buttons + recipes synced, but settings sync failed: ' . (string)($rtp['error'] ?? 'Unknown error'),
            'type' => 'error'
        ], 30);
        wp_safe_redirect(admin_url('options-general.php?page=' . ALLSPICE_SETTINGS_PAGE));
        exit;
    }
    $buttons_count = (int)($rb['count'] ?? (allspice_opt_get()['buttons_count'] ?? 0));
    $recipe_count = (int)($rr['count'] ?? 0);
    $premium = (bool)($rr['premium'] ?? true);
    $theme_ok = !empty($rtp['theme_ok']);
    $pol_ok = !empty($rtp['policy_ok']);
    set_transient('allspice_admin_notice', [
        'msg'  => 'Synced successfully. Buttons: ' . $buttons_count .
                ' | Recipes: ' . $recipe_count .
                ' | Theme: ' . ($theme_ok ? 'synced' : 'missing') .
                ' | Policies: ' . ($pol_ok ? 'synced' : 'missing') .
                ($premium ? '' : ' | Premium is not active for this domain.'),
        'type' => $premium ? 'success' : 'warning'
    ], 30);
    wp_safe_redirect(admin_url('options-general.php?page=' . ALLSPICE_SETTINGS_PAGE));
    exit;
}
    
add_action('admin_post_' . ALLSPICE_SYNC_BUTTONS_SETTINGS_ACTION, 'allspice_admin_post_sync_buttons_settings');
add_action('admin_post_' . ALLSPICE_SYNC_RECIPES_ACTION, 'allspice_admin_post_sync_recipes');
add_action('admin_post_' . ALLSPICE_SYNC_ALL_ACTION, 'allspice_admin_post_sync_all');