=== Lumen Pages ===
Contributors: samarora
Tags: seo, bulk pages, landing pages, programmatic, page generator
Requires at least: 6.0
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 1.1.2
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Bulk-create location or keyword targeted pages from one template and a list of terms. Drafts only, so you review every page before it goes live.

== Description ==

Lumen Pages turns a single template plus a list of terms into a batch of SEO-targeted draft pages — the fast way to stand up "service in {city}" style landing pages without copy-pasting.

Write your title and content once, drop `{term}` wherever the term should appear, paste your list of terms (one per line), and click generate. Every page is created as a **draft** so nothing goes live until you have reviewed it.

**Placeholders**

* `{term}` — the term exactly as typed
* `{Term}` — {Term} adds capitals to a word that has none and leaves a word you capitalised yourself alone: miami becomes Miami, while McAllen, DeLand, O'Fallon and HVAC come out exactly as you typed them. (The one thing it still cannot know: a term typed entirely in lower case has no internal capital to preserve, so `mcallen` becomes `Mcallen`. Type it the way you want it printed and it is left alone. Since 1.0.3 the reverse case is safe — `HVAC` and `MIAMI` are passed through as typed rather than flattened to `Hvac` and `Miami`.)
* `{TERM}` — UPPERCASE

**Your own placeholders, one per column (1.1.0)**

A term line can carry more than a term. Name the extra values in the **Columns** field — say `price, landmark` — and separate them in the terms box with a TAB (what you get when you copy cells out of a spreadsheet) or a pipe:

`Plano, TX | 1,900 to 6,400 | Legacy West`

That gives you `{price}` and `{landmark}` — plus `{Price}` and `{PRICE}` — usable anywhere `{term}` is. Not commas, because a place name usually contains one. Leave Columns empty and every line is a plain term, exactly as it was before 1.1.0.

This is the difference between a batch worth publishing and one that is not. One template and one term makes N pages that differ in a noun; the same template with a real cost, a real local rule and a real landmark per row makes N pages that differ in what they say.

**What you get**

* Bulk-create draft pages or posts from one template — no per-run limit
* Title + content templates with placeholders, plus your own named columns
* An optional slug template and an optional meta description template
* The meta description is written to Yoast SEO, Rank Math or SEOPress if one of them is active
* Blank lines are dropped, and two terms that would produce the same page title make one page
* A term whose page title already exists is skipped, so re-running a list after a timeout resumes instead of duplicating
* Every non-blank line you paste is accounted for in the result message
* Every generated draft carries a reminder on its own edit screen saying what it still needs, and
  how similar it currently is to its closest sibling page
* Publishing one with `[PLACEHOLDERS]` still in it warns you and names them
* Remembers your last template
* An example term list in the box: `examples/us-metros.txt`
* No account, no licence key, no external service, no data leaves your site

**What it does not do**

Stated up front, because a plugin that lets you find these out one at a time has wasted your afternoon:

* It does not write content. It fills a template you wrote. What comes out is as good as what you put in.
* It does not upload or parse a CSV or XLSX file. You paste columns into the terms box; there is no file picker and no column mapper.
* It does not set featured images, categories, tags, or any SEO field other than the meta description. Since 1.1.0 it sets title, body, and optionally slug and meta description — and nothing else.
* It does not support All in One SEO's meta description. AIOSEO keeps descriptions in its own database table rather than in post meta, and this plugin will not guess at another plugin's schema. Yoast, Rank Math and SEOPress are the three it writes to.
* It does not publish, schedule or drip-publish. Every page is a draft; publishing is a decision you make one page at a time.
* It does not generate schema, FAQ blocks, internal links or images.
* It does not talk to a server — not to check a licence, not to check for updates, not to send usage data. Which also means it cannot tell you a new version exists. The changelog is at https://lumenandink.com/plugin/versions if you want to check.

The plugin is free and has no paid edition: there is no licence key, no cap, and no feature in it that payment unlocks. A separate pack of template starting points and term lists is sold off-site; the plugin does not need it and does not check for it. See https://lumenandink.com/plugin for exactly what is and is not built.

**Reading it before you run it**

It is one PHP file. The whole source is published as plain text, and the download has a published SHA-256, both at https://lumenandink.com/plugin — so "read every line before you install it" is an instruction you can actually follow.

== Installation ==

1. Upload the `lumen-pages` folder to `/wp-content/plugins/`, or install the ZIP via Plugins → Add New → Upload Plugin.
2. Activate the plugin through the Plugins screen.
3. Open **Lumen Pages** in the admin menu, enter a title template, a content template, and your list of terms, then click **Generate draft pages**.
4. Review the drafts under Pages (or Posts) and publish the ones you want.

Start with three terms, not three hundred. Look at what comes out, adjust the template, then scale.

== Frequently Asked Questions ==

= Do generated pages go live automatically? =

No. Every page is created as a draft. You choose what to publish.

= Will {Term} mangle names like McAllen, or acronyms like HVAC? =

Not since 1.0.3, and the two halves were fixed in two different releases.

Versions 1.0.0 and 1.0.1 ran every term through an unconditional Title Case, which turned McAllen into "Mcallen" and DeLand into "Deland". 1.0.2 fixed that: a word already in mixed case is passed through untouched.

1.0.2 still flattened ALL-CAPS words, because it treated "no lower-case letters" as "no case information". `HVAC repair` came out "Hvac Repair", `AC repair` "Ac Repair", `SEO services` "Seo Services" — while the plugin's own admin screen said it never removes capitals. 1.0.3 leaves an all-upper-case word of more than one character exactly as typed. The trade-off, stated plainly: a term pasted as `MIAMI` now stays `MIAMI` rather than becoming `Miami`, because `MIAMI` is also what you typed.

A term typed entirely in lower case still has no internal capital to recover, so `mcallen` becomes `Mcallen`.

= I pasted both Miami and MIAMI and only got one page. Why? =

Because deduplication is case-insensitive and happens on the generated title, not on the term.

`{Term}` treats `Miami` and `MIAMI` as different — that is the 1.0.3 fix above — so they produce two different titles. But two pages whose titles differ only in capitalisation are two near-identical pages, which is the thing a bulk generator most needs to not do. So the second one is dropped and counted under "duplicate or already existing", and which one survives is whichever appeared first in your list.

If you genuinely want both, make the titles differ by more than case.

= My term had a straight apostrophe and the page title shows a curly one. Did the plugin change it? =

No. The title is stored exactly as you typed it — `O'Fallon, MO` goes into the database as
`O'Fallon, MO`, straight apostrophe and all. What you are seeing is `wptexturize`, a WordPress
display filter that turns straight quotes into typographic ones when a title is printed. It does
that to every post title on your site, whether this plugin made it or not, and it does not change
what is stored. If you want to turn it off, that is a WordPress-level decision (the
`run_wptexturize` filter), not something this plugin should do to your whole site behind your back.

= Is this "thin content" that hurts SEO? =

A batch of near-identical pages is exactly the pattern search engines discount, and this plugin cannot stop you making one. It fills a shared template, so what you publish is thin unless you edit every draft first. It creates drafts, never published pages, precisely so that editing step cannot be skipped. It does not write content for you.

What follows is the checklist used on the 91 metro pages at restorationanswers.com, which were built this way. It is a description of a process, not a promise about rankings — that site's traffic is small and no claim is made here about what it earns.

Before publishing a generated page, make at least these differ from every sibling:

* a price or cost range specific to that place, with the source named
* named local detail — neighbourhoods, counties or ZIPs a resident would recognise
* one rule that genuinely varies by location: a permit requirement, a state insurance deadline, a licensing body
* the meta description, written per page rather than templated
* the intro paragraph, so the first thing a reader sees is not the same sentence with a swapped noun

And do not leave these identical across the set: the H1 pattern with only the place name changed, a boilerplate hero, an FAQ block repeated verbatim.

Google's own guidance on this is worth reading before a bulk run — see the doorway pages page in Google Search Central's spam policies: https://developers.google.com/search/docs/essentials/spam-policies#doorways

If you cannot make a page meaningfully different from its neighbours, that page should not exist. Deleting the draft is a legitimate outcome of a run.

= Does it send my data anywhere? =

No. The plugin runs entirely on your own site and makes no external requests.

= Is there a per-run limit? =

No. Every term you paste gets a draft. Version 1.0.0 capped a run at 10 pages; 1.0.1 removed the cap entirely, along with the licence check and upgrade prompt that went with it. Very long lists take a moment, and a host with a short PHP execution limit may still cut a run short — if that happens, run the same list again. Since 1.0.2 that is safe: a term whose page title already exists is skipped rather than duplicated (post_exists), so a second run resumes where the first stopped. The screen also tells you roughly how far the interrupted run got.

= How long a list is actually safe? =

Measured rather than guessed, on a plain WordPress container over the real admin form:

* 5,000 terms went through in one request in about 20 seconds and created 5,000 drafts.
* Re-running that same 5,000-term list created 0 and skipped 5,000 in about 12 seconds — the resume path, at scale.
* 20,000 terms on a deliberately locked-down host (a 20-second PHP limit, `set_time_limit` disabled) stopped at roughly 12,800 with the host's own "critical error" page. The next visit to the Lumen Pages screen said "a previous run stopped after about 12,780 draft(s)", and running the identical list again created the remaining 2,212 and skipped the 17,788 that already existed — 2,212 + 17,788 = 20,000, and the totals reconciled on screen.

Your host is not this container, so treat those as shape rather than as a promise. The practical rule: a few thousand terms per run is comfortable, and nothing is lost if a longer one is cut short.

= How do the columns work? =

Put the names in the **Columns** field, comma-separated: `price, landmark`. Then separate the values in each term line with a TAB or a pipe. `Plano, TX | 1,900 to 6,400 | Legacy West` fills `{price}` with `1,900 to 6,400` and `{landmark}` with `Legacy West`, in the title, the body, the slug template and the meta description template alike. `{Price}` and `{PRICE}` give you the same value title-cased and upper-cased, by the same rule `{Term}` uses.

Commas are not a separator, deliberately: the terms this plugin exists for are place names, and "Plano, TX" has a comma in it.

A name is lower-cased and reduced to letters, digits and underscores, so `Price Range` becomes `{price_range}`. `term` is rejected as a column name — the first value on every line is already the term.

A row with fewer values than there are columns is not an error: the missing placeholders fill as empty, and the result message tells you how many rows that happened to, so you find out once rather than on page forty.

= Which SEO plugins does the meta description template write to? =

Yoast SEO (`_yoast_wpseo_metadesc`), Rank Math (`rank_math_description`) and SEOPress (`_seopress_titles_desc`). The screen names whichever one it detected before you run, so you know where it is going.

All in One SEO is detected and deliberately not written to: since v4 it stores descriptions in its own `aioseo_posts` table rather than in post meta, and writing there from here would mean guessing at another plugin's schema and half-working silently.

Either way the description is also stored on each draft as `_lumen_pages_meta_description`, so switching SEO plugins later does not lose what you wrote.

= What is the warning on a generated draft's edit screen? =

Every page this plugin creates carries a `_lumen_pages_generated` marker, and since 1.1.0 the edit screen for such a page says what it still needs before it is worth publishing. The generator screen's warning is seen once, by whoever ran the batch; the publish decision usually happens later and often by someone else. This one travels with the page.

Since 1.1.1 it also gives you the number that matters: how similar this page currently is to the closest of its siblings, as a percentage. "Make each page meaningfully different" is advice; "this page is 96% identical to the Venice one" is something you can act on.

If you publish a generated page that still contains `[SOMETHING IN CAPITALS AND BRACKETS]` or the word `REPLACE`, or that is 90% or more identical to another generated page — the convention templates use for the parts only you can fill — the next admin screen warns you and names what was left in, for every page in a bulk publish. It warns; it does not block. It is your site.

= Is there a paid version? =

No. The plugin is free and has no paid edition: there is no licence key, no cap, and no
feature in it that payment unlocks. This entry used to end “This plugin is the whole
product”, and that is still true of the plugin. What changed is that a separate pack of
template starting points and term lists is now sold off-site. The plugin does not need it,
does not check for it, and nothing was removed from the plugin to make room for it.

= What do I do if a run stops partway? =

Open Lumen Pages again. If the previous run was interrupted you will see a notice at the top saying
roughly how many drafts it had created before it stopped. Re-run the same list: a term whose page
already exists is skipped rather than duplicated, so the second run resumes where the first one
stopped instead of doubling your drafts. If it stops in the same place twice, your host's PHP time
limit is the cause — split the list in halves and run each half.

= Does the content template accept blocks, shortcodes or page-builder markup? =

It accepts HTML, filtered through WordPress's own `wp_kses_post`, which is the same filter that
governs what an Editor role may put in a post. Shortcodes survive and run when the page renders.
Block markup survives as HTML but the result is a Classic-style block, not a set of individual
blocks you can drag around. Page builders that keep their layout in post meta rather than in
post content will not see anything this plugin creates, because it writes post content only.

= What happens to my terms and templates if I delete the plugin? =

Deleting the plugin removes the one option it stores — the last title template, content template
and post type it remembered — and its short-lived transients. It does **not** touch the pages it
created. Those are your content, edited or not, published or not; a plugin that deleted a batch of
pages on uninstall would be the most destructive thing in the file. Generated pages carry a
`_lumen_pages_generated` post meta key if you want to find them later.

= Which capability does it require? =

`manage_options`, on both the menu screen and the generate action, plus a nonce on the form.
In practice that means administrators only.

== Support ==

Email hello@lumenandink.com. That is one person, not a helpdesk: there is no ticket system and no
promised reply time. The plugin is free and asks for no account and no licence key, so there is
nothing to refund and nothing to cancel. Include your WordPress version, your PHP version and the
exact terms you pasted and you will get a far better answer.

The same address is on the plugin's own screen in wp-admin and on its row on the Plugins screen,
so you never have to come back to the website to find it.

== Screenshots ==

1. The generator screen, filled in: a title template, a content template, and a list of terms. The help text under each field states the rules, including that there is no per-run limit and that an existing page is skipped rather than duplicated.
2. The same screen after the run. Twelve lines were pasted, two of them blank; "Miami" and "miami" collapse to one page. Nine drafts created, one dropped as a duplicate, out of ten non-blank lines — and the message says so.
3. The nine drafts, in the Pages list. Every one is a draft. McAllen, DeLand, O'Fallon, HVAC and SEO came out exactly as they were typed, and the line containing only "0" produced a page instead of vanishing.

== Changelog ==

= 1.1.2 =
* Added: one line at the bottom of the plugin's own screen pointing at the Templates & Term
  Lists Pack, which is a separate off-site product. It is one link, on that screen only, below
  the form — not a banner, not a notice, not a dashboard widget, and it does not appear on
  activation or on any other screen.
* Nothing is gated. The plugin is uncapped and complete, no code path in it branches on whether
  you own the pack, and it makes no network request to find out. No price is printed in the
  plugin: a price belongs on the store page, where it can change without a plugin release.
* Changed: the “no paid edition, no checkout and no upsell” sentence in the description and the
  “Is there a paid version?” answer were true when written and are not any more. Both are
  corrected above rather than removed.
* No change to what the plugin generates. 1.1.1's behaviour is byte-for-byte intact.

= 1.1.1 =
* Added: the reminder on a generated page now says how similar it actually is — "this page is about
  96% identical to <the closest other one>" — instead of only telling you to make it different. And
  publishing a page that is 90% or more identical to a sibling is reported the same way an unfilled
  placeholder is, including through a bulk publish. Still a warning, still not a block.
* Comparison is over stripped, lower-cased text, capped at 2,000 characters and 20 sibling pages, so
  an edit screen never gets slow to tell you something you could have seen by looking.
* Nothing else changes. Released as its own version rather than folded into 1.1.0 because 1.1.0's
  zip was already published with its SHA-256 beside it, and changing bytes behind a published hash
  is the one thing this plugin must never do.

= 1.1.0 =
* Added: **named columns**. A term line can carry extra values, separated by a TAB or a pipe, and
  the new Columns field names them — `price, landmark` gives you `{price}` and `{landmark}` (and
  `{Price}`, `{PRICE}`) anywhere `{term}` works. Up to 1.0.7 this plugin substituted exactly one
  value, which is why a run over a city list produced pages that were the same sentence with the
  place name swapped. The screen has warned since 1.0.6 that publishing that is doorway content;
  this is the first release that gives you a way to fix it rather than only a warning about it.
  Not commas: "Plano, TX" contains one. There is still no CSV *file* upload.
* Added: an optional **slug template**. Empty means WordPress derives the slug from the title, as
  it always has.
* Added: an optional **meta description template**, written to Yoast SEO, Rank Math or SEOPress if
  one of them is active, and stored on every draft as `_lumen_pages_meta_description` regardless.
  All in One SEO is named on screen as unsupported rather than silently skipped: it keeps
  descriptions in its own table, and this plugin will not guess at another plugin's schema.
* Added: the differentiation reminder now travels with the draft. Every generated page says on its
  own edit screen what it still needs, because the person publishing a batch a week later never saw
  the generator screen. (Found while implementing it: `admin_notices` output is invisible in the
  block editor — WordPress renders it into a container that JavaScript hides — so the block editor
  is served through its own notices API and the PHP notice is suppressed there to avoid a double.)
* Added: a publish guard. Publishing a generated page that still contains `[CAPITALS IN BRACKETS]`
  or the word `REPLACE` warns you afterwards and names what was left in, including for a bulk
  publish of the whole batch. It warns and does not block.
* Added: the generator screen now says which places you should not generate pages for — only ones
  you genuinely serve — and states, from measurement, how long a list one run can handle.
* Fixed: the link to Google's doorway-pages policy in the on-screen warning was plain text, so the
  one reader it was written for had to retype it. It is a link.
* Fixed: two first-run dead ends found by watching someone use the screen. An empty Title template
  reported "Created 0. Skipped 2." and named no cause; an empty terms box reported "From 0 non-blank
  line(s)" as though that were a result. Both now say what happened and what to do about it.
* Tested against WordPress 7.1 on PHP 8.3 and WordPress 6.6 on PHP 8.2, both on throwaway
  containers: the columns, the slug and meta-description templates, the block-editor reminder, the
  publish guard, adversarial markup in all three input fields, and the capability/nonce matrix
  (a forged nonce, a blank nonce, and a Subscriber, Contributor and Editor each blocked).
* Unchanged: `{Term}` casing, deduplication, idempotence, the counting the 1.0.7 fix produced, and
  the absence of any network call, licence check, cap or price. With the Columns field empty the
  plugin behaves exactly as 1.0.7 did, which the fixture corpus asserts rather than assumes.

= 1.0.7 =
* Fixed: the result message's own numbers did not add up. Lines that were an exact repeat of an
  earlier line were dropped before anything counted them, so pasting eighteen lines with one of them
  typed twice reported "Created 16 draft page(s). Skipped 0. Duplicate or already existing: 1. From
  18 non-blank line(s)" — 16 + 1 = 17, not 18 — while this readme promised every non-blank line you
  paste is accounted for. Repeated lines are now counted as duplicates, so created + skipped +
  duplicate/already-existing always equals the non-blank line count the message declares, and the
  message adds how many of those duplicates were the same line pasted twice.
* No change to which pages are created, how terms are cased, or anything the plugin writes to your
  database. This is a counting fix.

= 1.0.6 =
* Added: a non-dismissable caution on the generator screen, immediately above the Generate button.
  This plugin's whole job is making it easy to mass-produce near-identical "service in {city}" pages
  — which is the exact pattern Google's doorway-content policy targets — and until now the screen
  explained what the plugin does mechanically (drafts, dedup, no cap) but never said, at the point of
  action, that publishing the raw batch can be treated as thin/doorway content and that each page
  needs genuine local specifics first. It says so now, with a pointer to Google's policy. The full
  pre-publish checklist is still in the "Is this thin content?" answer below.
* No change to how terms are cased or how pages are generated. This release is a documentation and
  on-screen-honesty fix only; the {Term} behaviour and every build gate are unchanged.

= 1.0.5 =
* Fixed: the plugin stamped the wrong version on everything it created. `LUMEN_PAGES_VERSION` was
  never bumped past 1.0.3, and it is written into each generated draft as the
  `_lumen_pages_generated` post meta value — so every page 1.0.4 produced claimed to have been made
  by 1.0.3. The build gates read the header and never compared it to the constant. They compare it
  now, and the build fails if the two disagree.
* Fixed: the result notice said "From N line(s) pasted" while N counted only non-blank lines.
  Paste eleven lines of which two are blank and 1.0.4 answered "From 9 line(s) pasted", which is
  not what you pasted. It now reads "From N non-blank line(s)."
* Added: an uninstall routine. Up to 1.0.4, deleting the plugin left its saved templates in
  your options table forever. Deleting it now removes that option and the plugin's transients
  — and deliberately does not remove the pages it made, which are yours. It is registered from
  inside the one plugin file rather than from a second uninstall.php, so that the source
  published at lumenandink.com/plugin remains the whole of the plugin's code.
* Changed: the Plugin Name header is now "Lumen Pages" rather than "Lumen Pages – Bulk SEO Page
  Generator", so that the plugin's slug, its folder and its text domain are all the same string.
  Nothing about the plugin's behaviour changes.
* Removed: `load_plugin_textdomain()`. Unnecessary for a directory-hosted plugin since WordPress
  4.6, and this plugin requires 6.0. Translations still load.
* readme: added a "What it does not do" section, and answers for the Miami/MIAMI deduplication
  question, page-builder and shortcode support, what uninstalling removes, and which capability
  is required.
* No change to how terms are cased or how pages are generated.

= 1.0.4 =
* Support is now reachable from inside WordPress: the address is printed on the plugin screen and
  added as a Support link on the Plugins row, so a half-finished run no longer needs the website.
* Accessibility: the help text under the title, content and terms fields is now programmatically
  associated with the field (aria-describedby), so a screen reader announces the rules — including
  "no per-run limit" and "an existing page is skipped, not duplicated" — instead of leaving the
  field silent.
* readme: added a Support section and a "what do I do if a run stops partway" FAQ entry.
* No change to how pages are generated.

= 1.0.3 =
* Fixed: `{Term}` flattened ALL-CAPS words. `HVAC repair` became "Hvac Repair", `AC repair` "Ac Repair", `BBQ catering` "Bbq Catering", `SEO services` "Seo Services", `US 1 corridor` "Us 1 Corridor" — while this plugin's own admin screen said "{Term} only adds capitals, it never removes them". It was reported by someone who had verified the SHA-256 and diffed the published source against the zip, then ran the function on his own client list. An all-upper-case word of more than one character is now passed through exactly as typed. Consequence, stated because it is a real trade-off: `MIAMI` now stays `MIAMI` instead of becoming `Miami`.
* Fixed: the description of `{Term}` existed as three separately hand-written sentences — in the plugin file, in this readme, and on lumenandink.com — and the one in the plugin file was the false one. The rule is now defined once, in `LUMEN_PAGES_TERM_RULE` in lumen-pages.php, echoed by the admin screen and quoted verbatim here and on the website.
* Fixed: 1.0.2's changelog and code comments referred to "the plugin's own example list" / "this plugin's own Florida example corpus". No such file was in the 1.0.2 zip. Corrected, and an example list now actually ships (below).
* Added: `examples/us-metros.txt` — the 91 metro areas restorationanswers.com publishes water-damage cost pages for, one per line, derived from that site's public sitemap (https://restorationanswers.com/sitemap.xml). An example input, not a complete list of anything.
* Expanded the "is this thin content?" answer in this readme into the actual pre-publish checklist used on those 91 pages, with a link to Google's doorway-pages policy.

= 1.0.2 =
* Fixed: `{Term}` destroyed capitals you had already typed. McAllen became "Mcallen", DeLand "Deland", McKinney "Mckinney", LaGrange "Lagrange", DeSoto "Desoto", LaBelle "Labelle" and O'Fallon "O'fallon", using the plugin's own default title template. `{Term}` stopped re-casing a word that was already mixed case. (This entry said "now only adds capitals to a word that has none"; that was not true of ALL-CAPS words until 1.0.3. It also referred to "the plugin's own example list", which 1.0.2 did not ship.)
* Fixed: refreshing the browser after a run silently re-ran the whole batch. Submissions are now processed before output and redirected (post/redirect/get), so a refresh creates nothing.
* Fixed: re-running a list — which the FAQ and the admin screen both tell you to do after a timeout — created a second set of duplicates. A term whose page title already exists is now skipped.
* Fixed: a line containing exactly `0` was silently discarded when the term list was parsed.
* Fixed: "Miami" and "miami" both survived deduplication and then produced two pages with an identical title. Deduplication now happens on the generated title.
* Added: a run interrupted by a PHP time limit leaves a marker, and the next visit to the screen reports roughly how many drafts it managed and that re-running is safe.
* Added: the result message now accounts for every line pasted — created, skipped, duplicate or already existing, out of how many lines.

= 1.0.1 =
* Removed the 10-pages-per-run cap. All supplied terms are now generated.
* Removed the licence check, the self-hosted update channel and the in-admin upgrade prompt — the plugin has no paid tier and no network code of any kind.
* Added the verbatim GPL-2.0 licence text (LICENSE) and a copyright/warranty notice to the plugin file.
* Tested against WordPress 7.0.2.

= 1.0.0 =
* Initial release: bulk draft-page generation from a template and a term list, with {term}/{Term}/{TERM} placeholders.

== Upgrade Notice ==

= 1.1.2 =
Adds a single link at the bottom of the plugin's own screen to a separate, optional pack of templates and term lists. Nothing in the plugin is gated, capped or licence-checked, and its behaviour is unchanged — if you do not want the pack, this release changes nothing for you.

= 1.1.1 =
The reminder on each generated page now gives you the number: how similar this page is to its closest sibling, and a warning if you publish one that is 90% or more identical to another. Nothing else changes.

= 1.1.0 =
Adds named columns, so a batch can differ in its facts and not only in one noun — plus optional slug and meta-description templates, a reminder on every generated draft, and a warning if you publish one with placeholders left in. Leave Columns empty and it behaves exactly as 1.0.7 did.

= 1.0.7 =
Fixes the result message under-counting: a line pasted twice was silently dropped and appeared in none of the figures, so the totals did not add up to the line count the message stated. They add up now. No change to which pages get created.

= 1.0.6 =
Adds a plain warning above the Generate button: publishing a raw batch of near-identical pages is thin/doorway content, and each page needs real local detail before it goes live. No change to how pages are generated.

= 1.0.5 =
Fixes the version this plugin stamped on the drafts it created (it said 1.0.3 throughout 1.0.4), corrects a result message that under-counted the lines you pasted, and cleans up its saved option when you delete it. No change to how terms are cased.

= 1.0.3 =
Fixes {Term} turning HVAC into "Hvac" — an all-caps word is now left exactly as typed. Note that MIAMI now stays MIAMI too. Recommended for everyone, and required if your term list contains acronyms.

= 1.0.2 =
Fixes {Term} mangling names that already had capitals (McAllen became "Mcallen"), and makes a refresh or a re-run stop creating duplicate drafts. Recommended for everyone.

= 1.0.1 =
Removes the per-run page cap and all licence/upgrade code. Recommended for everyone.
