Plugin Directory

Changeset 3462124


Ignore:
Timestamp:
02/16/2026 01:18:05 AM (3 days ago)
Author:
rankpilotai
Message:

v1.0.7: Redesigned settings page with modern card-based layout, gradient header with plugin logo, model radio cards, improved navigation and responsive design. Modernized admin CSS for generate button and score badges.

Location:
ai-alt-text-builder/trunk
Files:
1 added
4 edited

Legend:

Unmodified
Added
Removed
  • ai-alt-text-builder/trunk/admin/assets/css/admin.css

    r3401714 r3462124  
    1 /*  RankPilotAI — Admin UI
    2     Shared styles for all settings pages; responsive-ready  */
     1/*!
     2 * AI Alt Text Builder — Admin Styles
     3 * Version: 1.0.7
     4 * Modern UI for Media Library generate button, score badges & list table
     5 */
    36
    4 :root{
    5   --dark:#243746;
    6   --blue:#0984e3;
    7   --blue-light:#0b9dfb;
    8   --border:#ddd;
    9   --bg:#f9f9f9;
     7/* ========== GENERATE BUTTON (Media Library / Attachment) ========== */
     8.aatb-gen {
     9  display: inline-flex;
     10  align-items: center;
     11  justify-content: center;
     12  gap: 6px;
     13  padding: 8px 16px;
     14  font-size: 13px;
     15  font-weight: 600;
     16  line-height: 1.4;
     17  border: none;
     18  border-radius: 6px;
     19  cursor: pointer;
     20  transition: all .2s ease;
     21  background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
     22  color: #fff !important;
     23  box-shadow: 0 1px 3px rgba(37, 99, 235, .25);
     24  text-shadow: none;
     25}
     26.aatb-gen:hover {
     27  background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
     28  box-shadow: 0 2px 8px rgba(37, 99, 235, .35);
     29  transform: translateY(-1px);
     30  color: #fff !important;
     31}
     32.aatb-gen:active {
     33  transform: translateY(0);
     34  box-shadow: 0 1px 2px rgba(37, 99, 235, .2);
     35}
     36.aatb-gen:disabled,
     37.aatb-gen[disabled] {
     38  opacity: .65;
     39  cursor: wait;
     40  transform: none;
     41  box-shadow: none;
     42  animation: aatb-pulse 1.5s ease-in-out infinite;
    1043}
    1144
    12 /*────────  LAYOUT  ────────*/
    13 .aissp-wrapper{
    14   max-width:1180px;
    15   margin:25px 20px 40px 0;
    16   display:flex;
    17   font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", Arial, sans-serif;
    18   font-size:17px;
    19 }
    20 .aissp-sidebar{
    21   width:260px;
    22   background:var(--dark);
    23   border-radius:8px 0 0 8px;
    24   padding:26px 22px;
    25   color:#fff;
    26 }
    27 .aissp-sidebar h2{
    28   font-size:24px;
    29   margin:0 0 20px;
    30   font-weight:700;
    31   border-bottom:1px solid rgba(255,255,255,.15);
    32   padding-bottom:14px;
    33   color:#fff;
    34 }
    35 .aissp-sidebar nav a{
    36   display:block;
    37   padding:12px 14px;
    38   border-radius:6px;
    39   color:#dfe6e8;
    40   text-decoration:none;
    41   font-size:18px;
    42   margin-bottom:10px;
    43   transition:background .2s;
    44 }
    45 .aissp-sidebar nav a.active,
    46 .aissp-sidebar nav a:hover{
    47   background:#2f4d64;
    48   color:var(--blue);
    49 }
    50 .aissp-content{
    51   flex:1;
    52   background:#fff;
    53   border:1px solid var(--border);
    54   border-left:none;
    55   border-radius:0 8px 8px 0;
    56   padding:34px;
    57 }
    58 .aissp-content h2{
    59   margin-top:0;
    60   font-size:30px;
    61   font-weight:700;
    62   color:var(--dark);
    63   line-height:1.15;
     45/* ========== SCORE BADGES ========== */
     46.aatb-score-badge,
     47td.aatb_score span {
     48  display: inline-flex;
     49  align-items: center;
     50  gap: 4px;
     51  font-size: 12px;
     52  padding: 4px 10px;
     53  color: #fff;
     54  border-radius: 6px;
     55  font-weight: 700;
     56  letter-spacing: 0.2px;
     57  line-height: 1.4;
    6458}
    6559
    66 /*────────  INPUTS / BUTTONS  ────────*/
    67 .regular-text{
    68   width:100%;
    69   padding:12px;
    70   font-size:17px;
    71   background:var(--bg);
    72   border:1px solid #ccc;
    73   border-radius:4px;
    74 }
    75 .primary-btn{
    76   background:var(--blue);
    77   color:#fff!important;
    78   border:none;
    79   border-radius:6px;
    80   padding:12px 28px;
    81   font-size:18px;
    82   cursor:pointer;
    83   transition:background .2s,transform .05s;
    84   text-decoration:none;
    85   display:inline-block;
    86 }
    87 .primary-btn:hover{
    88   background:var(--blue-light);
    89   transform:translateY(-1px);
    90   color:#fff!important;
     60/* ========== LIST TABLE SCORE COLUMN ========== */
     61.column-aatb_score {
     62  width: 100px;
    9163}
    9264
    93 .token-view{
    94   display:inline-block;
    95   background:#eef2f6;
    96   padding:6px 12px;
    97   border-radius:4px;
    98   font-size:18px;
     65/* ========== ATTACHMENT EDIT SCREEN ========== */
     66.aatb-post-wrap {
     67  margin-top: 8px;
     68  display: flex;
     69  align-items: center;
     70  gap: 10px;
     71}
     72.aatb-post-wrap button {
     73  min-width: 116px;
    9974}
    10075
    101 .desc,.intro-txt,.description{
    102   font-size:17px;
    103   color:#555;
    104   margin-top:12px;
    105   margin-bottom:0;
     76/* ========== ANIMATIONS ========== */
     77@keyframes aatb-pulse {
     78  0%, 100% { opacity: 1; }
     79  50% { opacity: .6; }
    10680}
    107 .linkpad{margin-bottom:18px;}
    108 .need-key{margin-left:8px;color:#666;font-size:17px;}
    109 
    110 .key-row{
    111   display:flex;
    112   align-items:center;
    113   gap:12px;
    114   margin-top:12px;
    115   max-width:540px;
    116 }
    117 .key-row input{flex:1;}
    118 
    119 .sub-head{
    120   margin:18px 0 6px;
    121   font-size:22px;
    122   color:var(--blue);
    123   font-weight:700;
    124 }
    125 
    126 /*────────  USAGE BOX  ────────*/
    127 .usage-card{
    128   border:1px solid var(--border);
    129   background:#fff;
    130   border-radius:6px;
    131   padding:20px;
    132   margin-top:28px;
    133   max-width:820px;
    134 }
    135 .usage-card h3{
    136   margin:0 0 14px;
    137   font-size:22px;
    138   color:var(--blue);
    139   font-weight:700;
    140 }
    141 .usage-table{
    142   width:100%;
    143   border-collapse:collapse;
    144   font-size:17px;
    145 }
    146 .usage-table th,.usage-table td{
    147   border:1px solid var(--border);
    148   padding:10px;
    149   text-align:left;
    150 }
    151 .bar-bg{
    152   background:#dbeafe;
    153   height:14px;
    154   border-radius:7px;
    155   overflow:hidden;
    156   width:100%;
    157 }
    158 .bar-fill{
    159   background:#3b82f6;
    160   height:100%;
    161 }
    162 
    163 /*────────  FORM  ────────*/
    164 .form-table th{
    165   width:260px;
    166   padding:10px 0;
    167   font-weight:600;
    168   color:var(--dark);
    169 }
    170 .form-table td{padding:10px 0;}
    171 textarea{font-family:inherit;}
    172 
    173 /*────────  MOBILE (<991px)  ────────*/
    174 @media (max-width:991px){
    175   html,body{overflow-x:hidden;}
    176   .aissp-wrapper{
    177     flex-direction:column;
    178     font-size:15px;
    179   }
    180   .aissp-sidebar{
    181     width:100%;
    182     border-radius:8px 8px 0 0;
    183     padding:20px;
    184   }
    185   .aissp-content{
    186     padding:24px;
    187     border-radius:0 0 8px 8px;
    188     overflow-x:hidden;
    189   }
    190   .aissp-sidebar nav a{
    191     font-size:17px;
    192     margin-bottom:6px;
    193     padding:11px 12px;
    194   }
    195   .primary-btn{
    196     width:100%;
    197     text-align:center;
    198     margin-top:12px;
    199   }
    200   .key-row{
    201     flex-direction:column;
    202     gap:10px;
    203   }
    204   .key-row input{width:100%;}
    205   .form-table select{
    206     width:100%;
    207     font-size:15px;
    208     padding:10px;
    209     appearance:auto;
    210     -webkit-appearance:menulist;
    211   }
    212   .usage-card{padding:18px;}
    213   .usage-table,.usage-table thead{display:none;}
    214   .usage-table tbody{display:block;}
    215   .usage-table tr{
    216     display:block;
    217     border:1px solid var(--border);
    218     border-radius:6px;
    219     margin:0 0 18px;
    220     background:#fff;
    221   }
    222   .usage-table td{
    223     display:block;
    224     padding:12px 14px;
    225     border:none;
    226   }
    227   .usage-table td::before{
    228     display:block;
    229     font-weight:600;
    230     color:var(--dark);
    231     margin-bottom:4px;
    232   }
    233   .usage-table td:nth-of-type(1)::before{content:"Website";}
    234   .usage-table td:nth-of-type(2)::before{content:"Usage";}
    235   .usage-table td:nth-of-type(3)::before{content:"Limit";}
    236   .usage-table td:nth-of-type(4)::before{content:"Remaining";}
    237   .usage-table td:nth-of-type(5)::before{content:"Progress";}
    238   .usage-table td:nth-of-type(6)::before{content:"Action";}
    239   .usage-table td:nth-of-type(5) .bar-bg{width:100%;margin-top:2px;}
    240   .usage-table td:nth-of-type(6) .primary-btn{
    241     width:100%;
    242     margin:6px 0 0;
    243   }
    244   .form-table,.form-table tbody,.form-table tr,
    245   .form-table td,.form-table th{
    246     display:block;width:100%;
    247   }
    248   .form-table th{margin-top:18px;}
    249 }
    250 
    251 /*────────  GLOBAL select arrow fix  ────────*/
    252 .aissp-content select,.form-table select{
    253   appearance:none;
    254   -webkit-appearance:none;
    255   -moz-appearance:none;
    256   background:#f9f9f9
    257     url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23243746'%3E%3Cpath d='M7 10l5 5 5-5'/%3E%3C/svg%3E")
    258     no-repeat right 14px center;
    259   background-size:14px 14px;
    260   padding-right:48px;
    261 }
    262 .form-table textarea{width:100%!important}
    263 .aatb-post-wrap button{min-width:116px}      /* prevent button shrinking; keep alignment */
  • ai-alt-text-builder/trunk/admin/settings-page.php

    r3421891 r3462124  
    11<?php
    22/*  AI Alt Text Builder — Settings Page (Admin)
    3  *  Version: 1.0.4
     3 *  Version: 1.0.7
    44 *  -----------------------------------------------------------
    55 *  • Two tabs: (1) Site Key Management, (2) Alt-Text Settings
     
    152152        ? 'https://rankpilotai.com/ai-alt-text-builder-pricing/'
    153153        : 'https://rankpilotai.com/tokens-1/?plugin=ai-alt-text-builder';
     154
     155    $dis = empty( $token ) ? 'disabled' : '';
    154156    ?>
    155     <div class="aissp-wrapper">
    156         <aside class="aissp-sidebar">
    157             <h2>AI Alt Text Builder</h2>
    158             <nav>
    159                 <a href="<?php echo esc_url( add_query_arg('tab','site-key',admin_url('admin.php?page=ai-alt-text-builder')) ); ?>"
    160                    class="<?php echo esc_attr( $tab === 'site-key' ? 'active' : '' ); ?>"><?php echo esc_html__( 'Site Key Management', 'ai-alt-text-builder' ); ?></a>
    161                 <a href="<?php echo esc_url( add_query_arg('tab','alt-settings',admin_url('admin.php?page=ai-alt-text-builder')) ); ?>"
    162                    class="<?php echo esc_attr( $tab === 'alt-settings' ? 'active' : '' ); ?>"><?php echo esc_html__( 'Alt-Text Settings', 'ai-alt-text-builder' ); ?></a>
    163             </nav>
    164         </aside>
    165 
    166         <main class="aissp-content">
     157
     158    <div class="aatb-settings-wrap">
     159
     160        <!-- HEADER -->
     161        <div class="aatb-header">
     162            <div class="aatb-header-inner">
     163                <div class="aatb-header-left">
     164                    <div class="aatb-logo-icon">
     165                        <img src="<?php echo esc_url( AATB_URL . 'admin/assets/img/header-logo.png' ); ?>" alt="AI Alt Text Builder" width="36" height="36">
     166                    </div>
     167                    <div>
     168                        <h1 class="aatb-header-title">AI Alt Text Builder</h1>
     169                        <p class="aatb-header-subtitle">Media Library ALT Text Generator</p>
     170                    </div>
     171                </div>
     172                <div class="aatb-header-right">
     173                    <span class="aatb-version-badge">v<?php echo esc_html( AATB_VERSION ); ?></span>
     174                </div>
     175            </div>
     176        </div>
     177
     178        <!-- LAYOUT -->
     179        <div class="aatb-layout">
     180
     181            <!-- SIDEBAR -->
     182            <aside class="aatb-sidebar">
     183                <nav class="aatb-nav">
     184                    <a href="<?php echo esc_url( add_query_arg('tab','site-key',admin_url('admin.php?page=ai-alt-text-builder')) ); ?>"
     185                       class="aatb-nav-item <?php echo $tab==='site-key' ? 'active' : ''; ?>">
     186                        <span class="aatb-nav-icon">
     187                            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
     188                                <path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"/>
     189                            </svg>
     190                        </span>
     191                        <span>Site Key Management</span>
     192                    </a>
     193                    <a href="<?php echo esc_url( add_query_arg('tab','alt-settings',admin_url('admin.php?page=ai-alt-text-builder')) ); ?>"
     194                       class="aatb-nav-item <?php echo $tab==='alt-settings' ? 'active' : ''; ?>">
     195                        <span class="aatb-nav-icon">
     196                            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
     197                                <circle cx="12" cy="12" r="3"/>
     198                                <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
     199                            </svg>
     200                        </span>
     201                        <span>Alt-Text Settings</span>
     202                    </a>
     203                </nav>
     204
     205                <div class="aatb-sidebar-info">
     206                    <p>Generate SEO-friendly ALT text for your Media Library images with one click. Supports bulk generation and multiple languages.</p>
     207                    <a href="https://rankpilotai.com/ai-alt-text-builder/" target="_blank" rel="noopener" class="aatb-sidebar-link">
     208                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
     209                        Documentation
     210                    </a>
     211                </div>
     212            </aside>
     213
     214            <!-- CONTENT -->
     215            <main class="aatb-content">
     216
     217            <?php /* ---- TAB 1: SITE KEY ---- */ ?>
     218            <?php if ( $tab === 'site-key' ) : ?>
     219
     220                <div class="aatb-content-header">
     221                    <h2>Site Key Management</h2>
     222                    <p>Connect your site to RankPilotAI by entering your Site Key below.</p>
     223                </div>
     224
     225                <?php if ( ! $token ) : ?>
     226
     227                    <div class="aatb-card">
     228                        <div class="aatb-card-header">
     229                            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"/></svg>
     230                            <h3>Enter Your Site Key</h3>
     231                        </div>
     232                        <form method="post" class="aatb-form">
     233                            <?php wp_nonce_field('aatb_save_site_key'); ?>
     234                            <input type="hidden" name="aatb_action" value="aatb_save_site_key">
     235                            <div class="aatb-field">
     236                                <label for="aatb_site_token">Site Key</label>
     237                                <input type="text" id="aatb_site_token" name="site_token" placeholder="rp_xxxxxxxxxxxxxxxxx" required>
     238                                <p class="aatb-field-hint">Generate a Site Key in your account at
     239                                    <a href="https://rankpilotai.com" target="_blank" rel="noopener">RankPilotAI.com</a></p>
     240                            </div>
     241                            <button type="submit" class="aatb-btn aatb-btn-primary">
     242                                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
     243                                Save Site Key
     244                            </button>
     245                        </form>
     246                    </div>
     247
     248                <?php else : ?>
     249
     250                    <!-- Active Key Card -->
     251                    <div class="aatb-card">
     252                        <div class="aatb-card-header">
     253                            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
     254                            <h3>Active Site Key</h3>
     255                            <span class="aatb-badge aatb-badge-success">Connected</span>
     256                        </div>
     257                        <div class="aatb-token-display">
     258                            <code><?php echo esc_html( $token ); ?></code>
     259                        </div>
     260                    </div>
     261
     262                    <?php if ( $usage !== null ) : ?>
     263                    <!-- Usage Card -->
     264                    <div class="aatb-card">
     265                        <div class="aatb-card-header">
     266                            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20V10"/><path d="M18 20V4"/><path d="M6 20v-4"/></svg>
     267                            <h3>Usage Overview</h3>
     268                        </div>
     269
     270                        <div class="aatb-usage-grid">
     271                            <div class="aatb-usage-stat">
     272                                <span class="aatb-usage-label">Website</span>
     273                                <span class="aatb-usage-value"><?php echo esc_html( parse_url( home_url(), PHP_URL_HOST ) ); ?></span>
     274                            </div>
     275                            <div class="aatb-usage-stat">
     276                                <span class="aatb-usage-label">Used</span>
     277                                <span class="aatb-usage-value aatb-usage-number"><?php echo esc_html( number_format_i18n( (int) $usage ) ); ?></span>
     278                            </div>
     279                            <div class="aatb-usage-stat">
     280                                <span class="aatb-usage-label">Plan Limit</span>
     281                                <span class="aatb-usage-value aatb-usage-number"><?php echo esc_html( number_format_i18n( (int) $limit ) ); ?></span>
     282                            </div>
     283                            <div class="aatb-usage-stat">
     284                                <span class="aatb-usage-label">Remaining</span>
     285                                <span class="aatb-usage-value aatb-usage-number <?php echo (int)$remain === 0 ? 'aatb-danger' : 'aatb-success'; ?>">
     286                                    <?php echo esc_html( number_format_i18n( (int) $remain ) ); ?>
     287                                </span>
     288                            </div>
     289                        </div>
     290
     291                        <?php
     292                        $pct = $limit ? round( ( ( (int) $limit - (int) $remain ) / max( 1, (int) $limit ) ) * 100 ) : 0;
     293                        $pct = max( 0, min( 100, (int) $pct ) );
     294                        $bar_color = ( (int) $remain === 0 ) ? '#ef4444' : '#3b82f6';
     295                        ?>
     296                        <div class="aatb-progress-wrap">
     297                            <div class="aatb-progress-bar">
     298                                <div class="aatb-progress-fill" style="width:<?php echo (int) $pct; ?>%;background:<?php echo esc_attr( $bar_color ); ?>;"></div>
     299                            </div>
     300                            <span class="aatb-progress-label"><?php echo (int) $pct; ?>% used</span>
     301                        </div>
     302
     303                        <div class="aatb-usage-cta">
     304                            <a href="<?php echo esc_url( $btn_url ); ?>" target="_blank" rel="noopener" class="aatb-btn aatb-btn-primary">
     305                                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
     306                                <?php echo esc_html( $btn_label ); ?>
     307                            </a>
     308                        </div>
     309                    </div>
     310                    <?php endif; ?>
     311
     312                    <!-- Update Key Card -->
     313                    <div class="aatb-card">
     314                        <div class="aatb-card-header">
     315                            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
     316                            <h3>Update Site Key</h3>
     317                        </div>
     318                        <form method="post" class="aatb-form">
     319                            <?php wp_nonce_field('aatb_save_site_key'); ?>
     320                            <input type="hidden" name="aatb_action" value="aatb_save_site_key">
     321                            <div class="aatb-field-row">
     322                                <input type="text" name="site_token" value="<?php echo esc_attr( $token ); ?>" placeholder="rp_xxxxxxxxxxxxxxxxx">
     323                                <button type="submit" class="aatb-btn aatb-btn-primary">Save</button>
     324                            </div>
     325                        </form>
     326                    </div>
     327
     328                <?php endif; ?>
     329
     330            <?php /* ---- TAB 2: ALT-TEXT SETTINGS ---- */ ?>
     331            <?php else : ?>
     332
     333                <div class="aatb-content-header">
     334                    <h2>Alt-Text Settings</h2>
     335                    <p>Configure how AI Alt Text Builder generates ALT text for your images.</p>
     336                </div>
     337
     338                <?php if ( empty( $token ) ) : ?>
     339                    <div class="aatb-alert aatb-alert-warning">
     340                        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
     341                        <div>
     342                            <strong>Site Key Required</strong>
     343                            <p>Please enter a Site Key in the <a href="<?php echo esc_url( add_query_arg('tab','site-key',admin_url('admin.php?page=ai-alt-text-builder')) ); ?>">Site Key Management</a> tab first.</p>
     344                        </div>
     345                    </div>
     346                <?php endif; ?>
     347
     348                <form method="post">
     349                    <?php wp_nonce_field('aatb_save_alt_settings'); ?>
     350                    <input type="hidden" name="aatb_action" value="aatb_save_alt_settings">
     351
     352                    <!-- Model Selection -->
     353                    <div class="aatb-card">
     354                        <div class="aatb-card-header">
     355                            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></svg>
     356                            <h3>AI Model</h3>
     357                        </div>
     358
     359                        <div class="aatb-model-grid">
     360                            <label class="aatb-model-card <?php echo ($model === 'gpt-4o-mini') ? 'selected' : ''; ?>">
     361                                <input type="radio" name="model_choice" value="gpt-4o-mini"
     362                                       <?php checked( $model, 'gpt-4o-mini' ); ?> <?php echo $dis; ?>>
     363                                <div class="aatb-model-card-inner">
     364                                    <span class="aatb-model-dot" style="background:#22c55e;"></span>
     365                                    <div class="aatb-model-info">
     366                                        <strong>GPT-4o-mini</strong>
     367                                        <span class="aatb-model-tag">Economical</span>
     368                                    </div>
     369                                    <span class="aatb-model-cost">1 token</span>
     370                                </div>
     371                            </label>
     372
     373                            <label class="aatb-model-card <?php echo ($model === 'gpt-4.1') ? 'selected' : ''; ?> <?php echo !in_array('gpt-4.1',$allowed_models_ui,true) ? 'disabled' : ''; ?>">
     374                                <input type="radio" name="model_choice" value="gpt-4.1"
     375                                       <?php checked( $model, 'gpt-4.1' ); ?>
     376                                       <?php echo (!in_array('gpt-4.1',$allowed_models_ui,true) || $dis) ? 'disabled' : ''; ?>>
     377                                <div class="aatb-model-card-inner">
     378                                    <span class="aatb-model-dot" style="background:#3b82f6;"></span>
     379                                    <div class="aatb-model-info">
     380                                        <strong>GPT-4.1</strong>
     381                                        <span class="aatb-model-tag">Balanced</span>
     382                                    </div>
     383                                    <span class="aatb-model-cost">3 tokens</span>
     384                                </div>
     385                            </label>
     386
     387                            <label class="aatb-model-card <?php echo ($model === 'gpt-4o') ? 'selected' : ''; ?> <?php echo !in_array('gpt-4o',$allowed_models_ui,true) ? 'disabled' : ''; ?>">
     388                                <input type="radio" name="model_choice" value="gpt-4o"
     389                                       <?php checked( $model, 'gpt-4o' ); ?>
     390                                       <?php echo (!in_array('gpt-4o',$allowed_models_ui,true) || $dis) ? 'disabled' : ''; ?>>
     391                                <div class="aatb-model-card-inner">
     392                                    <span class="aatb-model-dot" style="background:#a855f7;"></span>
     393                                    <div class="aatb-model-info">
     394                                        <strong>GPT-4o</strong>
     395                                        <span class="aatb-model-tag">Premium</span>
     396                                    </div>
     397                                    <span class="aatb-model-cost">5 tokens</span>
     398                                </div>
     399                            </label>
     400                        </div>
     401
     402                        <p class="aatb-card-hint">All models support vision (image analysis). Some models may be unavailable on your current plan.</p>
     403                    </div>
     404
     405                    <!-- Language Selection -->
     406                    <div class="aatb-card">
     407                        <div class="aatb-card-header">
     408                            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
     409                            <h3>Alt-Text Language</h3>
     410                        </div>
     411
     412                        <div class="aatb-field">
     413                            <select id="lang_choice" name="lang_choice" <?php echo $dis; ?>>
     414                                <option value="en" <?php selected($lang,'en'); ?>>English</option>
     415                                <option value="de" <?php selected($lang,'de'); ?>>Deutsch</option>
     416                                <option value="fr" <?php selected($lang,'fr'); ?>>Fran&ccedil;ais</option>
     417                                <option value="es" <?php selected($lang,'es'); ?>>Espa&ntilde;ol</option>
     418                                <option value="tr" <?php selected($lang,'tr'); ?>>T&uuml;rk&ccedil;e</option>
     419                                <option value="other" <?php if( ! in_array($lang,['en','de','fr','es','tr'],true) ) echo 'selected'; ?>>
     420                                    Other / Custom
     421                                </option>
     422                            </select>
     423                            <input type="text" id="lang_custom" name="lang_custom"
     424                                placeholder="e.g. Italian, Dutch, Arabic"
     425                                value="<?php echo esc_attr( ! in_array($lang,['en','de','fr','es','tr'],true) ? $lang : '' ); ?>"
     426                                style="margin-top:8px;<?php echo in_array($lang,['en','de','fr','es','tr'],true) ? 'display:none;' : ''; ?>">
     427                            <p class="aatb-field-hint">Select "Other / Custom" and type the language exactly as you'd like it.</p>
     428                        </div>
     429                    </div>
     430
     431                    <!-- Custom Prompt -->
     432                    <div class="aatb-card">
     433                        <div class="aatb-card-header">
     434                            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
     435                            <h3>Custom Prompt</h3>
     436                        </div>
     437
     438                        <div class="aatb-field">
     439                            <textarea id="custom_prompt" name="custom_prompt" rows="4"
     440                                      placeholder="e.g. Always mention the brand name first..." <?php echo $dis; ?>><?php
     441                                echo esc_textarea( $opt['custom_prompt'] ?? '' ); ?></textarea>
     442                            <p class="aatb-field-hint">
     443                                Optional. Add brand or style guidance. Your note is appended to the built-in instructions
     444                                and takes priority over general rules; the 125-character limit always applies.
     445                                Leave blank to use defaults.
     446                            </p>
     447                        </div>
     448                    </div>
     449
     450                    <!-- Save Button -->
     451                    <div class="aatb-form-actions">
     452                        <button type="submit" class="aatb-btn aatb-btn-primary aatb-btn-lg" <?php echo $dis; ?>>
     453                            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
     454                            <?php echo empty( $token ) ? 'Save (needs Site Key)' : 'Save Changes'; ?>
     455                        </button>
     456                    </div>
     457                </form>
     458
     459            <?php endif; ?>
     460
     461            </main>
     462        </div>
     463    </div>
     464
     465    <!-- =========================================================
     466          SETTINGS PAGE CSS
     467    ========================================================= -->
     468    <style>
     469    /* ========== RESET & BASE ========== */
     470    .aatb-settings-wrap{
     471        max-width:1200px;
     472        margin:0 20px 40px 0;
     473        font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Helvetica Neue",Arial,sans-serif;
     474        font-size:14px;
     475        color:#1e293b;
     476        line-height:1.6;
     477    }
     478    .aatb-settings-wrap *{box-sizing:border-box;}
     479
     480    /* ========== HEADER ========== */
     481    .aatb-header{
     482        background:linear-gradient(135deg,#0f172a 0%,#1e3a5f 50%,#1e40af 100%);
     483        border-radius:12px 12px 0 0;
     484        padding:0;
     485        margin-top:20px;
     486    }
     487    .aatb-header-inner{
     488        display:flex;
     489        align-items:center;
     490        justify-content:space-between;
     491        padding:24px 32px;
     492    }
     493    .aatb-header-left{display:flex;align-items:center;gap:16px;}
     494    .aatb-logo-icon{
     495        width:48px;height:48px;
     496        background:rgba(255,255,255,.12);
     497        border-radius:12px;
     498        display:flex;align-items:center;justify-content:center;
     499        color:#fff;
     500        flex-shrink:0;
     501    }
     502    .aatb-logo-icon img{border-radius:6px;}
     503    .aatb-header-title{
     504        font-size:22px;font-weight:700;color:#fff;margin:0;line-height:1.2;
     505    }
     506    .aatb-header-subtitle{
     507        font-size:13px;color:rgba(255,255,255,.65);margin:2px 0 0;
     508    }
     509    .aatb-version-badge{
     510        background:rgba(255,255,255,.15);
     511        color:rgba(255,255,255,.85);
     512        padding:4px 12px;
     513        border-radius:20px;
     514        font-size:12px;
     515        font-weight:600;
     516    }
     517
     518    /* ========== LAYOUT ========== */
     519    .aatb-layout{
     520        display:flex;
     521        background:#f8fafc;
     522        border:1px solid #e2e8f0;
     523        border-top:none;
     524        border-radius:0 0 12px 12px;
     525        min-height:500px;
     526    }
     527
     528    /* ========== SIDEBAR ========== */
     529    .aatb-sidebar{
     530        width:280px;
     531        background:#fff;
     532        border-right:1px solid #e2e8f0;
     533        border-radius:0 0 0 12px;
     534        padding:24px 16px;
     535        display:flex;
     536        flex-direction:column;
     537        flex-shrink:0;
     538    }
     539    .aatb-nav{display:flex;flex-direction:column;gap:4px;}
     540    .aatb-nav-item{
     541        display:flex;align-items:center;gap:12px;
     542        padding:12px 16px;
     543        border-radius:8px;
     544        color:#475569;
     545        text-decoration:none;
     546        font-size:14px;
     547        font-weight:500;
     548        transition:all .15s ease;
     549    }
     550    .aatb-nav-item:hover{background:#f1f5f9;color:#1e293b;}
     551    .aatb-nav-item.active{
     552        background:linear-gradient(135deg,#eff6ff,#dbeafe);
     553        color:#1d4ed8;
     554        font-weight:600;
     555        box-shadow:0 0 0 1px rgba(59,130,246,.2);
     556    }
     557    .aatb-nav-icon{display:flex;align-items:center;flex-shrink:0;}
     558
     559    .aatb-sidebar-info{
     560        margin-top:auto;
     561        padding:16px;
     562        background:#f8fafc;
     563        border-radius:8px;
     564        border:1px solid #e2e8f0;
     565    }
     566    .aatb-sidebar-info p{
     567        font-size:12px;color:#64748b;margin:0 0 12px;line-height:1.5;
     568    }
     569    .aatb-sidebar-link{
     570        display:inline-flex;align-items:center;gap:6px;
     571        font-size:12px;color:#3b82f6;text-decoration:none;font-weight:500;
     572    }
     573    .aatb-sidebar-link:hover{color:#1d4ed8;text-decoration:underline;}
     574
     575    /* ========== CONTENT ========== */
     576    .aatb-content{
     577        flex:1;
     578        padding:32px;
     579        min-width:0;
     580    }
     581    .aatb-content-header{margin-bottom:28px;}
     582    .aatb-content-header h2{
     583        font-size:22px;font-weight:700;color:#0f172a;margin:0 0 6px;
     584    }
     585    .aatb-content-header p{
     586        font-size:14px;color:#64748b;margin:0;
     587    }
     588
     589    /* ========== CARDS ========== */
     590    .aatb-card{
     591        background:#fff;
     592        border:1px solid #e2e8f0;
     593        border-radius:10px;
     594        padding:24px;
     595        margin-bottom:20px;
     596        transition:box-shadow .2s ease;
     597    }
     598    .aatb-card:hover{box-shadow:0 1px 8px rgba(0,0,0,.04);}
     599    .aatb-card-header{
     600        display:flex;
     601        align-items:center;
     602        gap:10px;
     603        margin-bottom:20px;
     604        padding-bottom:16px;
     605        border-bottom:1px solid #f1f5f9;
     606        color:#334155;
     607    }
     608    .aatb-card-header h3{
     609        font-size:16px;font-weight:600;margin:0;color:#0f172a;flex:1;
     610    }
     611    .aatb-card-hint{
     612        font-size:12px;color:#94a3b8;margin:16px 0 0;line-height:1.5;
     613    }
     614    .aatb-card-hint code{
     615        background:#f1f5f9;padding:1px 6px;border-radius:3px;font-size:11px;
     616    }
     617
     618    /* ========== BADGES ========== */
     619    .aatb-badge{
     620        padding:3px 10px;border-radius:20px;font-size:11px;font-weight:600;letter-spacing:.3px;text-transform:uppercase;
     621    }
     622    .aatb-badge-success{background:#dcfce7;color:#16a34a;}
     623
     624    /* ========== FORMS ========== */
     625    .aatb-form{display:flex;flex-direction:column;gap:20px;}
     626    .aatb-field{display:flex;flex-direction:column;gap:6px;}
     627    .aatb-field label{font-size:13px;font-weight:600;color:#334155;}
     628    .aatb-field input[type="text"],
     629    .aatb-field textarea,
     630    .aatb-field select{
     631        width:100%;
     632        padding:10px 14px;
     633        font-size:14px;
     634        border:1px solid #cbd5e1;
     635        border-radius:8px;
     636        background:#fff;
     637        color:#1e293b;
     638        transition:border-color .15s ease,box-shadow .15s ease;
     639        font-family:inherit;
     640    }
     641    .aatb-field input[type="text"]:focus,
     642    .aatb-field textarea:focus,
     643    .aatb-field select:focus{
     644        outline:none;
     645        border-color:#3b82f6;
     646        box-shadow:0 0 0 3px rgba(59,130,246,.12);
     647    }
     648    .aatb-field textarea{resize:vertical;min-height:100px;}
     649    .aatb-field select{
     650        appearance:none;
     651        -webkit-appearance:none;
     652        -moz-appearance:none;
     653        background:#fff
     654            url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23334155'%3E%3Cpath d='M7 10l5 5 5-5'/%3E%3C/svg%3E")
     655            no-repeat right 12px center;
     656        background-size:14px 14px;
     657        padding-right:40px;
     658    }
     659    .aatb-field-hint{
     660        font-size:12px;color:#94a3b8;margin:0;line-height:1.5;
     661    }
     662    .aatb-field-hint a{color:#3b82f6;text-decoration:none;}
     663    .aatb-field-hint a:hover{text-decoration:underline;}
     664
     665    .aatb-field-row{
     666        display:flex;gap:10px;align-items:center;
     667    }
     668    .aatb-field-row input[type="text"]{
     669        flex:1;
     670        padding:10px 14px;
     671        font-size:14px;
     672        border:1px solid #cbd5e1;
     673        border-radius:8px;
     674        background:#fff;
     675        color:#1e293b;
     676        transition:border-color .15s ease,box-shadow .15s ease;
     677    }
     678    .aatb-field-row input[type="text"]:focus{
     679        outline:none;
     680        border-color:#3b82f6;
     681        box-shadow:0 0 0 3px rgba(59,130,246,.12);
     682    }
     683
     684    /* ========== BUTTONS ========== */
     685    .aatb-btn{
     686        display:inline-flex;align-items:center;justify-content:center;gap:8px;
     687        padding:10px 20px;
     688        font-size:14px;font-weight:600;
     689        border:none;border-radius:8px;
     690        cursor:pointer;
     691        text-decoration:none;
     692        transition:all .15s ease;
     693        line-height:1.4;
     694    }
     695    .aatb-btn-primary{
     696        background:linear-gradient(135deg,#2563eb,#1d4ed8);
     697        color:#fff !important;
     698        box-shadow:0 1px 3px rgba(37,99,235,.3);
     699    }
     700    .aatb-btn-primary:hover{
     701        background:linear-gradient(135deg,#3b82f6,#2563eb);
     702        box-shadow:0 2px 8px rgba(37,99,235,.35);
     703        transform:translateY(-1px);
     704        color:#fff !important;
     705    }
     706    .aatb-btn-primary:active{transform:translateY(0);}
     707    .aatb-btn-primary:disabled,.aatb-btn-primary[disabled]{
     708        opacity:.5;cursor:not-allowed;transform:none;
     709    }
     710    .aatb-btn-lg{padding:12px 28px;font-size:15px;}
     711
     712    /* ========== TOKEN DISPLAY ========== */
     713    .aatb-token-display{
     714        background:#f8fafc;
     715        border:1px solid #e2e8f0;
     716        border-radius:8px;
     717        padding:12px 16px;
     718    }
     719    .aatb-token-display code{
     720        font-size:14px;color:#334155;background:none;
     721        word-break:break-all;
     722    }
     723
     724    /* ========== USAGE ========== */
     725    .aatb-usage-grid{
     726        display:grid;
     727        grid-template-columns:repeat(4,1fr);
     728        gap:16px;
     729        margin-bottom:20px;
     730    }
     731    .aatb-usage-stat{
     732        background:#f8fafc;
     733        border:1px solid #e2e8f0;
     734        border-radius:8px;
     735        padding:14px 16px;
     736        text-align:center;
     737    }
     738    .aatb-usage-label{
     739        display:block;font-size:11px;font-weight:600;
     740        color:#94a3b8;text-transform:uppercase;letter-spacing:.5px;
     741        margin-bottom:6px;
     742    }
     743    .aatb-usage-value{
     744        display:block;font-size:14px;font-weight:700;color:#0f172a;
     745        word-break:break-all;
     746    }
     747    .aatb-usage-number{font-size:20px;}
     748    .aatb-usage-value.aatb-danger{color:#ef4444;}
     749    .aatb-usage-value.aatb-success{color:#22c55e;}
     750
     751    .aatb-progress-wrap{
     752        display:flex;align-items:center;gap:12px;
     753        margin-bottom:20px;
     754    }
     755    .aatb-progress-bar{
     756        flex:1;height:10px;
     757        background:#e2e8f0;border-radius:10px;
     758        overflow:hidden;
     759    }
     760    .aatb-progress-fill{
     761        height:100%;border-radius:10px;
     762        transition:width .6s ease;
     763    }
     764    .aatb-progress-label{
     765        font-size:12px;font-weight:600;color:#64748b;
     766        white-space:nowrap;
     767    }
     768    .aatb-usage-cta{padding-top:4px;}
     769
     770    /* ========== MODEL CARDS ========== */
     771    .aatb-model-grid{
     772        display:grid;
     773        grid-template-columns:repeat(3,1fr);
     774        gap:12px;
     775    }
     776    .aatb-model-card{
     777        position:relative;
     778        border:2px solid #e2e8f0;
     779        border-radius:10px;
     780        padding:16px;
     781        cursor:pointer;
     782        transition:all .15s ease;
     783        background:#fff;
     784    }
     785    .aatb-model-card:hover:not(.disabled){
     786        border-color:#93c5fd;
     787        background:#f8fafc;
     788    }
     789    .aatb-model-card.selected{
     790        border-color:#3b82f6;
     791        background:linear-gradient(135deg,#eff6ff,#dbeafe);
     792        box-shadow:0 0 0 1px rgba(59,130,246,.2);
     793    }
     794    .aatb-model-card.disabled{
     795        opacity:.45;cursor:not-allowed;pointer-events:none;
     796    }
     797    .aatb-model-card input[type="radio"]{
     798        position:absolute;opacity:0;width:0;height:0;pointer-events:none;
     799    }
     800    .aatb-model-card-inner{
     801        display:flex;
     802        align-items:center;
     803        gap:10px;
     804    }
     805    .aatb-model-dot{
     806        width:10px;height:10px;
     807        border-radius:50%;
     808        flex-shrink:0;
     809    }
     810    .aatb-model-info{
     811        flex:1;min-width:0;
     812    }
     813    .aatb-model-info strong{
     814        display:block;font-size:14px;color:#0f172a;
     815    }
     816    .aatb-model-tag{
     817        font-size:11px;color:#64748b;
     818    }
     819    .aatb-model-cost{
     820        font-size:12px;font-weight:600;
     821        color:#3b82f6;
     822        white-space:nowrap;
     823        background:#eff6ff;
     824        padding:3px 8px;
     825        border-radius:6px;
     826    }
     827
     828    /* ========== ALERT ========== */
     829    .aatb-alert{
     830        display:flex;align-items:flex-start;gap:12px;
     831        padding:16px 20px;
     832        border-radius:10px;
     833        margin-bottom:24px;
     834    }
     835    .aatb-alert svg{flex-shrink:0;margin-top:2px;}
     836    .aatb-alert strong{display:block;margin-bottom:4px;}
     837    .aatb-alert p{margin:0;font-size:13px;}
     838    .aatb-alert a{color:inherit;text-decoration:underline;}
     839    .aatb-alert-warning{
     840        background:#fef3c7;border:1px solid #fde68a;color:#92400e;
     841    }
     842    .aatb-alert-warning svg{color:#f59e0b;}
     843
     844    /* ========== FORM ACTIONS ========== */
     845    .aatb-form-actions{
     846        padding-top:8px;
     847    }
     848
     849    /* ========== RESPONSIVE ========== */
     850    @media (max-width:900px){
     851        .aatb-settings-wrap{margin:0 10px 30px 0;}
     852        .aatb-header-inner{padding:20px 24px;}
     853        .aatb-layout{flex-direction:column;border-radius:0 0 12px 12px;}
     854        .aatb-sidebar{
     855            width:100%;
     856            border-right:none;
     857            border-bottom:1px solid #e2e8f0;
     858            border-radius:0;
     859            padding:20px 16px;
     860        }
     861        .aatb-nav{flex-direction:row;gap:8px;}
     862        .aatb-nav-item{font-size:13px;padding:10px 14px;}
     863        .aatb-sidebar-info{display:none;}
     864        .aatb-content{padding:24px 20px;}
     865
     866        .aatb-usage-grid{grid-template-columns:repeat(2,1fr);}
     867        .aatb-model-grid{grid-template-columns:1fr;}
     868        .aatb-field-row{flex-direction:column;}
     869        .aatb-field-row input[type="text"]{width:100%;}
     870        .aatb-btn{width:100%;justify-content:center;}
     871    }
     872
     873    @media (max-width:480px){
     874        .aatb-header-inner{flex-direction:column;align-items:flex-start;gap:12px;}
     875        .aatb-header-right{align-self:flex-end;}
     876        .aatb-usage-grid{grid-template-columns:1fr 1fr;}
     877        .aatb-nav{flex-direction:column;}
     878    }
     879    </style>
     880    <script>
     881    (function(){
     882        var cards = document.querySelectorAll('.aatb-model-card');
     883        cards.forEach(function(card){
     884            var radio = card.querySelector('input[type="radio"]');
     885            if(!radio) return;
     886            card.addEventListener('click', function(e){
     887                if(card.classList.contains('disabled')) return;
     888                if(radio.disabled) return;
     889                cards.forEach(function(c){ c.classList.remove('selected'); });
     890                card.classList.add('selected');
     891                radio.checked = true;
     892            });
     893        });
     894    })();
     895    </script>
    167896    <?php
    168     /* ─────────────────────────────────────────
    169      *  TAB 1 — SITE KEY
    170      * ─────────────────────────────────────────*/
    171     if ( $tab === 'site-key' ) : ?>
    172 
    173         <h2><?php echo esc_html__( 'RankPilotAI: Site Key Management', 'ai-alt-text-builder' ); ?></h2>
    174 
    175         <?php if ( ! $token ) : ?>
    176             <p class="intro-txt">Enter your Site Key below to get started.</p>
    177             <form method="post" class="aissp-form">
    178                 <?php wp_nonce_field('aatb_save_site_key'); ?>
    179                 <input type="hidden" name="aatb_action" value="aatb_save_site_key">
    180                 <input type="text" name="site_token" class="regular-text" placeholder="rp_xxxxxxxxxxxxxxxxx" required>
    181                 <p class="desc linkpad">Generate your Site Key on
    182                     <a href="https://rankpilotai.com" target="_blank" rel="noopener">RankPilotAI.com</a></p>
    183                 <button class="primary-btn">Save Site Key</button>
    184             </form>
    185 
    186         <?php else : ?>
    187             <p><strong>Your Active Site Key</strong></p>
    188             <code class="token-view"><?php echo esc_html( $token ); ?></code>
    189 
    190             <?php if ( $limit !== null ) : ?>
    191             <div class="usage-card">
    192                 <h3>Usage Overview</h3>
    193                 <table class="usage-table"><thead><tr>
    194                     <th>This&nbsp;Site</th>
    195                     <th>Usage</th>
    196                     <th>Plan&nbsp;Limit</th>
    197                     <th>Remaining</th>
    198                     <th>Progress</th>
    199                     <th></th>
    200                 </tr></thead><tbody><tr>
    201                     <td><?php echo esc_html( parse_url( home_url(), PHP_URL_HOST ) ); ?></td>
    202                     <td><?php echo esc_html( number_format_i18n( (int) $usage ) ); ?></td>
    203 <td><?php echo esc_html( number_format_i18n( (int) $limit ) ); ?></td>
    204 <td><?php echo esc_html( number_format_i18n( (int) $remain ) ); ?></td>
    205 <td>
    206     <?php
    207         $pct = $limit ? round( ( ( (int) $limit - (int) $remain ) / max( 1, (int) $limit ) ) * 100 ) : 0;
    208         $pct = max( 0, min( 100, (int) $pct ) );
    209         $bar = ( (int) $remain === 0 ) ? '#dc3232' : '#3b82f6';
    210         $style = sprintf(
    211             'width:%d%%;background:%s',
    212             $pct,
    213             $bar
    214         );
    215     ?>
    216     <div class="bar-bg">
    217         <div class="bar-fill" style="<?php echo esc_attr( $style ); ?>"></div>
    218     </div>
    219 </td>
    220                     <td>
    221                         <a href="<?php echo esc_url( $btn_url ); ?>" target="_blank" class="primary-btn">
    222                             <?php echo esc_html( $btn_label ); ?>
    223                         </a>
    224                     </td>
    225                 </tr></tbody></table>
    226             </div>
    227             <?php endif; ?>
    228 
    229             <form method="post" class="aissp-form" style="margin-top:26px;">
    230                 <?php wp_nonce_field('aatb_save_site_key'); ?>
    231                 <input type="hidden" name="aatb_action" value="aatb_save_site_key">
    232                 <h3 class="sub-head">Update / Change Site Key</h3>
    233                 <div class="key-row">
    234                     <input type="text" name="site_token" value="<?php echo esc_attr( $token ); ?>" class="regular-text">
    235                     <button class="primary-btn">Save</button>
    236                 </div>
    237             </form>
    238         <?php endif; ?>
    239 
    240     <?php
    241     /* ─────────────────────────────────────────
    242      *  TAB 2 — ALT-TEXT SETTINGS
    243      * ─────────────────────────────────────────*/
    244     else : ?>
    245 
    246         <h2><?php echo esc_html__( 'Alt-Text Settings', 'ai-alt-text-builder' ); ?></h2>
    247 
    248         <form method="post" class="aissp-form-wide">
    249             <?php wp_nonce_field('aatb_save_alt_settings'); ?>
    250             <input type="hidden" name="aatb_action" value="aatb_save_alt_settings">
    251             <table class="form-table">
    252 
    253                 <tr>
    254                     <th><label for="model_choice"><?php echo esc_html__( 'GPT Model', 'ai-alt-text-builder' ); ?></label></th>
    255                     <td>
    256                         <select id="model_choice" name="model_choice" <?php echo empty($token)?'disabled':''; ?>>
    257     <option value="gpt-4o-mini" <?php selected($model,'gpt-4o-mini'); ?>>
    258         <?php echo esc_html__( '🟢 Economical (gpt-4o-mini) - 1 token / alt-text', 'ai-alt-text-builder' ); ?>
    259     </option>
    260     <option value="gpt-4.1" <?php selected($model,'gpt-4.1'); ?>
    261         <?php echo in_array('gpt-4.1',$allowed_models_ui,true)?'':'disabled'; ?>>
    262         <?php echo esc_html__( '🔵 Balanced (gpt-4.1) - 3 tokens / alt-text', 'ai-alt-text-builder' ); ?>
    263     </option>
    264     <option value="gpt-4o" <?php selected($model,'gpt-4o'); ?>
    265         <?php echo in_array('gpt-4o',$allowed_models_ui,true)?'':'disabled'; ?>>
    266         <?php echo esc_html__( '🟣 Premium (gpt-4o) - 5 tokens / alt-text', 'ai-alt-text-builder' ); ?>
    267     </option>
    268 </select>
    269                     </td>
    270                 </tr>
    271 
    272                 <tr>
    273                     <th><label for="lang_choice"><?php echo esc_html__( 'Alt-Text Language', 'ai-alt-text-builder' ); ?></label></th>
    274                     <td>
    275                         <select id="lang_choice" name="lang_choice">
    276                             <option value="en" <?php selected($lang,'en'); ?>>English</option>
    277                             <option value="de" <?php selected($lang,'de'); ?>>Deutsch</option>
    278                             <option value="fr" <?php selected($lang,'fr'); ?>>Français</option>
    279                             <option value="es" <?php selected($lang,'es'); ?>>Español</option>
    280                             <option value="tr" <?php selected($lang,'tr'); ?>>Türkçe</option>
    281                             <option value="other" <?php if( ! in_array($lang,['en','de','fr','es','tr'],true) ) echo 'selected'; ?>>
    282                                 Other / Custom
    283                             </option>
    284                         </select>
    285                         <input type="text" id="lang_custom" name="lang_custom"
    286                             placeholder="e.g. Italian, Dutch, Arabic"
    287                             value="<?php echo esc_attr( ! in_array($lang,['en','de','fr','es','tr'],true) ? $lang : '' ); ?>"
    288                             style="margin-top:8px;<?php echo in_array($lang,['en','de','fr','es','tr'],true) ? 'display:none;' : ''; ?>">
    289                         <p class="desc">Select “Other / Custom” and type the language exactly as you’d like it.</p>
    290                     </td>
    291                 </tr>
    292 
    293                 <tr>
    294                     <th><label for="custom_prompt"><?php echo esc_html__( 'Custom Prompt', 'ai-alt-text-builder' ); ?></label></th>
    295                     <td>
    296                         <textarea id="custom_prompt" name="custom_prompt" rows="3" class="regular-text" style="width:420px;"><?php
    297                             echo esc_textarea( $opt['custom_prompt'] ?? '' );
    298                         ?></textarea>
    299                         <p class="description" style="margin-top:6px;">
    300         Optional. Add brand or style guidance. Your note is appended to the built-in instructions
    301         and takes priority over general rules; the 125-character limit always applies.
    302         Leave blank to use defaults.
    303 </p>
    304                     </td>
    305                 </tr>
    306 
    307             </table>
    308 
    309             <p>
    310                 <button type="submit" class="primary-btn" <?php echo empty($token)?'disabled':''; ?>>
    311                     Save Changes
    312                 </button>
    313                 <?php if (empty($token)) : ?><span class="need-key">← enter a Site Key first</span><?php endif; ?>
    314             </p>
    315         </form>
    316 
    317     <?php endif; ?>
    318         </main>
    319     </div>
    320 <?php }
     897}
  • ai-alt-text-builder/trunk/ai-alt-text-builder.php

    r3441081 r3462124  
    33Plugin Name: AI Alt Text Builder
    44Description: Generate SEO-friendly ALT text for Media Library images in one click, with scoring, bulk generation and language options.
    5 Version:     1.0.6
     5Version:     1.0.7
    66Author:      RankPilotAI
    77Author URI:  https://rankpilotai.com
     
    1616if ( ! defined( 'ABSPATH' ) ) exit;
    1717
    18 define( 'AATB_VERSION',       '1.0.6' );
     18define( 'AATB_VERSION',       '1.0.7' );
    1919define( 'AATB_PATH',          plugin_dir_path( __FILE__ ) );
    2020define( 'AATB_URL',           plugin_dir_url ( __FILE__ ) );
  • ai-alt-text-builder/trunk/readme.txt

    r3441081 r3462124  
    55Tested up to: 6.9
    66Requires PHP: 7.4
    7 Stable tag: 1.0.6
     7Stable tag: 1.0.7
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    7979== Changelog ==
    8080
     81= 1.0.7 =
     82* UI: Redesigned settings page with modern card-based layout, gradient header, improved navigation and responsive design.
     83* UI: Settings header now displays the official AI Alt Text Builder logo.
     84* UI: Modernized admin CSS for generate button and score badges.
     85
    8186= 1.0.6 =
    8287* Maintenance: minor version consistency updates and small documentation/copy cleanup. No functional changes.
Note: See TracChangeset for help on using the changeset viewer.