Plugin Directory

Changeset 3490375


Ignore:
Timestamp:
03/24/2026 09:18:30 PM (12 days ago)
Author:
1platform
Message:

Update to version 2.9.0 from GitHub

Location:
1platform-content-ai
Files:
10 added
6 deleted
40 edited
1 copied

Legend:

Unmodified
Added
Removed
  • 1platform-content-ai/tags/2.9.0/.git/FETCH_HEAD

    r3488242 r3490375  
    1 0b507e94120943ce7672264963c81dd809d669f1        branch 'main' of https://github.com/1platformlabs/1platform-content-ai
     1d0f02faf2b07912251edc6729f937622150be15e        branch 'main' of https://github.com/1platformlabs/1platform-content-ai
  • 1platform-content-ai/tags/2.9.0/.git/ORIG_HEAD

    r3488242 r3490375  
    1 0b507e94120943ce7672264963c81dd809d669f1
     1d0f02faf2b07912251edc6729f937622150be15e
  • 1platform-content-ai/tags/2.9.0/.git/config

    r3488242 r3490375  
    1010    auto = 0
    1111[http "https://github.com/"]
    12     extraheader = AUTHORIZATION: basic eC1hY2Nlc3MtdG9rZW46Z2hzXzZmZ1hBU01NQ25ORDF3aHA1aE5Rczl2dVpsRzBYMDNNcUZzVA==
     12    extraheader = AUTHORIZATION: basic eC1hY2Nlc3MtdG9rZW46Z2hzX20yVVRQdDlDdEdZdjVHOXNDZ0ZlelJGTXExV0N0VTNsSHI0Sw==
    1313[branch "main"]
    1414    remote = origin
  • 1platform-content-ai/tags/2.9.0/.git/logs/HEAD

    r3488242 r3490375  
    1 0000000000000000000000000000000000000000 0b507e94120943ce7672264963c81dd809d669f1 runner <runner@runnervm46oaq.2d4yuw4mzllehnbsihy5bpj2mb.cx.internal.cloudapp.net> 1774189052 +0000    checkout: moving from master to main
     10000000000000000000000000000000000000000 d0f02faf2b07912251edc6729f937622150be15e runner <runner@runnervm46oaq.ajpjuzvd0pcu5m1ughqrvmkcxa.bx.internal.cloudapp.net> 1774387066 +0000    checkout: moving from master to main
  • 1platform-content-ai/tags/2.9.0/.git/logs/refs/heads/main

    r3488242 r3490375  
    1 0000000000000000000000000000000000000000 0b507e94120943ce7672264963c81dd809d669f1 runner <runner@runnervm46oaq.2d4yuw4mzllehnbsihy5bpj2mb.cx.internal.cloudapp.net> 1774189052 +0000    branch: Created from refs/remotes/origin/main
     10000000000000000000000000000000000000000 d0f02faf2b07912251edc6729f937622150be15e runner <runner@runnervm46oaq.ajpjuzvd0pcu5m1ughqrvmkcxa.bx.internal.cloudapp.net> 1774387066 +0000    branch: Created from refs/remotes/origin/main
  • 1platform-content-ai/tags/2.9.0/.git/logs/refs/remotes/origin/main

    r3488242 r3490375  
    1 0000000000000000000000000000000000000000 0b507e94120943ce7672264963c81dd809d669f1 runner <runner@runnervm46oaq.2d4yuw4mzllehnbsihy5bpj2mb.cx.internal.cloudapp.net> 1774189052 +0000    fetch --prune --no-recurse-submodules origin +refs/heads/*:refs/remotes/origin/* +refs/tags/*:refs/tags/*: storing head
     10000000000000000000000000000000000000000 d0f02faf2b07912251edc6729f937622150be15e runner <runner@runnervm46oaq.ajpjuzvd0pcu5m1ughqrvmkcxa.bx.internal.cloudapp.net> 1774387066 +0000    fetch --prune --no-recurse-submodules origin +refs/heads/*:refs/remotes/origin/* +refs/tags/*:refs/tags/*: storing head
  • 1platform-content-ai/tags/2.9.0/.git/refs/heads/main

    r3488242 r3490375  
    1 0b507e94120943ce7672264963c81dd809d669f1
     1d0f02faf2b07912251edc6729f937622150be15e
  • 1platform-content-ai/tags/2.9.0/.git/refs/remotes/origin/main

    r3488242 r3490375  
    1 0b507e94120943ce7672264963c81dd809d669f1
     1d0f02faf2b07912251edc6729f937622150be15e
  • 1platform-content-ai/tags/2.9.0/1platform-content-ai.php

    r3488242 r3490375  
    55 * Plugin URI: https://1platform.pro/
    66 * Description: SaaS client for AI-powered content generation, SEO optimization, and site management. All AI processing happens on 1Platform external servers. Includes free local tools: Table of Contents and Internal Links.
    7  * Version: 2.7.0
     7 * Version: 2.9.0
    88 * Author: 1Platform
    99 * License: GPLv2 or later
  • 1platform-content-ai/tags/2.9.0/includes/admin/admin-ai-site-generator.php

    r3488242 r3490375  
    5252                    'page' => 'contai-ai-site-generator',
    5353                    'error' => 1,
    54                     'message' => urlencode( 'Please select a category before starting the site generation process.' ),
     54                    'message' => 'Please select a category before starting the site generation process.',
    5555                ),
    5656                admin_url( 'admin.php' )
     
    6969                    'page' => 'contai-ai-site-generator',
    7070                    'error' => 1,
    71                     'message' => urlencode( 'There is already an active site generation process running.' ),
     71                    'message' => 'There is already an active site generation process running.',
    7272                ),
    7373                admin_url( 'admin.php' )
     
    9393            ),
    9494            'keyword_extraction' => array(
    95                 'source_url' => esc_url_raw( wp_unslash( $_POST['contai_source_url'] ?? '' ) ),
     95                'source_topic' => sanitize_text_field( wp_unslash( $_POST['contai_source_topic'] ?? '' ) ),
    9696                'target_country' => sanitize_text_field( wp_unslash( $_POST['contai_target_country'] ?? 'us' ) ),
    9797                'target_language' => sanitize_text_field( wp_unslash( $_POST['contai_target_language'] ?? 'en' ) ),
     
    129129                    'page' => 'contai-ai-site-generator',
    130130                    'error' => 1,
    131                     'message' => urlencode( 'Failed to start site generation process.' ),
     131                    'message' => 'Failed to start site generation process.',
    132132                ),
    133133                admin_url( 'admin.php' )
     
    141141    if ( ! empty( $_POST['contai_site_category'] ) ) {
    142142        update_option( 'contai_site_category', sanitize_text_field( wp_unslash( $_POST['contai_site_category'] ) ) );
     143    }
     144
     145    // Save AdSense publisher ID immediately so it appears in Ads Manager
     146    // before the background job completes (fixes #12)
     147    $adsense_publisher = sanitize_text_field( wp_unslash( $_POST['contai_adsense_publisher'] ?? '' ) );
     148    if ( ! empty( $adsense_publisher ) && preg_match( '/^pub-\d+$/', $adsense_publisher ) ) {
     149        update_option( 'contai_adsense_publishers', $adsense_publisher );
     150        if ( function_exists( 'contai_generate_adsense_ads' ) ) {
     151            contai_generate_adsense_ads();
     152        }
    143153    }
    144154
     
    170180        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    171181        $msg = isset( $_GET['message'] ) ? sanitize_text_field( wp_unslash( $_GET['message'] ) ) : 'Success';
    172         echo '<div class="notice notice-success is-dismissible"><p>' . esc_html( urldecode( $msg ) ) . '</p></div>';
     182        echo '<div class="notice notice-success is-dismissible"><p>' . esc_html( $msg ) . '</p></div>';
    173183    }
    174184
     
    177187        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    178188        $msg = isset( $_GET['message'] ) ? sanitize_text_field( wp_unslash( $_GET['message'] ) ) : 'Error';
    179         echo '<div class="notice notice-error is-dismissible"><p>' . esc_html( urldecode( $msg ) ) . '</p></div>';
     189        echo '<div class="notice notice-error is-dismissible"><p>' . esc_html( $msg ) . '</p></div>';
    180190    }
    181191
  • 1platform-content-ai/tags/2.9.0/includes/admin/ai-site-generator/site-generator-form.php

    r3488242 r3490375  
    1212    $categories = $category_service->getActiveCategories();
    1313    $saved_category = get_option( 'contai_site_category', '' );
     14    $site_domain    = wp_parse_url( home_url(), PHP_URL_HOST );
     15    $default_email  = 'info@' . preg_replace( '/^www\./', '', $site_domain );
    1416
    1517    ?>
     
    1719        <?php wp_nonce_field( 'contai_site_generator_nonce', 'contai_site_generator_nonce' ); ?>
    1820        <input type="hidden" name="contai_start_site_generation" value="1">
    19 
    2021        <input type="hidden" id="contai_wordpress_theme" name="contai_wordpress_theme" value="astra">
    2122
    2223        <!-- Step 1: Website Identity -->
    23         <div class="contai-wizard-section">
     24        <div class="contai-wizard-section" data-step="1">
    2425            <div class="contai-section-header">
    2526                <div class="contai-step-indicator">
    2627                    <span class="contai-step-number">1</span>
     28                    <span class="contai-step-line"></span>
    2729                </div>
    2830                <div class="contai-section-title-group">
    29                     <h2 class="contai-section-title"><?php esc_html_e( 'Website Identity', '1platform-content-ai' ); ?></h2>
     31                    <h2 class="contai-section-title">
     32                        <span class="dashicons dashicons-admin-site-alt3 contai-section-icon"></span>
     33                        <?php esc_html_e( 'Website Identity', '1platform-content-ai' ); ?>
     34                    </h2>
    3035                    <p class="contai-section-description"><?php esc_html_e( 'Define what your website is about and who it targets.', '1platform-content-ai' ); ?></p>
    3136                </div>
    3237            </div>
    3338            <div class="contai-section-body">
    34                 <div class="contai-form-grid contai-grid-3">
    35                     <div class="contai-form-group">
     39                <div class="contai-form-grid contai-grid-2">
     40                    <div class="contai-form-group contai-span-full">
    3641                        <label for="contai_site_topic" class="contai-label">
    3742                            <?php esc_html_e( 'Site Topic', '1platform-content-ai' ); ?>
    3843                            <span class="contai-required">*</span>
    3944                        </label>
    40                         <input type="text" id="contai_site_topic" name="contai_site_topic" class="contai-input" placeholder="e.g., Technology News" autocomplete="off" required>
     45                        <div class="contai-input-wrap contai-input-icon">
     46                            <span class="dashicons dashicons-edit"></span>
     47                            <input type="text" id="contai_site_topic" name="contai_site_topic" class="contai-input" placeholder="<?php esc_attr_e( 'e.g., Indoor gardening, Personal finance, Pet care...', '1platform-content-ai' ); ?>" autocomplete="off" required>
     48                        </div>
    4149                        <span class="contai-help-text"><?php esc_html_e( 'The main subject of your website', '1platform-content-ai' ); ?></span>
    4250                    </div>
     
    9199                    </div>
    92100
    93                     <div class="contai-form-group contai-span-2">
     101                    <div class="contai-form-group">
    94102                        <label for="contai_adsense_publisher" class="contai-label">
    95103                            <?php esc_html_e( 'AdSense Publisher ID', '1platform-content-ai' ); ?>
    96104                            <span class="contai-required">*</span>
    97105                        </label>
    98                         <input type="text" id="contai_adsense_publisher" name="contai_adsense_publisher" class="contai-input" placeholder="pub-1234567890123456" autocomplete="off" spellcheck="false" required>
    99                         <span class="contai-help-text"><?php esc_html_e( 'Find it in your Google AdSense account under Account > Account information', '1platform-content-ai' ); ?></span>
     106                        <div class="contai-input-wrap contai-input-icon">
     107                            <span class="dashicons dashicons-money-alt"></span>
     108                            <input type="text" id="contai_adsense_publisher" name="contai_adsense_publisher" class="contai-input" placeholder="pub-1234567890123456" autocomplete="off" spellcheck="false" required>
     109                        </div>
     110                        <span class="contai-help-text"><?php esc_html_e( 'Account > Account information in AdSense', '1platform-content-ai' ); ?></span>
    100111                    </div>
    101112                </div>
     
    104115
    105116        <!-- Step 2: Legal Information -->
    106         <div class="contai-wizard-section">
     117        <div class="contai-wizard-section" data-step="2">
    107118            <div class="contai-section-header">
    108119                <div class="contai-step-indicator">
    109120                    <span class="contai-step-number">2</span>
     121                    <span class="contai-step-line"></span>
    110122                </div>
    111123                <div class="contai-section-title-group">
    112                     <h2 class="contai-section-title"><?php esc_html_e( 'Legal Information', '1platform-content-ai' ); ?></h2>
     124                    <h2 class="contai-section-title">
     125                        <span class="dashicons dashicons-shield contai-section-icon"></span>
     126                        <?php esc_html_e( 'Legal Information', '1platform-content-ai' ); ?>
     127                    </h2>
    113128                    <p class="contai-section-description"><?php esc_html_e( 'Used to generate privacy policy, terms of service, and cookie consent.', '1platform-content-ai' ); ?></p>
    114129                </div>
    115130            </div>
    116131            <div class="contai-section-body">
    117                 <div class="contai-form-grid contai-grid-3">
     132                <div class="contai-form-grid contai-grid-2">
    118133                    <div class="contai-form-group">
    119134                        <label for="contai_legal_owner" class="contai-label">
     
    121136                            <span class="contai-required">*</span>
    122137                        </label>
    123                         <input type="text" id="contai_legal_owner" name="contai_legal_owner" class="contai-input" placeholder="<?php esc_attr_e( 'John Doe', '1platform-content-ai' ); ?>" autocomplete="name" required>
     138                        <div class="contai-input-wrap contai-input-icon">
     139                            <span class="dashicons dashicons-businessperson"></span>
     140                            <input type="text" id="contai_legal_owner" name="contai_legal_owner" class="contai-input" placeholder="<?php esc_attr_e( 'John Doe', '1platform-content-ai' ); ?>" autocomplete="name" required>
     141                        </div>
    124142                    </div>
    125143
     
    129147                            <span class="contai-required">*</span>
    130148                        </label>
    131                         <input type="email" id="contai_legal_email" name="contai_legal_email" class="contai-input" placeholder="<?php esc_attr_e( 'contact@example.com', '1platform-content-ai' ); ?>" autocomplete="email" spellcheck="false" required>
     149                        <div class="contai-input-wrap contai-input-icon">
     150                            <span class="dashicons dashicons-email"></span>
     151                            <input type="email" id="contai_legal_email" name="contai_legal_email" class="contai-input" value="<?php echo esc_attr( $default_email ); ?>" placeholder="<?php esc_attr_e( 'info@domain.com', '1platform-content-ai' ); ?>" autocomplete="email" spellcheck="false" required>
     152                        </div>
    132153                    </div>
    133154
     
    137158                            <span class="contai-required">*</span>
    138159                        </label>
    139                         <input type="text" id="contai_legal_activity" name="contai_legal_activity" class="contai-input" placeholder="<?php esc_attr_e( 'e.g., Digital publishing', '1platform-content-ai' ); ?>" autocomplete="organization-title" required>
    140                     </div>
    141 
    142                     <div class="contai-form-group contai-span-full">
     160                        <div class="contai-input-wrap contai-input-icon">
     161                            <span class="dashicons dashicons-building"></span>
     162                            <input type="text" id="contai_legal_activity" name="contai_legal_activity" class="contai-input" placeholder="<?php esc_attr_e( 'e.g., Digital publishing', '1platform-content-ai' ); ?>" autocomplete="organization-title" required>
     163                        </div>
     164                    </div>
     165
     166                    <div class="contai-form-group">
    143167                        <label for="contai_legal_address" class="contai-label">
    144168                            <?php esc_html_e( 'Business Address', '1platform-content-ai' ); ?>
    145169                            <span class="contai-required">*</span>
    146170                        </label>
    147                         <input type="text" id="contai_legal_address" name="contai_legal_address" class="contai-input" placeholder="<?php esc_attr_e( '123 Main St, City, Country', '1platform-content-ai' ); ?>" autocomplete="street-address" required>
     171                        <div class="contai-input-wrap contai-input-icon">
     172                            <span class="dashicons dashicons-location"></span>
     173                            <input type="text" id="contai_legal_address" name="contai_legal_address" class="contai-input" placeholder="<?php esc_attr_e( '123 Main St, City, Country', '1platform-content-ai' ); ?>" autocomplete="street-address" required>
     174                        </div>
    148175                    </div>
    149176                </div>
     
    152179
    153180        <!-- Step 3: Content Generation Settings -->
    154         <div class="contai-wizard-section">
     181        <div class="contai-wizard-section" data-step="3">
    155182            <div class="contai-section-header">
    156183                <div class="contai-step-indicator">
     
    158185                </div>
    159186                <div class="contai-section-title-group">
    160                     <h2 class="contai-section-title"><?php esc_html_e( 'Content Generation', '1platform-content-ai' ); ?></h2>
     187                    <h2 class="contai-section-title">
     188                        <span class="dashicons dashicons-admin-post contai-section-icon"></span>
     189                        <?php esc_html_e( 'Content Generation', '1platform-content-ai' ); ?>
     190                    </h2>
    161191                    <p class="contai-section-description"><?php esc_html_e( 'Configure how AI generates your website content, keywords, and images.', '1platform-content-ai' ); ?></p>
    162192                </div>
    163193            </div>
    164194            <div class="contai-section-body">
    165                 <div class="contai-form-grid contai-grid-3">
     195                <div class="contai-form-grid contai-grid-2">
    166196                    <div class="contai-form-group contai-span-full">
    167                         <label for="contai_source_url" class="contai-label">
    168                             <?php esc_html_e( 'Competitor Website (for keyword extraction)', '1platform-content-ai' ); ?>
    169                             <span class="contai-required">*</span>
    170                         </label>
    171                         <input type="url" id="contai_source_url" name="contai_source_url" class="contai-input" placeholder="https://example.com" autocomplete="url" spellcheck="false" required>
    172                         <span class="contai-help-text"><?php esc_html_e( 'We\'ll analyze this site to extract relevant keywords for your content.', '1platform-content-ai' ); ?></span>
     197                        <label for="contai_source_topic" class="contai-label">
     198                            <?php esc_html_e( 'Keyword Topic', '1platform-content-ai' ); ?>
     199                            <span class="contai-required">*</span>
     200                        </label>
     201                        <div class="contai-input-wrap contai-input-icon">
     202                            <span class="dashicons dashicons-search"></span>
     203                            <input type="text" id="contai_source_topic" name="contai_source_topic" class="contai-input" placeholder="<?php esc_attr_e( 'e.g., indoor plants care, home workout routines...', '1platform-content-ai' ); ?>" autocomplete="off" spellcheck="false" required>
     204                        </div>
     205                        <span class="contai-help-text"><?php esc_html_e( 'We\'ll research this topic to extract relevant keywords for your content.', '1platform-content-ai' ); ?></span>
    173206                    </div>
    174207
     
    191224                    <div class="contai-form-group">
    192225                        <label for="contai_image_provider" class="contai-label">
    193                             <?php esc_html_e( 'Image Provider', '1platform-content-ai' ); ?>
     226                            <?php esc_html_e( 'Image Source', '1platform-content-ai' ); ?>
    194227                            <span class="contai-required">*</span>
    195228                        </label>
     
    206239        <div class="contai-wizard-section contai-wizard-submit">
    207240            <div class="contai-section-body">
    208                 <div class="contai-notice contai-notice-info">
    209                     <span class="dashicons dashicons-clock"></span>
    210                     <div>
    211                         <strong><?php esc_html_e( 'Background Process', '1platform-content-ai' ); ?></strong>
    212                         <p><?php esc_html_e( 'This process runs in the background and may take several hours depending on the number of posts. You can safely close this page and check back later.', '1platform-content-ai' ); ?></p>
    213                     </div>
    214                 </div>
    215 
    216                 <div class="contai-submit-area">
    217                     <button type="submit" id="contai_submit_btn" class="contai-btn contai-btn-primary contai-btn-lg">
    218                         <span class="dashicons dashicons-controls-play"></span>
    219                         <?php esc_html_e( 'Start Site Generation', '1platform-content-ai' ); ?>
    220                     </button>
     241                <div class="contai-launch-card">
     242                    <div class="contai-launch-info">
     243                        <div class="contai-launch-icon">
     244                            <span class="dashicons dashicons-clock"></span>
     245                        </div>
     246                        <div class="contai-launch-text">
     247                            <strong><?php esc_html_e( 'Background Process', '1platform-content-ai' ); ?></strong>
     248                            <p><?php esc_html_e( 'Generation runs in the background. You can safely close this page and check progress later.', '1platform-content-ai' ); ?></p>
     249                        </div>
     250                    </div>
     251                    <div class="contai-submit-area">
     252                        <button type="submit" id="contai_submit_btn" class="contai-btn contai-btn-primary contai-btn-lg">
     253                            <span class="dashicons dashicons-controls-play"></span>
     254                            <?php esc_html_e( 'Launch Site Generation', '1platform-content-ai' ); ?>
     255                        </button>
     256                    </div>
    221257                </div>
    222258            </div>
  • 1platform-content-ai/tags/2.9.0/includes/admin/assets/css/admin-ai-site-generator.css

    r3488242 r3490375  
    11/* ========================================================
    2    Site Wizard - Modern Admin UI
     2   Site Wizard — Refined Admin UI
     3   Design: Precision minimalism with connected timeline
    34   ======================================================== */
    45
    56/* -- Wizard Wrapper -- */
    67.contai-wizard-wrap {
    7     max-width: 960px;
    8     margin: 20px auto 40px;
     8    max-width: 860px;
     9    margin: 24px auto 48px;
    910    padding: 0;
    1011    background: transparent;
     
    1617/* -- Hero Header -- */
    1718.contai-wizard-header {
    18     background: linear-gradient(135deg, #1e3a5f 0%, var(--contai-primary) 50%, var(--contai-primary-light) 100%);
     19    background: linear-gradient(135deg, #0f172a 0%, #1e3a5f 40%, var(--contai-primary) 100%);
    1920    border-radius: var(--contai-radius-lg) var(--contai-radius-lg) 0 0;
    20     padding: 32px 36px;
     21    padding: 36px 40px;
    2122    margin-bottom: 0;
    2223    position: relative;
     
    2728    content: '';
    2829    position: absolute;
    29     top: -40%;
    30     right: -10%;
    31     width: 300px;
    32     height: 300px;
    33     background: radial-gradient(circle, rgba(255,255,255,0.08) 0%, transparent 70%);
    34     border-radius: 50%;
     30    inset: 0;
     31    background:
     32        radial-gradient(ellipse 600px 400px at 80% 20%, rgba(59, 130, 246, 0.2) 0%, transparent 70%),
     33        radial-gradient(ellipse 300px 300px at 20% 80%, rgba(37, 99, 235, 0.1) 0%, transparent 70%);
    3534    pointer-events: none;
    3635}
     
    3938    content: '';
    4039    position: absolute;
    41     bottom: -60%;
    42     left: 20%;
    43     width: 200px;
    44     height: 200px;
    45     background: radial-gradient(circle, rgba(255,255,255,0.05) 0%, transparent 70%);
    46     border-radius: 50%;
     40    inset: 0;
     41    background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.03'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
    4742    pointer-events: none;
    4843}
     
    5752
    5853.contai-wizard-icon {
    59     width: 56px;
    60     height: 56px;
    61     background: rgba(255, 255, 255, 0.15);
    62     border-radius: 14px;
     54    width: 52px;
     55    height: 52px;
     56    background: rgba(255, 255, 255, 0.1);
     57    border-radius: var(--contai-radius-lg);
    6358    display: flex;
    6459    align-items: center;
    6560    justify-content: center;
    6661    flex-shrink: 0;
    67     backdrop-filter: blur(10px);
    68     border: 1px solid rgba(255, 255, 255, 0.2);
     62    backdrop-filter: blur(12px);
     63    border: 1px solid rgba(255, 255, 255, 0.15);
    6964}
    7065
    7166.contai-wizard-icon .dashicons {
    72     font-size: 28px;
    73     width: 28px;
    74     height: 28px;
     67    font-size: 26px;
     68    width: 26px;
     69    height: 26px;
    7570    color: #fff;
    7671}
    7772
    7873.contai-wizard-header-text h1 {
    79     margin: 0 0 6px 0;
    80     font-size: 26px;
     74    margin: 0 0 4px 0;
     75    font-size: 22px;
    8176    font-weight: 700;
    8277    color: #fff;
    83     letter-spacing: -0.3px;
     78    letter-spacing: -0.4px;
    8479    line-height: 1.2;
    8580    padding: 0;
     
    8883.contai-wizard-header-text p {
    8984    margin: 0;
    90     color: rgba(255, 255, 255, 0.85);
    91     font-size: 14px;
     85    color: rgba(255, 255, 255, 0.7);
     86    font-size: 13.5px;
    9287    line-height: 1.5;
    93     max-width: 520px;
     88    max-width: 480px;
    9489}
    9590
    9691/* -- Form Container -- */
    9792.contai-site-generator-form {
    98     background: var(--contai-neutral-100);
     93    background: var(--contai-neutral-50);
    9994    border-radius: 0 0 var(--contai-radius-lg) var(--contai-radius-lg);
    100     padding: 28px;
     95    padding: 32px;
    10196    display: flex;
    10297    flex-direction: column;
    103     gap: 24px;
     98    gap: 0;
    10499}
    105100
     
    107102.contai-wizard-section {
    108103    background: #fff;
    109     border-radius: var(--contai-radius-md);
     104    border-radius: var(--contai-radius-lg);
    110105    border: 1px solid var(--contai-neutral-200);
    111106    overflow: hidden;
    112     transition: box-shadow var(--contai-duration-fast) ease;
     107    transition: border-color var(--contai-duration-fast) ease,
     108                box-shadow var(--contai-duration-fast) ease;
     109    margin-bottom: 0;
     110    position: relative;
     111}
     112
     113.contai-wizard-section:not(.contai-wizard-submit) {
     114    margin-bottom: 24px;
    113115}
    114116
    115117.contai-wizard-section:hover {
     118    border-color: var(--contai-neutral-300);
    116119    box-shadow: var(--contai-shadow-sm);
    117120}
    118121
    119 /* -- Section Header with Step Indicator -- */
     122.contai-wizard-section:focus-within {
     123    border-color: var(--contai-primary-border);
     124    box-shadow: 0 0 0 3px var(--contai-primary-glow), var(--contai-shadow-sm);
     125}
     126
     127/* -- Section Header with Timeline -- */
    120128.contai-section-header {
    121129    display: flex;
    122130    align-items: flex-start;
    123131    gap: 16px;
    124     padding: 20px 24px;
    125     border-bottom: 1px solid var(--contai-neutral-200);
    126     background: var(--contai-neutral-50);
     132    padding: 20px 24px 16px;
     133    background: #fff;
     134    border-bottom: none;
    127135}
    128136
    129137.contai-step-indicator {
     138    display: flex;
     139    flex-direction: column;
     140    align-items: center;
    130141    flex-shrink: 0;
     142    gap: 0;
    131143}
    132144
     
    135147    align-items: center;
    136148    justify-content: center;
    137     width: 36px;
    138     height: 36px;
    139     background: linear-gradient(135deg, var(--contai-primary), var(--contai-primary-dark));
     149    width: 32px;
     150    height: 32px;
     151    background: var(--contai-primary);
    140152    color: #fff;
    141     border-radius: var(--contai-radius-md);
    142     font-size: 15px;
     153    border-radius: 50%;
     154    font-size: 13px;
    143155    font-weight: 700;
    144156    line-height: 1;
     157    position: relative;
     158    z-index: 1;
     159    box-shadow: 0 0 0 4px var(--contai-primary-glow);
     160}
     161
     162.contai-step-line {
     163    display: block;
     164    width: 2px;
     165    height: 16px;
     166    background: var(--contai-neutral-200);
     167    margin-top: 4px;
    145168}
    146169
     
    148171    flex: 1;
    149172    min-width: 0;
    150     padding-top: 2px;
     173    padding-top: 4px;
    151174}
    152175
    153176.contai-section-title {
    154     margin: 0 0 4px 0;
    155     font-size: 17px;
    156     font-weight: 600;
     177    margin: 0 0 2px 0;
     178    font-size: 15px;
     179    font-weight: 650;
    157180    color: var(--contai-neutral-700);
    158181    line-height: 1.3;
     182    letter-spacing: -0.2px;
     183    display: flex;
     184    align-items: center;
     185    gap: 8px;
     186}
     187
     188.contai-section-icon {
     189    font-size: 17px;
     190    width: 17px;
     191    height: 17px;
     192    color: var(--contai-primary);
     193    opacity: 0.85;
    159194}
    160195
    161196.contai-section-description {
    162197    margin: 0;
    163     color: var(--contai-neutral-500);
    164     font-size: 13px;
     198    color: var(--contai-neutral-400);
     199    font-size: 12.5px;
    165200    line-height: 1.5;
    166201}
     
    168203/* -- Section Body -- */
    169204.contai-section-body {
    170     padding: 24px;
     205    padding: 4px 24px 24px;
    171206}
    172207
     
    174209.contai-form-grid {
    175210    display: grid;
    176     gap: 20px;
     211    gap: 18px;
     212}
     213
     214.contai-grid-2 {
     215    grid-template-columns: repeat(2, 1fr);
    177216}
    178217
     
    189228}
    190229
    191 /* -- Form Divider -- */
    192 .contai-form-divider {
    193     height: 1px;
    194     background: var(--contai-neutral-200);
    195     margin: 8px 0;
    196 }
    197 
    198230/* -- Form Group -- */
    199231.contai-form-group {
     
    206238    align-items: center;
    207239    gap: 4px;
    208     font-weight: 600;
    209     font-size: 13px;
     240    font-weight: 550;
     241    font-size: 12.5px;
    210242    margin-bottom: 6px;
    211     color: var(--contai-neutral-700);
     243    color: var(--contai-neutral-600);
    212244    line-height: 1.4;
     245    letter-spacing: 0.01em;
     246    text-transform: uppercase;
    213247}
    214248
     
    216250    color: var(--contai-error);
    217251    font-weight: 700;
    218     font-size: 14px;
     252    font-size: 13px;
     253}
     254
     255/* -- Input Wrapper with Icon -- */
     256.contai-input-wrap {
     257    position: relative;
     258    display: flex;
     259    align-items: center;
     260}
     261
     262.contai-input-icon > .dashicons {
     263    position: absolute;
     264    left: 12px;
     265    top: 50%;
     266    transform: translateY(-50%);
     267    font-size: 16px;
     268    width: 16px;
     269    height: 16px;
     270    color: var(--contai-neutral-400);
     271    pointer-events: none;
     272    transition: color var(--contai-duration-fast) ease;
     273    z-index: 1;
     274}
     275
     276.contai-input-icon > .contai-input {
     277    padding-left: 38px;
     278}
     279
     280.contai-input-icon:focus-within > .dashicons {
     281    color: var(--contai-primary);
    219282}
    220283
     
    223286.contai-select {
    224287    width: 100%;
    225     padding: 10px 14px;
    226     border: 1.5px solid var(--contai-neutral-300);
    227     border-radius: var(--contai-radius-lg);
    228     font-size: 14px;
     288    padding: 9px 13px;
     289    border: 1.5px solid var(--contai-neutral-200);
     290    border-radius: var(--contai-radius-md);
     291    font-size: 13.5px;
    229292    font-family: inherit;
    230293    color: var(--contai-neutral-700);
    231     background: #fff;
    232     transition: border-color var(--contai-duration-fast) ease, box-shadow var(--contai-duration-fast) ease;
     294    background: var(--contai-neutral-50);
     295    transition: border-color var(--contai-duration-fast) ease,
     296                box-shadow var(--contai-duration-fast) ease,
     297                background-color var(--contai-duration-fast) ease;
    233298    -webkit-appearance: none;
    234299    appearance: none;
    235300    box-sizing: border-box;
     301    line-height: 1.5;
    236302}
    237303
    238304.contai-select {
    239     background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23646970' d='M6 8.825L1.175 4 2.238 2.938 6 6.7l3.763-3.763L10.825 4z'/%3E%3C/svg%3E");
     305    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2394a3b8' d='M6 8.825L1.175 4 2.238 2.938 6 6.7l3.763-3.763L10.825 4z'/%3E%3C/svg%3E");
    240306    background-repeat: no-repeat;
    241     background-position: right 14px center;
     307    background-position: right 12px center;
    242308    background-size: 12px;
    243     padding-right: 36px;
     309    background-color: var(--contai-neutral-50);
     310    padding-right: 34px;
    244311    cursor: pointer;
    245312}
     
    247314.contai-input::placeholder {
    248315    color: var(--contai-neutral-400);
     316    font-weight: 400;
    249317}
    250318
    251319.contai-input:hover,
    252320.contai-select:hover {
    253     border-color: var(--contai-neutral-400);
     321    border-color: var(--contai-neutral-300);
     322    background: #fff;
    254323}
    255324
     
    258327    outline: none;
    259328    border-color: var(--contai-primary);
     329    background: #fff;
    260330    box-shadow: 0 0 0 3px var(--contai-primary-glow);
     331}
     332
     333/* Number input refinement */
     334.contai-input[type="number"] {
     335    font-variant-numeric: tabular-nums;
     336    font-family: var(--contai-font-mono);
     337    font-size: 13px;
     338    letter-spacing: -0.01em;
    261339}
    262340
     
    264342.contai-input-error {
    265343    border-color: var(--contai-error) !important;
    266     box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.1) !important;
     344    box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.08) !important;
    267345}
    268346
     
    270348    display: block;
    271349    color: var(--contai-error);
    272     font-size: 12px;
     350    font-size: 11.5px;
    273351    margin-top: 5px;
    274352    font-weight: 500;
     
    279357.contai-help-text {
    280358    display: block;
    281     color: var(--contai-neutral-500);
     359    color: var(--contai-neutral-400);
     360    font-size: 11.5px;
     361    margin-top: 5px;
     362    line-height: 1.5;
     363}
     364
     365/* -- Launch Card (Submit Section) -- */
     366.contai-wizard-submit {
     367    border: none;
     368    background: transparent;
     369    box-shadow: none !important;
     370    margin-top: 4px;
     371}
     372
     373.contai-wizard-submit:hover {
     374    box-shadow: none !important;
     375    border-color: transparent !important;
     376}
     377
     378.contai-wizard-submit:focus-within {
     379    box-shadow: none !important;
     380    border-color: transparent !important;
     381}
     382
     383.contai-wizard-submit .contai-section-body {
     384    padding: 0;
     385}
     386
     387.contai-launch-card {
     388    display: flex;
     389    align-items: center;
     390    justify-content: space-between;
     391    gap: 24px;
     392    padding: 20px 24px;
     393    background: linear-gradient(135deg, var(--contai-neutral-700) 0%, #1e3a5f 100%);
     394    border-radius: var(--contai-radius-lg);
     395    position: relative;
     396    overflow: hidden;
     397}
     398
     399.contai-launch-card::before {
     400    content: '';
     401    position: absolute;
     402    inset: 0;
     403    background:
     404        radial-gradient(ellipse 300px 200px at 90% 50%, rgba(37, 99, 235, 0.15) 0%, transparent 70%);
     405    pointer-events: none;
     406}
     407
     408.contai-launch-info {
     409    display: flex;
     410    align-items: center;
     411    gap: 14px;
     412    position: relative;
     413    z-index: 1;
     414    flex: 1;
     415    min-width: 0;
     416}
     417
     418.contai-launch-icon {
     419    width: 40px;
     420    height: 40px;
     421    background: rgba(255, 255, 255, 0.1);
     422    border-radius: 50%;
     423    display: flex;
     424    align-items: center;
     425    justify-content: center;
     426    flex-shrink: 0;
     427    border: 1px solid rgba(255, 255, 255, 0.1);
     428}
     429
     430.contai-launch-icon .dashicons {
     431    font-size: 18px;
     432    width: 18px;
     433    height: 18px;
     434    color: rgba(255, 255, 255, 0.7);
     435}
     436
     437.contai-launch-text {
     438    flex: 1;
     439    min-width: 0;
     440}
     441
     442.contai-launch-text strong {
     443    display: block;
     444    color: #fff;
     445    font-size: 13px;
     446    font-weight: 600;
     447    margin-bottom: 1px;
     448}
     449
     450.contai-launch-text p {
     451    margin: 0;
     452    color: rgba(255, 255, 255, 0.55);
    282453    font-size: 12px;
    283     margin-top: 6px;
    284454    line-height: 1.5;
    285455}
    286456
    287 /* -- Info Notice -- */
     457.contai-launch-card .contai-submit-area {
     458    position: relative;
     459    z-index: 1;
     460    flex-shrink: 0;
     461}
     462
     463/* -- Buttons -- */
     464.contai-btn {
     465    display: inline-flex;
     466    align-items: center;
     467    justify-content: center;
     468    gap: 8px;
     469    padding: 10px 24px;
     470    font-size: 13px;
     471    font-weight: 600;
     472    font-family: inherit;
     473    border: none;
     474    border-radius: var(--contai-radius-md);
     475    cursor: pointer;
     476    transition: all var(--contai-duration-fast) var(--contai-ease);
     477    line-height: 1;
     478    text-decoration: none;
     479    white-space: nowrap;
     480}
     481
     482.contai-btn .dashicons {
     483    font-size: 16px;
     484    width: 16px;
     485    height: 16px;
     486}
     487
     488.contai-btn-primary {
     489    background: #fff;
     490    color: var(--contai-neutral-700);
     491    box-shadow: var(--contai-shadow-sm);
     492}
     493
     494.contai-btn-primary:hover {
     495    background: #fff;
     496    color: var(--contai-primary);
     497    box-shadow: var(--contai-shadow-md), 0 0 0 1px rgba(255, 255, 255, 0.1);
     498    transform: translateY(-1px);
     499}
     500
     501.contai-btn-primary:active {
     502    transform: translateY(0);
     503    box-shadow: var(--contai-shadow-xs);
     504}
     505
     506.contai-btn-lg {
     507    padding: 12px 28px;
     508    font-size: 13.5px;
     509    border-radius: var(--contai-radius-md);
     510}
     511
     512.contai-btn:disabled,
     513.contai-btn-loading {
     514    opacity: 0.7;
     515    cursor: not-allowed;
     516    transform: none !important;
     517}
     518
     519/* -- Spinner -- */
     520.contai-spinning {
     521    animation: contai-spin 1s linear infinite;
     522}
     523
     524@keyframes contai-spin {
     525    from { transform: rotate(0deg); }
     526    to { transform: rotate(360deg); }
     527}
     528
     529/* -- Legacy Notice (keep for backward compat) -- */
    288530.contai-notice {
    289531    display: flex;
     
    323565}
    324566
    325 /* -- Submit Area -- */
    326 .contai-submit-area {
    327     display: flex;
    328     justify-content: flex-end;
    329 }
    330 
    331 /* -- Buttons -- */
    332 .contai-btn {
    333     display: inline-flex;
    334     align-items: center;
    335     justify-content: center;
    336     gap: 8px;
    337     padding: 12px 28px;
    338     font-size: 14px;
    339     font-weight: 600;
    340     font-family: inherit;
    341     border: none;
    342     border-radius: var(--contai-radius-lg);
    343     cursor: pointer;
    344     transition: all var(--contai-duration-fast) ease;
    345     line-height: 1;
    346     text-decoration: none;
    347 }
    348 
    349 .contai-btn .dashicons {
    350     font-size: 18px;
    351     width: 18px;
    352     height: 18px;
    353 }
    354 
    355 .contai-btn-primary {
    356     background: linear-gradient(135deg, var(--contai-primary), var(--contai-primary-dark));
    357     color: #fff;
    358     box-shadow: var(--contai-shadow-md);
    359 }
    360 
    361 .contai-btn-primary:hover {
    362     background: linear-gradient(135deg, var(--contai-primary-dark), var(--contai-primary-darker));
    363     box-shadow: var(--contai-shadow-lg);
    364     color: #fff;
    365     transform: translateY(-1px);
    366 }
    367 
    368 .contai-btn-primary:active {
    369     transform: translateY(0);
    370     box-shadow: var(--contai-shadow-sm);
    371 }
    372 
    373 .contai-btn-lg {
    374     padding: 14px 36px;
    375     font-size: 15px;
    376 }
    377 
    378 .contai-btn:disabled,
    379 .contai-btn-loading {
    380     opacity: 0.75;
    381     cursor: not-allowed;
    382     transform: none !important;
    383 }
    384 
    385 /* -- Spinner -- */
    386 .contai-spinning {
    387     animation: contai-spin 1s linear infinite;
    388 }
    389 
    390 @keyframes contai-spin {
    391     from { transform: rotate(0deg); }
    392     to { transform: rotate(360deg); }
    393 }
    394 
    395 /* -- Submit Section Override -- */
    396 .contai-wizard-submit {
    397     border: none;
    398     background: transparent;
    399     box-shadow: none;
    400 }
    401 
    402 .contai-wizard-submit:hover {
    403     box-shadow: none;
    404 }
    405 
    406 .contai-wizard-submit .contai-section-body {
    407     padding: 0;
    408 }
    409 
    410567/* -- Progress Section (Active Job) -- */
    411568.contai-settings-panel {
     
    450607.contai-progress-bar {
    451608    width: 100%;
    452     height: 12px;
     609    height: 10px;
    453610    background-color: var(--contai-neutral-200);
    454     border-radius: var(--contai-radius-md);
     611    border-radius: var(--contai-radius-pill);
    455612    overflow: hidden;
    456613    margin-bottom: 10px;
     
    460617    height: 100%;
    461618    background: linear-gradient(90deg, var(--contai-primary), var(--contai-primary-light));
    462     border-radius: var(--contai-radius-md);
    463     transition: width 0.3s ease;
     619    border-radius: var(--contai-radius-pill);
     620    transition: width 0.4s var(--contai-ease);
    464621}
    465622
     
    467624    text-align: center;
    468625    font-weight: 600;
    469     font-size: 14px;
     626    font-size: 13px;
    470627    color: var(--contai-neutral-700);
     628    font-variant-numeric: tabular-nums;
    471629}
    472630
     
    484642    display: inline-block;
    485643    padding: 3px 10px;
    486     border-radius: 20px;
     644    border-radius: var(--contai-radius-pill);
    487645    font-weight: 600;
    488646    text-transform: uppercase;
    489     font-size: 11px;
    490     letter-spacing: 0.3px;
     647    font-size: 10.5px;
     648    letter-spacing: 0.4px;
    491649}
    492650
     
    517675.contai-completed-steps h3 {
    518676    margin: 0 0 12px;
    519     font-size: 14px;
     677    font-size: 13px;
    520678    font-weight: 600;
    521679    color: var(--contai-neutral-700);
     680    text-transform: uppercase;
     681    letter-spacing: 0.02em;
    522682}
    523683
     
    543703
    544704.contai-completed-steps li .dashicons {
    545     color: #00a32a;
     705    color: var(--contai-success);
    546706    font-size: 18px;
    547707    width: 18px;
     
    562722    background: linear-gradient(135deg, var(--contai-primary), var(--contai-primary-dark));
    563723    border: none;
    564     border-radius: var(--contai-radius-lg);
     724    border-radius: var(--contai-radius-md);
    565725    color: #fff;
    566726    font-size: 13px;
     
    660820    }
    661821
     822    .contai-grid-2,
    662823    .contai-grid-3 {
    663824        grid-template-columns: repeat(2, 1fr);
     
    670831    .contai-span-2 {
    671832        grid-column: 1 / -1;
     833    }
     834
     835    .contai-launch-card {
     836        flex-direction: column;
     837        align-items: stretch;
     838        text-align: center;
     839    }
     840
     841    .contai-launch-info {
     842        flex-direction: column;
     843    }
     844
     845    .contai-submit-area {
     846        display: flex;
     847        justify-content: center;
    672848    }
    673849}
     
    690866
    691867    .contai-wizard-header-text h1 {
    692         font-size: 22px;
     868        font-size: 20px;
    693869    }
    694870
     
    700876        border-radius: 0;
    701877        padding: 16px;
    702         gap: 16px;
    703     }
    704 
    705     .contai-wizard-section {
    706         border-radius: var(--contai-radius-lg);
     878    }
     879
     880    .contai-wizard-section:not(.contai-wizard-submit) {
     881        margin-bottom: 16px;
    707882    }
    708883
    709884    .contai-section-header {
    710         padding: 16px;
     885        padding: 16px 16px 12px;
    711886    }
    712887
    713888    .contai-section-body {
    714         padding: 16px;
    715     }
    716 
     889        padding: 4px 16px 16px;
     890    }
     891
     892    .contai-grid-2,
    717893    .contai-grid-3 {
    718894        grid-template-columns: 1fr;
     
    726902    .contai-select {
    727903        font-size: 16px;
    728         padding: 12px 14px;
    729     }
    730 
    731     .contai-submit-area {
    732         justify-content: stretch;
     904        padding: 11px 13px;
     905    }
     906
     907    .contai-input-icon > .contai-input {
     908        padding-left: 38px;
    733909    }
    734910
    735911    .contai-btn-lg {
    736912        width: 100%;
    737     }
    738 }
     913        justify-content: center;
     914    }
     915
     916    .contai-launch-card {
     917        padding: 16px;
     918        flex-direction: column;
     919        gap: 16px;
     920    }
     921
     922    .contai-launch-info {
     923        flex-direction: column;
     924        text-align: center;
     925    }
     926
     927    .contai-submit-area {
     928        display: flex;
     929    }
     930
     931    .contai-submit-area .contai-btn {
     932        width: 100%;
     933    }
     934}
  • 1platform-content-ai/tags/2.9.0/includes/admin/content-generator/handlers/KeywordExtractionHandler.php

    r3483422 r3490375  
    100100
    101101        $payload = [
    102             'domain' => $validation['domain'],
     102            'topic' => $validation['topic'],
    103103            'country' => $validation['country'],
    104             'lang' => $validation['lang']
     104            'lang' => $validation['lang'],
    105105        ];
    106106
     
    123123            'success' => true,
    124124            'message' => sprintf(
    125                 /* translators: %s: domain name for keyword extraction */
    126                 __('Keyword extraction job has been queued. Domain: %s. You can check the Keywords List page for results.', '1platform-content-ai'),
    127                 $validation['domain']
     125                /* translators: %s: topic name for keyword extraction */
     126                __('Keyword extraction job has been queued for: %s. You can check the Keywords List page for results.', '1platform-content-ai'),
     127                $validation['topic']
    128128            )
    129129        ];
     
    132132    private function validateExtractionRequest(): array {
    133133        // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verified in handleRequest() via verifyNonce().
    134         $domain = esc_url_raw(wp_unslash($_POST['contai_source_url'] ?? ''));
     134        $topic = sanitize_text_field(wp_unslash($_POST['contai_topic'] ?? ''));
    135135        $country = sanitize_text_field(wp_unslash($_POST['contai_country'] ?? ''));
    136136        $lang = sanitize_text_field(wp_unslash($_POST['contai_target_language'] ?? ''));
    137137        // phpcs:enable WordPress.Security.NonceVerification.Missing
    138138
    139         if (empty($domain) || empty($country) || empty($lang)) {
    140             return [
    141                 'valid' => false,
    142                 'message' => __('Please fill in all required fields.', '1platform-content-ai')
    143             ];
     139        if (empty($topic)) {
     140            return ['valid' => false, 'message' => __('Please enter a topic for keyword discovery.', '1platform-content-ai')];
    144141        }
    145 
    146         if (!filter_var($domain, FILTER_VALIDATE_URL)) {
    147             return [
    148                 'valid' => false,
    149                 'message' => __('Please enter a valid URL.', '1platform-content-ai')
    150             ];
     142        if (mb_strlen($topic) < 3 || mb_strlen($topic) > 200) {
     143            return ['valid' => false, 'message' => __('Topic must be between 3 and 200 characters.', '1platform-content-ai')];
    151144        }
    152145
    153146        $validLanguages = ['en', 'es'];
    154147        if (!in_array($lang, $validLanguages, true)) {
    155             return [
    156                 'valid' => false,
    157                 'message' => __('Invalid language selected.', '1platform-content-ai')
    158             ];
     148            return ['valid' => false, 'message' => __('Invalid language selected.', '1platform-content-ai')];
    159149        }
    160150
    161151        $validCountries = ['us', 'es'];
    162152        if (!in_array($country, $validCountries, true)) {
    163             return [
    164                 'valid' => false,
    165                 'message' => __('Invalid country selected.', '1platform-content-ai')
    166             ];
     153            return ['valid' => false, 'message' => __('Invalid country selected.', '1platform-content-ai')];
    167154        }
    168155
    169156        return [
    170157            'valid' => true,
    171             'domain' => $domain,
     158            'topic' => $topic,
    172159            'country' => $country,
    173             'lang' => $lang
     160            'lang' => $lang,
    174161        ];
    175162    }
  • 1platform-content-ai/tags/2.9.0/includes/admin/content-generator/panels/keyword-extractor.php

    r3483422 r3490375  
    2424                    <div class="contai-form-grid contai-grid-2">
    2525                        <div class="contai-form-group">
    26                             <label for="contai_source_url" class="contai-label">
    27                                 <span class="dashicons dashicons-admin-site-alt3"></span>
    28                                 <?php esc_html_e('Source Website', '1platform-content-ai'); ?>
     26                            <label for="contai_topic" class="contai-label">
     27                                <span class="dashicons dashicons-lightbulb"></span>
     28                                <?php esc_html_e('Topic / Theme', '1platform-content-ai'); ?>
    2929                            </label>
    30                             <input type="url" id="contai_source_url" name="contai_source_url" required
    31                                    class="contai-input"
    32                                    placeholder="https://example.com"
     30                            <input type="text" id="contai_topic" name="contai_topic"
     31                                   class="contai-input" required
     32                                   placeholder="<?php esc_attr_e('e.g. plantas de interior, salud medioambiental', '1platform-content-ai'); ?>"
     33                                   minlength="3" maxlength="200"
    3334                                   value="<?php
    3435                                   // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Repopulating form field after submission; value is sanitized and escaped.
    35                                    echo isset($_POST['contai_source_url']) ? esc_attr( sanitize_text_field( wp_unslash( $_POST['contai_source_url'] ) ) ) : ''; ?>">
     36                                   echo isset($_POST['contai_topic']) ? esc_attr( sanitize_text_field( wp_unslash( $_POST['contai_topic'] ) ) ) : ''; ?>">
    3637                            <p class="contai-help-text">
    3738                                <span class="dashicons dashicons-info"></span>
    38                                 <?php esc_html_e('Enter the competitor website URL to analyze', '1platform-content-ai'); ?>
     39                                <?php esc_html_e('Enter a topic or theme to discover keywords from Google (2-4 words work best)', '1platform-content-ai'); ?>
    3940                            </p>
    4041                        </div>
     
    199200            <thead>
    200201                <tr>
    201                     <th><?php esc_html_e('Domain', '1platform-content-ai'); ?></th>
     202                    <th><?php esc_html_e('Topic', '1platform-content-ai'); ?></th>
    202203                    <th><?php esc_html_e('Country', '1platform-content-ai'); ?></th>
    203204                    <th><?php esc_html_e('Language', '1platform-content-ai'); ?></th>
     
    215216
    216217    private function renderJobRow(array $job): void {
    217         $payload = json_decode($job['payload'], true);
    218         $domain = $payload['domain'] ?? __('Unknown', '1platform-content-ai');
     218        $payload = json_decode($job['payload'], true) ?? [];
     219        $topic = $payload['topic'] ?? $payload['domain'] ?? __('Unknown', '1platform-content-ai');
    219220        $country = strtoupper($payload['country'] ?? 'N/A');
    220221        $lang = strtoupper($payload['lang'] ?? 'N/A');
     
    222223        ?>
    223224        <tr>
    224             <td><strong><?php echo esc_html($domain); ?></strong></td>
     225            <td><strong><?php echo esc_html($topic); ?></strong></td>
    225226            <td><?php echo esc_html($country); ?></td>
    226227            <td><?php echo esc_html($lang); ?></td>
     
    234235    }
    235236
    236     private function calculateElapsedTime(string $processedAt): string {
    237         $now = current_time('timestamp');
     237    private function calculateElapsedTime(?string $processedAt): string {
     238        if (empty($processedAt)) {
     239            return __('Just started', '1platform-content-ai');
     240        }
    238241        $processed = strtotime($processedAt);
    239         $diff = $now - $processed;
     242        if ($processed === false) {
     243            return __('Unknown', '1platform-content-ai');
     244        }
     245        $now = time();
     246        $diff = max(0, $now - $processed);
    240247
    241248        $minutes = floor($diff / 60);
  • 1platform-content-ai/tags/2.9.0/includes/services/api/OnePlatformEndpoints.php

    r3483422 r3490375  
    5353    // ── Content Generation ──────────────────────────────────────
    5454    const POSTS_KEYWORDS = '/posts/keywords/';
     55    const POSTS_KEYWORDS_TOPIC = '/posts/keywords/topic/';
    5556    const POSTS_CONTENT = '/posts/content/';
    5657    const POSTS_CONTENT_JOBS = '/posts/content/jobs/';
  • 1platform-content-ai/tags/2.9.0/includes/services/jobs/KeywordExtractionJob.php

    r3483422 r3490375  
    1919    public function handle(array $payload)
    2020    {
     21        $topic = $payload['topic'] ?? '';
    2122        $domain = $payload['domain'] ?? '';
    2223        $country = $payload['country'] ?? '';
    2324        $lang = $payload['lang'] ?? '';
    2425
    25         if (empty($domain) || empty($country) || empty($lang)) {
     26        // Support both topic-based (new) and domain-based (legacy) payloads
     27        $source = !empty($topic) ? $topic : $domain;
     28
     29        if (empty($source) || empty($country) || empty($lang)) {
    2630            return [
    2731                'success' => false,
    28                 'error' => 'Missing required parameters: domain, country, or lang'
     32                'error' => 'Missing required parameters: topic/domain, country, or lang'
    2933            ];
    3034        }
    3135
    32         $result = $this->extractor_service->extractAndSaveKeywords($domain, $country, $lang);
     36        $result = !empty($topic)
     37            ? $this->extractor_service->extractByTopicAndSave($topic, $country, $lang)
     38            : $this->extractor_service->extractAndSaveKeywords($domain, $country, $lang);
    3339
    3440        if ($result->isSuccess()) {
     
    3844                'skipped_count' => $result->getSkippedCount(),
    3945                'total_count' => $result->getTotalCount(),
    40                 'domain' => $domain
     46                'source' => $source,
    4147            ];
    4248        }
     
    4551            'success' => false,
    4652            'error' => $result->getErrorMessage(),
    47             'domain' => $domain
     53            'source' => $source,
    4854        ];
    4955    }
  • 1platform-content-ai/tags/2.9.0/includes/services/jobs/SiteGenerationJob.php

    r3483422 r3490375  
    203203        $service = ContaiKeywordExtractorService::create();
    204204
    205         $domain = $extractionConfig['source_url'] ?? '';
     205        $topic = $extractionConfig['source_topic'] ?? '';
    206206        $country = $extractionConfig['target_country'] ?? 'us';
    207207        $lang = $extractionConfig['target_language'] ?? 'en';
    208208
    209         $result = $service->extractAndSaveKeywords($domain, $country, $lang);
     209        $result = $service->extractByTopicAndSave($topic, $country, $lang);
    210210
    211211        if (!$result->isSuccess()) {
  • 1platform-content-ai/tags/2.9.0/includes/services/keyword/KeywordExtractorService.php

    r3483422 r3490375  
    5757    }
    5858
     59    public function extractByTopicAndSave(
     60        string $topic,
     61        string $country,
     62        string $lang
     63    ): ContaiKeywordExtractionResult {
     64        $response = $this->requestKeywordExtractionByTopic($topic, $country, $lang);
     65
     66        if (!$response->isSuccess()) {
     67            return ContaiKeywordExtractionResult::failure(
     68                $response->getMessage() ?? self::ERROR_API_REQUEST_FAILED
     69            );
     70        }
     71
     72        return $this->processAndSaveKeywords($response);
     73    }
     74
    5975    private function requestKeywordExtraction(
    6076        string $domain,
     
    6985
    7086        return $this->client->post(ContaiOnePlatformEndpoints::POSTS_KEYWORDS, $request_data);
     87    }
     88
     89    private function requestKeywordExtractionByTopic(
     90        string $topic,
     91        string $country,
     92        string $lang
     93    ): ContaiOnePlatformResponse {
     94        $request_data = [
     95            'topic' => $topic,
     96            'country' => $country,
     97            'lang' => $lang,
     98        ];
     99
     100        return $this->client->post(ContaiOnePlatformEndpoints::POSTS_KEYWORDS_TOPIC, $request_data);
    71101    }
    72102
  • 1platform-content-ai/tags/2.9.0/readme.txt

    r3488242 r3490375  
    55Tested up to: 6.9
    66Requires PHP: 7.4
    7 Stable tag: 2.7.0
     7Stable tag: 2.9.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
  • 1platform-content-ai/trunk/.git/FETCH_HEAD

    r3488242 r3490375  
    1 0b507e94120943ce7672264963c81dd809d669f1        branch 'main' of https://github.com/1platformlabs/1platform-content-ai
     1d0f02faf2b07912251edc6729f937622150be15e        branch 'main' of https://github.com/1platformlabs/1platform-content-ai
  • 1platform-content-ai/trunk/.git/ORIG_HEAD

    r3488242 r3490375  
    1 0b507e94120943ce7672264963c81dd809d669f1
     1d0f02faf2b07912251edc6729f937622150be15e
  • 1platform-content-ai/trunk/.git/config

    r3488242 r3490375  
    1010    auto = 0
    1111[http "https://github.com/"]
    12     extraheader = AUTHORIZATION: basic eC1hY2Nlc3MtdG9rZW46Z2hzXzZmZ1hBU01NQ25ORDF3aHA1aE5Rczl2dVpsRzBYMDNNcUZzVA==
     12    extraheader = AUTHORIZATION: basic eC1hY2Nlc3MtdG9rZW46Z2hzX20yVVRQdDlDdEdZdjVHOXNDZ0ZlelJGTXExV0N0VTNsSHI0Sw==
    1313[branch "main"]
    1414    remote = origin
  • 1platform-content-ai/trunk/.git/logs/HEAD

    r3488242 r3490375  
    1 0000000000000000000000000000000000000000 0b507e94120943ce7672264963c81dd809d669f1 runner <runner@runnervm46oaq.2d4yuw4mzllehnbsihy5bpj2mb.cx.internal.cloudapp.net> 1774189052 +0000    checkout: moving from master to main
     10000000000000000000000000000000000000000 d0f02faf2b07912251edc6729f937622150be15e runner <runner@runnervm46oaq.ajpjuzvd0pcu5m1ughqrvmkcxa.bx.internal.cloudapp.net> 1774387066 +0000    checkout: moving from master to main
  • 1platform-content-ai/trunk/.git/logs/refs/heads/main

    r3488242 r3490375  
    1 0000000000000000000000000000000000000000 0b507e94120943ce7672264963c81dd809d669f1 runner <runner@runnervm46oaq.2d4yuw4mzllehnbsihy5bpj2mb.cx.internal.cloudapp.net> 1774189052 +0000    branch: Created from refs/remotes/origin/main
     10000000000000000000000000000000000000000 d0f02faf2b07912251edc6729f937622150be15e runner <runner@runnervm46oaq.ajpjuzvd0pcu5m1ughqrvmkcxa.bx.internal.cloudapp.net> 1774387066 +0000    branch: Created from refs/remotes/origin/main
  • 1platform-content-ai/trunk/.git/logs/refs/remotes/origin/main

    r3488242 r3490375  
    1 0000000000000000000000000000000000000000 0b507e94120943ce7672264963c81dd809d669f1 runner <runner@runnervm46oaq.2d4yuw4mzllehnbsihy5bpj2mb.cx.internal.cloudapp.net> 1774189052 +0000    fetch --prune --no-recurse-submodules origin +refs/heads/*:refs/remotes/origin/* +refs/tags/*:refs/tags/*: storing head
     10000000000000000000000000000000000000000 d0f02faf2b07912251edc6729f937622150be15e runner <runner@runnervm46oaq.ajpjuzvd0pcu5m1ughqrvmkcxa.bx.internal.cloudapp.net> 1774387066 +0000    fetch --prune --no-recurse-submodules origin +refs/heads/*:refs/remotes/origin/* +refs/tags/*:refs/tags/*: storing head
  • 1platform-content-ai/trunk/.git/refs/heads/main

    r3488242 r3490375  
    1 0b507e94120943ce7672264963c81dd809d669f1
     1d0f02faf2b07912251edc6729f937622150be15e
  • 1platform-content-ai/trunk/.git/refs/remotes/origin/main

    r3488242 r3490375  
    1 0b507e94120943ce7672264963c81dd809d669f1
     1d0f02faf2b07912251edc6729f937622150be15e
  • 1platform-content-ai/trunk/1platform-content-ai.php

    r3488242 r3490375  
    55 * Plugin URI: https://1platform.pro/
    66 * Description: SaaS client for AI-powered content generation, SEO optimization, and site management. All AI processing happens on 1Platform external servers. Includes free local tools: Table of Contents and Internal Links.
    7  * Version: 2.7.0
     7 * Version: 2.9.0
    88 * Author: 1Platform
    99 * License: GPLv2 or later
  • 1platform-content-ai/trunk/includes/admin/admin-ai-site-generator.php

    r3488242 r3490375  
    5252                    'page' => 'contai-ai-site-generator',
    5353                    'error' => 1,
    54                     'message' => urlencode( 'Please select a category before starting the site generation process.' ),
     54                    'message' => 'Please select a category before starting the site generation process.',
    5555                ),
    5656                admin_url( 'admin.php' )
     
    6969                    'page' => 'contai-ai-site-generator',
    7070                    'error' => 1,
    71                     'message' => urlencode( 'There is already an active site generation process running.' ),
     71                    'message' => 'There is already an active site generation process running.',
    7272                ),
    7373                admin_url( 'admin.php' )
     
    9393            ),
    9494            'keyword_extraction' => array(
    95                 'source_url' => esc_url_raw( wp_unslash( $_POST['contai_source_url'] ?? '' ) ),
     95                'source_topic' => sanitize_text_field( wp_unslash( $_POST['contai_source_topic'] ?? '' ) ),
    9696                'target_country' => sanitize_text_field( wp_unslash( $_POST['contai_target_country'] ?? 'us' ) ),
    9797                'target_language' => sanitize_text_field( wp_unslash( $_POST['contai_target_language'] ?? 'en' ) ),
     
    129129                    'page' => 'contai-ai-site-generator',
    130130                    'error' => 1,
    131                     'message' => urlencode( 'Failed to start site generation process.' ),
     131                    'message' => 'Failed to start site generation process.',
    132132                ),
    133133                admin_url( 'admin.php' )
     
    141141    if ( ! empty( $_POST['contai_site_category'] ) ) {
    142142        update_option( 'contai_site_category', sanitize_text_field( wp_unslash( $_POST['contai_site_category'] ) ) );
     143    }
     144
     145    // Save AdSense publisher ID immediately so it appears in Ads Manager
     146    // before the background job completes (fixes #12)
     147    $adsense_publisher = sanitize_text_field( wp_unslash( $_POST['contai_adsense_publisher'] ?? '' ) );
     148    if ( ! empty( $adsense_publisher ) && preg_match( '/^pub-\d+$/', $adsense_publisher ) ) {
     149        update_option( 'contai_adsense_publishers', $adsense_publisher );
     150        if ( function_exists( 'contai_generate_adsense_ads' ) ) {
     151            contai_generate_adsense_ads();
     152        }
    143153    }
    144154
     
    170180        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    171181        $msg = isset( $_GET['message'] ) ? sanitize_text_field( wp_unslash( $_GET['message'] ) ) : 'Success';
    172         echo '<div class="notice notice-success is-dismissible"><p>' . esc_html( urldecode( $msg ) ) . '</p></div>';
     182        echo '<div class="notice notice-success is-dismissible"><p>' . esc_html( $msg ) . '</p></div>';
    173183    }
    174184
     
    177187        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    178188        $msg = isset( $_GET['message'] ) ? sanitize_text_field( wp_unslash( $_GET['message'] ) ) : 'Error';
    179         echo '<div class="notice notice-error is-dismissible"><p>' . esc_html( urldecode( $msg ) ) . '</p></div>';
     189        echo '<div class="notice notice-error is-dismissible"><p>' . esc_html( $msg ) . '</p></div>';
    180190    }
    181191
  • 1platform-content-ai/trunk/includes/admin/ai-site-generator/site-generator-form.php

    r3488242 r3490375  
    1212    $categories = $category_service->getActiveCategories();
    1313    $saved_category = get_option( 'contai_site_category', '' );
     14    $site_domain    = wp_parse_url( home_url(), PHP_URL_HOST );
     15    $default_email  = 'info@' . preg_replace( '/^www\./', '', $site_domain );
    1416
    1517    ?>
     
    1719        <?php wp_nonce_field( 'contai_site_generator_nonce', 'contai_site_generator_nonce' ); ?>
    1820        <input type="hidden" name="contai_start_site_generation" value="1">
    19 
    2021        <input type="hidden" id="contai_wordpress_theme" name="contai_wordpress_theme" value="astra">
    2122
    2223        <!-- Step 1: Website Identity -->
    23         <div class="contai-wizard-section">
     24        <div class="contai-wizard-section" data-step="1">
    2425            <div class="contai-section-header">
    2526                <div class="contai-step-indicator">
    2627                    <span class="contai-step-number">1</span>
     28                    <span class="contai-step-line"></span>
    2729                </div>
    2830                <div class="contai-section-title-group">
    29                     <h2 class="contai-section-title"><?php esc_html_e( 'Website Identity', '1platform-content-ai' ); ?></h2>
     31                    <h2 class="contai-section-title">
     32                        <span class="dashicons dashicons-admin-site-alt3 contai-section-icon"></span>
     33                        <?php esc_html_e( 'Website Identity', '1platform-content-ai' ); ?>
     34                    </h2>
    3035                    <p class="contai-section-description"><?php esc_html_e( 'Define what your website is about and who it targets.', '1platform-content-ai' ); ?></p>
    3136                </div>
    3237            </div>
    3338            <div class="contai-section-body">
    34                 <div class="contai-form-grid contai-grid-3">
    35                     <div class="contai-form-group">
     39                <div class="contai-form-grid contai-grid-2">
     40                    <div class="contai-form-group contai-span-full">
    3641                        <label for="contai_site_topic" class="contai-label">
    3742                            <?php esc_html_e( 'Site Topic', '1platform-content-ai' ); ?>
    3843                            <span class="contai-required">*</span>
    3944                        </label>
    40                         <input type="text" id="contai_site_topic" name="contai_site_topic" class="contai-input" placeholder="e.g., Technology News" autocomplete="off" required>
     45                        <div class="contai-input-wrap contai-input-icon">
     46                            <span class="dashicons dashicons-edit"></span>
     47                            <input type="text" id="contai_site_topic" name="contai_site_topic" class="contai-input" placeholder="<?php esc_attr_e( 'e.g., Indoor gardening, Personal finance, Pet care...', '1platform-content-ai' ); ?>" autocomplete="off" required>
     48                        </div>
    4149                        <span class="contai-help-text"><?php esc_html_e( 'The main subject of your website', '1platform-content-ai' ); ?></span>
    4250                    </div>
     
    9199                    </div>
    92100
    93                     <div class="contai-form-group contai-span-2">
     101                    <div class="contai-form-group">
    94102                        <label for="contai_adsense_publisher" class="contai-label">
    95103                            <?php esc_html_e( 'AdSense Publisher ID', '1platform-content-ai' ); ?>
    96104                            <span class="contai-required">*</span>
    97105                        </label>
    98                         <input type="text" id="contai_adsense_publisher" name="contai_adsense_publisher" class="contai-input" placeholder="pub-1234567890123456" autocomplete="off" spellcheck="false" required>
    99                         <span class="contai-help-text"><?php esc_html_e( 'Find it in your Google AdSense account under Account > Account information', '1platform-content-ai' ); ?></span>
     106                        <div class="contai-input-wrap contai-input-icon">
     107                            <span class="dashicons dashicons-money-alt"></span>
     108                            <input type="text" id="contai_adsense_publisher" name="contai_adsense_publisher" class="contai-input" placeholder="pub-1234567890123456" autocomplete="off" spellcheck="false" required>
     109                        </div>
     110                        <span class="contai-help-text"><?php esc_html_e( 'Account > Account information in AdSense', '1platform-content-ai' ); ?></span>
    100111                    </div>
    101112                </div>
     
    104115
    105116        <!-- Step 2: Legal Information -->
    106         <div class="contai-wizard-section">
     117        <div class="contai-wizard-section" data-step="2">
    107118            <div class="contai-section-header">
    108119                <div class="contai-step-indicator">
    109120                    <span class="contai-step-number">2</span>
     121                    <span class="contai-step-line"></span>
    110122                </div>
    111123                <div class="contai-section-title-group">
    112                     <h2 class="contai-section-title"><?php esc_html_e( 'Legal Information', '1platform-content-ai' ); ?></h2>
     124                    <h2 class="contai-section-title">
     125                        <span class="dashicons dashicons-shield contai-section-icon"></span>
     126                        <?php esc_html_e( 'Legal Information', '1platform-content-ai' ); ?>
     127                    </h2>
    113128                    <p class="contai-section-description"><?php esc_html_e( 'Used to generate privacy policy, terms of service, and cookie consent.', '1platform-content-ai' ); ?></p>
    114129                </div>
    115130            </div>
    116131            <div class="contai-section-body">
    117                 <div class="contai-form-grid contai-grid-3">
     132                <div class="contai-form-grid contai-grid-2">
    118133                    <div class="contai-form-group">
    119134                        <label for="contai_legal_owner" class="contai-label">
     
    121136                            <span class="contai-required">*</span>
    122137                        </label>
    123                         <input type="text" id="contai_legal_owner" name="contai_legal_owner" class="contai-input" placeholder="<?php esc_attr_e( 'John Doe', '1platform-content-ai' ); ?>" autocomplete="name" required>
     138                        <div class="contai-input-wrap contai-input-icon">
     139                            <span class="dashicons dashicons-businessperson"></span>
     140                            <input type="text" id="contai_legal_owner" name="contai_legal_owner" class="contai-input" placeholder="<?php esc_attr_e( 'John Doe', '1platform-content-ai' ); ?>" autocomplete="name" required>
     141                        </div>
    124142                    </div>
    125143
     
    129147                            <span class="contai-required">*</span>
    130148                        </label>
    131                         <input type="email" id="contai_legal_email" name="contai_legal_email" class="contai-input" placeholder="<?php esc_attr_e( 'contact@example.com', '1platform-content-ai' ); ?>" autocomplete="email" spellcheck="false" required>
     149                        <div class="contai-input-wrap contai-input-icon">
     150                            <span class="dashicons dashicons-email"></span>
     151                            <input type="email" id="contai_legal_email" name="contai_legal_email" class="contai-input" value="<?php echo esc_attr( $default_email ); ?>" placeholder="<?php esc_attr_e( 'info@domain.com', '1platform-content-ai' ); ?>" autocomplete="email" spellcheck="false" required>
     152                        </div>
    132153                    </div>
    133154
     
    137158                            <span class="contai-required">*</span>
    138159                        </label>
    139                         <input type="text" id="contai_legal_activity" name="contai_legal_activity" class="contai-input" placeholder="<?php esc_attr_e( 'e.g., Digital publishing', '1platform-content-ai' ); ?>" autocomplete="organization-title" required>
    140                     </div>
    141 
    142                     <div class="contai-form-group contai-span-full">
     160                        <div class="contai-input-wrap contai-input-icon">
     161                            <span class="dashicons dashicons-building"></span>
     162                            <input type="text" id="contai_legal_activity" name="contai_legal_activity" class="contai-input" placeholder="<?php esc_attr_e( 'e.g., Digital publishing', '1platform-content-ai' ); ?>" autocomplete="organization-title" required>
     163                        </div>
     164                    </div>
     165
     166                    <div class="contai-form-group">
    143167                        <label for="contai_legal_address" class="contai-label">
    144168                            <?php esc_html_e( 'Business Address', '1platform-content-ai' ); ?>
    145169                            <span class="contai-required">*</span>
    146170                        </label>
    147                         <input type="text" id="contai_legal_address" name="contai_legal_address" class="contai-input" placeholder="<?php esc_attr_e( '123 Main St, City, Country', '1platform-content-ai' ); ?>" autocomplete="street-address" required>
     171                        <div class="contai-input-wrap contai-input-icon">
     172                            <span class="dashicons dashicons-location"></span>
     173                            <input type="text" id="contai_legal_address" name="contai_legal_address" class="contai-input" placeholder="<?php esc_attr_e( '123 Main St, City, Country', '1platform-content-ai' ); ?>" autocomplete="street-address" required>
     174                        </div>
    148175                    </div>
    149176                </div>
     
    152179
    153180        <!-- Step 3: Content Generation Settings -->
    154         <div class="contai-wizard-section">
     181        <div class="contai-wizard-section" data-step="3">
    155182            <div class="contai-section-header">
    156183                <div class="contai-step-indicator">
     
    158185                </div>
    159186                <div class="contai-section-title-group">
    160                     <h2 class="contai-section-title"><?php esc_html_e( 'Content Generation', '1platform-content-ai' ); ?></h2>
     187                    <h2 class="contai-section-title">
     188                        <span class="dashicons dashicons-admin-post contai-section-icon"></span>
     189                        <?php esc_html_e( 'Content Generation', '1platform-content-ai' ); ?>
     190                    </h2>
    161191                    <p class="contai-section-description"><?php esc_html_e( 'Configure how AI generates your website content, keywords, and images.', '1platform-content-ai' ); ?></p>
    162192                </div>
    163193            </div>
    164194            <div class="contai-section-body">
    165                 <div class="contai-form-grid contai-grid-3">
     195                <div class="contai-form-grid contai-grid-2">
    166196                    <div class="contai-form-group contai-span-full">
    167                         <label for="contai_source_url" class="contai-label">
    168                             <?php esc_html_e( 'Competitor Website (for keyword extraction)', '1platform-content-ai' ); ?>
    169                             <span class="contai-required">*</span>
    170                         </label>
    171                         <input type="url" id="contai_source_url" name="contai_source_url" class="contai-input" placeholder="https://example.com" autocomplete="url" spellcheck="false" required>
    172                         <span class="contai-help-text"><?php esc_html_e( 'We\'ll analyze this site to extract relevant keywords for your content.', '1platform-content-ai' ); ?></span>
     197                        <label for="contai_source_topic" class="contai-label">
     198                            <?php esc_html_e( 'Keyword Topic', '1platform-content-ai' ); ?>
     199                            <span class="contai-required">*</span>
     200                        </label>
     201                        <div class="contai-input-wrap contai-input-icon">
     202                            <span class="dashicons dashicons-search"></span>
     203                            <input type="text" id="contai_source_topic" name="contai_source_topic" class="contai-input" placeholder="<?php esc_attr_e( 'e.g., indoor plants care, home workout routines...', '1platform-content-ai' ); ?>" autocomplete="off" spellcheck="false" required>
     204                        </div>
     205                        <span class="contai-help-text"><?php esc_html_e( 'We\'ll research this topic to extract relevant keywords for your content.', '1platform-content-ai' ); ?></span>
    173206                    </div>
    174207
     
    191224                    <div class="contai-form-group">
    192225                        <label for="contai_image_provider" class="contai-label">
    193                             <?php esc_html_e( 'Image Provider', '1platform-content-ai' ); ?>
     226                            <?php esc_html_e( 'Image Source', '1platform-content-ai' ); ?>
    194227                            <span class="contai-required">*</span>
    195228                        </label>
     
    206239        <div class="contai-wizard-section contai-wizard-submit">
    207240            <div class="contai-section-body">
    208                 <div class="contai-notice contai-notice-info">
    209                     <span class="dashicons dashicons-clock"></span>
    210                     <div>
    211                         <strong><?php esc_html_e( 'Background Process', '1platform-content-ai' ); ?></strong>
    212                         <p><?php esc_html_e( 'This process runs in the background and may take several hours depending on the number of posts. You can safely close this page and check back later.', '1platform-content-ai' ); ?></p>
    213                     </div>
    214                 </div>
    215 
    216                 <div class="contai-submit-area">
    217                     <button type="submit" id="contai_submit_btn" class="contai-btn contai-btn-primary contai-btn-lg">
    218                         <span class="dashicons dashicons-controls-play"></span>
    219                         <?php esc_html_e( 'Start Site Generation', '1platform-content-ai' ); ?>
    220                     </button>
     241                <div class="contai-launch-card">
     242                    <div class="contai-launch-info">
     243                        <div class="contai-launch-icon">
     244                            <span class="dashicons dashicons-clock"></span>
     245                        </div>
     246                        <div class="contai-launch-text">
     247                            <strong><?php esc_html_e( 'Background Process', '1platform-content-ai' ); ?></strong>
     248                            <p><?php esc_html_e( 'Generation runs in the background. You can safely close this page and check progress later.', '1platform-content-ai' ); ?></p>
     249                        </div>
     250                    </div>
     251                    <div class="contai-submit-area">
     252                        <button type="submit" id="contai_submit_btn" class="contai-btn contai-btn-primary contai-btn-lg">
     253                            <span class="dashicons dashicons-controls-play"></span>
     254                            <?php esc_html_e( 'Launch Site Generation', '1platform-content-ai' ); ?>
     255                        </button>
     256                    </div>
    221257                </div>
    222258            </div>
  • 1platform-content-ai/trunk/includes/admin/assets/css/admin-ai-site-generator.css

    r3488242 r3490375  
    11/* ========================================================
    2    Site Wizard - Modern Admin UI
     2   Site Wizard — Refined Admin UI
     3   Design: Precision minimalism with connected timeline
    34   ======================================================== */
    45
    56/* -- Wizard Wrapper -- */
    67.contai-wizard-wrap {
    7     max-width: 960px;
    8     margin: 20px auto 40px;
     8    max-width: 860px;
     9    margin: 24px auto 48px;
    910    padding: 0;
    1011    background: transparent;
     
    1617/* -- Hero Header -- */
    1718.contai-wizard-header {
    18     background: linear-gradient(135deg, #1e3a5f 0%, var(--contai-primary) 50%, var(--contai-primary-light) 100%);
     19    background: linear-gradient(135deg, #0f172a 0%, #1e3a5f 40%, var(--contai-primary) 100%);
    1920    border-radius: var(--contai-radius-lg) var(--contai-radius-lg) 0 0;
    20     padding: 32px 36px;
     21    padding: 36px 40px;
    2122    margin-bottom: 0;
    2223    position: relative;
     
    2728    content: '';
    2829    position: absolute;
    29     top: -40%;
    30     right: -10%;
    31     width: 300px;
    32     height: 300px;
    33     background: radial-gradient(circle, rgba(255,255,255,0.08) 0%, transparent 70%);
    34     border-radius: 50%;
     30    inset: 0;
     31    background:
     32        radial-gradient(ellipse 600px 400px at 80% 20%, rgba(59, 130, 246, 0.2) 0%, transparent 70%),
     33        radial-gradient(ellipse 300px 300px at 20% 80%, rgba(37, 99, 235, 0.1) 0%, transparent 70%);
    3534    pointer-events: none;
    3635}
     
    3938    content: '';
    4039    position: absolute;
    41     bottom: -60%;
    42     left: 20%;
    43     width: 200px;
    44     height: 200px;
    45     background: radial-gradient(circle, rgba(255,255,255,0.05) 0%, transparent 70%);
    46     border-radius: 50%;
     40    inset: 0;
     41    background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.03'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
    4742    pointer-events: none;
    4843}
     
    5752
    5853.contai-wizard-icon {
    59     width: 56px;
    60     height: 56px;
    61     background: rgba(255, 255, 255, 0.15);
    62     border-radius: 14px;
     54    width: 52px;
     55    height: 52px;
     56    background: rgba(255, 255, 255, 0.1);
     57    border-radius: var(--contai-radius-lg);
    6358    display: flex;
    6459    align-items: center;
    6560    justify-content: center;
    6661    flex-shrink: 0;
    67     backdrop-filter: blur(10px);
    68     border: 1px solid rgba(255, 255, 255, 0.2);
     62    backdrop-filter: blur(12px);
     63    border: 1px solid rgba(255, 255, 255, 0.15);
    6964}
    7065
    7166.contai-wizard-icon .dashicons {
    72     font-size: 28px;
    73     width: 28px;
    74     height: 28px;
     67    font-size: 26px;
     68    width: 26px;
     69    height: 26px;
    7570    color: #fff;
    7671}
    7772
    7873.contai-wizard-header-text h1 {
    79     margin: 0 0 6px 0;
    80     font-size: 26px;
     74    margin: 0 0 4px 0;
     75    font-size: 22px;
    8176    font-weight: 700;
    8277    color: #fff;
    83     letter-spacing: -0.3px;
     78    letter-spacing: -0.4px;
    8479    line-height: 1.2;
    8580    padding: 0;
     
    8883.contai-wizard-header-text p {
    8984    margin: 0;
    90     color: rgba(255, 255, 255, 0.85);
    91     font-size: 14px;
     85    color: rgba(255, 255, 255, 0.7);
     86    font-size: 13.5px;
    9287    line-height: 1.5;
    93     max-width: 520px;
     88    max-width: 480px;
    9489}
    9590
    9691/* -- Form Container -- */
    9792.contai-site-generator-form {
    98     background: var(--contai-neutral-100);
     93    background: var(--contai-neutral-50);
    9994    border-radius: 0 0 var(--contai-radius-lg) var(--contai-radius-lg);
    100     padding: 28px;
     95    padding: 32px;
    10196    display: flex;
    10297    flex-direction: column;
    103     gap: 24px;
     98    gap: 0;
    10499}
    105100
     
    107102.contai-wizard-section {
    108103    background: #fff;
    109     border-radius: var(--contai-radius-md);
     104    border-radius: var(--contai-radius-lg);
    110105    border: 1px solid var(--contai-neutral-200);
    111106    overflow: hidden;
    112     transition: box-shadow var(--contai-duration-fast) ease;
     107    transition: border-color var(--contai-duration-fast) ease,
     108                box-shadow var(--contai-duration-fast) ease;
     109    margin-bottom: 0;
     110    position: relative;
     111}
     112
     113.contai-wizard-section:not(.contai-wizard-submit) {
     114    margin-bottom: 24px;
    113115}
    114116
    115117.contai-wizard-section:hover {
     118    border-color: var(--contai-neutral-300);
    116119    box-shadow: var(--contai-shadow-sm);
    117120}
    118121
    119 /* -- Section Header with Step Indicator -- */
     122.contai-wizard-section:focus-within {
     123    border-color: var(--contai-primary-border);
     124    box-shadow: 0 0 0 3px var(--contai-primary-glow), var(--contai-shadow-sm);
     125}
     126
     127/* -- Section Header with Timeline -- */
    120128.contai-section-header {
    121129    display: flex;
    122130    align-items: flex-start;
    123131    gap: 16px;
    124     padding: 20px 24px;
    125     border-bottom: 1px solid var(--contai-neutral-200);
    126     background: var(--contai-neutral-50);
     132    padding: 20px 24px 16px;
     133    background: #fff;
     134    border-bottom: none;
    127135}
    128136
    129137.contai-step-indicator {
     138    display: flex;
     139    flex-direction: column;
     140    align-items: center;
    130141    flex-shrink: 0;
     142    gap: 0;
    131143}
    132144
     
    135147    align-items: center;
    136148    justify-content: center;
    137     width: 36px;
    138     height: 36px;
    139     background: linear-gradient(135deg, var(--contai-primary), var(--contai-primary-dark));
     149    width: 32px;
     150    height: 32px;
     151    background: var(--contai-primary);
    140152    color: #fff;
    141     border-radius: var(--contai-radius-md);
    142     font-size: 15px;
     153    border-radius: 50%;
     154    font-size: 13px;
    143155    font-weight: 700;
    144156    line-height: 1;
     157    position: relative;
     158    z-index: 1;
     159    box-shadow: 0 0 0 4px var(--contai-primary-glow);
     160}
     161
     162.contai-step-line {
     163    display: block;
     164    width: 2px;
     165    height: 16px;
     166    background: var(--contai-neutral-200);
     167    margin-top: 4px;
    145168}
    146169
     
    148171    flex: 1;
    149172    min-width: 0;
    150     padding-top: 2px;
     173    padding-top: 4px;
    151174}
    152175
    153176.contai-section-title {
    154     margin: 0 0 4px 0;
    155     font-size: 17px;
    156     font-weight: 600;
     177    margin: 0 0 2px 0;
     178    font-size: 15px;
     179    font-weight: 650;
    157180    color: var(--contai-neutral-700);
    158181    line-height: 1.3;
     182    letter-spacing: -0.2px;
     183    display: flex;
     184    align-items: center;
     185    gap: 8px;
     186}
     187
     188.contai-section-icon {
     189    font-size: 17px;
     190    width: 17px;
     191    height: 17px;
     192    color: var(--contai-primary);
     193    opacity: 0.85;
    159194}
    160195
    161196.contai-section-description {
    162197    margin: 0;
    163     color: var(--contai-neutral-500);
    164     font-size: 13px;
     198    color: var(--contai-neutral-400);
     199    font-size: 12.5px;
    165200    line-height: 1.5;
    166201}
     
    168203/* -- Section Body -- */
    169204.contai-section-body {
    170     padding: 24px;
     205    padding: 4px 24px 24px;
    171206}
    172207
     
    174209.contai-form-grid {
    175210    display: grid;
    176     gap: 20px;
     211    gap: 18px;
     212}
     213
     214.contai-grid-2 {
     215    grid-template-columns: repeat(2, 1fr);
    177216}
    178217
     
    189228}
    190229
    191 /* -- Form Divider -- */
    192 .contai-form-divider {
    193     height: 1px;
    194     background: var(--contai-neutral-200);
    195     margin: 8px 0;
    196 }
    197 
    198230/* -- Form Group -- */
    199231.contai-form-group {
     
    206238    align-items: center;
    207239    gap: 4px;
    208     font-weight: 600;
    209     font-size: 13px;
     240    font-weight: 550;
     241    font-size: 12.5px;
    210242    margin-bottom: 6px;
    211     color: var(--contai-neutral-700);
     243    color: var(--contai-neutral-600);
    212244    line-height: 1.4;
     245    letter-spacing: 0.01em;
     246    text-transform: uppercase;
    213247}
    214248
     
    216250    color: var(--contai-error);
    217251    font-weight: 700;
    218     font-size: 14px;
     252    font-size: 13px;
     253}
     254
     255/* -- Input Wrapper with Icon -- */
     256.contai-input-wrap {
     257    position: relative;
     258    display: flex;
     259    align-items: center;
     260}
     261
     262.contai-input-icon > .dashicons {
     263    position: absolute;
     264    left: 12px;
     265    top: 50%;
     266    transform: translateY(-50%);
     267    font-size: 16px;
     268    width: 16px;
     269    height: 16px;
     270    color: var(--contai-neutral-400);
     271    pointer-events: none;
     272    transition: color var(--contai-duration-fast) ease;
     273    z-index: 1;
     274}
     275
     276.contai-input-icon > .contai-input {
     277    padding-left: 38px;
     278}
     279
     280.contai-input-icon:focus-within > .dashicons {
     281    color: var(--contai-primary);
    219282}
    220283
     
    223286.contai-select {
    224287    width: 100%;
    225     padding: 10px 14px;
    226     border: 1.5px solid var(--contai-neutral-300);
    227     border-radius: var(--contai-radius-lg);
    228     font-size: 14px;
     288    padding: 9px 13px;
     289    border: 1.5px solid var(--contai-neutral-200);
     290    border-radius: var(--contai-radius-md);
     291    font-size: 13.5px;
    229292    font-family: inherit;
    230293    color: var(--contai-neutral-700);
    231     background: #fff;
    232     transition: border-color var(--contai-duration-fast) ease, box-shadow var(--contai-duration-fast) ease;
     294    background: var(--contai-neutral-50);
     295    transition: border-color var(--contai-duration-fast) ease,
     296                box-shadow var(--contai-duration-fast) ease,
     297                background-color var(--contai-duration-fast) ease;
    233298    -webkit-appearance: none;
    234299    appearance: none;
    235300    box-sizing: border-box;
     301    line-height: 1.5;
    236302}
    237303
    238304.contai-select {
    239     background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23646970' d='M6 8.825L1.175 4 2.238 2.938 6 6.7l3.763-3.763L10.825 4z'/%3E%3C/svg%3E");
     305    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2394a3b8' d='M6 8.825L1.175 4 2.238 2.938 6 6.7l3.763-3.763L10.825 4z'/%3E%3C/svg%3E");
    240306    background-repeat: no-repeat;
    241     background-position: right 14px center;
     307    background-position: right 12px center;
    242308    background-size: 12px;
    243     padding-right: 36px;
     309    background-color: var(--contai-neutral-50);
     310    padding-right: 34px;
    244311    cursor: pointer;
    245312}
     
    247314.contai-input::placeholder {
    248315    color: var(--contai-neutral-400);
     316    font-weight: 400;
    249317}
    250318
    251319.contai-input:hover,
    252320.contai-select:hover {
    253     border-color: var(--contai-neutral-400);
     321    border-color: var(--contai-neutral-300);
     322    background: #fff;
    254323}
    255324
     
    258327    outline: none;
    259328    border-color: var(--contai-primary);
     329    background: #fff;
    260330    box-shadow: 0 0 0 3px var(--contai-primary-glow);
     331}
     332
     333/* Number input refinement */
     334.contai-input[type="number"] {
     335    font-variant-numeric: tabular-nums;
     336    font-family: var(--contai-font-mono);
     337    font-size: 13px;
     338    letter-spacing: -0.01em;
    261339}
    262340
     
    264342.contai-input-error {
    265343    border-color: var(--contai-error) !important;
    266     box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.1) !important;
     344    box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.08) !important;
    267345}
    268346
     
    270348    display: block;
    271349    color: var(--contai-error);
    272     font-size: 12px;
     350    font-size: 11.5px;
    273351    margin-top: 5px;
    274352    font-weight: 500;
     
    279357.contai-help-text {
    280358    display: block;
    281     color: var(--contai-neutral-500);
     359    color: var(--contai-neutral-400);
     360    font-size: 11.5px;
     361    margin-top: 5px;
     362    line-height: 1.5;
     363}
     364
     365/* -- Launch Card (Submit Section) -- */
     366.contai-wizard-submit {
     367    border: none;
     368    background: transparent;
     369    box-shadow: none !important;
     370    margin-top: 4px;
     371}
     372
     373.contai-wizard-submit:hover {
     374    box-shadow: none !important;
     375    border-color: transparent !important;
     376}
     377
     378.contai-wizard-submit:focus-within {
     379    box-shadow: none !important;
     380    border-color: transparent !important;
     381}
     382
     383.contai-wizard-submit .contai-section-body {
     384    padding: 0;
     385}
     386
     387.contai-launch-card {
     388    display: flex;
     389    align-items: center;
     390    justify-content: space-between;
     391    gap: 24px;
     392    padding: 20px 24px;
     393    background: linear-gradient(135deg, var(--contai-neutral-700) 0%, #1e3a5f 100%);
     394    border-radius: var(--contai-radius-lg);
     395    position: relative;
     396    overflow: hidden;
     397}
     398
     399.contai-launch-card::before {
     400    content: '';
     401    position: absolute;
     402    inset: 0;
     403    background:
     404        radial-gradient(ellipse 300px 200px at 90% 50%, rgba(37, 99, 235, 0.15) 0%, transparent 70%);
     405    pointer-events: none;
     406}
     407
     408.contai-launch-info {
     409    display: flex;
     410    align-items: center;
     411    gap: 14px;
     412    position: relative;
     413    z-index: 1;
     414    flex: 1;
     415    min-width: 0;
     416}
     417
     418.contai-launch-icon {
     419    width: 40px;
     420    height: 40px;
     421    background: rgba(255, 255, 255, 0.1);
     422    border-radius: 50%;
     423    display: flex;
     424    align-items: center;
     425    justify-content: center;
     426    flex-shrink: 0;
     427    border: 1px solid rgba(255, 255, 255, 0.1);
     428}
     429
     430.contai-launch-icon .dashicons {
     431    font-size: 18px;
     432    width: 18px;
     433    height: 18px;
     434    color: rgba(255, 255, 255, 0.7);
     435}
     436
     437.contai-launch-text {
     438    flex: 1;
     439    min-width: 0;
     440}
     441
     442.contai-launch-text strong {
     443    display: block;
     444    color: #fff;
     445    font-size: 13px;
     446    font-weight: 600;
     447    margin-bottom: 1px;
     448}
     449
     450.contai-launch-text p {
     451    margin: 0;
     452    color: rgba(255, 255, 255, 0.55);
    282453    font-size: 12px;
    283     margin-top: 6px;
    284454    line-height: 1.5;
    285455}
    286456
    287 /* -- Info Notice -- */
     457.contai-launch-card .contai-submit-area {
     458    position: relative;
     459    z-index: 1;
     460    flex-shrink: 0;
     461}
     462
     463/* -- Buttons -- */
     464.contai-btn {
     465    display: inline-flex;
     466    align-items: center;
     467    justify-content: center;
     468    gap: 8px;
     469    padding: 10px 24px;
     470    font-size: 13px;
     471    font-weight: 600;
     472    font-family: inherit;
     473    border: none;
     474    border-radius: var(--contai-radius-md);
     475    cursor: pointer;
     476    transition: all var(--contai-duration-fast) var(--contai-ease);
     477    line-height: 1;
     478    text-decoration: none;
     479    white-space: nowrap;
     480}
     481
     482.contai-btn .dashicons {
     483    font-size: 16px;
     484    width: 16px;
     485    height: 16px;
     486}
     487
     488.contai-btn-primary {
     489    background: #fff;
     490    color: var(--contai-neutral-700);
     491    box-shadow: var(--contai-shadow-sm);
     492}
     493
     494.contai-btn-primary:hover {
     495    background: #fff;
     496    color: var(--contai-primary);
     497    box-shadow: var(--contai-shadow-md), 0 0 0 1px rgba(255, 255, 255, 0.1);
     498    transform: translateY(-1px);
     499}
     500
     501.contai-btn-primary:active {
     502    transform: translateY(0);
     503    box-shadow: var(--contai-shadow-xs);
     504}
     505
     506.contai-btn-lg {
     507    padding: 12px 28px;
     508    font-size: 13.5px;
     509    border-radius: var(--contai-radius-md);
     510}
     511
     512.contai-btn:disabled,
     513.contai-btn-loading {
     514    opacity: 0.7;
     515    cursor: not-allowed;
     516    transform: none !important;
     517}
     518
     519/* -- Spinner -- */
     520.contai-spinning {
     521    animation: contai-spin 1s linear infinite;
     522}
     523
     524@keyframes contai-spin {
     525    from { transform: rotate(0deg); }
     526    to { transform: rotate(360deg); }
     527}
     528
     529/* -- Legacy Notice (keep for backward compat) -- */
    288530.contai-notice {
    289531    display: flex;
     
    323565}
    324566
    325 /* -- Submit Area -- */
    326 .contai-submit-area {
    327     display: flex;
    328     justify-content: flex-end;
    329 }
    330 
    331 /* -- Buttons -- */
    332 .contai-btn {
    333     display: inline-flex;
    334     align-items: center;
    335     justify-content: center;
    336     gap: 8px;
    337     padding: 12px 28px;
    338     font-size: 14px;
    339     font-weight: 600;
    340     font-family: inherit;
    341     border: none;
    342     border-radius: var(--contai-radius-lg);
    343     cursor: pointer;
    344     transition: all var(--contai-duration-fast) ease;
    345     line-height: 1;
    346     text-decoration: none;
    347 }
    348 
    349 .contai-btn .dashicons {
    350     font-size: 18px;
    351     width: 18px;
    352     height: 18px;
    353 }
    354 
    355 .contai-btn-primary {
    356     background: linear-gradient(135deg, var(--contai-primary), var(--contai-primary-dark));
    357     color: #fff;
    358     box-shadow: var(--contai-shadow-md);
    359 }
    360 
    361 .contai-btn-primary:hover {
    362     background: linear-gradient(135deg, var(--contai-primary-dark), var(--contai-primary-darker));
    363     box-shadow: var(--contai-shadow-lg);
    364     color: #fff;
    365     transform: translateY(-1px);
    366 }
    367 
    368 .contai-btn-primary:active {
    369     transform: translateY(0);
    370     box-shadow: var(--contai-shadow-sm);
    371 }
    372 
    373 .contai-btn-lg {
    374     padding: 14px 36px;
    375     font-size: 15px;
    376 }
    377 
    378 .contai-btn:disabled,
    379 .contai-btn-loading {
    380     opacity: 0.75;
    381     cursor: not-allowed;
    382     transform: none !important;
    383 }
    384 
    385 /* -- Spinner -- */
    386 .contai-spinning {
    387     animation: contai-spin 1s linear infinite;
    388 }
    389 
    390 @keyframes contai-spin {
    391     from { transform: rotate(0deg); }
    392     to { transform: rotate(360deg); }
    393 }
    394 
    395 /* -- Submit Section Override -- */
    396 .contai-wizard-submit {
    397     border: none;
    398     background: transparent;
    399     box-shadow: none;
    400 }
    401 
    402 .contai-wizard-submit:hover {
    403     box-shadow: none;
    404 }
    405 
    406 .contai-wizard-submit .contai-section-body {
    407     padding: 0;
    408 }
    409 
    410567/* -- Progress Section (Active Job) -- */
    411568.contai-settings-panel {
     
    450607.contai-progress-bar {
    451608    width: 100%;
    452     height: 12px;
     609    height: 10px;
    453610    background-color: var(--contai-neutral-200);
    454     border-radius: var(--contai-radius-md);
     611    border-radius: var(--contai-radius-pill);
    455612    overflow: hidden;
    456613    margin-bottom: 10px;
     
    460617    height: 100%;
    461618    background: linear-gradient(90deg, var(--contai-primary), var(--contai-primary-light));
    462     border-radius: var(--contai-radius-md);
    463     transition: width 0.3s ease;
     619    border-radius: var(--contai-radius-pill);
     620    transition: width 0.4s var(--contai-ease);
    464621}
    465622
     
    467624    text-align: center;
    468625    font-weight: 600;
    469     font-size: 14px;
     626    font-size: 13px;
    470627    color: var(--contai-neutral-700);
     628    font-variant-numeric: tabular-nums;
    471629}
    472630
     
    484642    display: inline-block;
    485643    padding: 3px 10px;
    486     border-radius: 20px;
     644    border-radius: var(--contai-radius-pill);
    487645    font-weight: 600;
    488646    text-transform: uppercase;
    489     font-size: 11px;
    490     letter-spacing: 0.3px;
     647    font-size: 10.5px;
     648    letter-spacing: 0.4px;
    491649}
    492650
     
    517675.contai-completed-steps h3 {
    518676    margin: 0 0 12px;
    519     font-size: 14px;
     677    font-size: 13px;
    520678    font-weight: 600;
    521679    color: var(--contai-neutral-700);
     680    text-transform: uppercase;
     681    letter-spacing: 0.02em;
    522682}
    523683
     
    543703
    544704.contai-completed-steps li .dashicons {
    545     color: #00a32a;
     705    color: var(--contai-success);
    546706    font-size: 18px;
    547707    width: 18px;
     
    562722    background: linear-gradient(135deg, var(--contai-primary), var(--contai-primary-dark));
    563723    border: none;
    564     border-radius: var(--contai-radius-lg);
     724    border-radius: var(--contai-radius-md);
    565725    color: #fff;
    566726    font-size: 13px;
     
    660820    }
    661821
     822    .contai-grid-2,
    662823    .contai-grid-3 {
    663824        grid-template-columns: repeat(2, 1fr);
     
    670831    .contai-span-2 {
    671832        grid-column: 1 / -1;
     833    }
     834
     835    .contai-launch-card {
     836        flex-direction: column;
     837        align-items: stretch;
     838        text-align: center;
     839    }
     840
     841    .contai-launch-info {
     842        flex-direction: column;
     843    }
     844
     845    .contai-submit-area {
     846        display: flex;
     847        justify-content: center;
    672848    }
    673849}
     
    690866
    691867    .contai-wizard-header-text h1 {
    692         font-size: 22px;
     868        font-size: 20px;
    693869    }
    694870
     
    700876        border-radius: 0;
    701877        padding: 16px;
    702         gap: 16px;
    703     }
    704 
    705     .contai-wizard-section {
    706         border-radius: var(--contai-radius-lg);
     878    }
     879
     880    .contai-wizard-section:not(.contai-wizard-submit) {
     881        margin-bottom: 16px;
    707882    }
    708883
    709884    .contai-section-header {
    710         padding: 16px;
     885        padding: 16px 16px 12px;
    711886    }
    712887
    713888    .contai-section-body {
    714         padding: 16px;
    715     }
    716 
     889        padding: 4px 16px 16px;
     890    }
     891
     892    .contai-grid-2,
    717893    .contai-grid-3 {
    718894        grid-template-columns: 1fr;
     
    726902    .contai-select {
    727903        font-size: 16px;
    728         padding: 12px 14px;
    729     }
    730 
    731     .contai-submit-area {
    732         justify-content: stretch;
     904        padding: 11px 13px;
     905    }
     906
     907    .contai-input-icon > .contai-input {
     908        padding-left: 38px;
    733909    }
    734910
    735911    .contai-btn-lg {
    736912        width: 100%;
    737     }
    738 }
     913        justify-content: center;
     914    }
     915
     916    .contai-launch-card {
     917        padding: 16px;
     918        flex-direction: column;
     919        gap: 16px;
     920    }
     921
     922    .contai-launch-info {
     923        flex-direction: column;
     924        text-align: center;
     925    }
     926
     927    .contai-submit-area {
     928        display: flex;
     929    }
     930
     931    .contai-submit-area .contai-btn {
     932        width: 100%;
     933    }
     934}
  • 1platform-content-ai/trunk/includes/admin/content-generator/handlers/KeywordExtractionHandler.php

    r3483422 r3490375  
    100100
    101101        $payload = [
    102             'domain' => $validation['domain'],
     102            'topic' => $validation['topic'],
    103103            'country' => $validation['country'],
    104             'lang' => $validation['lang']
     104            'lang' => $validation['lang'],
    105105        ];
    106106
     
    123123            'success' => true,
    124124            'message' => sprintf(
    125                 /* translators: %s: domain name for keyword extraction */
    126                 __('Keyword extraction job has been queued. Domain: %s. You can check the Keywords List page for results.', '1platform-content-ai'),
    127                 $validation['domain']
     125                /* translators: %s: topic name for keyword extraction */
     126                __('Keyword extraction job has been queued for: %s. You can check the Keywords List page for results.', '1platform-content-ai'),
     127                $validation['topic']
    128128            )
    129129        ];
     
    132132    private function validateExtractionRequest(): array {
    133133        // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verified in handleRequest() via verifyNonce().
    134         $domain = esc_url_raw(wp_unslash($_POST['contai_source_url'] ?? ''));
     134        $topic = sanitize_text_field(wp_unslash($_POST['contai_topic'] ?? ''));
    135135        $country = sanitize_text_field(wp_unslash($_POST['contai_country'] ?? ''));
    136136        $lang = sanitize_text_field(wp_unslash($_POST['contai_target_language'] ?? ''));
    137137        // phpcs:enable WordPress.Security.NonceVerification.Missing
    138138
    139         if (empty($domain) || empty($country) || empty($lang)) {
    140             return [
    141                 'valid' => false,
    142                 'message' => __('Please fill in all required fields.', '1platform-content-ai')
    143             ];
     139        if (empty($topic)) {
     140            return ['valid' => false, 'message' => __('Please enter a topic for keyword discovery.', '1platform-content-ai')];
    144141        }
    145 
    146         if (!filter_var($domain, FILTER_VALIDATE_URL)) {
    147             return [
    148                 'valid' => false,
    149                 'message' => __('Please enter a valid URL.', '1platform-content-ai')
    150             ];
     142        if (mb_strlen($topic) < 3 || mb_strlen($topic) > 200) {
     143            return ['valid' => false, 'message' => __('Topic must be between 3 and 200 characters.', '1platform-content-ai')];
    151144        }
    152145
    153146        $validLanguages = ['en', 'es'];
    154147        if (!in_array($lang, $validLanguages, true)) {
    155             return [
    156                 'valid' => false,
    157                 'message' => __('Invalid language selected.', '1platform-content-ai')
    158             ];
     148            return ['valid' => false, 'message' => __('Invalid language selected.', '1platform-content-ai')];
    159149        }
    160150
    161151        $validCountries = ['us', 'es'];
    162152        if (!in_array($country, $validCountries, true)) {
    163             return [
    164                 'valid' => false,
    165                 'message' => __('Invalid country selected.', '1platform-content-ai')
    166             ];
     153            return ['valid' => false, 'message' => __('Invalid country selected.', '1platform-content-ai')];
    167154        }
    168155
    169156        return [
    170157            'valid' => true,
    171             'domain' => $domain,
     158            'topic' => $topic,
    172159            'country' => $country,
    173             'lang' => $lang
     160            'lang' => $lang,
    174161        ];
    175162    }
  • 1platform-content-ai/trunk/includes/admin/content-generator/panels/keyword-extractor.php

    r3483422 r3490375  
    2424                    <div class="contai-form-grid contai-grid-2">
    2525                        <div class="contai-form-group">
    26                             <label for="contai_source_url" class="contai-label">
    27                                 <span class="dashicons dashicons-admin-site-alt3"></span>
    28                                 <?php esc_html_e('Source Website', '1platform-content-ai'); ?>
     26                            <label for="contai_topic" class="contai-label">
     27                                <span class="dashicons dashicons-lightbulb"></span>
     28                                <?php esc_html_e('Topic / Theme', '1platform-content-ai'); ?>
    2929                            </label>
    30                             <input type="url" id="contai_source_url" name="contai_source_url" required
    31                                    class="contai-input"
    32                                    placeholder="https://example.com"
     30                            <input type="text" id="contai_topic" name="contai_topic"
     31                                   class="contai-input" required
     32                                   placeholder="<?php esc_attr_e('e.g. plantas de interior, salud medioambiental', '1platform-content-ai'); ?>"
     33                                   minlength="3" maxlength="200"
    3334                                   value="<?php
    3435                                   // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Repopulating form field after submission; value is sanitized and escaped.
    35                                    echo isset($_POST['contai_source_url']) ? esc_attr( sanitize_text_field( wp_unslash( $_POST['contai_source_url'] ) ) ) : ''; ?>">
     36                                   echo isset($_POST['contai_topic']) ? esc_attr( sanitize_text_field( wp_unslash( $_POST['contai_topic'] ) ) ) : ''; ?>">
    3637                            <p class="contai-help-text">
    3738                                <span class="dashicons dashicons-info"></span>
    38                                 <?php esc_html_e('Enter the competitor website URL to analyze', '1platform-content-ai'); ?>
     39                                <?php esc_html_e('Enter a topic or theme to discover keywords from Google (2-4 words work best)', '1platform-content-ai'); ?>
    3940                            </p>
    4041                        </div>
     
    199200            <thead>
    200201                <tr>
    201                     <th><?php esc_html_e('Domain', '1platform-content-ai'); ?></th>
     202                    <th><?php esc_html_e('Topic', '1platform-content-ai'); ?></th>
    202203                    <th><?php esc_html_e('Country', '1platform-content-ai'); ?></th>
    203204                    <th><?php esc_html_e('Language', '1platform-content-ai'); ?></th>
     
    215216
    216217    private function renderJobRow(array $job): void {
    217         $payload = json_decode($job['payload'], true);
    218         $domain = $payload['domain'] ?? __('Unknown', '1platform-content-ai');
     218        $payload = json_decode($job['payload'], true) ?? [];
     219        $topic = $payload['topic'] ?? $payload['domain'] ?? __('Unknown', '1platform-content-ai');
    219220        $country = strtoupper($payload['country'] ?? 'N/A');
    220221        $lang = strtoupper($payload['lang'] ?? 'N/A');
     
    222223        ?>
    223224        <tr>
    224             <td><strong><?php echo esc_html($domain); ?></strong></td>
     225            <td><strong><?php echo esc_html($topic); ?></strong></td>
    225226            <td><?php echo esc_html($country); ?></td>
    226227            <td><?php echo esc_html($lang); ?></td>
     
    234235    }
    235236
    236     private function calculateElapsedTime(string $processedAt): string {
    237         $now = current_time('timestamp');
     237    private function calculateElapsedTime(?string $processedAt): string {
     238        if (empty($processedAt)) {
     239            return __('Just started', '1platform-content-ai');
     240        }
    238241        $processed = strtotime($processedAt);
    239         $diff = $now - $processed;
     242        if ($processed === false) {
     243            return __('Unknown', '1platform-content-ai');
     244        }
     245        $now = time();
     246        $diff = max(0, $now - $processed);
    240247
    241248        $minutes = floor($diff / 60);
  • 1platform-content-ai/trunk/includes/services/api/OnePlatformEndpoints.php

    r3483422 r3490375  
    5353    // ── Content Generation ──────────────────────────────────────
    5454    const POSTS_KEYWORDS = '/posts/keywords/';
     55    const POSTS_KEYWORDS_TOPIC = '/posts/keywords/topic/';
    5556    const POSTS_CONTENT = '/posts/content/';
    5657    const POSTS_CONTENT_JOBS = '/posts/content/jobs/';
  • 1platform-content-ai/trunk/includes/services/jobs/KeywordExtractionJob.php

    r3483422 r3490375  
    1919    public function handle(array $payload)
    2020    {
     21        $topic = $payload['topic'] ?? '';
    2122        $domain = $payload['domain'] ?? '';
    2223        $country = $payload['country'] ?? '';
    2324        $lang = $payload['lang'] ?? '';
    2425
    25         if (empty($domain) || empty($country) || empty($lang)) {
     26        // Support both topic-based (new) and domain-based (legacy) payloads
     27        $source = !empty($topic) ? $topic : $domain;
     28
     29        if (empty($source) || empty($country) || empty($lang)) {
    2630            return [
    2731                'success' => false,
    28                 'error' => 'Missing required parameters: domain, country, or lang'
     32                'error' => 'Missing required parameters: topic/domain, country, or lang'
    2933            ];
    3034        }
    3135
    32         $result = $this->extractor_service->extractAndSaveKeywords($domain, $country, $lang);
     36        $result = !empty($topic)
     37            ? $this->extractor_service->extractByTopicAndSave($topic, $country, $lang)
     38            : $this->extractor_service->extractAndSaveKeywords($domain, $country, $lang);
    3339
    3440        if ($result->isSuccess()) {
     
    3844                'skipped_count' => $result->getSkippedCount(),
    3945                'total_count' => $result->getTotalCount(),
    40                 'domain' => $domain
     46                'source' => $source,
    4147            ];
    4248        }
     
    4551            'success' => false,
    4652            'error' => $result->getErrorMessage(),
    47             'domain' => $domain
     53            'source' => $source,
    4854        ];
    4955    }
  • 1platform-content-ai/trunk/includes/services/jobs/SiteGenerationJob.php

    r3483422 r3490375  
    203203        $service = ContaiKeywordExtractorService::create();
    204204
    205         $domain = $extractionConfig['source_url'] ?? '';
     205        $topic = $extractionConfig['source_topic'] ?? '';
    206206        $country = $extractionConfig['target_country'] ?? 'us';
    207207        $lang = $extractionConfig['target_language'] ?? 'en';
    208208
    209         $result = $service->extractAndSaveKeywords($domain, $country, $lang);
     209        $result = $service->extractByTopicAndSave($topic, $country, $lang);
    210210
    211211        if (!$result->isSuccess()) {
  • 1platform-content-ai/trunk/includes/services/keyword/KeywordExtractorService.php

    r3483422 r3490375  
    5757    }
    5858
     59    public function extractByTopicAndSave(
     60        string $topic,
     61        string $country,
     62        string $lang
     63    ): ContaiKeywordExtractionResult {
     64        $response = $this->requestKeywordExtractionByTopic($topic, $country, $lang);
     65
     66        if (!$response->isSuccess()) {
     67            return ContaiKeywordExtractionResult::failure(
     68                $response->getMessage() ?? self::ERROR_API_REQUEST_FAILED
     69            );
     70        }
     71
     72        return $this->processAndSaveKeywords($response);
     73    }
     74
    5975    private function requestKeywordExtraction(
    6076        string $domain,
     
    6985
    7086        return $this->client->post(ContaiOnePlatformEndpoints::POSTS_KEYWORDS, $request_data);
     87    }
     88
     89    private function requestKeywordExtractionByTopic(
     90        string $topic,
     91        string $country,
     92        string $lang
     93    ): ContaiOnePlatformResponse {
     94        $request_data = [
     95            'topic' => $topic,
     96            'country' => $country,
     97            'lang' => $lang,
     98        ];
     99
     100        return $this->client->post(ContaiOnePlatformEndpoints::POSTS_KEYWORDS_TOPIC, $request_data);
    71101    }
    72102
  • 1platform-content-ai/trunk/readme.txt

    r3488242 r3490375  
    55Tested up to: 6.9
    66Requires PHP: 7.4
    7 Stable tag: 2.7.0
     7Stable tag: 2.9.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
Note: See TracChangeset for help on using the changeset viewer.