=== DigitalM InsideSEO - full changelog ===

= 0.270.5 =
* New: reviewed and accepted issues - mark a finding as reviewed with a reason and a note, so it drops out of the count, the plan and the score but stays in its own table; postponed ones return after their date.
* New speed checks (main image lazy-loaded, oversized or old format; several oversized images; third-party fonts; missing font-display; missing preconnect; tracking before consent) and a new "external calls" server measurement.
* New: an optimization log that records each speed and file-loading rule change with the before/after numbers, plus a per-page view of which CSS and JavaScript load where, cache-plugin detection and a warning when another optimization plugin is doing the same job.
* Keyword impression counts now match Google Search Console exactly, and traffic figures are shown against the previous period.
* Documentation: the readme now describes the above. This release also carries the earlier Plugin Check clean-up (file-level annotations on internal data-layer queries, recommended WordPress file functions, unslashed and sanitized admin inputs, a translators note) with the stable tag aligned to the version. No change in behaviour for the code-quality items.

= 0.267.10 =
* English interface completed. The Spelling screen and its whole panel are now translated, along with a set of labels that were still showing in the source language: the database table names on the System information screen, the "Orders" metric, and the broken-install notice. Every interface string in the plugin and its scripts now has an English translation.

= 0.267.9 =
* Plugin Check: the proofreading data layer now suppresses the direct-query warnings at file level (internal core tables, all values through prepare), clearing the last "incorrect number of replacements" warning on its post lookup.

= 0.267.8 =
* Plugin Check: aligned the stable tag with the plugin version; the new proofreading and task-queue data layers now suppress the direct-query warnings at file level (internal tables, values passed through prepare); the proofreader AJAX handlers note that the nonce and capability are verified in their guard; and the queue on/off, URL and ID inputs are now unslashed and sanitized.

= 0.267.1 =
* Money is no longer taken as proof that the meaning check succeeded - a truncated answer is paid for too. The flag travelling with the answer decides; the cost is only consulted when talking to a hub old enough not to send one. This is what was wiping grammar and style notes on a re-check.
* A single-item check reports the real number of texts read instead of always saying one.

= 0.267.0 =
* A re-check no longer erases what only the model can find. Grammar, meaning and style notes survive a pass in which the meaning check did not run - because it failed, was switched off, or the limit was gone - instead of being wiped by the dictionary-only result. They are dropped only when a real meaning pass replaces them.

= 0.266.99 =
* The Fix button no longer appears on advice. A model answering a grammar note sometimes returns an instruction - "lower case for common nouns and finish the sentence" - rather than replacement text, and writing that into the page would destroy the sentence. A suggestion now earns its button only if it reads like a literal replacement: short enough, no brackets, quotes or "e.g.", and sharing at least half its words with the text it replaces. Everything else says "fix by hand".
* The same test runs server-side, so an old page left open in a tab cannot push advice into the content either.

= 0.266.98 =
* A run that was charged for is a run that happened. If the hub billed the request, the meaning check is reported as done even when the flag travelling with it says otherwise - which it could, when an older paid plugin passed the hub's own "I handed this to the site" marker straight through.
* When it really did not run, the line now names who was asked and how many texts were sent, so the reason is on screen instead of guessed.

= 0.266.97 =
* "Do we have a working local AI" is now answered by asking it, not by trusting its own claim. Some providers report that text generation is supported and then match no model to any real request. The answer is cached - six hours for yes, ten minutes for no - so it costs a couple of tokens a day.

= 0.266.96 =
* One more fallback for "No models found": a separate system instruction is itself a capability the provider has to declare, so the last attempt folds the rules into the task text and asks without one. Four ways are now tried in order before giving up.
* When it still fails, a live probe runs four one-word requests and reports which way of asking the provider accepts - or the exact error for each - instead of a generic "check your settings".

= 0.266.95 =
* "No models found that support text_generation" is fixed at the source. The requested output ceiling is part of what WordPress matches models against, so a request asking for more than the provider advertises finds nothing. The call now retries with a smaller ceiling and then with none at all before giving up.
* Provider errors are translated: no matching model, provider quota reached, no credit - each gets a plain sentence saying what to do instead of the raw English message.

= 0.266.94 =
* The Spelling screen says out loud whether the meaning check happened. A green line - "meaning check ran through your AI account, 4 texts" - or a red one carrying the exact reason it did not. No more guessing from the absence of findings.

= 0.266.93 =
* The Spelling run shows what it costs while it runs: total spent and the average per item, measured from the actual run rather than estimated. Checking a single item reports its own cost too.

= 0.266.92 =
* A search box next to the item dropdown in the Spelling tab. Typing narrows the list as you go, so one product out of six hundred is two keystrokes away instead of a long scroll. It counts what is left, keeps the current selection if it still matches, and clears itself when the type changes.

= 0.266.91 =
* Findings are now an accordion, like the task queue: content type on top, each checked item under it, and every item keeps its own notes. Types open, items closed, counts on both levels.
* Each note shows the sentence it was found in, with the offending words highlighted inside it, so the place is visible without opening the editor.

= 0.266.90 =
* The Spelling tab is a hook, not a locked door. It appears only when the paid part has attached itself; without it there is no tab, no greyed-out button and no mention of a paid feature anywhere in the free plugin, exactly like the Pending tasks tab.

= 0.266.89 =
* Spelling check now sends only the human text. Shortcodes and their attributes, editor block comments, inline styles and scripts, CSS rules, key=value settings, links, e-mail addresses, base64 strings and letter-plus-digit codes are all removed before the text leaves the site. A page-builder page was reporting theme settings as spelling mistakes; it now reports only what a reader would see.
* The original field is untouched by this - cleaning happens on the copy that is checked, so a fix still writes back into the real content.

= 0.266.88 =
* A used-up monthly limit no longer stops the spelling run. The meaning check switches itself off, the batch is read again with the dictionary and punctuation - which cost nothing - and the run carries on to the end. The message now matches what actually happens.

= 0.266.87 =
* Spelling tab tidied: the two explanatory paragraphs are gone, the button reads "Check", the meaning checkbox says it needs an API key, and the re-read checkbox now says what it does - check the ones already checked - with the full explanation on hover.

= 0.266.86 =
* Spelling tab reads like a teacher marking an essay. Each finding is tagged by kind - spelling, grammar, punctuation, meaning, style - with its own colour, and shows the sentence it sits in.
* The free layer now includes punctuation, so a run without the paid full check already catches spacing and unclosed brackets.
* With the full check on, objects are read three at a time instead of ten, because whole descriptions are sent for marking.

= 0.266.85 =
* Spelling tab: a type filter and a second filter for one specific product, page, post or category - the same pair as in Uniqueness. Pick an item and only that item is read, straight away, whether or not it was read before.
* Audit tabs reordered: Errors, Speed, Images, Content, Uniqueness, Spelling, Pending tasks, Log, Crawl.

= 0.266.84 =
* The Spelling tab sends the site's language along with the text, so the Bulgarian dictionary is only applied to a Bulgarian site.

= 0.266.83 =
* New Audit tab: "Spelling". It reads the text already on the site - products, pages, posts and categories - across title, short description, description and the meta texts, and lists what is wrong with a suggested correction.
* Nothing is changed on its own. Each note has a "Fix" button that replaces that one word in that one field, keeping the capital letter, and a "Not an error" button that remembers the word for good.
* Each object is read once. A fingerprint of its text is stored, and it is read again only if someone changed it, so a second run over a large catalogue is nearly free.
* Words the shop itself uses everywhere are never called errors - brands and model names are skipped automatically.
* The category name is never rewritten from here, because it moves the address.

= 0.266.82 =
* Queue rows show the guard's notes. When the hub flags an expression that is not said that way in Bulgarian, the row carries a yellow box with the phrase, the reason and the better wording, right above the Save button.

= 0.266.81 =
* The image title in the media library is written too. Until now only the alt text and the description were saved, so the title stayed the camera number - "250228_205_1". It is now taken from the alt text, shortened to one clean phrase, and written at the moment you save that image.
* A human written title is never touched. The title is replaced only when it is empty, has no real word, repeats the file name or is a camera pattern like IMG_2201.
* The attachment slug stays as it was, so no address changes and no redirects are needed.
* A rename fixes the title too. Nothing runs in bulk and nothing runs on its own - one image is touched only when that image is saved.

= 0.266.80 =
* New shop vocabulary: the words this site actually uses, collected from product titles, categories, tags and attribute values, cached for a day. It exists so the text checks do not have to be written by hand for every shop.
* It also builds a stem index, so a word can be matched to the shop word it was derived from - "norkova" to "norka" - even though neither is a prefix of the other.

= 0.266.79 =
* Images tab: three checkboxes decide what gets written - file name, alt text, description - the same way the Uniqueness screen lets you pick fields. They apply both to "Save" on a single row and to "Save all filled in". An unticked field is left exactly as it is on the site.
* The search results now mark images that already have an earlier analysis with a green "suggestion ready" badge, so you can see before ticking which ones cost nothing.

= 0.266.78 =
* An image that already has an earlier Vision analysis no longer pays for a second one. When such an image enters the queue it goes straight to "waiting for review" with the stored suggestion, and the message says how many came for free.
* The search now also explains an empty image result: "the site has N such images, but all N are already waiting in the queue" - which is what "no generic file names found" actually meant while 195 image tasks were sitting in the queue.

= 0.266.77 =
* The queue's meta problems were named "Title missing / too long / too short", which reads like the H1 findings next to them in the Errors tab. They now say "Meta title", so a count of 6 for "H1 too long" is not mistaken for the meta title finding, which had 1.
* An empty search result now explains itself: how many findings the audit has for that type and why none of them qualified - already queued, address does not resolve to a page on this site, or a different content type.
* A finding address that is percent-encoded is now also tried decoded before giving up on it.

= 0.266.76 =
* Pending tasks is now two clearly separated parts. The top one, "Add to the tasks", searches the SITE by content type and problem type, lists what it finds with a tick per row, and adds the ticked ones to the queue. Things already waiting are left out of the results.
* The bottom part, "The task queue", is always fully visible and is only for processing. The content-type and problem filters no longer narrow it - they belong to the search above. Only the state filter and the six counters narrow the queue.
* The run button follows the same rule as before: with rows ticked it runs only those, otherwise everything waiting.

= 0.266.75 =
* The confirmation before a run now shows money, not just a request count: the estimated cost in euro, how much is left this month, and a warning when the job is bigger than the remainder. An article counts as about ten requests, a meta task as up to two, an image as one - so the estimate matches what is actually spent.
* When the hub has no price list yet, the wording falls back to counting requests instead of inventing a sum.

= 0.266.74 =
* Errors tab: tick individual rows and press "Add the ticked ones to the queue". The old button that took the whole finding is still there, next to the explanation.
* Fixed items now disappear from the Errors tab as soon as the queue writes them, instead of waiting for the next audit. Nothing is removed on trust - the page is re-checked live, and the row stays if the problem is still there. A renamed file is re-checked after the rename actually runs.
* The "Add to the queue" button under a finding no longer fires several times. Its handler was attached on every redraw of the explanation panel, so the request piled up.

= 0.266.73 =
* The night checkbox is renamed. "run at night too" said what the plugin does, not what you get; it now says "process in deferred (night) mode", with a tooltip explaining that saving to the site still waits for you.

= 0.266.72 =
* "Run the queue now" now respects what you ticked. With rows ticked it runs only those - at any of the three levels - and the confirmation says how many. With nothing ticked it works by the filters, as before.
* The queue can be restricted to an exact list of task ids, which is what makes the above possible.
* The tick column in Images and Uniqueness is hidden when the paid part is not installed. Without it there is nothing those ticks could do.

= 0.266.71 =
* The run button now says "Run the queue now" instead of "Run by this filter", with a tooltip and a line below it: this is the only button that calls the AI and spends requests, it runs in the background, and it processes whatever matches the filters above.

= 0.266.70 =
* Clearer wording in Pending tasks. "Approve and save the selected" is now "Save to the site", "Decline the selected" is "Do not save them", and "Remove from the queue" is "Delete from the queue". Each button has a tooltip and there is one line under them explaining all three.
* Declining a whole level now covers ready suggestions too, not just the ones still waiting. Before, a ready suggestion survived the decline and could be written later by accident.
* Fixed an undefined variable when a bulk action was run on individually ticked rows.

= 0.266.69 =
* The Mirror no longer prints a PHP warning inside the "Indirect sales" card. The channel rows come from the hub and can arrive in a different shape; a row without a name is skipped, and the name is also read from inside the row when the hub sends a numbered list.

= 0.266.68 =
* New kind of task: a new article. The queue holds a finished skeleton, shows it for review, and starts writing only when you press "Write the text". The draft is created empty at the moment of queueing and nothing is published at any point.
* The article row shows the plan, how far the writing got, and a link to open the draft.
* The list of findings the queue can take moved into one shared place, so the Errors tab and the monthly work plan offer the same set.

= 0.266.67 =
* Meta titles and descriptions are now a kind of task. "Add to the queue" in the Errors tab works for missing, too long and too short titles and descriptions; the page itself is the target, not an image.
* Duplicated titles and descriptions deliberately stay out: they are a group problem and belong with the uniqueness work, not with one page at a time.
* Reasons merge. A page already waiting because of a long title keeps that reason when it also enters because of a missing description.
* A page already waiting for uniqueness is skipped - the meta texts are written there anyway.
* The review row for a meta task shows the current text with its length and an editable field for the new one; "Save" writes into the active SEO plugin (Rank Math, Yoast, SEOPress, All in One SEO).
* Writing meta into the SEO plugin now happens in one shared place, used by both the Errors tab and the queue.
* Restored the three missing save helpers behind the queue - approving a task called methods that were not in the file, which was a fatal error.
* The "more N" link inside the insight blocks now marks itself as handled, so the pro script does not toggle it a second time.

= 0.266.66 =
* Checkboxes are square and readable again. The control-height rule was squashing them, so it was impossible to tell an empty box from a ticked one.
* Ticking a level now says so in words ("selected: 189") and a parent shows a half-state when only part of what is under it is picked. Rows loaded after the tick inherit it, so what will be affected is no longer a guess.
* New "Add to the queue" button in the Errors tab, under "How to fix it", for the image findings. One click from "141 images without alt" to a filled queue, with the same count and credit confirmation as elsewhere.

= 0.266.65 =
* Uniqueness tab: "in the queue" was misleading - it also lit up when only the product's images were queued. It now says exactly what is waiting: "queued for text" or "queued for images".
* Images: "No description" now reads "No image description", in the column header and in the filter, so it is not confused with the product description.
* The "Indirect sales" card now links to the screen that lists which pages actually assist.

= 0.266.64 =
* The plugin now tracks where each image is actually used - as a featured image and inside product galleries - and flags the ones that live in several places at once. Text written for one product would be wrong for the blog post using the same picture, so those images get their own problem type ("Shared image") that can be filtered, and a red badge with the number of places.
* The lookup deliberately reads only the two indexed meta sources. Searching post content by file name was tried and dropped: it scans the whole posts table per image and matches "dress" against "dress-2", which produced false positives.
* The usage answer is cached on the image for seven days and forgotten when the file is renamed.
* Accordion: the checkboxes now read as checkboxes with a "select" label next to them, and expanding a level uses a triangle instead of a plus sign.

= 0.266.63 =
* Pending tasks is now an accordion on three levels: kind of work, then the product or page, then the rows themselves. With hundreds of tasks the flat list was unreadable; now nothing loads until you open it.
* A checkbox on every level. Ticking a kind or a product sends a scope to the server instead of hundreds of ids through the browser, and ticking a level also ticks everything opened under it.
* Approving in bulk now happens on the server from the stored suggestions, so it no longer breaks on a row type it did not expect and no longer stops halfway.

= 0.266.62 =
* Making the text unique now runs from the queue, so "Add to be made unique" is a real button: the series root travels with the task and the finished text waits for review in the series screen.
* One product, one preparation is now active: adding a product to be made unique merges its pending image tasks into it, and images are skipped for products already waiting.
* The confirmation for "Add for analysis" warns about credits again - the analysis does go through the AI, not only the local pre-pass.
* The finished unique text is now reviewed inside the Pending tasks tab: the current value and the new one side by side for title, short and long description and the meta pair, editable before saving, plus the list of images that will be renamed. Saving goes through the same apply path the series screen uses.
* Merging image tasks into a uniqueness task no longer deletes suggestions that were already prepared and paid for - only the ones still waiting.

= 0.266.61 =
* Pending tasks: the list no longer opens filtered to "waiting for review", so the tasks waiting to be processed are visible straight away. The five counters became six (a separate "in progress") and each one is now a button that filters the list to it.
* One height for every control inside the plugin screens - buttons, selects and text fields now line up. Buttons grew a little, fields shrank a little, and both met at 32 pixels.

= 0.266.60 =
* All buttons inside the plugin screens now share one shape and height, so a WordPress button and an AI-module button no longer look different side by side.
* Uniqueness tab: a checkbox in front of every product, a state column (analysed / made unique / in the queue), and two buttons - "Add for analysis" and "Add to be made unique".
* Making a product unique requires a finished analysis. Products without one are skipped with a note instead of burning a request.
* Note: absorbing the image tasks into a uniqueness task is deliberately NOT active yet, because the unique text is still finished by hand from the series screen. Nothing is deleted, so no work is lost.
* Identical image files are recognised by content and analysed once; the text is then copied to the twins where their fields are empty. Model shots reused across every colour no longer cost one request each.

= 0.266.59 =
* New "Pending tasks" sub-tab under SEO. One screen for the whole preparation queue: counters, filters by content type, problem type and state, tasks grouped per page or product, and approval right in the row - read the suggestion, correct a word, press "Save".
* The queue now lives in its own database table (SEOH_Queue), so it can hold thousands of rows and filter fast. Adding is free; the AI cost starts only when the processing is run.
* Images listing: a checkbox in front of every row plus "Add the selected to the queue". Before adding, the plugin counts the images behind the ticked rows and asks the hub how many AI requests are left, then asks for confirmation.
* The "Night image preparation" panel moved out of the Images tab into the new tab.
* One shared button style for the whole plugin, so "Save" and "Suggest with AI" are the same height and shape. "Update" is now "Save" where it means saving.

= 0.266.58 =
* Removed the "save automatically" checkbox from the Images tab. The night queue only prepares suggestions; nothing reaches the site without a human approving it.

= 0.266.57 =
* Image renaming now also fixes the addresses in the content. After a rename, the plugin replaces the old file path with the new one inside post content, covering the full-size file and every generated size in a single pass. Serialized meta is left untouched on purpose.
* Image renaming no longer marks the live image as pending deletion. That was a leftover flag which listed every renamed image among the ones marked for deletion. The files left over from the old name are recorded separately and a daily job removes them after 30 days.
* A suggested name containing a dot ("chair 2.0 oak") is no longer cut short; only real image extensions are stripped.
* Images tab: new "Night image preparation" panel at the top — counts of images waiting for review, done, queued and failed, a per-page list with a "Review" button, and a "Run the preparation now" button. It is drawn only when the Pro queue is present.
* New public helper SEOH_Images::is_generic_name(), so the Pro side can decide whether a file name is nondescript enough to be replaced automatically.

= 0.266.56 =
* Images tab: new "Suggest for all" slot next to "Update all filled" — the AI module walks the listed images one by one and fills name, alt text and description.
* Images tab: new "Add to the task queue" button in the listing — sends every problematic image under the current filter to the Pro night queue.
* New helper SEOH_Images::problem_images() returns the problematic images for a filter; image collection extracted into post_image_ids().

= 0.266.55 =
* Declared compatibility with WordPress 7.1 (Tested up to: 7.1).
* Plugin Check: fixed a LIKE query in the URL-parameter scan to pass its wildcards through a placeholder, and restored the data-layer file-level suppression that a stray phpcs:enable had cancelled, so the rankings and change-history queries no longer report direct-query warnings.

= 0.266.54 =
* Code quality for the Plugin Check tool: passed database table names through %i placeholders in the rankings, change history, database stats and audit queries, and used the full sniff names in the remaining direct-query annotations so the newer Plugin Check recognizes them.

= 0.266.53 =
* Completed the English interface translation: added the remaining page and JavaScript strings so nothing stays in Bulgarian when English is selected.
* Mirror screen: the change figure (for example "(+11,664)") now sits on its own line under the main number and is smaller, so it no longer overlaps the channel breakdown on the right.

= 0.266.52 =
* Conversions & Traffic is now available for every site type, not only WooCommerce stores. Service sites and blogs see conversion goals (calls, emails, forms, bookings, newsletter) plus a Traffic sub-tab. The menu was renamed from "Visits and sales" to "Conversions & Traffic" in both the top and side menus.
* Added a change history that records edits to titles, meta descriptions and content, with who made them and when.
* Many fixes and refinements across the audit, content, speed and settings screens.
* Code quality for the Plugin Check tool: aligned the stable tag with the plugin version, added a missing translator comment, and passed database identifiers through %i placeholders in three prepared statements.

= 0.266.3 =
* Documentation refresh: the readme now describes the full feature set (Mirror, over 100 audit checks, speed from real visitors, GEO/AEO with llms.txt, WooCommerce conversions, rankings, crawl, content planning and monthly tasks).

= 0.266.2 =
* Code quality pass for the Plugin Check tool: translator comments for all placeholder strings, input sanitizing and unslashing on admin AJAX handlers, and database identifiers passed through %i placeholders in prepared statements.
* Replaced array_is_list() with an internal helper to stay compatible with the declared WordPress 6.2 minimum.

= 0.213.5 =
* Removed all remaining paid/locked wording from the English translations and the readme.
* The site now registers with our connection service only when the user clicks "Connect Google account" (no automatic call before consent).

= 0.213.2 =
* The Google connection origin check now runs entirely inside the plugin, with no dependency on the hub database.

= 0.213.1 =
* Fixed the admin stylesheet not loading after the slug change.

= 0.213.0 =
* The "Fix" button now writes titles and meta descriptions directly into the active SEO plugin (Yoast, RankMath, SEOPress, All in One SEO) for everyone, with no restrictions.
* Text domain aligned with the plugin slug (digitalm-insideseo).
* Hardened the Google account connection with an origin check on the return step.

= 0.212.0 =
* Renamed to DigitalM InsideSEO.
* Streamlined the plugin for the WordPress.org directory.
* Menu reorganized: Mirror, Conversions, Rankings, SEO, GEO/AEO, Settings, System info, About.

= 0.211.0 =
* Google connection simplified to a single button.
* Full English translation of the interface.
* Compatibility with WordPress 7.0 AI Connectors (with graceful fallback for WordPress 6.2+).

= 0.210.0 =
* Fixed input sanitization and output escaping across admin screens.
* Added translator comments for all strings with placeholders.
