# Changelog

All notable changes to **WedFlow Atelier** are tracked here. The format
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.4.0] — More payment gateways

Adds six payment providers alongside Stripe on a pluggable gateway layer.
One migration; **no new Composer dependencies** (each gateway talks to its
provider's REST API via the HTTP client). Existing Stripe installs are
unaffected — Stripe keeps its current configuration.

### Added

- **PayPal, Paystack, Flutterwave, Razorpay, Mollie, and an offline /
  bank-transfer option** — each with a hosted checkout, a signature-verified
  webhook, a redirect-return verification, and idempotent plan activation.
  Buyers pick a method at checkout whenever more than one is enabled.
- **Admin → Payment Gateways** (`/admin/payment-gateways`) to enable and
  configure each provider (secrets masked, per-gateway webhook URLs shown) and
  set the shared price + currency the non-Stripe gateways charge.
- **Admin → Payments** (`/admin/payments`) — payment history plus a one-click
  approve for offline bank transfers (which then activates the plan).
- A pluggable `PaymentGateway` layer (`PaymentManager` + per-provider gateway
  classes), so further providers are a drop-in class.

### Database

These run automatically and preserve existing data:

- `add_payment_gateways` — a `payments` table (records every attempt; the
  unique reference makes activation idempotent across the return and webhook)
  plus an encrypted `payment_gateways` config column on `platform_settings`
  (reversible).

## [1.3.1] — Demo login fix (mobile Safari)

Patch release. No migrations, no new dependencies.

### Fixed

- **Direct login on mobile Safari / iOS for public demo installs.** A demo
  embedded in the CodeCanyon preview iframe needs `SameSite=None; Partitioned`
  session cookies, but Safari/iOS drop those in a first-party context, so
  *direct* top-level login appeared to do nothing. Cookies now default to
  `Lax` (direct login works everywhere) and are upgraded to
  `SameSite=None; Secure; Partitioned` **only** for sessions actually embedded
  in an iframe — detected via `Sec-Fetch-Dest` on the framed document load and
  persisted per session (a new `DetectEmbedding` middleware). Demo-mode only;
  normal installs are untouched.
  - Demo operators: set `SESSION_SAME_SITE=lax`, `SESSION_PARTITIONED_COOKIE=false`
    and `SESSION_DOMAIN=.yourdomain.com` (covers www + non-www), then clear the
    config cache.

## [1.3.0] — Premium website themes

Adds a set of premium public-wedding-site themes and an in-editor visual
theme picker. Frontend only — no migrations, no new dependencies, no
configuration changes; fully backward compatible.

### Added

- **Four premium wedding-site themes**, each a distinct full layout (its own
  typography, ornaments, hero treatment and section rhythm) rather than a
  palette swap over the shared layout:
  - **Lumière** — romantic, airy, centred; Cormorant Garamond + champagne gold.
  - **Marquee** — bold, modern, asymmetric split-screen hero; Fraunces + claret.
  - **Botanica** — light organic garden; Gilda Display + sage, botanical sprigs.
  - **Soirée** — dark art-deco glamour; engraved Cinzel caps + gold on charcoal.
  They share the existing section data model, so couples switch with one click,
  and they ship localised (en/fr) via the shared `wsite.*` keys. Fonts load
  on demand (preconnect + hoisted `<link>`), only when a theme renders.
- **Theme preview gallery in the website editor** — the theme picker is now a
  grid of live mini-mockups that render each theme's hero with the couple's
  real names in that theme's display font and palette, so themes are chosen by
  sight rather than by name.
- **Plan-gated premium themes.** Premium themes follow the wedding owner's plan
  (like guest/collaborator limits): selectable on Premium/Planner, shown locked
  with an upgrade prompt to `/pricing` on Free. Enforced server-side in
  `WebsiteController` as a backstop to the editor lock.

### Changed

- Premium themes render as their own full layout (keyed in `PREMIUM_LAYOUTS`)
  rather than via the shared palette-swap renderer — adding future themes is a
  drop-in component plus a one-line registration.

## [1.2.0] — Localisation, vendor comparison & data export

A feature release built on top of the 1.1 structural work. Everything is
additive and backward-compatible — existing installs upgrade with
`php artisan migrate` (one new nullable column; see Database). No data is
lost and no configuration changes are required.

### Added

- **Full interface localisation.** Every screen — the whole app
  (dashboard, checklist, budget, guests, seating, vendors, timeline, crew,
  inspiration, website, collaborators, photos), the auth and invite flows,
  the public RSVP / photo-share / wedding-site pages, the marketing landing
  and pricing pages, and the admin panel — now renders through the
  `useTranslator()` hook. **English and French ship complete**, at full key
  parity (`lang/en.json` / `lang/fr.json`), and dates on guest-facing pages
  format per the active locale.
- **In-app language switcher** in the sidebar footer, backed by the
  `SetLocale` middleware and a session-stored locale (works for signed-in
  users and anonymous guests alike).
- **Admin language management** (`/admin/languages`) — add a locale, set the
  default, enable/disable which locales appear in the switcher, and edit
  every translation string in a per-language editor with live progress and
  search. New languages start as English and are translated from the panel;
  blank strings fall back to English.
- **Editable plan display prices.** The Premium / Planner prices shown on the
  public `/pricing` page are now set from **Admin → Settings → Billing**
  (free-text, e.g. "$99") instead of being hard-coded.
- **Vendor ratings.** Research vendors can be rated 0–5 stars, inline from
  the list or in the edit form.
- **Side-by-side vendor comparison.** In the Vendors → Research tab, a
  **Compare** mode lets the couple tick candidates and see them compared in a
  pinned panel — category, status, rating, price estimate, contact details
  and notes — with the **lowest price** and **top rating** highlighted and a
  flag when vendors from different categories are mixed.
- **Vendor search, filter and sort.** The research list gains a name/category
  search box, a status filter, and sorting by name, rating, price or
  category.
- **PDF + Excel export** on every list page — Guests, Vendors, Budget,
  Checklist, Timeline, Seating and Crew. An **Export** menu produces a
  print-ready PDF (jsPDF + jspdf-autotable) or a real `.xlsx` workbook
  (SheetJS); budget amounts export as numbers so they stay summable. Both
  libraries load on demand.
- **Direct sharing link on the QR cards.** The seat-finder and photo-share
  QR card dialogs now show the public share URL with a Copy button, for
  guests who can't scan the printed card.

### Changed

- **Website editor image upload is clearer.** Image fields (including the
  hero background) now show a proper **Upload image** button with a live
  thumbnail preview, an uploading state, and a **Remove** action — and the
  control no longer disappears when a long image URL is present. Pasting a
  URL still works.

### Database

These migrations run automatically and preserve existing data:

- `create_locales_table` — new `locales` table (code, label, JSON string
  overrides, default/enabled flags) backing the language manager; seeded with
  English (default) and French from the bundled `lang/*.json` files.
- `add_plan_prices_to_platform_settings` — nullable `premium_price` and
  `planner_price` columns on `platform_settings` for the editable display
  prices (reversible).
- `add_rating_to_vendors` — nullable `rating` (0–5) column on `vendors`
  (reversible).

## [1.1.0] — Structural reviewer-audit release (Q11 follow-through)

Clears the remaining shippable items from the Q11 audit: the v1.0.7 bug
batch plus the structural reworks that were committed for v1.1. All data is
migrated in place — existing installs upgrade with `php artisan migrate`
(see Database below); no manual data entry is lost.

### Fixed (bugs)

- **#2 "New Wedding" silently did nothing at the plan cap.** Premium (like
  Free) allows one wedding, so clicking *New Wedding* with one already
  created submitted into a server-side gate with no feedback. The page now
  knows the cap up front and shows an upgrade panel instead of a dead-end
  form.
- **#6 Floor-plan grid only appeared after a tab switch.** The v1.0.1
  `requestAnimationFrame` patch bumped a state value that was never rendered,
  so the DOM never changed and the SVG `<pattern>` never repainted. The `<svg>`
  now remounts one layout pass after mount via `useLayoutEffect` (before
  paint, no flash), so the grid renders on first load.
- **#8 Uploaded photos didn't display on some hosts.** The `/storage/{path}`
  serve route was bound to the private `local` disk, so on shared/cPanel
  hosts without the `public/storage` symlink uploaded images 404'd. The
  `public` disk now owns that route and streams files through PHP as a
  fallback when the symlink is absent.
- **#14 Inspiration "Add" could silently fail.** The image upload swallowed
  error responses (an oversized or unsupported file left the URL unset with
  no feedback). Uploads now report failures and show progress, form
  validation errors are surfaced, and the image field accepts relative
  storage paths as well as absolute URLs.

### Changed (structural reworks)

- **#21 Seating and Floor Plan unified.** Seating tables are now the single
  source of truth — adding, renaming, re-sizing, or deleting a table
  automatically creates, updates, or removes the matching floor-plan element
  (positions the couple arranges are preserved). The floor-plan palette now
  offers only décor and features. This also retires the old **#7**
  "shouldn't be able to add a 7th table" over-creation by design.
- **#10 Expenses merged into Categories.** The separate Expenses tab and the
  dual *Estimated / Final cost* model are gone. Each budget category is now
  the line item: a **Budgeted** amount plus a **Spent** amount the couple
  updates as real costs land. The Overview flags categories that go over.
- **#5 Contributors removed from the budget.** The couple just sets a total
  budget; the dashboard and budget summary now show **Budget remaining**
  (total − spent) instead of a contributor-derived cash surplus.
- **#25 Collaborators simplified.** The page leads with plain-English role
  summaries ("Family — can edit Inspiration; can view Checklist, Budget, …");
  the full role × section matrix moved behind an owner-only **Advanced
  permissions** disclosure.
- **#17 Timeline category dropped.** Redundant once an event has a title — the
  category selector and its colour-coding are gone in favour of one clean
  card style.

### Added

- **#9 Multi-name party members.** When a guest's party size is greater than
  one, the couple can optionally name the other members; the names flow
  through to the seating chart's per-person assignment (falling back to
  "<First> Guest N" when left blank, so existing seat assignments still
  resolve).

### Internationalisation (Q1)

The translation scaffolding shipped in v1.0 but was never wired up and had no
way to switch languages. It's now functional:

- **`t()` is wired through the app chrome** — the whole sidebar navigation
  (plus My Weddings, Admin Panel, Log out) renders from `lang/{locale}.json`.
  Per-screen string extraction is ongoing; the helper and the shared
  `translations` prop are in place for it.
- **In-app language switcher** in the sidebar footer, backed by a new
  `SetLocale` middleware and a session-stored locale (works for guests and
  signed-in users). Locales are declared in `config('wedflow.locales')`.
- **French sample** — `lang/fr.json` ships as a complete second locale (full
  key parity with `lang/en.json`); add another language by dropping in its
  JSON file and listing it in the config.

### Database

These migrations run automatically and preserve existing data:

- `add_party_members_to_guests` — nullable JSON column for #9.
- `drop_budget_contributors_table` — #5 (reversible).
- `merge_expenses_into_categories` — adds `spent_amount` + `notes` to
  `budget_categories`, folds each category's expense actuals into
  `spent_amount`, promotes any uncategorised expense to its own category,
  then drops the `expenses` table (reversible).

### Still investigating (need a reviewer capture)

- **#1** intermittent navigation and **#3** "had to quit and try again" when
  the admin creates a wedding could not be reproduced locally (the admin
  account is on the unlimited Planner plan, so no gate fires). Both need a
  browser **Network-tab capture** taken during a failure to diagnose between
  an Inertia prefetch race, a transient server response, and a client-side
  navigation block.

## [1.0.6] — Second reviewer-audit patch (Q11)

A returning-reviewer audit on v1.0.5 surfaced a second batch of bugs and
UX rough edges. Path A scope: ship the obvious fixes in this release,
keep investigation-required items for v1.0.7, structural reworks for v1.1.

### Fixed (bugs)

- **Seating "0 / 0888888 seated" math.** `tables.reduce()` was summing
  capacity values that arrived from the API as strings, so `0 + "8"` got
  coerced to `"08"` and so on. Wrapped each value in `Number()` —
  six 8-seat tables now correctly read "48 seats" instead of "0888888".
- **Guest "party count cannot be cleared."** `Number('')` returns `0`,
  which then can't be backspaced because the field already shows `0`.
  Switched both add and edit forms from `type="number"` to
  `type="text" inputMode="numeric"` and used the same on-blur-defaults-to-1
  pattern the Budget Vision input adopted in v1.0.1.
- **Expense partial-fill 500 error.** When a couple filled Estimated but
  left Final cost blank (or vice versa), the `target_amount` /
  `actual_amount` columns received `NULL` after `ConvertEmptyStringsToNull`
  ran — but both columns are `NOT NULL DEFAULT 0` at the DB layer. Added a
  `?? 0` coercion in `storeExpense` / `updateExpense` so partial fills now
  save cleanly with zero on the unfilled side.
- **Budget categories: no add or delete UI.** The seeder created the
  starter categories but couples had no way to delete ones they didn't
  need (e.g. Flowers) or add ones they did (e.g. Alcohol). Added an
  inline add form at the top of the Categories tab plus a per-row
  Delete button with a confirm prompt. Controller routes and `Plans` rules
  already supported this — only the UI was missing.
- **Budget category percentage now shows alongside the total.** "12%"
  was opaque on its own; now displays "12% of $30,000" using the wedding's
  Total Budget so the percentage actually means something at a glance.
- **Vendor edit button.** Research vendors and Booked vendors both had
  delete but no edit — meaning the couple couldn't change a vendor's
  Status (e.g. Researching → Quote Received) or Contract Signed toggle
  after creation. Re-wired the existing Add modal to double as an Edit
  modal, with an Edit button on every vendor row.
- **Floor plan over-creates tables.** When Seating had 6 tables and the
  couple started dropping tables onto the floor plan, nothing stopped them
  from dropping a 7th, 8th, 20th… Added a soft cap that matches the
  Seating table count: once reached, the Round / Rect / Sweetheart Table
  palette buttons disable with a tooltip explaining the cap (and a small
  "Tables: 4 / 6" counter underneath).
- **Expense "Label *" required even when a Category was selected.** Made
  Label optional with a `required_without:category_id` rule. When left
  blank, the controller backfills the label from the category name so
  the expenses table still has something to display.
- **Extra events confusion.** Adding a wedding-level extra event made
  the per-guest "Invited to extra events" toggles appear in every guest
  edit dialog, which the reviewer read as "this event was just appended
  to every guest's invitation list." Behaviour was correct (the default
  for every guest is empty), but the copy was misleading. Updated the
  Settings → Extra Events helper text to spell out that adding an event
  doesn't invite anyone, and added a "Tap to toggle. Nothing is opt-in
  by default" line above the per-guest toggle row.

### Changed (UX)

- **Current subscription plan now visible in the sidebar.** A small
  chip under the user name in the sidebar footer (`● FREE PLAN`,
  `● PREMIUM PLAN`, `● PLANNER PLAN`) — was previously only available by
  navigating to Settings → Billing. Driven by a new `plan` field on the
  shared `auth.user` Inertia prop.
- **Help descriptions on unclear sections.** Added inline guidance:
  the Day-of Timeline page now explains what a "day-of timeline" actually
  is (one-line intro + who it's shared with); the Website builder now
  shows a per-section helper line under the active section heading
  ("RSVP" → "Adds a button on your public site that points guests to
  /rsvp/{token}. The form is configured in Guests → RSVP Settings.",
  "Timeline" → "Pulled live from your Timeline tab — edit it there.",
  etc.) plus the same text as a `title=` hover tooltip on the section
  list buttons.

### Investigating (likely v1.0.7)

These items from the same review weren't shippable in the v1.0.6 window
either because they need reproduction with dev-tools data (#1) or because
they need distinguishing between two distinct causes (#2/#3) or because
the previous fix regressed and the next attempt needs a different
approach (#6):

- **#1** Navigation flakiness — needs a Network-tab capture from the
  reviewer to diagnose between Inertia prefetch race, server 503, or
  client-side hook blocking nav.
- **#2 / #3** "New Wedding" silently fails on first attempt — likely two
  separate causes (free-plan limit silently rejecting + a save-flow race).
  Both need investigation, but the fix for at least the first one is
  trivial (flash the upgrade prompt instead of doing nothing).
- **#6** Floor plan grid still requires a tab-switch to render despite
  the `requestAnimationFrame` patch from v1.0.1 — needs a different
  approach (likely `useLayoutEffect` + ResizeObserver-driven viewBox).
- **#8** Uploaded website pics not displaying — likely a storage symlink
  rewrite issue on the live cPanel install; needs verification against
  the actual `.htaccess`.
- **#14** Inspiration add silently doesn't save — needs reproduction.

### Already committed for v1.1

- **#10** Budget repetition (Expenses-merging-into-Categories rework).
- **#17** Timeline category vs title simplification.
- **#21** Seating ↔ Floor Plan unification — adding a Seating table will
  auto-create the corresponding floor plan element (also resolves
  the other half of #7 — "7th-table" — by design).

## [1.0.5] — Documentation catch-up

### Changed
- **`DEPLOYMENT.md § 1N`** iframe-compatibility table now includes a row for **third-party cookie blocking (CHIPS)** and how the `PartitionedCookies` middleware addresses it. The previous version implied `SameSite=None + Secure` was sufficient, which stopped being true for Chrome 118+ and Safari 17+.
- **`SUPPORT.md`** adds a new "Login does nothing inside a cross-origin iframe" troubleshooting section with the verification curl and a four-step checklist for diagnosing why `Partitioned` might be missing from a deployed install.

No code changes — only docs catching up with what's already shipping since v1.0.4.

## [1.0.4] — Iframe login fix (PartitionedCookies, take 3 — middleware order)

### Fixed
- **`PartitionedCookies` was running too early on the response chain.** The middleware was appended to the web group, but the Laravel middleware "onion" runs appended middlewares LAST on request and FIRST on response. By the time our middleware inspected `$response->headers->getCookies()`, neither `StartSession` (which queues the session cookie) nor `AddQueuedCookiesToResponse` (which moves it into the managed cookie collection) had run yet — so the collection was empty and nothing got patched. Re-registered as PREPENDED to the web group, which makes it run AFTER the cookie machinery on the response side. The session and XSRF cookies now ship with the `Partitioned` attribute as intended.

## [1.0.3] — Iframe login fix (PartitionedCookies, take 2)

### Fixed
- **`PartitionedCookies` middleware now actually patches Laravel's cookies.** v1.0.2 only rewrote raw `Set-Cookie` headers — but Laravel's session + XSRF cookies are queued as managed Symfony `Cookie` value-objects via `Cookie::queue()` and `AddQueuedCookiesToResponse`. They serialize to `Set-Cookie` AFTER middleware runs, so the v1.0.2 implementation never saw them. Rewritten to walk `$response->headers->getCookies()` and call `withPartitioned(true)` on each (Symfony 7.1+ API, available in Laravel 13's bundled Symfony version). Defensive raw-header rewriting kept as a fallback.

## [1.0.2] — Iframe login fix (CHIPS partitioned cookies)

### Fixed
- **Login broken inside CodeCanyon's live-preview iframe.** Modern Chrome (118+) and Safari (17+) block third-party cookies in cross-origin iframes even with `SameSite=None; Secure`. Without the cookie persisting, login appeared to do nothing: POST submitted, session cookie was set but dropped by the browser, redirect followed without the session, user landed back on the login page.
- **New `PartitionedCookies` middleware** appends the `Partitioned` attribute (CHIPS — Cookies Having Independent Partitioned State) to every `Set-Cookie` response header when `WEDFLOW_DEMO_MODE=true`. Browsers now scope the cookie to the (preview.codecanyon.net, wedflow.site) partition pair — usable within the iframe, can't track the user across sites. Also defensively forces `SameSite=None; Secure` on any cookie that escaped the earlier `AppServiceProvider` session-config override.
- Strictly scoped to demo mode. Production installs (where `WEDFLOW_DEMO_MODE` is unset) keep unpartitioned cookies because their users aren't in iframes.

## [1.0.1] — Reviewer feedback patch

A 21-item patch shipped in response to the first comprehensive reviewer
audit. Every item from that review either fixed in this release, scheduled
for v1.1, or scheduled for v1.2. See `marketing-previews/envato-comment-replies.md`
(author-only) for the full triage.

### Fixed (bugs)
- **Currency:** Budget categories, contributors, expenses and vendor prices now display in the wedding's selected currency instead of hardcoded USD. New shared `lib/currency.ts` helper.
- **Budget vision input:** Couples can now backspace the default 0 — input switched from `type=number` (which blocks empty state) to `type=text` with `inputmode=decimal` and a numeric-only filter.
- **Floor plan grid:** Forced re-render one frame after mount/plan-switch so the SVG grid renders correctly on first load (previously required tab switch to appear).
- **Floor plan unsaved changes:** Added `beforeunload` browser warning + Inertia route-change guard so couples no longer lose work silently.
- **Vendor prices:** Same currency wiring as budget — `total_cost` and `price_estimate` honour the wedding's currency.
- **Public wedding site:** Hero heading now displays when set (previously always fell back to couple names). RSVP, Registry, FAQ and Day-of Timeline sections now actually render — they were saved to the database but missing from the public view. Timeline pulls live data from the couple's TimelineEvent records.
- **Single event detail:** When the couple has only ceremony OR reception (not both), the single card is centred instead of left-aligned in an empty 2-column grid.
- **Event Details auto-heading:** The default "Ceremony & Reception" placeholder is no longer auto-populated — couples were unknowingly pushing it live when their setup only had one or the other.
- **Sidebar Settings bug:** The `/edit` page was passing `weddingId` instead of `wedding` to AppLayout, which caused the sidebar to collapse to just "My Weddings" while editing the wedding. Now passes the full wedding object so all nav items remain visible.

### Changed (UX)
- **Checklist default view is now a single editable list.** A toggle exposes the original phase-grouped view for couples who want it. Reviewers said not every wedding fits the 9-12 / 6-9 / 3-6 phase template.
- **Budget header simplified** to just Total Budget + Actual Spend (was 4 cards). Dropped the always-static category percentages, which never updated based on actual spend.
- **Sidebar order:** Seating now comes before Guests so couples create tables first, then assign during guest entry.
- **Seating bulk-create:** New `Quantity` field on the add-table form auto-creates N tables with sequential names (e.g. Quantity 8 + base name "Table" → Table 1..8). Couples no longer need to add identical tables one-by-one.
- **Bulk guest emails:** Save-the-Date and Invite buttons now disable when no guests exist, prompt for confirmation before sending, and have descriptive tooltips so it's clear what they do.
- **Website builder:** Hero `backgroundUrl` (and any future `*Url` field) now has an inline "Upload photo" button next to the URL input — no more URL-only workflow.

### Added
- **Day-of Timeline section** on the public wedding website — pulls from the couple's TimelineEvent records and renders as a guest-facing schedule.

### What's still to come
- **v1.1 (~3 weeks):** Contributors removed; Expenses merged into Categories; multi-name entry for `+N` party counts; unified Seating + Floor Plan workflow; Collaborators role-matrix simplification.
- **v1.2:** Cloud storage providers (Google Drive, Dropbox) alongside local + S3/R2.

## [1.0.0] — Initial release

The first public release of WedFlow Atelier — a premium, self-hosted wedding
planning platform with an editorial design system.

### Features

**For couples and collaborators**
- Multi-collaborator workspaces with per-role permissions (5 roles × 13 sections, read / write / none).
- Wedding dashboard at a glance: budget burn, guest progress, days remaining.
- **Budget tracker** with categories, contributors, allocations, and split payments.
- **Guest list** with party counts, dietaries, sides, RSVP tokens, bulk Save-the-Date / Invite email send.
- **Drag-and-drop seating chart** with printable layout + per-guest QR place cards.
- **3D floor plan** powered by React Three Fiber.
- **Public wedding website** at `/w/{slug}` in 6 themes (Atelier, Rose, Garden, Minimal, Luxe, Coastal).
- **Public RSVP page** themed per wedding.
- **Guest photo gallery** at `/share/{slug}` — visitors upload directly from a QR card, no app needed.
- **QR seat finder** — guests scan at the door, see their named welcome + table assignment on a private screen.
- **Planning checklist** with 58 tasks across 15 phases, auto-seeded from the wedding date.
- **Run-of-show timeline** with categorised events shared across vendors / family / planner.
- **Vendor research** + booked-vendor contract / deposit / balance tracking.
- **Inspiration board** with image and YouTube video support.
- **Wedding Crew** — custom team groupings (groomsmen, bridesmaids, kids, etc.).
- **Custom email templates** for Save-the-Date / Invite / RSVP reminder.

**For operators**
- **Web installer at `/install`** — 4-step browser wizard (requirements check → DB → admin account → migrate + seed). Auto-locks when complete.
- **Admin panel at `/admin`** — user counts, plan distribution, recent events.
- **`/admin/settings` browser-based configuration** — Branding (logo, name, accent), Email Delivery (Resend / SMTP / log driver with test-send), File Storage (local / S3 / R2), Google Sign-In, Stripe Billing (live keys, webhook secret, price IDs).
- **Per-user admin actions** — toggle admin, suspend, change plan, delete.
- **Encrypted-at-rest secrets** on `platform_settings` (Stripe, SMTP, Resend, S3, Google).
- **Branded error pages** (403, 404, 419, 500, 503) in the Atelier design system.
- **Light / dark theme toggle** with `prefers-color-scheme` fallback.
- **`lang/en.json` translation scaffolding** shared via Inertia (`t('common.save')` in TSX).
- **Mandatory email verification** before sign-in — registration sends a verification email with no auto-login, and the login page surfaces a resend CTA for unverified accounts. Public resend route is rate-limited and silent on unknown emails to prevent enumeration.

**For Envato authors running a public demo**
- **Demo mode** (`WEDFLOW_DEMO_MODE=true`) — blocks admin writes, shows a "DEMO MODE" banner in the sidebar, and relaxes iframe headers so the install loads cleanly inside CodeCanyon's `preview.codecanyon.net` live-preview wrapper.
- **`php artisan wedflow:reset-demo`** command for daily cron — wipes DB + uploads, re-seeds, refuses to run unless demo mode is enabled.
- **Iframe-compatible headers** in demo mode — `CSP: frame-ancestors` allow-list for `preview.codecanyon.net` + `*.envato.com`, `X-Frame-Options` stripped, `SameSite=None; Secure` session cookies so the iframe doesn't drop sessions. None of these relaxations apply to production installs.

### Stack

- Laravel 13, Inertia.js 3, Ziggy
- React 19 + TypeScript 6 on Vite 8
- Tailwind CSS 4 with the Atelier design system (espresso + warm ivory + brushed gold)
- React Three Fiber + Drei for the 3D floor plan
- Stripe PHP SDK 20, Resend PHP 1
- jsPDF + qrcode for printable place cards and PDFs

### Compatibility tested on

- Shared hosting: cPanel + MySQL/MariaDB 10.x + PHP 8.3 (HostGator, Bluehost, Hostinger, Namecheap)
- VPS: Ubuntu 24.04 + Nginx + PHP-FPM + MySQL 8
- Managed: Laravel Forge, Ploi, Cleavr, Cloudways

### Documentation

The release ships with a complete buyer support kit:
- `README.md` — overview, installation, configuration, plan limits, project layout
- `DEPLOYMENT.md` — shared hosting, VPS, managed PaaS walkthroughs; troubleshooting matrix; cPanel-only command reference for buyers without SSH; full public-demo + iframe-compatibility guide
- `SUPPORT.md` — buyer support guide with log-pattern → fix table
- `LICENSE.md` — Envato Regular + Extended licence summary
- `CHANGELOG.md` — this file
- Atelier brand assets at `public/marketing/` (favicon, OG image, wordmarks light / dark) wired into the blade head
- 12 atelier marketing previews at `marketing-previews/` (author-only, excluded from the buyer zip) for CodeCanyon listing screenshots
