# Freelance Studio Changelog

## Version 8.6.4 <!-- @hidden -->
- **Fixed:** Filtering expenses by category or date produced "Sorry, you are not allowed to access this page" error. The filter form was submitting to the old `fs-expenses` page slug which no longer exists since Expenses moved under the Finances hub. Now correctly submits to `fs-finances` with `tab=expenses`.
- **Fixed:** Same stale page slug issue in invoice filters (`fs-invoices` → `fs-finances&tab=invoices`), other income filters, and reporting year filter (`fs-reporting` → `fs-finances&tab=reporting`).

## Version 8.6.0
- **New: Email Delivery Settings** — Built-in integration with Brevo and Postmark transactional email APIs. Ensures invoices, proposals, contracts, booking confirmations, and all other emails reliably reach clients' inboxes instead of landing in spam.
- **New: Setup Wizard** — Step-by-step wizard in Settings → Email Delivery walks users through choosing a provider, creating an account, entering their API key, verifying the connection, and sending a test email. Supports three paths: Brevo (free 300 emails/day), Postmark (fastest delivery), or "I already use an SMTP plugin."
- **New: Email Delivery Service** (`class-fs-email-delivery.php`) — Hooks into WordPress's `pre_wp_mail` filter to route all emails through the configured provider's API. Includes full header parsing (From, Reply-To, CC, Content-Type), automatic fallback to default `wp_mail()` if the API call fails, and error logging.
- **New: API Key Verification** — Verify Connection button tests the API key against the provider's account endpoint before saving, preventing misconfiguration.
- **New: Test Email** — Send a branded test email from both the setup wizard and the configured status view to confirm end-to-end delivery.
- **Improved:** Email Templates settings tip now links to the new Email Delivery settings instead of generically suggesting an SMTP plugin.

## Version 8.5.5 <!-- @hidden -->
- **Fixed:** All outgoing emails (contracts, invoices, proposals, etc.) were rendering raw HTML tags instead of formatted content. The `wrap_in_template()` and `get_email_wrapper()` methods were using `esc_html()` on the HTML body content, converting tags to visible text. Changed to `wp_kses_post()` which allows safe HTML.
- **Fixed:** All PDF downloads were empty/corrupted. The `FS_PDF::Output()` method was applying `esc_html()` to raw PDF binary data before echoing it, destroying the file. Removed the escaping on binary output.
- **Fixed:** Tax deductions PDF export fatal error — was calling nonexistent `FS_PDF_Service::generate_pdf_from_html()`. Now properly uses `FS_PDF` class with `WriteHTML()` and `Output()`. Also fixed the HTML fallback output which had the same `esc_html()` issue.
- **Fixed:** CSV export from Forms responses was corrupted by `esc_html()` on the raw CSV data.
- **Fixed:** Task titles missing in weekly digest emails and preview — `$task->name` corrected to `$task->title` in the email HTML template, AJAX preview renderer, and demo digest data.
- **Fixed:** Undefined `$color_mode` PHP warning on Settings page — moved variable definitions before their first use in the Light Logo preview.
- **Improved:** Weekly digest email headlines now rotate randomly from 7 options for personality and variety.
- **Improved:** Timer stop button now uses Lucide circle-stop SVG icon across all timer views (timer widget, dashboard, time tracking list, popup timer).
- **Improved:** Background tabs no longer drain battery — all 1-second timer intervals pause when the browser tab is hidden and resume when visible, eliminating Safari's "significant energy" warning.
- **Improved:** Bug report footer button positioned with `margin-left: 130px`.
- **Improved:** Removed "Sent On" column from main Invoice list view.

## Version 8.5.4 <!-- @hidden -->
- **New:** Pagination added to all remaining list views — Clients, Projects, Contracts, Proposals, Expenses, and Bookings. Each list defaults to 25 items per page with options for 10, 25, 50, or 100. Shows "Showing X–Y of Z" info, page number links with ellipsis for large sets, and Prev/Next buttons. Pagination preserves all active filters and tab state. Created reusable `includes/views/partials/pagination.php` partial used across all views.
- **Model updates:** Added `count()` method to Contract and Booking models. Updated Expense `count()` to accept filter arguments. Updated Project `count()` to properly handle `archived` and `billing_type` filters. Added `archived` filter support to Proposal `count()`. Added `limit`/`offset` support to Contract `get_all()`.

## Version 8.5.3 <!-- @hidden -->
- **Improved:** Project dropdowns in all time entry forms (timer, quick add, add/edit form, edit modal) now exclude completed, cancelled, and archived projects. Filter dropdowns on the time tracking list and reports pages still show all projects. When editing an existing time entry, the entry's current project is always included even if completed/archived.
- **New:** Bulk archive/restore for projects — checkboxes on the desktop project table with a bulk actions bar. Select individual projects or use "Select all", then archive or restore in one click.

## Version 8.5.2 <!-- @hidden -->
- **Fixed:** Quick Add and Timer widgets inside a project view could have their project overridden by the task/project sync JS handler. Both handlers now skip the override when in project context (`data-context="project"`).
- **Fixed:** Quick Add form AJAX call now uses `fs_ajax.ajax_url` instead of the WordPress global `ajaxurl`.

## Version 8.5.1 <!-- @hidden -->
- **Improved:** Client view Invoices tab — replaced the Issue Date column with a Project column that links to the associated project. Due Date column is retained. Invoices without an associated project show an em dash.

## Version 8.4.6 <!-- @hidden -->
- **New:** Transaction Review UI — click any imported transaction to open a slide-out sidebar panel showing details, match suggestions, and quick actions.
  - **Auto-matching engine**: Debits show matching expenses (by amount + date proximity), credits show matching invoices (by total/remaining balance + date proximity).
  - **Link to existing records**: One-click linking of transactions to expenses or invoices. For invoice matches, optional "Mark as Paid" checkbox.
  - **Create Expense modal**: For unmatched debits, create a new expense pre-filled with transaction data (vendor, amount, date) and link it automatically. Fields: category, vendor, project.
  - **Mark Personal**: Ignore non-business transactions to keep the review clean. Restore with one click.
  - **Unlink**: Undo any match to return a transaction to unmatched.
  - **Filter tabs**: All / Unmatched / Matched / Ignored with live counts.
- **New:** Auto-categorization learning (Part 5) — when you create an expense from a transaction or assign a category, the system extracts keywords from the bank description and remembers the mapping. Future transactions with similar descriptions will show a suggested category (highlighted in the sidebar panel). Stored in `fs_transaction_category_map` option, max 500 mappings.

## Version 8.4.4 <!-- @hidden -->
- **New:** Bank transaction CSV import — 5-step import flow (upload, column mapping, preview with duplicate detection, batched processing, results) on the Finances > Bank Transactions page. Supports 7 bank-specific column presets (Chase, Bank of America, Wells Fargo, Capital One, Citi, American Express, PayPal) plus auto-detection for generic CSVs. Handles both single-amount and split debit/credit column formats. Includes import history with batch management and delete.

## Version 8.4.3 <!-- @hidden -->
- **New:** Bank Transactions database table (`wpbg_fs_transactions`) and `FS_Transaction` model — foundational layer for upcoming bank statement import feature. Includes CRUD operations, batch import with duplicate detection, auto-matching engine for linking transactions to invoices (credits) and expenses (debits) by amount and date proximity, bulk status updates, import batch management, and summary statistics.

## Version 8.4.1 <!-- @hidden -->
- **Fixed:** Annual Tax Deductions section — deduction amounts showing $0.00 due to wrong model property name (`amount` → `annual_amount`), quick-add preset buttons appearing blank due to wrong array key (`label` → `name`), and form field names not matching the save handler (`category` → `deduction_category`, `description` → `deduction_name`, `year` → `deduction_year`).
- **Fixed:** Quarterly tax payments save handler using wrong nonce name and POST field format.
- **Fixed:** Copy deductions from previous year using wrong model field names.

## Version 8.4.0
### New Features
- **Finances Hub**: New unified "Finances" menu in the WordPress sidebar, consolidating all financial features into one location with sidebar navigation (same pattern as Bookings and Settings).
  - **Invoices** — Invoice list, creation, editing, and management
  - **Recurring Invoices** — Automated billing schedules (Pro)
  - **Other Income** — Track non-invoiced income (Etsy, commissions, royalties, etc.) — now its own dedicated sub-page instead of a tab inside Invoices
  - **Expenses** — Expense tracking with recurring expenses tab
  - **Reporting** — Financial charts, top clients, and CSV exports
  - **Tax** — Consolidated tax planning page combining Tax Settings (from Settings), Annual Tax Summary and Quarterly Tax Estimates (from Reporting), Annual Deductions (from Settings), and Quarterly Tax Payments (from Settings) into one dedicated workspace
  - **Bank Transactions** — Placeholder for upcoming bank statement import and transaction matching feature
- **Backward Compatibility**: Old bookmarked URLs (`?page=fs-invoices`, `?page=fs-expenses`, `?page=fs-recurring-invoices`, `?page=fs-reporting`) automatically redirect to the correct Finances sub-page with all query parameters preserved.
- WordPress admin sidebar reduced from 14 items to 11 for a cleaner navigation experience.

## Version 8.3.6 <!-- @hidden -->
- **Fixed:** HTML rendering as raw text (`<strong>`, `<code>`, `<span>`) on the public changelog page and across multiple Settings views (Calendar setup instructions, tax disclaimers, repair tool notices), Reporting (tax disclaimer), Projects (non-billable label), Tasks (pagination), Calendar ("+X more" events), Dashboard (week glance), Retainer widget, and Timer widget. Root cause: the WordPress.org compliance tool had wrapped intentional HTML output in `esc_html()` / `esc_html_e()`, which escapes HTML tags into visible text. All 25 instances across 9 files replaced with `wp_kses_post()`.
- **Updated:** Freemius SDK updated from 2.13.0 to 2.13.1.

## Version 8.3.5 <!-- @hidden -->
- **Improved:** Public changelog page now hides minor patch versions (X.X.1, X.X.2, etc.) while keeping feature releases (X.X.0) visible. Hidden versions remain in the CHANGELOG.md file for internal records — add `<!-- @hidden -->` to any version heading line to exclude it from the public page. The `parse_changelog()` method accepts an `$include_hidden` parameter to retrieve all entries when needed.

## Version 8.3.4 <!-- @hidden -->
- **Fixed:** Expense import showing invoice field options (Client Name, Project Name, etc.) in column mapping dropdowns instead of expense fields (Date, Amount, Merchant, Category). The import type was not being applied when the mapping table was built. Also fixed invoice-specific checkboxes not hiding when Expenses type was selected, and added support for switching import type after a file is already uploaded (re-runs auto-mapping with the correct field definitions).

## Version 8.3.3
- **Added:** Expense CSV Import — the Settings > Import tool now supports importing expenses in addition to invoices. A type selector at the top lets you choose between "Invoices" and "Expenses" before uploading. Expense import auto-maps columns for Date, Amount, Merchant, Category, Description/Purpose, Notes, and Sales Tax. Expense names are built from Merchant + Purpose (e.g., "Adobe Creative Cloud — Software"). Preview shows expense count, total amount, and a full preview table before committing. Batched processing with progress bar, same as invoice import.

## Version 8.3.2
- **Added:** Pagination on the Invoices list page. Defaults to 25 per page with options for 10, 25, 50, or 100. Shows "Showing X–Y of Z invoices" info, page number links with ellipsis for large page counts, and prev/next navigation. Filters (status, client) are preserved across pages. Per-page preference is carried in the URL.

## Version 8.3.1 <!-- @hidden -->
- **Fixed:** Merge Client modal not opening — was using incorrect modal markup pattern (`fs-modal-overlay` wrapper with `fadeIn/fadeOut`) instead of the existing plugin pattern (`fs-modal` overlay with `fs-modal-content` inner box toggled via `.active` class). Also changed AJAX calls from `ajaxurl` to `fs_ajax.ajax_url` for consistency.
- **Fixed:** Archiving a client wiped all client data (name, email, address, etc.) because `FS_Client::update()` rebuilds the full field array with empty defaults for any unspecified fields. Archive/unarchive now use a targeted `$wpdb->update()` that only touches the `status` column.

## Version 8.3.0
- **Added:** CSV Import Tool in Settings > Import. Upload a CSV file containing historical invoice data (from Wave, FreshBooks, QuickBooks, or any other tool) and import clients, projects, and invoices into Freelance Studio. Features include:
  - Drag-and-drop CSV file upload
  - Automatic column detection and mapping with manual override
  - Dry-run preview step showing how many new clients, projects, and invoices will be created, plus total revenue
  - Deduplication — skips invoices with existing invoice numbers and matches existing clients/projects by name
  - Batched processing (25 records per batch) with progress bar for large imports
  - Options to skip duplicate invoice numbers and mark imported projects as "Completed"
  - Status auto-detection from CSV dates (paid, sent, overdue, draft)
  - Results summary with error reporting
  - Full high-contrast mode support
- **Added:** Client Merge — reassign all projects, invoices, proposals, contracts, expenses, tasks, bookings, and contacts from one client to another, then delete the source client. Available via "Merge Into..." in the client card/row action menus. Includes a preview step showing exactly how many records will be reassigned before confirming.
- **Added:** Client Archive — archive and restore clients from the card/row action menus. Archived clients move to the existing "Archived" tab and are excluded from active views and dropdowns.

## Version 8.2.1 <!-- @hidden -->
- **Fixed:** WordPress compliance escaping broke status badges across the entire plugin — badges were rendering as raw HTML text (`<span class="fs-status-badge...">`) instead of colored pills. The compliance tool had wrapped `status_badge()` output in `esc_html()` which escapes intentional HTML. Fixed 24 instances across 14 files by replacing with `wp_kses_post()`, which is the WordPress-compliant way to output safe HTML. Affected: Dashboard, Projects, Invoices, Proposals, Contracts, Bookings, and Client views.
- **Fixed:** Compliance tool applied `esc_url()` to CSS class conditionals (e.g., tab active states) which broke tab highlighting across Clients, Projects, Contracts, Bookings, and Proposals list pages. Replaced with `esc_attr()`.
- **Fixed:** Compliance tool broke `wp_nonce_url()` parentheses in archive/unarchive/delete links across Projects, Contracts, Invoices, and Bookings — the nonce action string ended up outside the function call. Fixed parenthesis placement in all affected files.
- **Fixed:** Dynamic `<style>` block for activity colors in `FS_View_Context` was wrapped in `esc_html()`, breaking all custom activity color CSS. Restored to plain `echo` (the CSS values are already escaped with `esc_attr()` internally).
- **Fixed:** `esc_url()` applied to a CSS color value (`$primary_color`) in email service, breaking button background colors in client-facing emails.
- **Added:** Client View page redesigned as a mini-dashboard. The default tab is now an **Overview** with a profile header (name, email, phone, website, location), quick action buttons (New Project, New Invoice, New Proposal, New Contract), key stats cards (Total Earned, Outstanding, Billable Hours, Projects, Active), a 2/3 + 1/3 layout with Recent Projects, Recent Invoices, and a 12-month Earnings bar chart in the main column, plus Open Tasks (with checkbox completion), Pending Documents (proposals/contracts awaiting action), Client Notes, and Client Portal link in the sidebar. All existing tabs preserved.

## Version 8.1.5 <!-- @hidden -->
- **Fixed:** Time Tracking filter task dropdown was showing duplicate recurring task instances (e.g., multiple "Design Team Meeting" entries). The dropdown now only lists tasks that actually have associated time entries, eliminating stale recurring children and deleted-but-orphaned references.
- **Fixed:** "Hide until due date" setting was not respected for recurring parent tasks or non-recurring tasks — only recurring children were being hidden. The `hide_until_due` check now applies to all task types in the smart recurring query, so a new recurring task with a future due date and "hide until due" enabled will correctly stay hidden until its due date arrives. Both the task list and the task count queries were updated.

## Version 8.1.4 <!-- @hidden -->
- **Fixed:** Pause timer display bug — when pausing the dashboard timer widget, the displayed time would jump (sometimes doubling). The root cause was that `paused_duration` already accumulated the current running segment on pause, but the display formula was adding `(now - start_time)` on top of it, double-counting. The widget now shows just `paused_duration` when the timer is paused. Resuming and stopping still track time correctly.
- **Fixed:** Quick Add task dropdown overflowing the card boundary on the Time Tracking page. Added `min-width: 0` on flex children and `max-width: 100%` on selects to keep them contained.
- **Fixed:** `.popup-btn .dashicons` now has `background-color: #fff` to prevent transparent icon backgrounds in the popup timer.
- **Added:** Starting a timer from a popup window now automatically updates the dashboard, Time Tracking page, and floating timer bar via `BroadcastChannel`. Previously only `timer_stopped` was synced — now `timer_started`, `timer_paused`, and `timer_resumed` events all trigger a page reload on open tabs, so the dashboard timer widget stays in sync without a manual refresh.

## Version 8.1.3 <!-- @hidden -->
- **Fixed:** Quick Add manual time entry date field was showing tomorrow's date after ~7pm Eastern. The field was using server time (UTC) instead of the user's configured timezone from Settings → Time Tracking. Now reads the `fs_user_timezone` setting to display the correct local date.
- **Added:** When a retainer invoice is generated, all billable uninvoiced time entries within that retainer period's date range are now automatically marked as invoiced. Previously, the retainer period itself was marked as invoiced but the associated tracked time entries remained showing as "Billable" and uninvoiced in the Time Tracking list.

## Version 8.1.2 <!-- @hidden -->
- **Added:** Client Contact override on contracts. When creating or editing a contract, a new **Client Contact** dropdown appears below the Client selector. You can choose an existing contact from the client's contact list, or select "Custom contact" to enter a name, email, and phone for someone not in the system (e.g., a supervisor, accounts payable rep, or legal representative). The selected contact's info appears in the Parties section on the contract preview, the public client-facing view, and the PDF. If no override is selected, the contract falls back to the client's default info. Contact details are stored as a snapshot on the contract so they remain accurate even if the contact record changes later.
- **Fixed:** Contracts with an empty or zeroed-out End Date (e.g., `0000-00-00` from an unfilled date field) no longer display "November 30, -0001" in the admin preview or the public contract view. The End Date now correctly hides when no valid date is set.

## Version 8.1
- **Changed:** Weekly digest email header now uses the teal-to-lime gradient (`--fs-teal-lime`) with dark text (`--fs-gray-800`) instead of teal-to-purple with white text. Applied to both the real email and the settings preview/test email.

## Version 8.0.31 <!-- @hidden -->
- **Changed:** Weekly digest email and settings preview now use Freelance Studio's design system colors. Header gradient uses teal-to-purple (`#2BA891` → `#5D4E80`), section borders use primary-light (`#77D5C9`), headings use gray-800 (`#374151`), invoice borders use accent (`#FFA26E`), project borders use secondary (`#81A5DF`), milestone borders use highlight (`#D2E768`), danger states use the FS danger red (`#E85A5A`), and the CTA button uses the primary teal gradient. Both the real email and the preview/test template were updated to match.

## Version 8.0.30 <!-- @hidden -->
- **Fixed:** Weekly digest preview (Settings → Calendar → Show Preview) and test email now match the actual digest format — emojis removed, task items show task name + project name + status badge. Previously the preview/test used a separate renderer that was out of sync with the real email.

## Version 8.0.29 <!-- @hidden -->
- **Fixed:** Weekly digest email task items now display both the **task name** (bold) and the **project name** (subtitle) on separate lines. Previously only the project name was visible.
- **Added:** Task status badge in the weekly digest email — each task now shows an inline pill (e.g., "Todo", "In Progress") next to the project name.
- **Changed:** Removed all emojis from the weekly digest email for a cleaner look. Affected: subject line, header banner, section headers (Invoices Due, Project Deadlines, Milestones Due, Tasks Due, Expiring Soon), proposal/contract labels, and the "All Clear" empty state.

## Version 8.0.28 <!-- @hidden -->
- **Added:** Client-specific payment terms. The client profile now has a **Payment Terms** dropdown under Billing Defaults with preset options: Due on Receipt (7 days), Net 14, Net 15, Net 21, Net 30, Net 45, Net 60, Net 90, and Custom. Previously this was a plain number input.
- **Added:** Invoices now auto-calculate the due date based on the selected client's payment terms. When creating a new invoice (manually, from a project, via quick-create, or via duplicate), the due date is set using the client's configured terms instead of only the global default. Changing the client on the invoice form dynamically updates the due date field.
- **Added:** Recurring invoices now use the client's payment terms when generating each invoice instance, falling back to the global default if no client-specific terms are set.

## Version 8.0.26 <!-- @hidden -->
- **Changed:** Project > Tasks tab — replaced the card grid layout with a consistent table-based list view matching the main Tasks page and Client > Tasks tab. Includes checkbox, task title, milestone (when applicable), assigned to, priority, inline status dropdown, due date, and row actions (edit, archive, start timer). The progress/stats card at the top is preserved. Filters (assignee, status) now operate on table rows.

## Version 8.0.25 <!-- @hidden -->
- **Added:** Status dropdown in the task edit modal — visible when editing an existing task, allows changing status directly from the modal without needing to use the inline dropdown on the list.
- **Added:** Archive option in the Client > Tasks row menu (⋯), matching the main Tasks page behavior.

## Version 8.0.24 <!-- @hidden -->
- **Changed:** Client portal task tabs renamed from "Active / Completed" to "Active / Inactive" — the Inactive tab now correctly includes completed, archived, and cancelled tasks with status labels. Previously, archived tasks were incorrectly shown in the Active tab as "Pending."
- **Changed:** Client admin view (Client > Tasks tab) now shows only open tasks (pending, in_progress, needs_info, with_client, requested). Completed and archived tasks are excluded.
- **Added:** Sortable columns on Client > Tasks tab — Task, Priority, Status, and Due Date headers are clickable to sort. Default sort is due date ascending.
- **Fixed:** Client portal "Request Task" button no longer bumps against the right edge of the card.

## Version 8.0.23 <!-- @hidden -->
- **Added:** Status column with inline dropdown to Client > Tasks tab, matching the main Tasks list view.
- **Added:** Inline status change JS handler on Client > Tasks tab so status dropdowns are functional.

## Version 8.0.22 <!-- @hidden -->
- **Fixed:** Client portal task toggle (mark complete) was silently failing — the AJAX handler validated the portal token against the wrong database table (`fs_portal_tokens` with expiration) instead of `fs_clients.portal_token`. The optimistic UI update made it appear to work until page refresh reverted it.
- **Added:** Active / Completed tabs on the client portal dashboard and project portal task sections, replacing the old collapsed "Completed (N)" toggle.

## Version 8.0.21 <!-- @hidden -->
- **Fixed:** Recurring task `recurring_days` field (day-of-week selection) was never saved to the database — the field was missing from `FS_Task::create()`, `create_generic()`, and `update()` methods. Day selections now persist correctly.
- **Fixed:** Recurring tasks not regenerating when completed via the task edit modal — `generate_next_recurring()` was only called from checkbox toggle and kanban drag, not from `save_task` or `save_kanban_task` AJAX handlers.
- **Fixed:** Bookings > Availability and Bookings > Settings sidebar navigation was empty (nav links were never added to those views).

## Version 8.0.14 <!-- @hidden -->
- **Added:** Per-page chooser on the Tasks list pagination bar — options are 10, 25, or 50 tasks per page. Selection is saved per-user (user meta) and persists across sessions. Defaults to 25. The pagination bar now shows the per-page controls on the right side.
- **Fixed:** Timer pop-out window — "Stop & Save" button text was not vertically centered with the stop icon. The label text is now properly wrapped in a `<span>` so the `inline-flex` gap aligns the icon and label correctly on both the server-rendered and dynamically injected button states.
- **Fixed:** Dashboard timer widget — "Stop" button would briefly shift layout when changing to "Stopping..." text during the AJAX request. Added `min-width: 140px` to keep the button a stable size.
- **Fixed:** Send Invoice/Proposal/Contract modal — radio buttons and checkboxes in the recipient To/CC grid appeared vertically squished due to missing `vertical-align` and `flex-shrink` properties. Inputs now align cleanly with their labels.
- **Fixed:** Timer stop button dashicon had an erroneous `background-color: #fff` that rendered as a white square behind the stop icon. Removed.

## Version 8.0.13 <!-- @hidden -->
- **Added:** Pagination on the Tasks list view (Active and Archived tabs). Displays 50 tasks per page with prev/next navigation and page number buttons. The "Showing X–Y of Z tasks" count updates per page. Filters and sort order are preserved across page navigation. Resetting filters always returns to page 1. The `FS_Task` model now supports an `offset` argument in `get_all()` and has a new `count_all()` method that mirrors the same WHERE conditions for efficient total count queries without loading all records.

## Version 8.0.10 <!-- @hidden -->
- **Fixed:** Changelog page (`/changelog/`) now renders correctly. Removed a conflicting rewrite rule that was intercepting the real WordPress page's URL, causing "No changelog entries found" on the frontend and preventing the page from being edited in the admin. Switched from shortcode to a `the_content` filter so changelog content renders even when Thrive Architect intercepts `do_shortcode`.

## Version 8.0.9 <!-- @hidden -->
- **Added:** Public changelog page at `/changelog/` — displays a live, formatted version history parsed directly from `CHANGELOG.md`. Content updates automatically with every plugin version without requiring any manual page creation. Linked from the **Freelance Studio** settings tab ("What's New" card). Latest version is expanded by default; all others are collapsible.

## Version 8.0.6 <!-- @hidden -->
- **Added:** New **"Freelance Studio"** tab in Settings. Includes a branded hero banner with plugin version, three quick-link cards (Support, Documentation, What's New), an announcements section (content managed via the `fs_announcements` option), a prominent support CTA banner, and a **Request a Feature** form. Feature requests are emailed directly to `admin@freelancestud.io` via `wp_mail()` with the submitter's name, category, priority level, and full description. Announcement content can be updated server-side at any time without a plugin update by setting the `fs_announcements` option.

## Version 8.0.5 <!-- @hidden -->
- **Added:** "Purge All Archived Tasks" button on the Archived tab. Appears above the task list when archived tasks exist; permanently deletes all archived tasks after a confirmation prompt.
- **Added:** Auto-archive completed tasks setting under **Settings → Time Tracking → Task Management**. Choose to automatically move completed tasks to the Archived tab after 7, 14, 30, or 60 days (handled by the daily cron). Defaults to "Never" (manual only).
- **Added:** When a project is archived, all of its non-archived tasks are now automatically archived at the same time (preserving their previous status for restore). Unarchiving a project does **not** automatically restore tasks — use the Archived tab to restore selectively.

## Version 7.10.26 <!-- @hidden -->
- **Added:** Hour cap tracker widget now appears in the Project Services card when a service has an hour cap set. Shows a progress bar, hours used vs. budgeted, overage amount, and turns red when the cap is reached — same visual treatment as the retainer tracker.
- **Added:** Hour cap badge on each capped service row in the display view showing current tracked hours vs. cap (e.g. "8.0 / 20.0 hrs cap"), highlighted red when the cap is exceeded.
- **Fixed:** "Monthly Retainer" now shown as the pricing type label in the service row display (previously fell through to "flat fee" label).

## Version 7.10.25 <!-- @hidden -->
- **Fixed:** Sidebar cards (Deposit, Milestones) were rendering at the bottom of the page instead of in the right column. Caused by an extra `</div>` introduced in v7.10.22's retainer service fields block, which prematurely closed the `fs-terms-main` grid column.

## Version 7.10.24 <!-- @hidden -->
- **Fixed:** "New Period" retainer reset button now uses a self-contained vanilla JS handler embedded directly in the widget, bypassing a potential admin JS registration issue that was preventing the click from firing.

## Version 7.10.22 <!-- @hidden -->
**Form parity audit fixes — all Add/Edit form pairs now have identical fields:**

- **Fixed (Issue 1):** The "Add Service" JS template in the project view inline editor was missing 10 fields that exist in the full project form: `hour_cap`, `hour_cap_enabled`, `hour_cap_notify_client`, `hour_cap_notify_contact_id`, `hour_cap_notify_freelancer`, `hour_cap_overage_rate`, `hour_cap_warn_threshold`, `retainer_amount`, `retainer_hours`, `retainer_overage_rate`, `retainer_rollover`. Adding a new service inline would silently drop all cap/retainer settings on save.
- **Fixed (Issue 2):** Existing retainer-type services in the project view inline editor were missing retainer fields (`retainer_amount`, `retainer_hours`, `retainer_overage_rate`, `retainer_rollover`). Also added "Monthly Retainer" as a pricing type option to the inline editor's service select (was only available in the full form). Editing a retainer service inline would wipe retainer data on save.
- **Fixed (Issue 3):** The time entry modal form was missing the `duration_seconds` field present in the standalone time entry form. Added seconds input to modal, updated all duration calculations (calculateEndTime, calculateDuration, save handler, resetForm) to include seconds. Renamed hidden field from `entry_date` to `start_date` to match the standalone form and AJAX handler field name. Updated the project view edit-entry handler to populate the seconds field when loading an existing entry.

## Version 7.10.21 <!-- @hidden -->
- **Fixed:** "Notify me when cap is reached" and "Send early warning at X%" checkboxes were missing from the Project Services inline editor on the project view page (they were only in the full project edit form).
- **Fixed:** Cancel button in the Project Services inline editor had no effect — it was looking for `.fs-project-section` but the wrapper uses `.fs-project-card`.

## Version 7.10.20 <!-- @hidden -->
- **Added:** "Hours reset on the __ th of each month" dropdown on retainer services in the project form. Set a specific reset day (1–28) that overrides the project start date for billing cycle calculations. Capped at 28 to work reliably across all months.
- **Added:** "New Period" button on the Retainer Status widget in the project view. Click it to immediately close the current period and start a fresh one beginning tomorrow — no need to wait for the daily cron to fire.
- **Added:** Current period date range is now displayed under the Retainer Status widget header so you can always see exactly which dates are being tracked.
- **Fixed:** Retainer period cron now respects the new `retainer_reset_day` field when calculating when a new period should begin.

## Version 7.10.19 <!-- @hidden -->
- **Added:** "Notify me when cap is reached" checkbox on the Project Hours Cap service settings. Previously the freelancer email was always sent; it is now opt-in so you can disable it if preferred. (Legacy projects with no box checked will still receive the freelancer email to preserve existing behaviour.)
- **Added:** "Send early warning at __% of hours used" option on Project Hours Cap. Enter any percentage (e.g. 80) to receive an advance warning email — sent to the freelancer and/or client per their respective notify checkboxes — when that share of the hour budget is consumed. The warning fires once per project and resets if the cap value changes.

## Version 7.10.18 <!-- @hidden -->
- **Added:** "Task" option in Time Tracking Reports "Group by" dropdown. Groups time entries by the task they were logged against, showing Task name, Project, Entries, Total Time, Billable, Amount, and Invoiced % columns.
- **Added:** Duplicate action for Other Income entries. Duplicating creates a copy with today's date, appends "(copy)" to the source name, and immediately opens the edit form for the new entry.

## Version 7.10.16 <!-- @hidden -->
- **Fixed:** Recurring task "day of week" selection was not populating correctly when editing an existing task. The JS was attempting to `JSON.parse()` the value, but it is stored as a comma-separated string (e.g. `"tue"`), causing a silent parse error and leaving all day checkboxes unchecked. Now handles both formats.
- **Fixed:** Dashboard task list was not using `smart_recurring` mode, which could cause recurring parent "template" tasks and orphaned child tasks to appear as ghost entries after deletion. Dashboard now uses the same smart recurring filter as the task list view.
- **Fixed:** Added a safety guard in `generate_next_recurring()` to ensure the calculated next due date is always strictly after the base date, preventing potential same-day looping.

## Version 7.10.15 <!-- @hidden -->
- **Fixed:** Task List view status filter dropdown now shows custom Kanban column names instead of hardcoded defaults.
- **Fixed:** Dashboard task widget filter/sort dropdown was being clipped by the card boundary. The dropdown now renders fully visible.

## Version 7.10.14 <!-- @hidden -->
- **Added:** Project name now appears on invoice PDFs between the "Bill To" section and the line items table.

## Version 7.10.13 <!-- @hidden -->
- **Fixed:** Invoice Project modal for retainer projects now shows a "Monthly Retainer Fee" option instead of just Project Services/Tracked Time. The correct retainer amount for the current billing period is billed as a single line item (e.g. "Monthly Retainer — March 2026"), and the period is marked as invoiced so it can't be double-billed. Overage/unbilled time remains available as an optional add-on.
- **Fixed:** Retainer periods were not resetting automatically at the start of each billing cycle because the daily cron had no period-generation logic. Added `generate_retainer_periods()` to the daily task runner — it now creates a new period for each active retainer project whenever the current date has passed the billing start day and no period covering today exists yet. Rollover hours from the previous period are calculated automatically.

## Version 7.10.12 <!-- @hidden -->
- **Fixed:** Toggle switches rendering incorrectly in WordPress admin — WordPress core CSS applies its own styles to the `.fs-switch` class (background, border, box-shadow, height:18px, padding). Resolved by increasing selector specificity to `label.fs-switch` and `.fs-wrap .fs-switch` on all toggle rules (base, small variant, high-contrast), which cleanly overrides the WP admin defaults without !important.
- **Changed:** Default timezone now defaults to `America/New_York` instead of `wp_timezone_string()` when no timezone has been explicitly set.
- **Added:** Pro overlay on Quarterly Tax Payments section (Settings > Tax tab) — the full payment tracker with income summary cards, quarterly table, and reminders is now gated behind a frosted overlay with placeholder content for free users.
- **Added:** Pro overlay on Project Templates (Settings > Templates tab) — the template manager, template library, and template editor are now gated behind a frosted overlay for free users.

## Version 7.10.11 <!-- @hidden -->
- **Fixed:** Timer popup accessible to free users — the quick-timer clock button on task cards (kanban, list, project view, client view, dashboard widget) now hidden for free plan users. Also added server-side blocking on the `popup_timer` route so even direct URL access returns a permission denied message.
- **Fixed:** Estimated Tax summary card at top of Reporting page now hidden for free users — previously showed real calculated tax data.
- **Fixed:** Annual Tax Summary section on Reporting page now fully gated — free users see a frosted overlay with placeholder dash values instead of real income/tax breakdowns. Previously the entire section (Gross Income, Self-Employment Tax, Federal/State Tax, Total Estimated Tax) rendered with real computed data behind a CSS-only frosted overlay that could be bypassed by disabling styles.
- **Security:** Audited all frosted glass pro overlays to ensure no real user data is rendered behind the blur. All overlays now use static placeholder text (`$—`) instead of actual computed values, preventing CSS-disable bypass.

## Version 7.10.10 <!-- @hidden -->
- **Fixed:** Pro overlay not appearing for Quarterly Tax Estimates on Reports page or Calendar/Tax settings — the `file_exists()` check was truthy on premium installs running without a license. Changed to gate on `fs_studio_can_use_premium()` instead, so the overlay now correctly shows for free-plan users regardless of which plugin ZIP (free or premium) is installed.
- **Fixed:** Toggle switches in Settings appearing with a visible checkbox behind the slider. Added `position: absolute`, `appearance: none`, `pointer-events: none`, `margin: 0`, `padding: 0`, and `border: 0` to `.fs-switch input` to fully suppress WordPress admin checkbox rendering. Also added `:focus` ring on the slider for accessibility and changed the checked color from hardcoded `#4caf50` to `var(--fs-primary)` for brand consistency.
- **Added:** Pro overlay on Settings > Tax tab — income tax bracket/state configuration is now gated behind frosted overlay for free plan users. Invoice Sales Tax section remains available on free plan.
- **Added:** Pro overlay on Preset Services (Settings > Services tab) — the full section shows a frosted overlay with placeholder table for free plan users.
- **Added:** Pro overlay on Recurring Expenses — both the recurring section in the expense form and the Recurring tab on the expenses list page now show frosted overlays on free plan.

## Version 7.10.9 <!-- @hidden -->
- **Added:** PRO badge on sidebar menu labels for Bookings and Recurring Invoices (matching the Forms badge from 7.10.8).
- **Added:** Frosted glass pro overlay (`.fs-pro-overlay-wrapper`) — a reusable component that shows blurred placeholder content behind a centered PRO badge, description, and upgrade button. Applied to:
  - Quarterly Tax Estimates section on Reporting page (with placeholder table)
  - Export & Reports sidebar on Reporting page (with placeholder buttons)
  - Time Tracking Reports tab (with placeholder filters and summary table)
  - Project Files tab (with placeholder file table)
  - Google Calendar settings tab (with placeholder credential fields)
  - Client Portal section on Client form (with placeholder checkbox)
- **Added:** Compact variant (`.fs-pro-overlay-compact`) for smaller sections like Client Portal and Export sidebar.
- **Added:** High-contrast override for `.fs-pro-overlay`.

## Version 7.10.8 <!-- @hidden -->
- **Added:** Usage counters on list pages for free plan users — clients ("Using 1 of 3 free client profiles"), proposals, contracts, invoice sends, and projects now show a visible counter with a progress bar and upgrade link.
- **Added:** Pro overlay on the timer widget for free plan users — displays a frosted glass overlay with a PRO badge and upgrade button over the timer card.
- **Added:** Forms is now a Pro-only feature — free plan users see a dedicated upgrade page when navigating to Forms, and the sidebar menu shows a PRO badge next to the Forms label.
- **Added:** Premium-required page partial for reusable pro-gating of full pages (used for Forms and available for future pro-only pages).
- **Added:** CSS for usage counters (`.fs-usage-counter`), timer pro overlay (`.fs-timer-pro-overlay`), and menu pro badge (`.fs-menu-pro-badge`) with high-contrast overrides.

## Version 7.10.7 <!-- @hidden -->
- **Fixed:** "Create Project" from contract now carries over the full services array (name, description, pricing type, rate, quantity, amount) instead of just a text summary. Services appear pre-populated in the project form's service items section.
- **Fixed:** "Create Project" from proposal now carries over the full services array with the same detail.
- **Fixed:** Contract notes now populate the project description field when creating a project from a contract. Same for proposal notes.
- **Fixed:** Signed contract PDF was missing service descriptions, terms & conditions, and notes. Rewrote `save_signed_contract_pdf()` to mirror the full `generate_contract()` output: service descriptions under each service line, full terms & conditions via `WriteHTML` (from `terms_content` or `enabled_sections`), notes section, complete party contact details (phone, address, city/state/zip), and services total line.

## Version 7.10.6 <!-- @hidden -->
- **Fixed:** Client signature not displaying on proposals or contracts after signing. The `esc_url()` function was corrupting base64 data URIs used for signature images. Changed to `esc_attr()` across all four views: public proposal, admin proposal, public contract, admin contract.
- **Fixed:** Contract signing form and "Awaiting signature" placeholder still appeared after the client signed. The `can_sign` check excluded `approved` and `converted` statuses (from proposals) instead of the contract-specific `signed` and `completed` statuses.
- **Fixed:** Contract client signature block only displayed when status was `approved` (a proposal status). Updated to check for `signed` or `completed` status. Also fixed `signature_date` reference to use the correct field `signature_client_date`.
- **Fixed:** Contract "Signatures" section only appeared when status was `approved` — updated to check for `signed` or `completed`.
- **Added:** `FS_PDF_Service::save_signed_contract_pdf()` method that generates and saves a signed contract PDF to disk. This PDF is now attached to the notification emails sent to both the freelancer and client when a contract is signed.
- **Added:** Client-facing notes now display on the public contract view (before the signature area). Previously, the `notes` field was stored in the database but never rendered for the client.
- **Added:** "Create Project" button on the proposal view (appears when proposal is approved). Links to the new project form with client, project name, budget, and description pre-filled from the proposal.
- **Improved:** "Create Project" from contract view now pre-fills the project form with: client, project name (from contract title), budget (from services total), start/due dates, deposit settings, and description (from service names). Previously opened a blank form.
- **Improved:** "Create Project" from proposal pre-fills: client, project name, budget (from total amount), deposit settings, and description (from service names).

## Version 7.10.5 <!-- @hidden -->
- **Fixed:** Proposal signing ("Approve Proposal") always failed with "Security check failed" due to nonce mismatch. The form generated a nonce with action `fs_sign_proposal` in field `fs_sign_nonce`, but the AJAX handler looked for field `nonce` with action `fs_public_proposal`. Handler now correctly reads `fs_sign_nonce` with action `fs_sign_proposal`.
- **Fixed:** Proposal signing handler expected `token` and `signature` field names but the form sends `proposal_id` and `signature_data`. Handler now accepts both field names for backward compatibility and can look up proposals by either token or ID.
- **Fixed:** Contract signing had the same nonce mismatch — handler used `check_ajax_referer('fs_sign_contract', 'nonce')` but form field is `fs_sign_nonce`. Updated to `check_ajax_referer('fs_sign_contract', 'fs_sign_nonce')`.
- **Fixed:** Contract signing handler expected `signature` and `name` fields but form sends `signature_data` and `signer_name`. Updated to check both field names.
- **Fixed:** Contract signing handler required `token` field which the form doesn't send. Token verification is now optional (contract is looked up by ID, token verified only if provided).
- **Fixed:** All contract signing error responses now return `array('message' => ...)` format instead of plain strings, ensuring the JS error handler can properly display messages.

## Version 7.10.4 <!-- @hidden -->
- **Fixed:** Approving a proposal that was in `revision_requested` or `revised` status now works correctly. The AJAX signing handler previously only allowed `sent` and `viewed` statuses — it now also accepts `revision_requested` and `revised`, so clients can approve without needing the freelancer to resend first.
- **Fixed:** Error alerts on the public proposal signing page showed `[object Object]` instead of the actual error message. Now properly extracts `data.data.message` from the AJAX response. Same fix applied to the contract signing page.
- **Fixed:** The `can_sign` check in the frontend router now includes `revised` status so the signature form appears for revised proposals.

## Version 7.10.3 <!-- @hidden -->
- **Fixed:** "Send Revised Proposal" now automatically prepends "Revised" to the email subject line when the proposal status is `revised` or `revision_requested`. The pre-populated subject in the send modal will read "Revised Proposal: [Title] from [Business]" instead of the standard subject.
- **Fixed:** Viewing a re-sent proposal now correctly triggers the "Proposal Viewed" notification email and updates the status to `viewed`. Previously, proposals that had been viewed before (with `viewed_at` already set) would not fire the viewed notification on subsequent sends. The fix clears `viewed_at` each time a proposal is sent, so every send/re-send cycle gets a fresh view notification.
- **Fixed:** Proposal `{amount}` variable in the email subject template now correctly uses `total_amount` field instead of non-existent `total` field (was showing $0.00).
- **Improved:** Contracts now follow the same resend/re-view pattern as proposals:
  - Resending a contract clears `viewed_at` so the "Contract Viewed" notification fires again on the next client view.
  - Resending a previously viewed contract prepends "Updated" to the email subject line.
  - Contract view tracking now explicitly updates the `viewed_at` timestamp in the database.

## Version 7.10.2 <!-- @hidden -->
- **New:** "Action Needed" dashboard widget — a unified card that surfaces everything requiring your attention, sorted by urgency. Includes:
  - **Requested Tasks** — Client-submitted tasks awaiting your review (priority 1)
  - **Proposal Revision Requests** — Clients who requested changes to proposals (priority 2)
  - **Overdue Invoices** — Unpaid invoices past due date, showing days overdue and amount (priority 3)
  - **Proposals Expiring Soon** — Sent/viewed proposals expiring within 7 days (priority 4)
  - **Tasks Needing Info** — Tasks in "Needs Information" status (priority 4)
  - **Contracts Viewed but Not Signed** — Clients who opened but haven't signed yet (priority 5)
- Widget appears at the top of the sidebar by default. Shows a red count badge in the header. Displays "You're all caught up!" with a checkmark when there are no action items. Each item is clickable and links directly to the relevant record.
- Widget is draggable/reorderable like all other dashboard widgets. Existing users with saved layouts will have it automatically injected at the top of their sidebar.
- Includes color-coded icons per action type, mobile responsive styles, and high-contrast overrides.
- **Improved:** Invoice `get_overdue()` query now also fetches `company_name` for better client name display.

## Version 7.10.1 <!-- @hidden -->
- **Fixed:** Added `margin-top: 25px` to `.fs-revision-alert` so the "Client Requested Changes" banner doesn't sit flush against the status bar.
- **Fixed:** Dashboard "Pending Proposals" card now shows correct amount (uses `total_amount` field instead of non-existent `total`) and correct status badges for `revision_requested` and `revised` statuses.
- **New:** Proposal section templates can now be deleted directly from the "Insert Template" dropdown. Each template entry now shows an × delete button on the right side with confirmation prompt. To edit a saved template, insert it into a section, make your changes, then save it as a new template with the same name (the old one can then be deleted).

## Version 7.10.0
- **New:** Preset Service Templates are now available on Proposal and Contract forms (previously only on Projects). Select "Load Preset Service" to populate services from your saved templates.
- **Fixed:** Proposal and Contract email templates no longer show escaped backslash in apostrophes (e.g., `don\'t` → `don't`). Also fixed the template save method to prevent future occurrences by properly unslashing POST data.
- **Fixed:** Proposal Viewed notification email now correctly shows the client name (uses `get_display_name` instead of deprecated `name` field), the correct proposal total (`total_amount` field), and the timestamp in the site's local timezone instead of UTC. Same fixes applied to Contract Viewed and Invoice Viewed notifications.
- **Fixed:** Send email modal now truncates long client contact names so the email address is always visible, making it easier to identify the correct recipient.
- **Fixed:** Proposal revision requests from the client public view now work correctly. The form was missing an AJAX submission handler and had incorrect nonce/field names, so submissions would silently fail. Now properly sends the revision request, stores the notes, updates status to "Revision Requested," sends notification email to freelancer, and displays the revision notes on the admin Proposal view.
- **Improved:** `addPresetService` JS function now handles both `.fs-service-name-input` (project form) and `.fs-service-name` (proposal/contract form) class names when filling in preset service data.

## Version 7.9.9 <!-- @hidden -->
- **New:** Project Notes tab now features an inline WYSIWYG editor (WordPress TinyMCE) instead of requiring navigation to the full project edit form. Notes save via AJAX with a "Save Notes" button and status feedback. Supports bold, italic, underline, strikethrough, lists, links, and text color.
- **Note:** Project notes use the `notes` field (internal, not visible to clients) rather than the `description` field. The hint "Internal notes — not visible to clients" is displayed alongside the save button.

## Version 7.9.8 <!-- @hidden -->
- **Fixed:** Milestone completion now succeeds even when invoice generation fails (e.g., no budget set for percentage-based milestones). Previously the milestone was marked complete in the database but the AJAX response returned an error, showing "Failed to complete milestone" even though it worked.
- **Fixed:** Milestone invoice generation now falls back to the project's services total (`total_amount`) when `budget` is not set, so percentage-based milestone invoices work correctly for projects using the services/line items pricing.
- **New:** Milestone indicators on the Terms tab sidebar are now clickable — hover reveals a teal highlight and clicking marks the milestone as complete with a confirmation prompt, same as the Milestones tab.
- **Fixed:** Archived tasks (from Kanban "Archive" action) now display as completed on the Project Tasks tab — they get the completed card style, checked checkbox, and count toward the completed/progress stats.
- **Fixed:** Status filter on Project Tasks tab now includes archived tasks when filtering by "Completed".

## Version 7.9.7 <!-- @hidden -->
- **Improved:** Logo upload fields now show recommended specs — PNG or SVG, at least 600px wide, transparent background. Light logo description updated to mention email usage.
- **Fixed:** Completing tasks via checkbox on the project Tasks tab now properly updates the card visually (completed class, strikethrough title, progress bar, and stats). Previously the checkbox handler was targeting a legacy selector (`.fs-task-item`) that didn't match the current card layout (`.fs-task-card`).
- **Fixed:** Task checkbox now reverts on error and reloads the page when a recurring task is generated.
- **Improved:** Payment Received email template now supports `{project_name}` and `{view_link}` variables.

## Version 7.9.6 <!-- @hidden -->
- **New:** Payment receipt prompt — after recording a payment, you're prompted to send a receipt email to the client with the familiar send modal (choose recipients, customize subject/message)
- **New:** Payment receipt emails use the customizable "Payment Received" email template from Settings → Email Templates
- **Fixed:** Client-facing emails now use the light logo on the colored header background, matching how invoices, proposals, and contracts display. Falls back to the dark logo if no light version is uploaded.
- **Fixed:** Email template live preview in Settings now shows the light logo on the colored header, accurately reflecting what clients receive.
- **Fixed:** Email header preview on the template list page now uses the light logo.

## Version 7.9.5 <!-- @hidden -->
- **Improved:** Email template editor live preview now shows the full email wrapper including the branded header (logo/business name on primary color) and footer, matching how clients actually see the email.
- **New:** Email header preview on the Email Templates settings list page so users can see their current branding at a glance, with a link to edit logo and color settings.

## Version 7.9.4 <!-- @hidden -->
- **New:** Email notification when a proposal is viewed for the first time by a client.
- **New:** Email notification when a contract is viewed for the first time by a client.
- **New:** Email notification when an invoice is viewed for the first time by a client.
- **New:** Toggle settings for each notification type under Settings → Email Templates → Email Notifications. All three are off by default.

## Version 7.9.3 <!-- @hidden -->
- **New:** Payment reminders (both automatic and manual) now send to the same contacts that the invoice was originally sent to, including CC recipients.
- **New:** When an invoice is sent via the To/CC modal, the selected recipients are saved on the invoice record so reminders can reuse them.
- **Improved:** `send_styled_email()` now supports CC recipients via an optional `cc` array in options.
- **Improved:** Falls back to primary client email if the original recipients are no longer available.
- **Improved:** Backwards-compatible with legacy `send_to_contacts` format (plain contact ID arrays).

## Version 7.9.2 <!-- @hidden -->
- **New:** Contact picker for hour cap notifications — choose which client contact receives the overage email instead of defaulting to the primary contact. Available on both the project edit form and inline service editor.
- **Improved:** Contact dropdown dynamically loads available contacts via AJAX when enabling the notification option on new services.
- **Improved:** Notification sender resolves the selected contact ID to send to the correct person, with fallback to primary email.

## Version 7.9.1 <!-- @hidden -->
- **Fixed:** Hour cap fields now appear on the inline service editor (project view Terms tab), not just the full project edit form.
- **Improved:** "Notify client" checkbox now shows the client's email address so you know who will receive the cap notification.

## Version 7.9.0
- **New:** Capped Hours for Hourly Projects — When a service is set to "Hourly > From tracked time," you can now enable "Cap my hours" to set a time budget. This shows a retainer-style progress widget on the project view with Hours Budgeted / Used / Remaining and a visual progress bar.
- **New:** Configurable overage rate — When hours exceed the cap, overage time can be billed at a different rate (e.g., 1.5x). Defaults to the service's standard hourly rate if not set.
- **New:** Cap reached notifications — When tracked hours cross the cap threshold, the system emails the freelancer automatically. Optionally notify the client too (per-project toggle).
- **New:** Invoice auto-split — When generating an invoice from tracked time on a capped project, line items automatically split into standard hours (at regular rate) and overage hours (at overage rate) with clear labeling.
- **Updated:** Retainer widget partial refactored to support both retainer and capped hourly modes as a unified "time budget widget."
- **Fixed:** AJAX service sanitizer now properly saves retainer and hour cap fields when updating services inline.

## Version 7.8.1 <!-- @hidden -->
- **Fixed:** Dashboard widget timer drifted from the persistent corner timer and popup timer over time. The dashboard was using tick-counting (`counter++` every second) which accumulated `setInterval` imprecision. All timers now use timestamp-based calculation (`Date.now() - startTime`) so they always show the same elapsed time regardless of JS timing jitter.
- **Fixed:** Timer widget and popup timer were parsing `start_time` without UTC suffix, causing potential timezone offset between timers when the server timezone isn't UTC. All timer displays now consistently use `strtotime($start_time . ' UTC')`.
- **Fixed:** Popup timer was subtracting `paused_duration` instead of adding it, which would show incorrect time after pause/resume cycles. The pause/resume model accumulates worked time in `paused_duration` and resets `start_time` on resume, so elapsed = `(now - start_time) + paused_duration`.
- **Fixed:** Timer widget PHP initial render now includes `paused_duration` in the elapsed time calculation, so the first-render display matches the JS-updated display.

## Version 7.8.0
- **New:** Email sending for invoices, proposals, and contracts now sends a single email with the primary recipient in "To" and additional contacts in "CC", instead of separate emails to each contact. All recipients see the same email thread.
- **New:** Send modal redesigned with To/CC selection — users can choose which contact receives the email directly (To radio button) and which contacts get a copy (CC checkboxes). Primary client contact defaults to "To" with additional contacts defaulting to CC.
- **New:** Email personalization (e.g., `{client_name}`) now uses the selected "To" recipient's name, ensuring the greeting matches the primary recipient even when CC'd contacts have different names.
- **New:** Added `resolve_contact_info()` helper to email service for consistent contact name/email resolution across all document types.

## Version 7.7.0
- **New:** Each time entry now shows both actual logged time and rounded billable time when time rounding is enabled. A subtle "→ 00:20:00" indicator appears below the actual duration (e.g., 00:17:03 → 00:20:00 for 5-minute rounding), making it clear how rounding affects each entry. Only displays when rounding actually changes the value and the entry is billable.
- **New:** Summary bar on time tracking page now shows total rounded billable hours alongside the raw billable time (e.g., "Billable: 33:37:41 / 35.52 hrs rounded (5-min)"), making it transparent where the uninvoiced dollar amount comes from.
- **New:** Project view timer tab also shows rounded hours in the billable stat and per-entry rounded durations.
- **New:** Summary SQL now returns `rounded_billable_hours` and `rounded_uninvoiced_hours` for display in summary bars.

## Version 7.6.9 <!-- @hidden -->
- **Fixed:** Unified time rounding across all three billing paths. Previously, the reports summary ($3,550), invoice wizard ($3,552), and recurring invoices ($3,375) could show different totals for the same time entries. Root cause: the recurring invoice was summing raw seconds per task group, then rounding once (losing individual entry rounding), while the SQL paths weren't rounding hours to 2 decimal places before multiplying by rate (causing floating-point drift vs PHP). Now all paths round each entry individually to 2dp hours first, then sum — matching what appears on invoice line items.
- **Fixed:** Time tracking summary bar (green box) now responds to Activity, Task, and Invoiced Status filters. Previously it only updated when changing Client, Project, or Date Range — other filters were not passed to the summary calculation.
- **Changed:** Recurring invoice generation now uses entry-level rate fallback (entry rate → global default) matching the invoice wizard, instead of using the raw entry `hourly_rate` with no fallback.

## Version 7.6.8 <!-- @hidden -->
- **Fixed:** Multi-project invoices now correctly distribute "Amount Invoiced" across all linked projects. Previously, only the primary (first) project received the invoiced amount credit — secondary projects showed $0.00. The fix uses the `fs_invoice_projects` junction table subtotals to calculate each project's proportional share of the invoice total (including tax). Applies to both the projects list view and the project detail view (total billed, total paid, outstanding).

## Version 7.6.7 <!-- @hidden -->
- **Fixed:** Unified hourly rate resolution across all billing paths to eliminate income discrepancies between time tracking reports, invoice wizard previews, and generated invoices. All paths now use the same logic: entry's own hourly_rate (set at creation time from project service rate) → global default rate. Previously, the reports summary used a different strategy (looking up the project's current service rate at query time), which could diverge from the rate stored on individual time entries — especially if a project's service rate had changed since entries were created.
- **Fixed:** Time tracking reports breakdown query now includes a fallback to the global default rate for entries with missing hourly_rate values, preventing $0 amounts for legacy time entries.
- **Migration:** One-time backfill of hourly_rate on legacy time entries that had NULL or zero values. Stamps each entry with its project's hourly service rate (or the global default). Runs automatically on update and only once.

## Version 7.6.6 <!-- @hidden -->
- **Fixed:** Unable to remove/clear a project from an invoice — the project_id was not being set to NULL in the database due to PHP's `isset()` returning false for null values. Changed to `array_key_exists()` so clearing the project dropdown to "No project" now correctly saves.
- **Fixed:** Invoice project dropdown now dynamically reloads when the client is changed, showing only projects belonging to the selected client. Previously, changing the client left the old client's projects in the dropdown.
- **Added:** Left scroll arrow on the Task Kanban board — when the user has scrolled right past the first columns, a left arrow button now appears to scroll back. The existing right arrow continues to show when more columns are available to the right.

## Version 7.6.5 <!-- @hidden -->
- **Fixed:** Deleting a recurring task now cascades to all child instances, preventing orphaned records from appearing in task dropdowns and selection lists.
- **Fixed:** Deleting a recurring task now stops the recurring series first, preventing the cron job from regenerating deleted tasks.
- **Fixed:** `generate_next_recurring` now verifies the parent task still exists before creating a new child instance. If the parent was deleted, the series silently stops.
- **Fixed:** Strengthened duplicate prevention in `generate_next_recurring` — now checks for ANY active (non-completed/cancelled/archived) child of the parent, not just one with the same due date. Prevents double-creation from race conditions or rapid toggling.
- **Fixed:** Daily cron (`generate_recurring_entries`) now cleans up orphaned recurring child tasks whose parent no longer exists in the database.
- **Fixed:** Archiving/deleting a recurring task from the AJAX handler now calls `stop_recurring` before deletion to ensure the entire series is properly terminated.

## Version 7.6.4 <!-- @hidden -->
- **Fixed:** Time rounding setting (e.g., 5-minute rounding) was not being applied to invoice line items, time tracking reports, project view amounts, or client portal calculations — only the raw duration was used. Rounding is now consistently applied across all billing and reporting paths.
- **Added:** New `FS_Time_Entry::get_rounded_hours()` helper method as the single canonical source for converting raw durations to billable hours with rounding applied.
- **Fixed:** Time tracking reports summary SQL (both main reports and time tracking controller) now applies minimum billable and rounding settings per-entry before calculating amounts.
- **Fixed:** CSV time entry export now uses rounded hours.
- **Fixed:** Invoice wizard entry preview amounts now reflect rounded hours.
- **Fixed:** Mismatch between reported uninvoiced amounts and generated invoice totals caused by rounding being applied inconsistently.

## Version 7.5.7 <!-- @hidden -->
- **Improved:** Signature preview image max-width capped at 220px on mobile
- **Improved:** Email Templates settings tab shows "best used on desktop" notice on mobile with greyed-out content
- **Improved:** Preset Services, Project Templates, and Contract Sections tables stack into card rows on mobile
- **Improved:** Tax settings form-table rows stack vertically on mobile
- **Improved:** Tax deductions table stacks into card rows on mobile
- **Improved:** Individual Client > Projects tab uses mobile card row layout
- **Improved:** Individual Project tab content padding removed on mobile
- **Improved:** Individual Project > Timer tab time entries use mobile card row layout

## Version 7.5.6 <!-- @hidden -->
- **Improved:** Calendar mobile timeline: removed widget padding and reduced day header width to 45px for more event text room

## Version 7.5.5 <!-- @hidden -->
- **Fixed:** Long event names no longer stretch calendar page horizontally on mobile (added min-width: 0 and overflow constraints to flex containers)
- **Fixed:** Added overflow-x: hidden to fs-wrap on mobile as safety net

## Version 7.5.4 <!-- @hidden -->
- **Fixed:** Settings page vertical sidebar now hidden on mobile with dropdown replacement (was hiding sidebar without showing dropdown)
- **Added:** Mobile tab dropdown navigation for Settings page (11 tabs: Business, Documents, Invoices, Time Tracking, Email Templates, Services, Tax, Templates, Contract Sections, Calendar, Tools)
- **Added:** Mobile card view for Bookings list — meeting type + attendee/duration on left, date + time on right, with status badge
- **Fixed:** Calendar page mobile mini calendar constrained to max-width 340px to match dashboard mini calendar sizing
- **Added:** Prev/Next month navigation footer below calendar mobile events list for easy month browsing
- **Added:** "Today" quick-nav button in calendar mobile month navigation
- **Improved:** Added 20px margin-top to bookings sidebar on mobile
- **Improved:** Dashboard widget card rows (invoices, proposals, contracts) scaled down slightly for better fit inside widget cards

## Version 7.5.3 <!-- @hidden -->
- **Improved:** Calendar mobile view now uses "Week at a Glance" timeline layout — events grouped by day with colored pills, day number/name on the left with timeline dots
- **Improved:** Calendar navigation header hidden on mobile (mini calendar arrows provide month navigation)
- **Improved:** Dashboard Invoices card redesigned with mobile card row layout (title + invoice #/client, amount, status badge, due date)
- **Improved:** Dashboard Pending Proposals card redesigned with mobile card row layout (title, client, status, amount, expiry date)
- **Improved:** Dashboard Awaiting Signature card redesigned with mobile card row layout (title, client, status, expiry date)
- **Improved:** Bookings tab navigation uses dropdown on mobile (matching project tab pattern) instead of vertical sidebar
- **Added:** Mobile tab dropdown navigation to all Bookings views (Bookings, Meeting Types, Availability, Settings)
- **Improved:** "Export for Tax Preparer" button hidden on mobile Expenses page
- **Improved:** Bookings vertical sidebar hidden on mobile (replaced by dropdown)

## Version 7.5.2 <!-- @hidden -->
- **Added:** Mobile card view for Time Tracking list — duration + activity on left, project + client on right, time/date, and billable/invoiced dot indicator
- **Added:** Recurring icon (🔄) and auto-generated icon (↩) now appear on mobile expense cards in All Expenses tab
- **Added:** Simplified mobile Calendar page — shows mini calendar card, Add Event button, and events list (using dashboard calendar list format) instead of the full grid
- **Improved:** Bookings page on mobile — hidden redundant "Upcoming Bookings" sidebar card since it duplicates the main list
- **Improved:** Added spacing between mini calendar and bookings list on mobile
- **Improved:** Calendar legend, view toggle (Month/Week), and sidebar hidden on mobile for cleaner layout
- **Improved:** Time tracking bulk actions bar hidden on mobile (requires checkboxes which aren't shown)

## Version 7.5.1 <!-- @hidden -->
- **Added:** Mobile card view for Expenses list — two-column layout with expense name, category/project, amount, and date (matching Invoices mobile design)
- **Added:** Recurring tab on Expenses list page — view, edit, duplicate, and delete recurring expense templates from a central location
- **Added:** Mobile card view for Recurring Expenses — name, category · frequency, amount, and end date

## Version 7.5.0
- **Added:** Recurring Tasks tab on Tasks list page — view, edit, and delete recurring task templates from a central location
- **Added:** Mobile card view for Recurring Invoices list — matching the Invoices mobile design with name, client, frequency, amount, and next invoice date
- **Added:** Mobile card view for Invoices list — two-column layout with client name, invoice number, status badge, amount, and due date
- **Added:** Overdue invoice auto-detection — invoices with status sent/viewed/partial that are past due automatically update to "overdue" status when viewing the invoices page
- **Improved:** Past-due invoice dates display in danger red on mobile, checking actual due date rather than just status field
- **Improved:** Invoice mobile dates use compact MM/DD/YY format

## Version 7.4.4 <!-- @hidden -->
- **Added:** Archive/Restore functionality for Proposals — archive dismissed or expired proposals to keep the active list clean
- **Added:** Archive/Restore functionality for Contracts — archive completed or rejected contracts 
- **Added:** Active/Archived tabs on Proposals list page with counts, matching the Projects page pattern
- **Added:** Active/Archived tabs on Contracts list page with counts
- **Added:** Archive and Restore actions in both mobile card menus and desktop table row menus for Proposals and Contracts
- **Added:** Database migration adds `archived` column to proposals and contracts tables
- **Fixed:** Client card dropdown menus now work correctly (removed duplicate inline JS handler that conflicted with global handler)

## Version 7.4.0 – 7.4.3
- **Added:** Mobile card view for Projects list — cards with client logo, project name, status badge, and invoiced amount (matching Clients card design)
- **Added:** Mobile card view for Proposals list
- **Added:** Mobile card view for Contracts list
- **Added:** Active/Archived tabs for Clients list page (replaced All/Active/Inactive dropdown)
- **Improved:** `matchMedia` used for mobile detection in tab dropdown JS (works with browser DevTools responsive mode)
- **Fixed:** Card dropdown menus no longer clipped by cards below (z-index management)
- **Fixed:** `.fs-mobile-only` breakpoint corrected from 600px to 782px

## Version 7.2.1 <!-- @hidden -->
- **Added:** Home screen bookmark support — when users add the plugin dashboard to their phone's home screen, it displays the Freelance Studio logo icon instead of a generic browser screenshot
- **Added:** Apple Touch Icons (76px, 120px, 152px, 180px) and Android icons (192px, 512px) for all device sizes
- **Added:** Web App Manifest with standalone display mode, teal theme color, and proper app naming
- **Added:** PWA meta tags on popup timer window for home screen bookmarking the timer separately
- **Added:** Custom favicon (32x32) for browser tabs on FS admin pages

## Version 7.2.0
- **Improved:** "Report a Bug" button collapses to just the bug icon on mobile screens, tucked against the left edge to save space
- **Fixed:** "Report a Bug" button repositioned to bottom-left to avoid overlapping with the floating timer indicator

## Version 7.1.7 <!-- @hidden -->
- **Added:** Floating timer indicator — when a timer is running, a small persistent widget appears in the bottom-right corner of all Freelance Studio admin pages showing elapsed time with a live counter, pulsing dot, and task/project name
- **Added:** Pop-out timer button on floating indicator — click the external link icon to (re)open the popup timer window at any time
- **Added:** Pop-out timer button on inline timer widget — when a timer is running on the Time Tracking or project page, a new button lets you pop it out to a separate window
- **Added:** Clicking the time display on the floating indicator navigates to the Time Tracking page

## Version 7.1.6 <!-- @hidden -->
- **Fixed:** Non-billable task auto-detection now works in the quickadd time entry form on project detail pages (handler was previously inside a conditional block that didn't render in project context)
- **Fixed:** Timer widget task dropdown now shows "(non-billable)" label next to non-billable tasks for consistency with the quickadd form

## Version 7.1.5 <!-- @hidden -->
- **Added:** Persistent task sorting — sort preference (field and direction) is saved per-user and remembered across sessions
- **Added:** Status as a sortable column in the task list view, with logical ordering (Requested → Pending → In Progress → Needs Info → With Client → Completed → Archived → Cancelled)
- **Added:** Dashboard task widget now uses the same persistent sort preference as the task list page, with the subheading reflecting the current sort field
- **Added:** Non-billable checkbox on tasks — marking a task as non-billable automatically sets any time tracked against it (via timer or manual entry) as non-billable
- **Added:** Non-billable badge displayed next to task titles in the task list view
- **Added:** `is_non_billable` field on tasks table (auto-migrated)
- **Added:** Non-billable setting carries over to recurring task instances

## Version 7.1.4 <!-- @hidden -->
- **Changed:** Recurring tasks now follow "one at a time" visibility — only the current active instance per recurring parent shows in task lists (Kanban, list view, project view, client view)
- **Changed:** Recurring parent tasks are hidden from lists when an active child instance exists (parents serve as templates)
- **Added:** "Hide until due date" option for recurring tasks — next instance won't appear in lists until its due date arrives
- **Added:** `hide_until_due` field on tasks table (auto-migrated)
- **Added:** `smart_recurring` filter option in task queries for intelligent recurring task visibility
- **Fixed:** Invoice Wizard task grouping now consolidates time entries from recurring task instances under the parent task name
- **Fixed:** Task selection dropdowns (timer, quick add, time entry modal) no longer show duplicate entries for recurring task instances
- **Fixed:** PDF status badge rectangle was mispositioned (double unit conversion in Rect coordinates)

## Version 7.1.0
- **Security:** Removed TCPDF library (v6.7.5) which had multiple disclosed CVEs (LFI, ReDoS, XSS, hash comparison bypass)
- **Changed:** Replaced TCPDF with new built-in FS_PDF library for all PDF generation (invoices, proposals, contracts, income reports)
- **Changed:** PDF output is now clean PDF 1.4 with no /OpenAction, /JS, or /AcroForm directives — prevents Gmail virus scanner false positives
- **Changed:** Signature images in contract PDFs now rendered via temp file conversion instead of inline base64
- **Changed:** UTF-8 text (smart quotes, emoji, special characters) properly converted to WinAnsi for PDF compatibility
- **Changed:** Contract terms HTML rendered via lightweight HTML parser (supports headings, bold, italic, lists, paragraphs, horizontal rules)
- **Improved:** Plugin size reduced ~1.5MB by removing bundled TCPDF library and font files
- **Improved:** WordPress output buffer handling hardened — clears all nested buffer levels before PDF output to prevent corruption

## Version 7.0.9 <!-- @hidden -->
- **Added:** `function_exists`/`set_basename` wrapper for automatic free version deactivation when premium version activates (and vice versa)
- **Changed:** Renamed SDK init function `fs_studio()` → `freelance_studio_fs()` to match Freemius naming convention for auto-deactivation mechanism
- **Changed:** Updated all `get_upgrade_url()` references across admin, views, and AJAX handlers to use renamed function
- **Removed:** Old custom license system (`class-fs-license.php`) — licensing is now fully handled by Freemius SDK
- **Removed:** All `is_licensed()` gate checks from admin render methods (16 methods)
- **Removed:** License tab from Settings page (nav item, data loading, activation form, status card)
- **Removed:** `handle_license_action()` from settings controller
- **Removed:** ~150 lines of `.fs-license-*` CSS from stylesheet
- **Removed:** `render_license_required()` method and its inline styles from admin class

## Version 7.0.8 <!-- @hidden -->
- **Enhancement:** Updated `premium_slug` from `freelance-studio-premium` to `freelance-studio-pro` to match Freemius dashboard configuration.
- **Enhancement:** Added `is_org_compliant` flag for WordPress.org repository compatibility.
- **Enhancement:** Enabled `has_affiliation` (selected) for Freemius affiliate platform.
- **Enhancement:** Trial now requires payment method (`is_require_payment: true`) — 14-day trial with card on file.
- **Enhancement:** Disabled Freemius contact menu item (`contact: false`).

## Version 7.0.7 <!-- @hidden -->
- **Fix:** Restored Freemius SDK in ZIP at `/freemius/` (standard path) — SDK is required for deployment. Used single-line `@fs_ignore /freemius/, /includes/libraries/` directive to prevent preprocessor from parsing SDK (202 files) and TCPDF (23 files), resolving memory exhaustion during deployment.
- **Fix:** Cleaned SDK of dev tooling (scss, .po source files, gulp, composer, phpstan) to minimize file count.

## Version 7.0.6 <!-- @hidden -->
- **Fix:** Excluded Freemius SDK from deployment ZIP — Freemius auto-includes its own SDK during processing. Reduces plugin from 390+ files to 190 files, resolving "too many files to parse (memory exhausted)" deployment error.
- **Fix:** Updated SDK require path from `/vendor/freemius/start.php` to `/freemius/start.php` (standard Freemius convention).

## Version 7.0.5 <!-- @hidden -->
- **Fix:** Added `@fs_ignore` directive to plugin header — tells Freemius preprocessor to skip `/vendor/` (SDK, 257 files) and `/includes/libraries/` (TCPDF, 23 files) during deployment, resolving "too many files to parse" memory exhaustion error.

## Version 7.0.4 <!-- @hidden -->
- **Fix:** Removed `uninstall.php` to resolve Freemius SDK conflict — Freemius requires uninstall logic to be registered via its `after_uninstall` hook so it can track uninstall feedback. Cleanup function moved to `freelance-studio.php` and registered with `fs_studio()->add_action('after_uninstall', ...)`.

## Version 7.0.3 <!-- @hidden -->
- **Feature:** Real Freemius WordPress SDK integrated — replaced development stub with the official SDK from github.com/Freemius/wordpress-sdk.
- **Feature:** Freemius credentials configured — Product ID (24684) and Public Key set in SDK init snippet. Plugin is now ready for Freemius Dashboard deployment.
- **Enhancement:** SDK cleanup — removed dev tooling (gulp, phpcs, composer) from vendored SDK to minimize plugin size.
- **Note:** Phase 3 milestone. Upon activation with the real SDK, WordPress will show the Freemius opt-in screen. The SDK manages licensing, updates, trials, and the upgrade flow automatically.

## Version 7.0.2 <!-- @hidden -->
- **Feature:** Premium-only page render for Bookings, Recurring Invoices, and Project Templates — free plan users see branded upgrade page with feature description and trial CTA instead of the feature content.
- **Feature:** `render_premium_required()` helper method on FS_Admin — reusable upgrade page with emoji icon, feature title, description, and upgrade button. Supports booking, recurring, templates, and gcal feature keys.
- **Feature:** Hard gate on Google Calendar settings tab — shows upgrade banner on free plan instead of OAuth configuration.
- **Feature:** Hard gate on e-signatures — public contract view disables `$can_sign` on free plan, and `sign_contract` AJAX handler returns error. Contracts can still be viewed, just not e-signed.
- **Feature:** Hard gate on project file uploads — `upload_project_file` AJAX handler returns error on free plan. Files tab in project view shows upgrade banner with empty state.
- **Feature:** Hard gate on client portal — portal toggle in client form shows disabled checkbox with upgrade notice on free plan.
- **UI:** Added `.fs-premium-required` CSS for premium feature upgrade pages — centered card with icon, heading, description, CTA button, and trial messaging.
- **UI:** High-contrast override for `.fs-premium-required` card.
- **Note:** Phase 2 complete. All hard gates and premium-only feature locks are now implemented. Ready for Phase 3 (Freemius Dashboard setup, trial flow testing, final polish).

## Version 7.0.1 <!-- @hidden -->
- **Feature:** Hard gate on client creation — blocks new clients when free plan 3-client limit is reached. Shows PRO badge on button and upgrade banner when at/near limit.
- **Feature:** Hard gate on project creation — blocks new active projects when free plan 1-per-client limit is reached.
- **Feature:** Hard gate on invoice sending — blocks send (both direct and AJAX modal paths) when free plan 3/month limit is reached. Increments monthly counter after each successful send.
- **Feature:** Hard gate on proposal creation — blocks new proposals when free plan 1-active-proposal limit is reached. Shows PRO badge on "New proposal" button.
- **Feature:** Hard gate on contract creation — blocks new contracts when free plan 1-active-contract limit is reached. Shows PRO badge on "New contract" button.
- **Feature:** Hard gate on all PDF downloads — invoice, proposal, and contract PDF generation returns redirect with `pro_required` message on free plan. PDF buttons show PRO badge overlay instead of linking to download.
- **Feature:** Hard gate on live timer — `start_timer` and `quick_start_timer` AJAX handlers return error with upgrade message on free plan. Manual time entry remains available.
- **Feature:** Hard gate on time-to-invoice linking — `generate_invoice_from_time` AJAX handler returns error on free plan.
- **Feature:** Hard gate on milestone creation — `save_milestone` AJAX handler returns error on free plan.
- **Feature:** Hard gate on time tracking Reports tab — shows upgrade banner with empty state on free plan instead of report data.
- **Feature:** Hard gate on report exports — Export & Reports sidebar in reporting view shows upgrade notice instead of CSV/PDF export buttons on free plan.
- **Feature:** Quarterly tax section conditionally loads only when premium file exists; shows upgrade notice on free plan.
- **Feature:** "Powered by Freelance Studio" badge added to public invoice, proposal, and contract views when on free plan. Links to freelancestud.io with ref tracking.
- **Note:** This completes Phase 2 (hard gates). Phase 3 (UI polish, trial flow testing, Freemius Dashboard setup) is next.

## Version 7.0.0
- **Major:** Freemius SDK integration for freemium distribution model.
- **Architecture:** Renamed all `FS_` plugin constants to `JEEB_FS_` (FS_VERSION → JEEB_FS_VERSION, FS_PLUGIN_DIR → JEEB_FS_PLUGIN_DIR, etc.) to prevent namespace conflicts with Freemius SDK's own `FS_` constants.
- **Architecture:** Added `@fs_premium_only` file exclusion list to plugin header for automatic free version generation. Files for booking, Google Calendar, project templates, milestones, file uploads, recurring invoices, tax tools, email templates, and the premium helper directory will be stripped from the free version.
- **Architecture:** Conditional file loading in `load_dependencies()` — premium-only models, controllers, and services now use `file_exists()` checks so the free version doesn't fatal error when stripped files are missing.
- **Feature:** Freemius SDK initialization with `fs_studio()` helper function, 14-day free trial support, and Pro plan configuration.
- **Feature:** Complete premium gating helper system — `fs_studio_is_free_plan()`, `fs_studio_can_use_premium()`, `fs_studio_limit_reached()`, `fs_studio_get_usage_count()`, `fs_studio_increment_invoice_sends()`, `fs_studio_upgrade_notice()`, and `fs_studio_show_powered_badge()`.
- **Feature:** Free plan limit configuration — 3 clients, 1 active project per client, 3 invoice sends/month, 1 active proposal, 1 active contract.
- **Feature:** Upgrade notice rendering system with four display contexts (inline, banner, modal, tooltip) and centralized message registry for all gated features.
- **UI:** Complete upgrade notice CSS — PRO badges, inline notices, banner notices, locked button states, limit warning banners, and "Powered by Freelance Studio" badge styling.
- **UI:** High-contrast overrides for all upgrade notice components.
- **Architecture:** Created `/includes/premium/` directory for future premium-only helper classes.
- **Architecture:** Created `/vendor/freemius/` with development stub SDK that grants full access during local development. Must be replaced with real Freemius SDK before deployment.
- **Architecture:** Updated `uninstall.php` to integrate with Freemius after_uninstall hook for proper cleanup with user feedback collection.
- **Note:** This version establishes the Freemius foundation. Phase 2 (hard gates on individual features) and Phase 3 (usage limit enforcement in controllers/views) are next.

## Version 6.7.0
- **Feature:** Solid or gradient option for header color. Users can now choose between a solid color or a two-color gradient for document headers.
- **Feature:** Gradient direction picker with 8 directional presets (↑ ↗ → ↘ ↓ ↙ ← ↖) for controlling gradient angle.
- **Feature:** Live color swatch preview showing the current solid/gradient result.
- **Feature:** Solid/Gradient segmented toggle with animated active state, replacing the single color picker.
- **Enhancement:** Header preview in settings now reflects gradient in real-time as colors and direction change.
- **Enhancement:** Light logo thumbnail background also updates with gradient when in gradient mode.
- **Architecture:** New `--primary-bg` CSS variable in document styles — backgrounds use gradient/solid, while text colors and borders remain solid via `--primary-color`.
- **Backend:** Added `fs_invoice_primary_color_mode`, `fs_invoice_primary_color_2`, and `fs_invoice_gradient_direction` options across activator, admin save handler, AJAX auto-save, settings export/import, and all controllers.
- **Applies to:** Public invoice, proposal, and contract headers + admin preview headers. Portal, booking, email, and PDF accents remain solid color.

## Version 6.6.9 <!-- @hidden -->
- **Fix:** Removed white background pill from logo on colored document headers (public and admin preview). Light logos now display cleanly against the brand color without a white box.

## Version 6.6.8 <!-- @hidden -->
- **Feature:** Dual logo system — users can now upload separate Default (Dark) and Light logo variants from Settings > Business > Branding.
- **Feature:** Light logo is automatically used on colored backgrounds (invoice, proposal, and contract headers). Default logo is used on white backgrounds (client portal, booking pages, emails, PDFs).
- **Feature:** Side-by-side header preview in settings shows both logo contexts simultaneously — colored header and white header — updating in real-time.
- **Feature:** "Where Each Logo Is Used" reference map in settings shows exactly which pages use which logo variant.
- **Enhancement:** Light logo upload thumbnail previews against the user's actual brand color instead of a checkerboard.
- **Enhancement:** Dark/Light variant tags displayed next to logo labels for quick identification.
- **Enhancement:** Refactored logo upload JavaScript into a reusable factory pattern, eliminating duplicate code.
- **Backend:** Added `fs_business_logo_light` option to activator, admin save handler, AJAX auto-save mapping, settings export/import, and all controllers/services that supply settings to views.
- **Fallback:** If no light logo is uploaded, all colored-header views gracefully fall back to the default logo (with existing white background pill for contrast).

## Version 6.6.7 <!-- @hidden -->
- **UI Tweak:** Changed active tab background color from `primary-xtralight` to `primary-light` across all segmented tab navigation for stronger visual distinction.
- **Feature:** Added live document header preview to Business > Branding settings. Users can now see how their logo and brand color will appear on invoice, proposal, and contract headers in real-time as they make changes.
- **Feature:** Added "Logo on white" vs "Logo directly on color" toggle in the header preview so users can preview both display modes for transparent PNG logos.
- **UI Enhancement:** Logo preview thumbnail now shows a checkerboard pattern behind logos with transparent backgrounds, making transparency visible at a glance.
- **UI Enhancement:** Updated logo upload description text to clarify PNG transparency support.

## Version 6.6.6 <!-- @hidden -->
- **UI Redesign:** Redesigned all horizontal tab navigation throughout the plugin from underline-style tabs to a modern segmented pill/toggle style. Tabs now use a rounded container with a subtle border, uppercase labels, and a soft teal fill for the active state. Applies to:
  - Time Tracking tabs (Timer view / Week view / Reports)
  - Projects list tabs (Active / Archived)
  - Tasks list tabs (Active / Archived)
  - Invoices tabs (Invoices / Other Income)
  - Client detail tabs (all sub-tabs)
  - Project detail tabs (all sub-tabs)
- **UI Redesign:** Updated tab count badges to use slightly stronger background colors for better contrast within the new pill-style containers.
- **Accessibility:** Updated high-contrast CSS overrides for all tab variants to work with the new pill design.

## Version 6.6.5 <!-- @hidden -->
- **Feature:** Added Task filter dropdown to the Time Tracking page filters. Users can now filter time entries by specific task in addition to existing client, project, activity, and invoiced status filters.
- **UI Fix:** Changed Filter and Reset buttons on the Time Tracking page from WordPress default `.button` styling to FS secondary button styling (`.fs-btn .fs-btn-secondary .fs-btn-small`) for consistency with the rest of the plugin.

## Version 6.6.4 <!-- @hidden -->
- **Bug Fix:** Quickadd task dropdown in Project > Timer tab now reuses the timer widget's already-loaded task data when both are on the same page, ensuring identical results. Added fallback query without status filter if the primary query returns empty, to catch tasks in unexpected custom statuses.
- **Bug Fix:** Protected `fs_get_project_hourly_rate()` function with `function_exists()` check to prevent fatal errors if the quickadd partial is included multiple times on the same page.

## Version 6.6.3 <!-- @hidden -->
- **Bug Fix:** Project > Tasks tab layout was broken — the overview stats card and task cards were displaying side-by-side instead of stacked. Caused by the tab pane div having the CSS class `fs-tasks-tab` (meant for kanban tab buttons, which has `display: inline-flex`), making the entire content area an inline-flex container. Removed the incorrect class.
- **Bug Fix:** Quickadd task dropdown on Project > Timer tab was empty. Simplified the rendering logic — when in project context, tasks are now listed directly without optgroup wrappers, since they're already filtered server-side to the current project.
- **Bug Fix:** Added `requested` to the task status filter for both timer widget and quickadd task dropdowns. Tasks in the `requested` status (custom kanban column) were being excluded from the task selection options.

## Version 6.6.2 <!-- @hidden -->
- **Bug Fix:** Fixed project status filter using non-existent `'running'` status instead of `'active'` across multiple files. This caused project dropdowns (timer widget, quickadd, task modal, kanban, task list) to show wrong or missing projects. Affected: projects controller, time tracking controller, dashboard, kanban view, task list view.
- **Bug Fix:** Timer widget and quickadd on Project > Timer tab now properly pre-select the current project. Previously `$selected_project_id` wasn't being passed from the project view, and the wrong status filter meant the project wasn't in the dropdown anyway.
- **Bug Fix:** Task dropdowns on the Project > Timer tab (both timer widget and quickadd) now filter to only show tasks belonging to that project, instead of showing all tasks from all projects.
- **Bug Fix:** Task dropdown optgroup labels were showing as empty strings because the project name lookup failed (projects weren't in the `$projects` array due to wrong status filter). Now correctly displays project names as group headers.
- **Feature:** Quickadd widget task dropdown now auto-filters when a project is selected (on the main Time Tracking page). Selecting a project dynamically hides tasks from other projects, matching the existing behavior in the timer widget.

## Version 6.6.1 <!-- @hidden -->
- **Bug Fix:** The Invoices tab content (New Invoice button, invoice stats, invoice list) was rendering inside the Timer tab due to a missing `elseif ($current_tab === 'invoices')` guard in `projects/view.php`. The Invoices tab `<div>` was placed directly after the Timer tab's closing `</div>` without its own PHP conditional, so it displayed whenever the Timer tab was active. This also caused the "New Invoice" button to appear below the time entries section on the Timer tab, and likely contributed to incorrect project/client association when creating invoices from that misplaced button.

## Version 6.6.0
- **Improvement:** Added `margin-top: 15px` to the button header area on the Project > Timer tab so the action buttons aren't flush against the timer/quickadd widgets above.
- **Feature:** Custom activity types now display with color-coded badges on time entry lists, matching the styling of default activities (Development, Design, Meeting, etc.). Colors are assigned deterministically from the FS palette using a hash of the activity name, so they remain consistent. Applies to Time Tracking list, Project timer tab, and Client time entries tab. High-contrast mode is automatically covered by existing `.fs-activity-badge` override.
- **Feature:** Time entries list (Timer View) is now paginated at 25 entries per page. Shows "Showing X–Y of Z entries" info, page number links, and prev/next navigation. All active filters are preserved across pagination. Added `count_all()` method to `FS_Time_Entry` model.

## Version 6.5.9 <!-- @hidden -->
- **Bug Fix:** Payment Instructions HTML now persists correctly. The traditional form POST save handler in `early_save_settings()` was using `sanitize_textarea_field()` which strips all HTML — changed to `wp_kses_post()` to match the AJAX auto-save handler.
- **Bug Fix:** Default Activity setting now respected across all time entry points. Five locations were ignoring the `fs_default_activity` option:
  - Popup timer (`popup-timer.php`) — was only setting selected if a timer was already running
  - Manual time entry form (`form.php`) — was only setting selected in edit mode
  - Quick-add time entry (`quickadd-time-entry.php`) — had no default selection at all
  - Time entry modal (`time-entry-modal-form.php`) — had no default selection at all
  - Timer widget (`timer-widget.php`) — had hardcoded fallback to 'development' instead of reading the setting

## Version 6.5.8 <!-- @hidden -->
- **Bug Fix:** Kanban scroll hint arrow now scrolls all the way to the last column — reduced the hide threshold from 100px to 20px and the scroll amount now calculates remaining distance so the final click reaches the end instead of stopping short.
- **Bug Fix:** Kanban horizontal scrollbar forced always-visible on Safari/WebKit. Changed `overflow-x` from `auto` to `scroll`, added `-webkit-appearance: none` and `min-width` on the scrollbar thumb. Safari's macOS system setting ("Show scroll bars: When scrolling") hides custom scrollbars by default; these overrides force them visible.

## Version 6.5.7 <!-- @hidden -->
- **Feature:** Payment Instructions in Settings is now a WYSIWYG editor (WordPress TinyMCE) supporting bold, italic, underline, links, and lists. Content auto-saves like other settings fields. HTML output renders properly on public invoices, admin invoice previews, and PDF exports.
- **Improvement:** Kanban board scroll hint arrow repositioned from bottom-right to mid-screen right, preventing overlap with the "Report a Bug" button.

## Version 6.5.6 <!-- @hidden -->
- **Bug Fix:** Dashboard "Add Task" button now works — the `fsTaskModal` config object was missing on the dashboard page, causing the task modal JS to bail early. Added the config initialization script block.
- **Improvement:** Moved "Add Task" button to the task widget card header (next to the limit dropdown and filter icon). "View All →" link is now in the footer, right-aligned, matching all other dashboard cards.

## Version 6.5.5 <!-- @hidden -->
- **Improvement:** Dashboard task widget now uses the full task modal (with project, priority, due date, assignee, description, etc.) instead of the inline quick-add bar. "Add Task" button appears in the card footer.
- **Improvement:** Dashboard task widget now includes a "View All →" link in the footer, matching the pattern used by all other dashboard cards (projects, invoices, time tracking, etc.).

## Version 6.5.4 <!-- @hidden -->
- **Feature:** Dashboard task widget now has a dropdown to choose how many tasks to display: 5 (default), 10, 20, or All. The selection is saved persistently via `fs_dashboard_task_limit` option, so it remembers your choice across sessions and page loads.

## Version 6.5.3 <!-- @hidden -->
- **Feature:** Kanban board horizontal scrollbar is now visible (thin styled scrollbar) so users can see there's more content to scroll to.
- **Feature:** Scroll hint indicator — a small teal arrow button appears in the bottom-right corner of the kanban board when there are more columns to scroll to. Clicking it scrolls one column right. Automatically hides when you've reached the end.
- **Feature:** Custom kanban column names now reflect everywhere — renaming a column on the kanban board (e.g. "To Do" → "Backlog") now propagates to task status pills in the list view, dashboard task widget, project detail task filters, and client portal. Powered by a new centralized `FS_View_Context::get_task_status_labels()` helper.
- **Improvement:** Project detail task status filter now shows all 5 statuses (previously only showed 3).

## Version 6.5.2 <!-- @hidden -->
- **Improvement:** Send email modal now pre-populates the subject line with the resolved template default (with variables like invoice number, client name, etc. already filled in), so users can see and edit the actual subject before sending.
- **Improvement:** Updated subject field label from "Email Subject (optional)" to "Email Subject" and helper text to "Edit to customize, or leave as-is to use your template default."

## Version 6.5.1 <!-- @hidden -->
- **Feature:** Per-invoice payment link field — each invoice can now have its own payment URL that overrides the global default. Set it on the invoice edit form under "Payment Link."
- **Feature:** Pay Now button status indicator on admin invoice preview — shows whether the client-facing invoice will display a Pay Now button, and links to configure it if not.
- **Bug Fix:** Invoice model now saves the `payment_link` field (the database column existed but was never written to).
- **Improvement:** Public invoice Pay Now button now checks both the per-invoice payment link and the global setting (per-invoice takes priority).

## Version 6.5.0
- **Bug Fix:** Fixed WordPress `wpdb` NULL handling across all models — `wpdb->update()` and `wpdb->insert()` convert PHP `null` to empty strings, which MySQL casts to `0` for bigint columns instead of SQL `NULL`. This affected nullable foreign key fields (project_id, contract_id, proposal_id, task_id, invoice_id, milestone_id, parent_task_id, client_id, etc.) across the entire plugin, preventing users from clearing/removing assignments.
- **New:** Added `FS_DB_Helper` utility class with `fix_null_fields()` method to properly handle NULL values after wpdb operations, used consistently across all models.
- **Models Fixed:** Invoice, Contract, Project, Task, Expense, Recurring Invoice, Time Entry, Milestone, Retainer Period, Form, Form Response

## Version 6.4.9 <!-- @hidden -->
- **Feature:** Smart payment link amount pass-through — PayPal.Me and Venmo links now automatically have the invoice balance and reference pre-filled in the URL so clients see the exact amount when they click "Pay Now" (e.g. `paypal.me/Name/600.00` or `venmo.com/Name?txn=pay&amount=600.00&note=Invoice+INV-001`)
- **Feature:** Stripe and other generic payment links continue to work with `client_reference_id` and `prefilled_email` parameters as before
- **Improvement:** Payment Link settings field now includes an info box explaining which providers support automatic amount pass-through, with example URL formats for PayPal.Me and Venmo
- **Improvement:** Updated placeholder text in Payment Link field from Stripe example to PayPal.Me example since it supports the best client experience

## Version 6.4.8 <!-- @hidden -->
- **Bug Fix:** "Pay Now" button now appears on client-facing invoice view when a payment link is set in Invoice Settings — previously the button never showed because the template was checking for non-existent `fs_paypal_enabled`/`fs_stripe_enabled` options instead of using the configured payment URL
- **Bug Fix:** Payment Link setting now saves and loads correctly — three naming inconsistencies were causing the value to be saved under one option key (`fs_payment_link`) but read from another (`fs_payment_url`), and the auto-save field name didn't match the form input name
- **Improvement:** Pay Now button now displays the balance due amount (e.g. "Pay Now — $600.00") for clarity
- **Improvement:** Payment link now automatically appends `client_reference_id` (invoice number) and `prefilled_email` (client email) as URL parameters — works with Stripe Payment Links and most payment processors that accept URL parameters
- **Migration:** Any payment URL previously saved under the old `fs_payment_link` key is automatically migrated to the correct `fs_payment_url` key on first access

## Version 6.4.7 <!-- @hidden -->
- **Bug Fix:** Invoices marked as Paid can now be changed to other statuses (Pending, Draft, Sent, etc.) — previously, changing the status via the list page dropdown or edit form would appear to work but the invoice would revert to Paid on next load because payment fields (paid_date, amount_paid) were not cleared when changing away from Paid status
- **Bug Fix:** Changing invoice status away from Paid via the inline dropdown now properly clears paid_date, paid_at, amount_paid, and recalculates balance_due
- **Bug Fix:** Editing an invoice with a paid_date set no longer forces the status back to Paid if the user explicitly selected a different status — the auto-pay detection now respects the user's explicit status choice
- **Bug Fix:** Removing a project from an invoice that was previously Paid now works correctly — the underlying issue was the status being locked to Paid which prevented saving other changes

## Version 6.4.6 <!-- @hidden -->
- **Bug Fix:** Invoice wizard totals now match between the live preview (Step 2) and the review/created invoice (Step 3) — PHP server-side calculations were using raw unrounded duration values while JS preview used pre-rounded hours, causing small discrepancies (e.g. $515.00 vs $515.08)
- **Bug Fix:** Hours shown in invoice line item descriptions now match the actual Qty/Hours value — previously descriptions used 1 decimal place (e.g. "5.2 hrs") while quantity used 2 decimal places (e.g. 5.15), creating client-facing inconsistencies
- **Bug Fix:** Invoice line item amounts are now calculated as `rounded_quantity × rate` consistently across all grouping modes (activity, task, date, project, individual entry), eliminating floating-point accumulation errors

## Version 6.4.5 <!-- @hidden -->
- **Bug Fix:** Invoices created with status "Paid" and a paid date now properly count toward Gross Income — `paid_at` timestamp was not being set during invoice creation, causing `get_total_revenue()` to exclude them
- **Bug Fix:** The "Paid On" date on invoices now correctly reflects the user-entered paid date instead of always showing today's date — `paid_at` was being set to `current_time()` instead of using the `paid_date` field value
- **Bug Fix:** "Record Payment" button on the invoice view page now works correctly — previously linked to an unhandled `record_payment` action which fell through to the invoice list page
- **Bug Fix:** Amount Paid field is now available when creating new invoices (not just when editing) — allows marking an invoice as paid during initial creation
- **Bug Fix:** Recording payment via any method (dropdown status change, Record Payment button, or manual edit) now consistently sets both `paid_at` and `paid_date` fields
- **Bug Fix:** Setting a paid date on an invoice without specifying an amount now auto-fills the full invoice total as the paid amount
- **Improvement:** Record Payment button on invoice view now uses FS button styling instead of WordPress default button

## Version 6.4.4 <!-- @hidden -->
- **Bug Fix:** Restored colored pill styling on dashboard task status dropdowns — previous fix incorrectly removed the `.fs-inline-status-select` class that provides the pill CSS; now the invoice handler in JS skips elements with `.fs-dashboard-task-status` instead

## Version 6.4.3 <!-- @hidden -->
- **Bug Fix:** Fixed "Invalid invoice ID" error when changing task status from the dashboard inline dropdown — the task select had the `.fs-inline-status-select` class which triggered the invoice status handler in addition to the correct task handler

## Version 6.4.2 <!-- @hidden -->
- **Bug Fix:** Fixed "headers already sent" error causing blank pages after form submissions and actions (archive project, duplicate expense, save settings, add client/project, connect Google Calendar, etc.)
- **Bug Fix:** Output buffering now starts early for ALL Freelance Studio admin pages (GET and POST), not just POST requests — ensures redirects work reliably after any action
- **Bug Fix:** Replaced raw `wp_redirect()` calls in project archive/unarchive with `FS_Admin::safe_redirect()` for consistent redirect handling
- **Bug Fix:** Replaced `wp_safe_redirect()` in license activation with `FS_Admin::safe_redirect()` for consistent redirect handling

## Version 6.4.0
- **Improvement:** Dashboard task widget status pills are now inline editable dropdowns — change task status directly from the dashboard without navigating away
- **Improvement:** Dashboard status dropdowns match the original pill size and shape (11px, 3px/10px padding, 20px border-radius) so they look identical to the previous static badges
- **Improvement:** Selecting "Done" from the dashboard dropdown animates the task out, same as checking the completion checkbox

## Version 6.3.9 <!-- @hidden -->
- **Bug Fix:** Imported calendar events toggle on dashboard Week at a Glance widget now always visible (removed conditional that hid it when no external events existed in current week)

## Version 6.3.8 <!-- @hidden -->
- **Bug Fix:** Changed `.fs-calendar-event.fs-event-milestone` background from `--fs-primary-light` to `--fs-primary-xtralight`
- **Improvement:** Added "Imported" checkbox toggle to the dashboard Week at a Glance widget header — lets user show/hide imported calendar events (Google Calendar, etc.) from the week view
- **Improvement:** Preference persists per-user via user meta (`fs_dashboard_show_external_events`), defaults to showing imported events
- **Improvement:** Toggle only appears when external events exist or have been hidden, keeping the UI clean when no external calendars are connected
- **Improvement:** High-contrast overrides included for the new toggle

## Version 6.3.7 <!-- @hidden -->
- **Improvement:** Inline status select font size reduced to .65rem for a tighter, cleaner look
- **Improvement:** Task list table columns now use auto layout instead of fixed widths — Assignee, Priority, Status, and Due Date columns shrink to fit content, giving more room to Task and Project/Client columns
- **Improvement:** Added Edit option to the task row action menu (⋯) — opens the task modal for quick editing directly from the list view

## Version 6.3.6 <!-- @hidden -->
- **Improvement:** Task status pill on the Task list view is now an inline editable dropdown — change task status directly from the list without opening a modal
- **Improvement:** Status dropdown uses color-coded kanban status styling (matching kanban board colors) with smooth visual feedback on change
- **Improvement:** Completed status updates row styling in real-time (checkbox, strikethrough title)
- **Improvement:** Archived tab tasks retain static badge (non-editable) since status changes aren't relevant there

## Version 6.3.3 <!-- @hidden -->
- **Bug Fix:** Moved margin from `.fs-settings-sidebar` to `.fs-settings-content` (`margin-left: 15px`) so content panel sits flush against sidebar
- **Bug Fix:** Services Edit/Delete buttons now styled as teal/red text links matching the rest of Settings (was still showing as bordered WP buttons)
- **Improvement:** Added `margin-bottom: 15px` to `.fs-schedule-days` for spacing below availability schedule
- **Improvement:** Responsive mobile override resets `margin-left: 0` on `.fs-settings-content`

## Version 6.3.2 <!-- @hidden -->
- **Improvement:** Settings/Bookings layout — removed gap, sidebar gets `border-radius: 10px 0 0 10px` with 15px right margin, content gets `border-radius: 0 10px 10px 0`, both borderless, content fills vertical space via `align-items: stretch`
- **Improvement:** All WordPress `.button` elements inside `.fs-settings-content` now automatically inherit FS styling (secondary = white with gray border, primary = teal gradient) — applies to Settings, Bookings, and any future pages using the layout
- **Improvement:** Edit/Delete text links in settings tables now consistently styled — Edit links use teal, Delete links use danger red
- **Improvement:** Responsive overrides updated to match new borderless panel design

## Version 6.3.1 <!-- @hidden -->
- **Bug Fix:** Bookings Availability and Settings tabs now use the new vertical sidebar layout (matching Bookings and Meeting Types tabs from v6.3.0)
- **Improvement:** Added 24px gap between sidebar nav and content area in `fs-settings-layout` — applies to both Settings and Bookings pages
- **Improvement:** Sidebar and content panels now have full `border-radius` on all corners (no longer joined edge-to-edge)
- **Improvement:** Bookings Settings "Save Settings" button restyled from WordPress `.button-primary` to FS `.fs-btn-primary`

## Version 6.3.0 — Brand Consistency Pass
- **Improvement:** Projects page — added "Projects" page title, moved "New project" button to top right
- **Improvement:** Proposals page — added "Proposals" page title, moved "New proposal" button to top right
- **Improvement:** Contracts page — added "Contracts" page title, moved "New contract" button to top right
- **Improvement:** Clients page — moved view toggle (grid/list) next to "Clients" title, "New client" button stays top right
- **Improvement:** Bookings page — removed icon from title, replaced horizontal WordPress nav tabs with vertical sidebar tabs matching Settings page layout (`fs-settings-layout` / `fs-settings-sidebar`)
- **Improvement:** Calendar page — removed icon from title, styled as `fs-page-title`
- **Improvement:** Forms page — restyled buttons from WordPress `.button` / `.button-primary` to FS `.fs-btn-secondary` / `.fs-btn-primary`, consistent with all other pages
- **Improvement:** Reporting — Income Summary (PDF) button changed from primary to secondary
- **Improvement:** Expenses — Export for Tax Preparer button restyled from WordPress `.button` to `.fs-btn-secondary`
- **Bug Fix:** Removed 📅 emoji from Bookings upcoming widget, replaced with dashicon

## Version 6.2.9 <!-- @hidden -->
- **Improvement:** Reporting sidebar — Top Clients and Export & Reports now share a single grid column via `.fs-report-sidebar-stack` wrapper, so both cards stack vertically in the sidebar next to the tax estimation block without Top Clients taking a full-height column

## Version 6.2.8 <!-- @hidden -->
- **Improvement:** View toggles now use `.fs-btn-primary` / `.fs-btn-secondary` pattern consistently across all pages (Tasks, Bookings, Clients)
- **Improvement:** Clients view toggle JS updated to swap between `fs-btn-primary`/`fs-btn-secondary` classes instead of `.active`
- **Improvement:** Reporting > Top Clients table: removed outside border (borderless), removed left padding on first column, removed Invoices count column — matches Expense Categories table treatment
- **Improvement:** Moved Top Clients card to right sidebar above Export & Reports card in Reporting page

## Version 6.2.7 <!-- @hidden -->
- **Improvement:** Removed emojis (⚠️💰) from Dashboard > Upcoming Events card — invoice events now use dashicons (`dashicons-warning`, `dashicons-money-alt`) consistent with all other event types
- **Improvement:** Standardized all `.fs-view-toggle` components to use `.fs-view-btn` / `.fs-view-btn.active` pattern from UI Components Reference — Tasks list/kanban and Bookings list were using `.fs-btn-primary` / `.fs-btn-secondary` instead of the proper toggle component
- **Improvement:** View toggles on Tasks page now positioned next to page title (consistent placement)
- **Improvement:** Standardized "New" button labels across all list pages: "New client" (was "Add client"), "New expense" (was "Add Expense"), "New invoice" (was "New Invoice" — capitalization fix)
- **Improvement:** Reporting > Expenses by Category table: removed outside border (`fs-table-borderless`), removed left padding on first column so text aligns with section heading, removed Count column
- **New CSS utilities:** `.fs-table-borderless` (removes all table borders/shadow), `.fs-col-no-pad-left` (removes left padding for column alignment)

## Version 6.2.6 <!-- @hidden -->
- **Bug Fix:** Dashboard task list status badges now stay left-aligned next to task names instead of being pushed right — reverted `.fs-task-info` from `flex: 1` back to `flex-shrink: 1` so it only takes the space it needs, while project tags still right-align via `margin-left: auto` on `.fs-task-meta`

## Version 6.2.5 <!-- @hidden -->
- **Bug Fix:** Dashboard quick timer button now works — click handler was never binding because `fs-task-modal.js` exited early when `window.fsTaskModal` was undefined (dashboard doesn't set it). Moved timer launcher to a standalone block that runs on all pages.
- **Improvement:** Dashboard task list quick timer icon now always visible at 40% opacity (full opacity on hover) instead of hidden until hover
- **Improvement:** Dashboard task list project tags are now right-aligned with consistent positioning (`min-width: 200px`, `justify-content: flex-end`) so they line up cleanly regardless of project name length

## Version 6.2.4 <!-- @hidden -->
- **Improvement:** Migrated inline styles to CSS classes across 4 major view files
  - `projects/view.php`: Replaced 10 inline styles (table column widths, icon colors, margin, opacity) with utility classes
  - `invoices/view.php`: Replaced 10 inline styles (text colors, text alignment, layout helpers, button widths) with utility classes; conditional balance-due coloring now uses class-based approach
  - `time-tracking/list.php`: Replaced 7 inline column widths with `.col-*` CSS classes
  - `clients/form.php`: Replaced 4 inline flex/layout styles with `.fs-field-wide`, `.fs-field-narrow`, and component classes
- **New CSS utility classes:** `.fs-icon-success`, `.fs-icon-danger`, `.fs-icon-muted`, `.fs-text-success`, `.fs-text-danger`, `.fs-text-muted-sm`, `.fs-text-muted-xs`, `.fs-text-share-hint`, `.fs-ml-sm`, `.fs-mt-md`, `.fs-flex-row-gap`, `.fs-btn-full`, `.fs-delete-link`, `.fs-empty-icon-lg`, `.text-center`, `.text-right`
- **New table column width classes:** `.col-checkbox`, `.col-duration`, `.col-time`, `.col-activity`, `.col-amount`, `.col-status`, `.col-actions`
- **New form field width classes:** `.fs-field-wide` (flex: 2), `.fs-field-narrow` (flex: 1), `.fs-field-zip`, `.fs-field-phone`, `.fs-field-email`, `.fs-field-state`
- **Improvement:** Contacts empty state now uses `.fs-contacts-empty-state` CSS class with proper CSS variables instead of hardcoded colors
- **Accessibility:** Added high-contrast overrides for all new utility color classes

## Version 6.2.3 <!-- @hidden -->
- **Bug Fix:** Reverted `.fs-btn-deposit` to solid accent color (`var(--fs-accent)`) with light text — gradient was too busy for inline usage context

## Version 6.2.2 <!-- @hidden -->
- **Enhancement:** Comprehensive UI/UX design audit — Phase 1 implementation based on Bakusevych UI Component Series best practices
- **Accessibility:** Added `:focus-visible` states to all button variants (`.fs-btn`, `.fs-btn-icon`, `.fs-icon-btn`, `.fs-btn-card`) — keyboard focus rings only appear during keyboard navigation, not on mouse click
- **Accessibility:** Added `:active` (pressed) states to all button variants for tactile click feedback — primary, secondary, success, accent, danger, deposit, icon buttons
- **Accessibility:** Added global `.fs-btn[disabled]` styling with reduced opacity, `not-allowed` cursor, and `pointer-events: none`
- **Accessibility:** Added `:focus-visible` support to high-contrast mode for buttons and links
- **Enhancement:** Added `.fs-btn-loading` utility class with inline spinner animation for async actions (prevents double-submission)
- **Bug Fix:** Modal form inputs (`.fs-modal-body`) now use brand teal (`--fs-primary`) for focus states instead of off-brand Tailwind blue (`#3b82f6`)
- **Bug Fix:** Timer select/input focus now uses brand teal box-shadow instead of blue
- **Enhancement:** Added hover states to all form inputs (`.fs-form-group` and `.fs-modal-body`) — border darkens on hover before focus
- **Enhancement:** Added standardized form error states (`.has-error` class) with red border, red label color, and `.fs-field-error` message styling
- **Bug Fix:** Replaced 12+ hardcoded hex colors with CSS variables for consistency and high-contrast mode compatibility:
  - Modal borders: `#d1d5db` → `var(--fs-gray-300)`
  - Modal text: `#111827` → `var(--fs-gray-900)`
  - Description text: `#6b7280` → `var(--fs-gray-600)` (3 locations)
  - Note text: `#9ca3af` → `var(--fs-gray-500)`
  - Muted background: `#f3f4f6` → `var(--fs-gray-100)`
  - Signature border: `#d1d5db` → `var(--fs-gray-300)`
  - Contract section hover: `#d1d5db`/`#fafafa` → CSS variables
  - Empty state blue: `#3b82f6` → `var(--fs-secondary)`
  - Quickadd icon: `#3b82f6` → `var(--fs-secondary)`
- **Bug Fix:** Consolidated duplicate `.fs-icon-btn` definitions (was defined at two locations with conflicting styles)
- **Enhancement:** Increased `.fs-btn-icon` touch target from 28×28px to 36×36px to approach WCAG minimum (44px)
- **Enhancement:** Updated `.fs-btn-deposit` to use teal-lime gradient with dark text (matching UI Components Reference) instead of solid orange with white text

## Version 6.2.1 <!-- @hidden -->
- **Bug Fix:** Extended High Contrast Mode to cover all remaining UI elements:
  - **Financial overview cards** — gradient backgrounds replaced with white cards with colored left-border accents (green for gross, navy for pending, red for expenses, purple for tax); ring chart strokes darkened; labels and values forced to dark text
  - **Invoice status badges** (dashboard) — now follow the same shaped-border system as other status badges
  - **Contact fs-badge** variants (primary, billing, project, internal) — distinct border colors with light backgrounds
  - **Assignee badges** — designer and client variants get colored backgrounds with dark borders
  - **Priority badges** — overrides inline styles with solid borders and dark text
  - **Stat value colored variants** (success, warning, danger) — darkened to WCAG AAA colors with extra font weight
  - **Activity badges** (development, design, meeting, research, admin, other) — each gets a unique border color matching its category
  - **Billable/non-billable/invoiced status** — distinct border treatments: solid green for billable, solid navy for invoiced, dashed gray for non-billable; status dots also get high-contrast borders
  - **Calendar events** — all event types get white backgrounds with distinct left-border accents: solid navy for projects, solid green for milestones, dashed gray for tasks, solid orange for invoices, solid red for expiring, solid purple for tax, double green for bookings, double navy for Google calendar; day cells get stronger grid lines and bold today highlighting

## Version 6.2.0
- **New Feature:** High Contrast Mode — a toggle in Settings > Business > Accessibility that transforms the entire plugin UI to meet WCAG AAA contrast standards (7:1 ratio):
  - **Status badges** gain distinct visual shapes per category: dashed borders for draft/inactive, solid left accent for pending/warning, double left accent for active/info, thick left accent + bold for success, and dark red left accent + bold for danger — ensuring statuses are distinguishable without relying on color alone
  - **Buttons** switch to solid high-contrast fills: black primary, white-with-black-border secondary, dark green success, dark red danger/stop, dark orange accent
  - **Form inputs** get 2px dark borders; focus states use a thick 3px black outline
  - **Cards, sections, modals, and tables** receive strong borders and heavier dividers
  - **Text** is darkened across the board — all muted/gray text shifts to near-black
  - **Links** are always underlined for discoverability
  - **Toggle switches** and view toggles get stronger contrast
  - **Timer buttons** use semantic dark green (start) and dark red (stop)
  - **Settings sidebar** active state inverts to white-on-black
  - Implemented via `admin_body_class` filter adding `.fs-high-contrast` class, with all overrides scoped under that selector — zero impact when disabled

## Version 6.1.2 <!-- @hidden -->
- **Bug Fix:** Fixed "Duplicate column name" database error on recurring invoice migration — replaced individual `SHOW COLUMNS` queries with a single `DESCRIBE` query to reliably detect existing columns

## Version 6.1.1 <!-- @hidden -->
- **Bug Fix:** Added failsafe to prevent recurring invoices from generating empty or $0 invoices when there are no billable time entries for the period — if `include_time_entries` is enabled but no unbilled time exists (and no static line items are configured), the invoice generation is skipped, the next invoice date is still advanced to prevent retry loops, and an informational message is logged

## Version 6.1.0
- **New Feature:** Quick timer button on dashboard task list — each task now shows a clock icon on hover that opens a popup timer pre-populated with the task and project; only appears on tasks that have a project assigned
- **New Feature:** Timer task dropdown now filters by selected project — when a project is selected in the timer widget (on both the dashboard and time tracking page), the task dropdown automatically hides tasks from other projects, showing only relevant tasks; reverts to showing all tasks when no project is selected
- **New Feature:** Recurring invoices can now auto-populate with unbilled time entries — new "Billable Time" section on the recurring invoice form with options to:
  - Enable/disable automatic inclusion of unbilled time entries from the billing period
  - Choose grouping: by task (default), by activity type, or individual entries
  - Optionally mark time entries as invoiced after generation
  - Time entries are pulled from the assigned project for the billing period matching the invoice frequency (weekly=7 days, monthly=30 days, quarterly=90 days, etc.)
  - Line items include date ranges and are added alongside any static invoice items
  - Database migration adds `include_time_entries`, `time_grouping`, and `mark_time_invoiced` columns to the recurring invoices table
  - Recurring invoice view page shows time entry settings in the details panel

## Version 6.0.9 <!-- @hidden -->
- **New Feature:** "Report a Bug" button — a fixed footer button appears on all Freelance Studio admin pages with a mouse/chat icon; clicking opens a modal to submit bug reports directly to admin@freelancestud.io
  - Structured form with fields for: page/section (auto-populated), what the user was doing, expected behavior, and actual behavior
  - Screenshot upload support via drag-and-drop or click, with image previews and remove capability (up to 3 images, 5MB each)
  - Automatically includes debug information: plugin version, DB version, PHP version, WordPress version, active theme, server info, memory limits, and max upload size
  - Sends as a formatted email via `wp_mail()` with screenshots as attachments, includes user email as reply-to
  - Success confirmation with auto-close, input validation requiring the two most critical fields
  - Clean modal UI matching the plugin's design system with overlay, transitions, and responsive layout

## Version 6.0.8 <!-- @hidden -->
- **Bug Fix:** Payment Link changes now save correctly — `payment_url` was missing from `early_save_settings()` (same root cause as the custom activities and payment instructions bugs)
- **Bug Fix:** Client-facing invoice view now displays Payment Instructions correctly even if the database value was previously corrupted to an array by the old save handler; added array-to-string recovery in public invoice view and invoices controller
- **Bug Fix:** Added all remaining missing fields to `early_save_settings()`: `my_role`, `internal_tracking_enabled`, `contract_notice_days`, `contract_cure_days`, `contract_suspension_days`, `contract_late_fee_percent`; eliminated the duplicate `save_settings()` in the settings controller (replaced with fallback redirect) to prevent future sync issues between the two handlers
- **New Feature:** Settings page now uses a vertical sidebar tab layout instead of horizontal tabs — cleaner, more modern appearance that matches the plugin's aesthetic with a sticky sidebar, active state highlighting, and responsive mobile fallback

## Version 6.0.7 <!-- @hidden -->
- **Bug Fix:** Payment Instructions now save correctly — `early_save_settings()` was treating the `payment_instructions` form field as an array (checking `is_array()`) when the actual form submits it as a plain string from a textarea; the `is_array` check always failed, causing an empty array to overwrite the saved content on every save

## Version 6.0.6 <!-- @hidden -->
- **Bug Fix:** Custom activities and all time tracking settings now save correctly — the root cause was a duplicate save handler (`early_save_settings()` in `class-fs-admin.php`) that runs during `admin_init` before the settings controller, handles the form submission, redirects, and exits — meaning the controller's `save_settings()` never executed. The early handler was missing all time tracking settings (custom activities, work days, timezone, timer warnings, keyboard shortcuts, browser notifications) as well as tax and reminder settings
- **Bug Fix:** Added tab-gating to `early_save_settings()` for checkbox and array settings (late fees, payment methods, payment instructions) to prevent cross-tab data wipes — saving one settings tab no longer resets values on other tabs
- **Bug Fix:** `fs_payment_instructions` was being unconditionally saved as an array by the early save handler even from non-invoices tabs, which caused the "Array to string conversion" PHP warning; now only saved when on the invoices tab

## Version 6.0.5 <!-- @hidden -->
- **Bug Fix:** Resolved PHP warning "Array to string conversion" on settings page — `fs_payment_instructions` option was stored as an array (likely from a prior data corruption), and the `(string)` cast in `get_all_settings()` triggered the warning; now safely checks for array values before casting

## Version 6.0.4 <!-- @hidden -->
- **Bug Fix:** Custom activities in Time Tracking settings now save correctly — previously, saving settings from any other tab (Business, Invoices, etc.) would silently wipe custom activities and work days due to unchecked `else` branches in the save handler
- **Bug Fix:** Checkbox settings (timer warning, keyboard shortcuts, browser notifications, payment reminders, auto sales tax) are now gated to their respective tabs so saving one tab no longer resets toggle values on other tabs

## Version 6.0.0
- **New Feature:** Task Archiving — archive completed tasks to declutter your Kanban board and task list while preserving them for reference
  - Archive individual tasks via the archive button (📦) on Kanban cards or from the task list row menu
  - "Archive all completed" button in the Completed column header to bulk-archive
  - New **Active / Archived** tabs on the task list page
  - Archived tasks show their original project, client, priority, and dates for reference
  - **Restore** button on archived tasks returns them to their previous status
  - **Delete Permanently** option available for archived tasks you no longer need
  - Tasks remember their pre-archive status (e.g., a task archived from "In Progress" restores to "In Progress")
- **Database:** Added `previous_status` column to tasks table with automatic migration

## Version 5.9.4 <!-- @hidden -->
- **Bug Fix:** Removed `overflow: hidden` from `.fs-kanban-card` that was clipping card content
- **Improvement:** Kanban column scrollbar color lightened to `--fs-gray-200` for subtler appearance
- **New Feature:** Dragging a task into the Completed column now automatically applies strikethrough and dimmed styling; dragging it back out restores normal appearance

## Version 5.9.0
- **Bug Fix:** Kanban columns now stretch to fill the full board height instead of sizing to their content — columns with fewer cards fill the same vertical space as columns with many
- **Bug Fix:** Individual column bodies scroll vertically when cards overflow, instead of the entire board scrolling — changed board from `align-items: flex-start` to `stretch` and added `overflow-y: hidden` on board
- **Bug Fix:** Removed `max-height` from columns; height is now properly constrained by the flex container chain (wrap → board → column → body)
- **Bug Fix:** Column headers no longer shrink when column body has many cards (`flex-shrink: 0`)

## Version 5.8.9 <!-- @hidden -->
- **Improvement:** Kanban columns now fit within the viewport height — columns scroll internally when cards overflow instead of scrolling the entire page
- **Improvement:** Hidden the horizontal scrollbar on the kanban board while preserving scroll functionality; added right padding so the furthest-right column is always partially visible, cueing users to scroll for more columns
- **Improvement:** Column body scrollbars are thin and subtle (5px, gray, rounded) for a clean look

## Version 5.8.8 <!-- @hidden -->
- **Improvement:** Removed Sort dropdown from task list — sorting is now handled entirely via clickable column headers
- **Improvement:** Sortable columns (Task, Project/Client, Priority, Due Date) now show a subtle ↕ arrow when inactive, turning into ↑/↓ when active to indicate sort direction
- **Improvement:** Inactive sort arrows are muted gray, highlight to teal on hover, and show teal-dark when active

## Version 5.8.7 <!-- @hidden -->
- **Bug Fix:** Sort dropdown no longer hidden behind WordPress sidebar — a duplicate `.fs-list-header` CSS rule had `flex-wrap: wrap`, causing the Sort button to wrap to a new line on the left; changed to `nowrap` so filter row and Sort stay on one line
- **Bug Fix:** Filter row no longer wraps — `.fs-filters-form` changed from `flex-wrap: wrap` to `nowrap`

## Version 5.8.6 <!-- @hidden -->
- **New Feature:** Task list view is now sortable by Due Date, Client, Priority, Task Name, and Date Created
  - Sort dropdown added to the header (matching the Projects list pattern)
  - Column headers for Task, Project/Client, Priority, and Due Date are clickable — click to sort, click again to reverse direction
  - Active sort column is highlighted with an arrow indicator (↑/↓)
  - Sort selection persists when applying filters
  - Added `client_name` as a sortable field in the Task model query (sorts by company name with fallback to client name)

## Version 5.8.5 <!-- @hidden -->
- **Bug Fix:** Client logos set via external URL now display in the Projects list — the query was only fetching `logo_id` (uploaded attachments) but not `logo_url`; now falls back to `logo_url` when no uploaded logo exists
- **New Feature:** Added "Status" column to the Task list view showing the task's current Kanban column (e.g. To Do, In Progress, With Client, Done). Uses customized Kanban column labels if set, with color-coded pill badges matching Kanban column colors.

## Version 5.8.4 <!-- @hidden -->
- **Bug Fix:** Fixed fatal error when saving time entries from the edit form — the time input sends `H:i:s` format (e.g. `09:00:00`) and the handler was blindly appending `:00`, producing an invalid datetime like `09:00:00:00`. Now checks whether seconds are already present before appending.

## Version 5.8.3 <!-- @hidden -->
- **Bug Fix:** Quick Add time entries now correctly save the "Billable" toggle state — unchecking Billable was ignored because `isset($_POST['billable'])` returns true even for value `0`; changed to `!empty()` check
- **Bug Fix:** Quick Add time entries now correctly save the selected Task — `$wpdb->insert` was converting `null` task_id to `0` instead of SQL NULL, preventing the task JOIN from matching; refactored to only include task_id in the insert when a task is actually selected
- **Fix:** Time entry update method now uses `array_key_exists` for task_id to allow properly clearing or setting tasks on existing entries

## Version 5.8.2 <!-- @hidden -->
- **Bug Fix:** Fixed 5-hour time offset on manually-entered time entries (Quick Add and edit form)
  - Root cause: Manual entries stored local time but display treated all times as UTC, causing double timezone conversion
  - Added `local_to_utc()` helper to FS_Time_Entry model
  - `save_time_entry` and `quick_add_time` AJAX handlers now convert local→UTC before storage
  - Edit form now properly converts UTC→local when displaying times for editing
  - Dashboard recent activity widget also fixed to use proper UTC→local conversion
  - **Note:** Existing entries created before this fix will still show the offset. You can re-save them via the edit form to correct the stored times.
- **Bug Fix:** Expense "Duplicate" action now works (was missing controller handler and model method)
- **UI:** Added rounded top corners (border-radius: 15px) to Kanban column and column headers

## Version 5.8.0
- **New Feature:** Weekly/biweekly recurring tasks now support day-of-week selection
- Added day picker UI (M/T/W/T/F/S/S chips) to the recurring task options — shown when frequency is Weekly or Every 2 Weeks
- Tasks recur on the next matching selected day rather than a fixed 7/14-day interval
- New `recurring_days` database column (auto-migrated on activation)
- Recurring days carried forward to all future recurring instances

## Version 5.7.1 <!-- @hidden -->
- Changed task timer button icon from play (▶) to clock for better clarity

## Version 5.7.0
- **New Feature:** Popup timer for tasks — click the play button on any task row to launch a small desktop timer window
- Timer popup pre-fills task name, project, and activity; includes start/stop/pause/resume/discard controls
- Timer window title updates with elapsed time for easy tracking from the taskbar
- Auto-closes with confirmation after stopping and saving
- Warns if another timer is already running
- Added to all task views: Tasks list, Kanban board, Project tasks tab, and Client tasks tab
- Timer button only shows on incomplete tasks

## Version 5.6.2 <!-- @hidden -->
- **Bug Fix:** Fixed double success message on Calendar settings tab when saving credentials or sync settings
- **Bug Fix:** Fixed Google Calendar OAuth redirect landing on blank page instead of returning to Calendar settings
- Moved OAuth callback handling to admin_init hook (before any HTML output) for reliable redirect

## Version 5.6.1 <!-- @hidden -->

### Changed
- **Project Timer Edit**: Editing a time entry from the Project > Timer tab now opens an inline modal instead of navigating away to the Time Tracking edit form. This keeps users on the project page and avoids the redirect issue where saving would navigate to the main Projects list instead of returning to the timer tab.
- Added `fs_get_time_entry` AJAX handler for fetching individual time entry data.

## Version 5.5.9 <!-- @hidden -->

### Changed
- **Tasks View Preference**: The Tasks page now remembers whether the user last chose List or Kanban view and defaults to that view on return. Stored per-user via WordPress user meta.

## Version 5.5.8 <!-- @hidden -->

### Changed
- **Unified Task Modal**: Consolidated three separate task modal implementations (Kanban, Project, Client views) into a single shared partial (`includes/views/partials/task-modal.php`) and shared JS file (`admin/js/fs-task-modal.js`). All views now use consistent field IDs (`fs-task-*`), the same AJAX action (`fs_save_task`), and identical open/close/save/edit behavior. Context-specific fields (milestones for projects, project dropdown for kanban/client, client-visible checkbox for projects) are conditionally rendered based on a `$task_modal_context` variable.
- **Unified Task Save Handler**: `fs_save_task` AJAX handler now also handles `client_id` and `status` fields. The `fs_save_kanban_task` action is aliased to `save_task` for backward compatibility.

## Version 5.5.7 <!-- @hidden -->

### Fixed
- **Task Project Assignment Not Saving on Edit**: `FS_Task::update()` was missing a handler for `project_id` — the field was simply ignored during updates. This affected all task editing across the Kanban board, project view, and client view. Added `project_id` support to the update method alongside the existing `client_id` handler.

## Version 5.5.6 <!-- @hidden -->

### Fixed
- **Client Tasks Not Clickable**: Task titles in the client view Tasks tab are now clickable links that open the edit modal, matching the behavior users expect.
- **Task Project Assignment Not Saving**: Added Project dropdown to the client task modal. Previously the modal had no project field, so `project_id` was never sent in the AJAX save request, causing project assignments to be lost on both create and edit.

### Changed
- **Client Task Modal**: Now supports both creating and editing tasks. Clicking a task title fetches the task data via AJAX and populates the modal for editing. The save handler sends `id` for existing tasks (triggering update) and `status: 'pending'` for new tasks (triggering create).

## Version 5.5.5 <!-- @hidden -->

### Added
- **Client Time Entries Tab**: Added "Time Entries" tab to individual client view, showing all time tracked for that client across all projects. Uses the same layout as the main Time Tracking view with Duration, Time, Activity, Project (with description), Task, Status, and ⋯ action menu.

### Fixed
- **Quick Add Task Not Saving**: Fixed bug where selecting a task in the Quick Add time entry form was not being saved. The `task_id` field was missing from the AJAX submission data.

## Version 5.5.4 <!-- @hidden -->

### Changed
- **Project Time Entries Table**: Redesigned the time entries list under individual projects to match the main Time Tracking view. Now displays Duration, Time (with date), Activity badge, Description, Task, Amount, and Status columns with the same dot-badge status indicators and ⋯ row action menu (Edit, Mark Billable/Non-billable, Delete) instead of the old plain Edit button.

## Version 5.5.3 <!-- @hidden -->

### Fixed
- **Contact Deletion**: Fixed type mismatch in contact deletion comparison — database IDs (strings) weren't matching submitted IDs (integers) in `in_array` check. Added `intval()` cast for consistent comparison.
- **Cleanup**: Removed debug logging from deletion settings handler and contact save flow.

## Version 5.5.2 <!-- @hidden -->

### Fixed
- **Contact Roles/Tags Not Saving**: The actual save path was `early_save_contacts()` in `class-fs-admin.php` (runs during `admin_init`), not the controller's `save_contacts()`. This early handler had stale field mappings: `title` instead of `role`, `isset()` instead of value check for `is_primary` (making every contact primary since the hidden field always exists), and old field names (`receives_invoices`/`receives_proposals`/`receives_contracts`) instead of `is_billing_contact`/`is_project_contact`. Updated to match the current contact schema.
- **Primary Contact Always Switching to Last**: Caused by the same `isset()` bug — since `contacts[N][is_primary]` hidden field is always present, every contact was saved with `is_primary=1`, and `unset_primary()` was called each time, leaving only the last contact as primary.

## Version 5.5.1 <!-- @hidden -->

### Fixed
- **Double Contact Forms**: Fixed clicking "+ Add Contact" creating two contact forms instead of one. A duplicate event handler in `freelance-studio-admin.js` was firing alongside the handler in the client form's inline script, causing each click to append two contact items.
- **Contact Tags Not Saving on Creation**: The duplicate contact forms caused index confusion — billing/project checkboxes and role fields weren't reliably associated with the correct contact data when submitted. Removing the duplicate handler resolves this.
- **Primary Contact Switching**: The duplicate handler also disrupted the auto-primary logic on new contacts. With the fix, the primary radio selection now persists correctly through save.
- **Contact Edit Checkbox Click Area**: Fixed checkbox labels (Primary/Billing/Project) in the contact edit modal on the client view page being full-width clickable — clicking anywhere across the row would toggle the checkbox. Changed the container from `.fs-form-group` (which applies `display: block` to labels) to `.fs-contact-badges` (which uses `display: flex` with proper inline sizing).

## Version 5.5.0

### Fixed
- **Client Card View Delete**: Fixed delete not working in card view. The card view was using a direct URL link (`action=delete`) but the clients controller had no `delete` case — it fell through to `default` which just re-rendered the list. Changed to use the same AJAX-based `fs-delete-btn` pattern that the list view uses, which calls the existing `delete_client` AJAX handler.

## Version 5.4.9 <!-- @hidden -->

### Fixed
- **Public Links Not Working**: Changed `template_redirect` hook priority from default (10) to priority 1, so Freelance Studio's public page handler (invoices, proposals, contracts, booking, portal, forms) runs before theme handlers that may intercept and redirect/404 the request. This addresses issues with Thrive Theme Builder and similar themes that aggressively handle template routing.

## Version 5.4.8 <!-- @hidden -->

### Fixed
- **Kanban Task Columns**: Columns now auto-height to fit all task cards instead of cutting off content at a fixed viewport height. Removed `max-height` and `overflow: hidden` constraints from columns on both desktop and mobile.

## Version 5.4.7 <!-- @hidden -->

### Fixed
- **Dashboard Quick Add Task (continued)**: Fixed PHP fatal error `Undefined constant FS_Task::ASSIGNED_DESIGNER` — the constant was renamed to `ASSIGNED_FREELANCER` but two references in the `create()` and `create_generic()` methods still used the old name
- **Task Status**: Fixed quick add task using invalid status value `incomplete` — changed to valid `pending` status

## Version 5.4.6 <!-- @hidden -->

### Fixed
- **Client Card View Delete**: Fixed delete action not working in card view — the dropdown menu was using CSS `:hover` to stay visible, which closed when the browser confirm dialog appeared, preventing the delete action from completing. Now uses click-to-toggle (`.open` class) exclusively.
- **Dashboard Quick Add Task**: Fixed "Add" button on dashboard tasks widget doing nothing — the AJAX action name was `fs_add_task` but the registered handler is `fs_quick_add_task`.

## Version 5.4.5 <!-- @hidden -->

### Fixed
- **Version Merge**: Reconciled version drift between v5.4.3 and v5.4.4
  - Restored service description display on project view that was lost in v5.4.4
  - Restored client card dropdown menu click-to-toggle JavaScript
  - Restored CSS for `.fs-service-info` flex layout and `.fs-service-desc-modern` class
  - Restored client contact sync logic for backward compatibility (syncs primary contact to client record)
  - Retained all v5.4.4 additions: data export (JSON/CSV), deletion setting fix, direct client field saving

## Version 5.4.4 <!-- @hidden -->

### Fixed
- **Data Deletion Setting**: Fixed "Delete data on uninstall" checkbox not saving properly
  - Created dedicated form handler for better reliability
  - Added visual confirmation showing current saved state
  - Added debug logging to help diagnose issues
  - Shows success message after saving with clear status indication

## Version 5.4.3 <!-- @hidden -->

### Added
- **CSV Export Option**: Added "Export as CSV" button alongside JSON export in Settings → Tools
  - Creates a ZIP file containing separate CSV files for each data table
  - CSV files include BOM for proper Excel/Google Sheets compatibility
  - Settings exported as key-value pairs in `_settings.csv`
  - Ideal for data analysis in spreadsheet applications

## Version 5.4.2 <!-- @hidden -->

### Fixed
- Fixed "Export All Data" button not responding - JS was in wrong tab conditional block

## Version 5.4.1 <!-- @hidden -->

### Added
- **Full Data Export**: New "Export All Data" feature in Settings → Tools → Data Management
  - One-click download of complete database backup in JSON format
  - Exports all clients, projects, invoices, proposals, contracts, time entries, expenses, tasks, bookings, forms, and settings
  - Includes metadata (plugin version, export date, site info) for reference
  - Supports the "Full database access - export anytime" promise for data ownership
  - Admin-only capability requirement for security

## Version 5.4.0

### Added
- **Recurring Tasks**: Tasks can now be set to automatically reappear on a schedule
  - Frequency options: Daily, Weekly, Every 2 Weeks, Monthly, Quarterly
  - Optional end date to stop recurrence
  - When a recurring task is completed, a new copy is automatically created with the next due date
  - Recurring indicator badge (↻) shown on Kanban cards and project task list
  - Recurring toggle and options available in both Kanban and Project task modals
  - Daily cron job as safety net for generating missed recurring task instances
  - Stop recurring method to cancel a recurring task series
- **Database**: Added `is_recurring`, `recurring_frequency`, `recurring_end_date`, and `parent_task_id` columns to tasks table with automatic migration for existing installs

## Version 5.3.1 <!-- @hidden -->

### Fixed
- **Portal project view PHP warning**: Fixed `Undefined variable $token` on line 734 of project.php — the file upload JS was referencing `$token` instead of `$portal_token` (pre-existing bug surfaced during v5.3.0 testing)

## Version 5.3.0

### Added
- **Client Task Requests**: Clients can now request tasks directly from the client portal — both from the project Tasks tab and a new Tasks section on the portal dashboard
- **Portal Dashboard Tasks Section**: New Tasks section on the portal dashboard displays all client-visible tasks across all active projects, with status labels, priority indicators, due dates, and a collapsible completed tasks section
- **Task Request Modal**: Clean modal interface for clients to submit task requests with title, description, priority, due date, and project selection
- **"Requested" Task Status**: New `requested` status for tasks submitted by clients — these appear in the Pending column on the admin kanban board with a distinctive pink "Request" badge
- **Accept Task Request Flow**: Kanban cards with requested status show an Accept button (green checkmark) — clicking it changes the status to Pending and notifies the client
- **Task Request Email Notifications**: When a client submits a request, an email is sent to the freelancer with full task details and a link to the Tasks board; when the freelancer accepts, a confirmation email is sent to the client with a link to their portal
- **Success Toast Notifications**: Client portal now shows a brief success toast when a task request is submitted

### Improved
- **Kanban Board**: Requested tasks are included in the kanban query and mapped into the Pending column for easy visibility
- **Task Model**: Added `get_client_visible_tasks()` method for efficiently fetching all client-visible tasks across projects

## Version 5.2.12 <!-- @hidden -->

### Fixed
- **Client invoice view missing services/charges**: Public invoice page was referencing `line_items` field instead of the correct `items` field, causing the services table to be empty for clients
- **Client invoice PDF download not working**: Public invoice page had no backend handler for the PDF download link; added `download_public_invoice_pdf` method to the frontend router
- **Download PDF button hidden on paid invoices**: The Download PDF button was inside a payment-gated block that only showed for unpaid invoices; moved it outside so clients can always download the PDF
- **Invoice totals calculation**: Fixed public invoice to use correct model field names (`subtotal`, `amount_paid` instead of incorrectly computed values); added discount display to totals breakdown

### Improved
- **Time entry redirect from project**: When adding or editing a time entry from a project's Timer tab, saving now redirects back to that project's Timer tab instead of the main Time Tracking page
- **Time entry edit link in project view**: Edit links now pass `from_project` context so the return URL is preserved

## Version 5.2.11 <!-- @hidden -->

### Improved
- **Expense creation flow**: After adding a new expense, user is now redirected to the expense list with a success message instead of staying on the form
- **Project expense creation flow**: When adding an expense from a project's Expenses tab, user is redirected back to that project's expense list with a success message
- **Delete success messages**: Deleting any item (expense, project, client, invoice, proposal, contract) now shows an inline success message at the top of the list after the row is removed
- **Notice display consistency**: Switched all list view notices from WordPress native `notice` class to plugin's own `fs-notice` class to prevent the bounce/reposition caused by WordPress admin JS auto-moving notices

### Fixed
- **Expense notice bounce on load**: Success messages on the expense list no longer visually jump from one position to another on page load

## Version 5.2.10 <!-- @hidden -->

### Bug Fixes
- Fixed PHP 8.1+ deprecation notices: `json_decode(): Passing null to parameter #1 ($json) of type string is deprecated` across all model files (invoice, contract, proposal, project, form, time entry, etc.). Added null coalescing (`?? ''`) guards to all `json_decode()` calls where database columns could be NULL.

## Version 5.2.9 <!-- @hidden -->

### Bug Fixes
- Fixed WordPress 6.7+ PHP notice: "Translation loading for the `freelance-studio` domain was triggered too early." Added explicit `load_plugin_textdomain()` on `init` hook.

### New Features
- **Delete Data on Uninstall Setting:** New checkbox in Settings → Tools → Data Management. When checked, deleting the plugin from the WordPress Plugins page will permanently remove ALL Freelance Studio data. Unchecked by default — data is always preserved unless explicitly opted in.
- **uninstall.php:** Added proper WordPress uninstall handler that respects the above setting.

## Version 5.2.8 <!-- @hidden -->

### Bug Fixes

#### Critical: Database Schema Conflicts Causing Data Loss on Upgrade
- **Root Cause:** Multiple schema definition conflicts between `create_*_table()` methods and `verify_database_schema()` in the activator. When `dbDelta()` ran during plugin activation on MyISAM tables, conflicting column types and names caused data corruption/loss.
- **Conflicts Fixed:**
  - **Projects:** `billing_type` was defined as `enum` in CREATE TABLE but `varchar` everywhere else — caused MyISAM corruption during ALTER TABLE. Removed `hourly_rate` from CREATE (was deprecated). Fixed `end_date` → `due_date` in verify method. Aligned status defaults.
  - **Tasks:** Fixed column name mismatches — `name` → `title`, `sort_order` → `position`. Fixed `assigned_to` type mismatch (was `bigint` in verify, actual `varchar`). Aligned status defaults.
  - **Proposals:** Fixed verify referencing old column names (`content`, `signature_data`, `signer_name`) that no longer exist in current schema. Updated to match current columns.
  - **Contracts:** Fixed verify referencing old column names (`client_signature`, `designer_signature`) vs current (`signature_freelancer`, `signature_client`). Updated to match current schema.
- **Migration Added:** Converts existing `enum` billing_type columns to `varchar` on upgrade to prevent future dbDelta conflicts.

## Version 5.2.7 <!-- @hidden -->

### Bug Fixes

#### Deposit Invoice Shows $0.00 After Save
- Fixed deposit invoice creation not storing line items in the JSON `items` column
- Invoice edit form reads from JSON column, so empty array caused $0.00 on save

## Version 5.2.6 <!-- @hidden -->

### Bug Fixes

#### Google Calendar Two-Way Sync - Critical Data Corruption Bug Fix
- **Critical Fix:** Fixed bug where two-way Google Calendar sync could corrupt project data
- **Root Cause:** Two issues combined to cause data loss:
  1. `sync_project_from_google()` used `end_date` but projects use `due_date` field
  2. When comparing `$project->end_date` (undefined) to calendar date, they were always "different"
  3. This triggered `FS_Project::update()` with only the date field, but `update()` expects ALL fields
  4. Result: Projects synced from Google had name, client_id, and other fields set to null
- **Fix implemented:**
  1. Changed field name from `end_date` to `due_date` in sync comparison
  2. Changed `$project->end_date` to `$project->due_date` for current value check
  3. Added new `FS_Project::update_date_field()` method for safe partial updates
  4. Sync now uses partial update method instead of full update

## Version 5.2.0

### Bug Fixes

#### Google Calendar Sync - Cancelled Bookings Fix
- Fixed issue where cancelled bookings kept reappearing on Google Calendar
- Root cause: When a booking was cancelled, the event was deleted from Google, but subsequent sync operations would recreate it
- Three-part fix implemented:
  1. `cancel()` now clears `gcal_event_id` after deleting the Google event, preventing future re-sync attempts
  2. `update()` now checks booking status before syncing - cancelled bookings are never synced to Google
  3. `sync_to_gcal()` has an early return for cancelled bookings as an additional safeguard
  4. `sync_booking_from_google()` uses direct database update when cancelling from Google sync to avoid triggering the sync cycle

#### Contractor Signature Display Fixed
- Fixed critical bug where contractor signature was not displaying on proposals and contracts
- Root cause: `esc_url()` was stripping base64 data URIs (the `data:` protocol is not in WordPress's allowed protocols list)
- Changed to `esc_attr()` for signature image src attributes in all 4 views:
  - Admin proposal preview
  - Admin contract preview
  - Public proposal view
  - Public contract view

#### Business Logo Upload Fixed
- Fixed "wp.media is not a function" error when uploading business logo in Settings
- Added `wp_enqueue_media()` call for the settings page to load WordPress media library scripts

#### Signature Upload Feature Fixed
- Restructured JavaScript to use event delegation for signature-related buttons
- Tab switching, file upload, and remove button now work regardless of canvas state
- File upload handlers no longer require canvas to be present

#### Frontend Signature Retrieval Fixed
- Added `contractor_signature`, `contractor_signer_name`, and `contractor_signer_title` to frontend router's `get_business_settings()` method
- Signatures now properly display on client-facing views

### Previous Fixes (5.1.14-5.1.18)
- PHP Warning fix for undefined `is_default` property in contract sections
- CSS color variable updates for document preview title
- Payment Reminders card relocated to invoice sidebar

---

## Version 5.1.13 <!-- @hidden -->

### Major Update: Unified Document System

This version implements a comprehensive unification of document styling across the plugin, ensuring that Proposals, Contracts, and Invoices all share the same visual design in both client-facing views and admin previews.

#### Unified Public Document Views
- Created shared `document-styles.php` containing all common CSS for public document views
- Proposal, Contract, and Invoice client-facing pages now use identical styling
- All documents feature:
  - Solid color header (using freelancer's brand color, no gradient)
  - Logo with white background badge
  - Business name, document title, document number, and status badge in header
  - "Parties" section with Contractor and Client cards side by side
  - Color-coded labels: Contractor (coral #EB8861), Client (teal #3ECFB2)
  - Dates section with proper formatting
  - Services table with uppercase column headers
  - Totals section with grand total highlight
  - Signatures grid with pending/signed states
  - Consistent sign form for proposals and contracts

#### Unified Admin Document Previews
- Admin preview pages now match client-facing views exactly
- New `.fs-doc-preview` CSS classes for admin previews
- Colored header matches the public view
- Same parties cards, dates, services table, and signatures layout
- Preview shows exactly what client will see

#### Consistent Sidebar with Client Link
- All three document types (Proposal, Contract, Invoice) now have:
  - Two-column layout with main content and sidebar
  - Client Link card in sidebar with copy/preview buttons
  - Summary card showing key document information

#### New CSS Classes (Admin)
- `.fs-doc-preview` - Main preview container
- `.fs-doc-preview-header` - Colored header section
- `.fs-doc-preview-body` - Content area
- `.fs-doc-preview-parties` / `.fs-doc-preview-parties-grid` - Party cards layout
- `.fs-doc-preview-party-card` / `.fs-doc-preview-party-label` / `.fs-doc-preview-party-name`
- `.fs-doc-preview-dates` / `.fs-doc-preview-date-item`
- `.fs-doc-preview-section` / `.fs-doc-preview-section-title`
- `.fs-doc-preview-table` - Services table
- `.fs-doc-preview-totals` / `.fs-doc-preview-totals-row`
- `.fs-doc-preview-custom-section` / `.fs-doc-preview-notes`
- `.fs-doc-preview-terms` / `.fs-doc-preview-term-section` - Contract terms
- `.fs-doc-preview-signatures` / `.fs-doc-preview-signature-block`
- `.fs-client-link-card` - Sidebar client link card

### Files Changed
- `includes/views/public/document-styles.php` (new) - Shared public CSS
- `includes/views/public/proposal.php` - Rebuilt with unified styling
- `includes/views/public/contract.php` - Rebuilt with unified styling  
- `includes/views/public/invoice.php` - Rebuilt with unified styling
- `includes/views/proposals/view.php` - Updated admin preview
- `includes/views/contracts/view.php` - Updated admin preview
- `includes/views/invoices/view.php` - Updated admin preview with sidebar
- `admin/css/freelance-studio.css` - Added unified document preview styles

---

## Version 5.1.12 <!-- @hidden -->

### Improvements

#### Unified Document Styling
- Standardized client-facing views for Proposals and Contracts to use identical styling
- Both documents now use the same CSS classes and visual design
- Consistent header, parties, dates, services table, totals, and signature sections
- Removed gradient headers in favor of solid color backgrounds using freelancer's brand color
- Fixed duplicate/corrupted code in proposal public view

#### Admin UI Improvements
- Added `margin-top: 20px` to `.fs-view-sidebar` for better spacing
- Removed padding from Client Link Card body for cleaner appearance
- Updated `.fs-proposal-preview` and `.fs-contract-preview` CSS to match unified document styling
- Unified preview styles: section titles, party labels, totals, signatures all match public views

### CSS Changes
- New `.fs-link-card .fs-card-body` rule removes padding
- Updated `.fs-preview-section h2` to use uppercase labels with primary color border
- Added `.fs-preview-type` for document type labels
- Added `.fs-preview-dates` section styling
- Added `.fs-signature-pending` and `.fs-signature-label` styles
- Signature line now uses 2px solid border instead of 1px

---

## Version 5.1.11 <!-- @hidden -->

### New Features

#### Proposal & Contract Admin Sidebar
- Added right sidebar to Proposal and Contract view pages
- Client-facing link now appears in sidebar card with copy/preview buttons
- Added Summary card showing total amount, deposit, dates at a glance
- Two-column responsive layout collapses on mobile

#### Proposal Client View Redesign
- Updated public proposal view to match contract view styling
- Added branded gradient header using freelancer's brand color
- Header now includes logo, business name, title, date, and status badge
- Parties section redesigned with modern card layout
- Added term info section showing start date and valid until

#### Contract Client View Brand Color
- Contract gradient header now uses freelancer's brand color instead of hardcoded green
- Gradient is automatically calculated to create a complementary color shift

### Bug Fixes

#### PDF Line Breaks
- Proposal PDF service descriptions now properly handle line breaks with `nl2br()`

---

## Version 5.1.10 <!-- @hidden -->

### Bug Fixes

#### Invoice PDF Field Name Fix
- **Fixed invoice PDF using wrong field name**: Was using `$invoice->total_amount` but invoices use `$invoice->total`
- Fixed in 3 places: subtotal fallback, total display, and balance due calculation
- Invoice PDFs should now display correct totals

#### Codebase Audit Complete
Field name verification across all document types:
| Document | Database Column | Correct Usage |
|----------|----------------|---------------|
| Invoice | `total` | `$invoice->total` ✓ |
| Contract | `total_amount` | `$contract->total_amount` ✓ |
| Proposal | `total_amount` | `$proposal->total_amount` ✓ |
| Services (array) | `amount` | `$service['amount']` ✓ |
| Line Items (array) | `amount` | `$item['amount']` ✓ |

---

## Version 5.1.9 <!-- @hidden -->

### Bug Fixes

#### Proposal Email Amount Placeholder Fix
- **Fixed `{amount}` placeholder** in proposal email templates returning empty/zero
- Was using `$proposal->total` (doesn't exist) → now uses `$proposal->total_amount`
- Affects: Custom proposal email templates that use the `{amount}` variable

#### Email Service Audit Results
- Contract emails: ✓ Correctly using `total_amount`
- Invoice emails: ✓ Correctly using `total`
- Proposal HTML emails: ✓ Already correct
- Proposal template variables: Fixed (this update)

---

## Version 5.1.8 <!-- @hidden -->

### Bug Fixes

#### PDF Service Amount Display Fix
- **Fixed service amounts showing $0.00**: Contract and Proposal PDFs were looking for `service['total']` but the actual field is `service['amount']`
- Now correctly checks for both `total` and `amount` fields (like invoices already did)
- Affects: Contract PDFs, Proposal PDFs
- Invoice PDFs were already working correctly

---

## Version 5.1.7 <!-- @hidden -->

### Bug Fixes

#### Google Calendar Inbound Sync Fix
- **Fixed column name mismatch**: Inbound sync was trying to update non-existent columns (`booking_date`, `start_time`, `end_time`)
- Now correctly updates `start_datetime` and `end_datetime` columns when bookings are modified in Google Calendar
- This was causing database errors: `Unknown column 'booking_date' in 'SET'`

### Changes
- **Default reminder time**: Changed default reminder email timing from 24 hours to 1 hour before meeting (for new installations)
- Existing installations keep their current setting - change in Bookings → Settings if desired

---

## Version 5.1.6 <!-- @hidden -->

### New Features

#### Client Reminder Preference
- **Reminder Opt-In**: Clients can now choose whether they want to receive a reminder email when booking a meeting
- Checkbox appears on booking form with text like "Send me a reminder email 24 hours before the meeting"
- Default is checked (opt-in) but can be unchecked by the client
- Legacy bookings (before this update) will still receive reminders by default
- Database migration adds `wants_reminder` column to bookings table

#### Timezone Confirmation Notice (Availability Page)
- Added prominent timezone confirmation notice at the top of the Availability settings page
- Shows current timezone, UTC offset, and current time in that timezone
- Includes link to Settings tab if timezone needs to be updated
- Helps ensure availability schedule is configured correctly

### Bug Fixes
- Added `meet_link` column to bookings table schema (was missing from base schema)
- Database migration ensures both new columns are added for existing installations

---

## Version 5.1.5 <!-- @hidden -->

### Bug Fixes

#### Booking System - Single Meeting Link Guarantee
- **Removed Guest as Attendee**: Google Calendar events no longer add the guest as an attendee
  - This eliminates any possibility of Google creating or managing duplicate meeting links
  - Guest information (name, email, phone) remains in the event description for your reference
  - All guest communication goes exclusively through Freelance Studio emails
- **Idempotent Conference Request**: Changed Google Meet request ID to use only booking ID (not timestamp)
  - Ensures the same booking always references the same conference, even if synced multiple times
- **One-Time Link Creation**: Meet links are now only requested when the booking doesn't already have one
  - Prevents any chance of a new link being created when updating an existing booking

### How It Works Now
1. Booking is created → Google Calendar event created on YOUR calendar only
2. Google generates ONE Meet link → stored in Freelance Studio database  
3. Confirmation email sent to guest with that exact link
4. .ics attachment includes that same link
5. Your Google Calendar event has that same link

**Result**: You and your client will always have the identical meeting link.

---

## Version 5.1.4 <!-- @hidden -->

### Bug Fixes

#### Booking System - Timezone Support
- **Visitor Timezone Detection**: Booking page now automatically detects the visitor's timezone
- **Timezone Selector**: Added dropdown allowing visitors to change their timezone view
- **Time Conversion**: Meeting times are now displayed in the visitor's local timezone
- **Common Timezones**: Pre-populated list of common US and international timezones

#### Booking System - Duplicate Emails Fix
- **Suppressed Google Calendar Invitations**: Added `sendUpdates=none` to Google Calendar API calls
- **Single Email Source**: Guests now only receive booking emails from Freelance Studio, not duplicate invitations from Google Calendar
- **Consistent Meeting Links**: All emails now reference the same Google Meet link stored in the booking

### Technical Changes
- Google Calendar event creation/updates no longer trigger automatic invitation emails
- Google Calendar event deletions no longer trigger automatic cancellation emails
- Plugin handles all booking email notifications directly

---

## Version 5.1.3 <!-- @hidden -->

### UI Improvements

#### Terms Tab Refinements
- **Tab Styling**: Removed background and shadow from tab content for cleaner look with card layout
- **Tab Navigation**: Changed tab link color to `var(--fs-gray-900)` for better contrast
- **Edit Buttons**: Replaced emoji pencils with dashicons, styled as secondary buttons
- **Section Description**: Added `margin-top: 10px` for better spacing
- **Quick Add Button**: Hover state now shows white text on primary color background
- **Tracked Time Display**: Services using tracked time now show as "5.50 tracked hrs × $100.00" in a styled pill badge

---

## Version 5.1.2 <!-- @hidden -->

### UI Improvements

#### Project Detail Page - Terms Tab
- **Two-Column Card Layout**: Restructured the Terms tab with a modern two-column design
  - Main column: Project info card with billing schedule, and separate Project services card
  - Sidebar: Deposit card and Milestones preview card
- **Card-Based Design**: Each section now has its own distinct card with rounded corners and subtle shadows
- **Improved Services Display**: Services shown in a cleaner list format with rate, quantity, and amount aligned
- **Milestones Preview**: Quick view of project milestones with status indicators directly on the Terms tab
- **"Set date" Links**: Clickable links to quickly edit empty date fields
- **Responsive Design**: Layout adjusts to single column on smaller screens

---

## Version 5.1.0

### Improvements

#### Client Upload Page
- **Dynamic File List**: Uploaded files now appear instantly in the list without page refresh
- **Longer Success Message**: Success notification now stays visible for 8 seconds
- **Highlight Animation**: Newly uploaded files are highlighted with a subtle green animation

#### Client Upload Notification Email
- **HTML Formatting**: Email notifications now match the plugin's styled HTML email template
- **Brand Colors**: Uses your configured brand color for consistency
- **Direct Link**: Includes a button to view the project files directly

#### Client Portal Files
- **New Files Tab**: Files section moved from sidebar to dedicated "Files" tab
- **Card Grid Layout**: Files displayed in a responsive grid of cards instead of a list
- **Improved Upload Area**: Larger, more prominent drag-and-drop zone
- **Better Organization**: Clear separation between shared files and upload section

#### Contracts Page
- **Improved Empty State**: Contracts list now shows a friendly empty state with icon when no contracts exist, matching the proposals page design

#### Email Deliverability
- **Improved Headers**: Admin notification emails now use proper From headers for better deliverability with Gmail and other providers

### Bug Fixes
- Fixed undefined property warnings in file enrichment when attachment is missing
- Fixed portal files not displaying all shared files (was filtering files without URLs)

---

## Version 5.0.15 <!-- @hidden -->

### New Feature: Project File Management

Comprehensive file management system for projects, enabling file sharing with clients and secure client uploads.

#### Core Features
- **Files Tab**: New tab in project view with drag-and-drop upload area
- **File Categories**: Organize files into categories (Contracts, Deliverables, Reference Materials, Client Uploads, General)
- **File List**: View all project files with name, size, upload date, and uploader
- **Actions**: Download, preview, edit, and delete files
- **Client Access Toggle**: Share/unshare files with clients via toggle switch

#### Client Upload Link
- **Private Upload Links**: Generate secure, time-limited upload links for clients
- **Expiration Options**: 1, 3, 7, 14, or 30 days
- **Client Upload Page**: Clean, branded page where clients can drag-and-drop files
- **Automatic Categorization**: Client uploads automatically tagged as "Client Uploads"

#### Client Portal Integration
- **Shared Files Section**: Clients see files marked for sharing in their portal
- **Portal Upload**: Clients can upload files directly from their project portal
- **Upload Status**: Real-time upload feedback with success/error indicators

#### Technical Details
- New database tables: `fs_project_files`, `fs_upload_tokens`
- WordPress Media Library integration for file storage
- Prepared for future external storage integration (Google Drive, Dropbox)

### Bug Fix

- **Client List Table**: Fixed row height offset issue (same fix as time tracking table)

---

## Version 5.0.13 <!-- @hidden -->

### UI Refinements

- **Calendar Upcoming Widget**: Fine-tuned styling per design specifications
  - Date box padding reduced to 3px
  - Day font size adjusted to 14px
  - Month line-height set to 1.2
  - Title color changed to gray-700 for better readability

- **Calendar Week Start**: Calendar now displays Sunday through Saturday
  - Main calendar grid starts on Sunday
  - Mini calendar in sidebar also starts on Sunday
  - Weekend highlighting correctly applied to Saturday and Sunday

---

## Version 5.0.12 <!-- @hidden -->

### UI Improvements

- **Client Cards Action Menu**: Added three-dot menu to top-right corner of client cards
  - Quick access to View, Edit, New Project, New Invoice, and Delete actions
  - Appears on hover for clean design
  
- **Calendar Upcoming Widget**: Cleaned up the upcoming events list
  - Removed emoji icons that were taking up too much space
  - Made date boxes smaller and more compact (40px vs 48px)
  - Tighter spacing between items

---

## Version 5.0.11 <!-- @hidden -->

### New Feature

- **Two-Way Google Calendar Sync**: Changes made in Google Calendar now sync back to Freelance Studio
  - Enable via Settings > Calendar > Two-Way Sync toggle
  - Configurable sync frequency: every 15 minutes, 30 minutes, or hourly
  - What syncs back:
    - Booking date/time changes
    - Task due date changes
    - Milestone due date changes
    - Invoice due date changes
    - Project start/end date changes
    - Deleted events mark items as completed/cancelled
  - Manual "Sync from Google Now" button for immediate sync
  - Uses polling approach for security and reliability (no webhooks required)
  - Only processes events created by Freelance Studio (identified by `extendedProperties`)

---

## Version 5.0.10 <!-- @hidden -->

### Bug Fixes

- **Fixed Client Logo URL Not Saving**: Root cause identified and fixed
  - The `early_save_client()` method in `class-fs-admin.php` was missing the `logo_url` field entirely
  - This early handler intercepts form submissions before the controller runs, so the logo URL was never being passed to the database
  - Also added missing `is_internal` field to the same handler
  - Removed debug logging added during investigation

---

## Version 5.0.8 <!-- @hidden -->

### Bug Fixes

- **Fixed Contacts Table Rendering Issue**: The action buttons column was breaking the table layout
  - Removed `display: flex` from the table cell which was overriding the natural `table-cell` display
  - Replaced emoji icons (✏️🗑️) with WordPress dashicons for consistent cross-browser rendering
  - Added proper styling for dashicon buttons with hover states

---

## Version 5.0.7 <!-- @hidden -->

### Bug Fixes

- **Fixed "Total Billed" showing $0 in Client Cards**: The card view was referencing `total_billed` which doesn't exist in the stats array
  - Changed to use `total_invoiced` which correctly shows the sum of all invoice totals for the client
  - Fixes PHP warning "Undefined array key total_billed"

---

## Version 5.0.6 <!-- @hidden -->

### Bug Fixes

- **Client Logos Now Display Properly**: Both uploaded logos (via Media Library) and linked logo URLs now display correctly in card and list views
  - Previously only checked for `logo_id` (uploaded), now also checks `logo_url` (external link)
  - Logos take priority over initial avatars when available

### Improvements

- **Alphabetical Sorting**: Clients are now sorted alphabetically by display name
  - Uses company name if available, otherwise falls back to contact name
  - Sorting applies to both card view and list view
  - Status filter now properly applied (Active by default)

---

## Version 5.0.5 <!-- @hidden -->

### Improvements

- **Clients View Toggle**: Added ability to switch between Card view and List view
  - Toggle button in top-right toolbar (grid icon / list icon)
  - User preference is saved to browser localStorage and persists across sessions
  - Card view: Visual grid with avatars, stats, and quick action buttons
  - List view: Traditional table with sortable columns and row action menus
  - Both views now show color-coded avatars with initials when no logo is uploaded

---

## Version 5.0.4 <!-- @hidden -->

### New Features

- **Redesigned Clients List with Card Layout**: The Clients page now uses a modern card-based grid layout
  - Each client displays in a clean card with avatar, company name, and contact name
  - Shows project count and total billed amount at a glance
  - Quick action buttons: View, New Project, Invoice
  - Color-coded avatars with initials (or client logo if uploaded)
  - Responsive grid: 3 columns on desktop, 2 on tablet, 1 on mobile

---

## Version 5.0.3 <!-- @hidden -->

### Bug Fixes

- **Fixed Booking Owner Notification Email Failing**: The admin/owner notification email for new bookings was missing proper email headers
  - Added `From:` header using `notifications@[site-domain]` to avoid "sending to yourself" rejection issues
  - Added `Reply-To:` header with the guest's email, making it easy to reply directly to the person who booked
  - This resolves the issue where the guest confirmation email succeeded but the owner notification failed

---

## Version 5.0.2 <!-- @hidden -->

### Bug Fixes

- **Fixed TCPDF "Headers Already Sent" Error**: PDF generation for proposals, contracts, and invoices now works correctly
  - Added early interception of PDF requests in `admin_init` hook, before WordPress admin starts outputting
  - PDF generation now happens before any headers or content are sent
  - This fixes the "Cannot modify header information" PHP warning

---

## Version 5.0.1 <!-- @hidden -->

### Bug Fixes

- **Fixed Backslashes in Contract/Proposal Notes**: Fixed issue where apostrophes and special characters appeared with backslashes (e.g., "Here\'s" instead of "Here's") in client-facing contract and proposal views
  - Applied `wp_unslash()` to notes display in public contract view
  - Applied `wp_unslash()` to notes display in public proposal view
  - Applied `wp_unslash()` to notes display in admin contract view
  - Applied `wp_unslash()` to notes display in admin proposal view

### Debugging Improvements

- **Client Logo URL Debugging**: Added comprehensive debug logging to help diagnose logo URL saving issues
  - Added logging in client controller to track incoming logo_id and logo_url values
  - Added logging in client model create/update functions to track database operations
  - Logs will appear in WordPress debug log when WP_DEBUG_LOG is enabled

---

## Version 5.0.0

### New Features

- **Consolidated Tools Tab**: All repair and diagnostic tools now live in one place under Settings → Tools
  - **Repair URLs**: Fixes 404 errors for booking pages, client portal, proposals, contracts, and invoices by flushing WordPress rewrite rules
  - **Repair Database**: Verifies and repairs database schema, adding any missing columns
  - **Debug Information**: Shows plugin version, database version, PHP version, and WordPress version

### Improvements

- Moved "Repair URLs" button from Booking Settings to the centralized Tools tab
- Added fallback form submission for Repair URLs when JavaScript is disabled
- Updated booking settings to reference the new Tools tab location for URL repair

---

## Version 4.0.16 <!-- @hidden -->

### Bug Fixes

- **Fixed Add/Remove Column Loop**: Removed deprecated `hourly_rate` from the projects table "required columns" list - was being added by activator then removed by migrator on every load

---

## Version 4.0.15 <!-- @hidden -->

### New Features

- **Database Repair Tool**: Added new "Tools" tab in Settings with a "Repair Database Schema" button that users can click to fix missing columns
- **Debug Information Panel**: Added debug info display in Tools tab showing plugin version, database version, PHP version, and WordPress version

### Bug Fixes

- **Fixed Missing logo_url Column**: Added migration to ensure `logo_url` column exists in clients table - fixes "Unknown column 'logo_url'" errors for beta testers
- **Improved Schema Verification on Activation**: Plugin now sets a flag on activation to force a full database schema check on first load
- **Added Missing Columns Migration**: New `ensure_clients_columns()` function checks for and adds missing columns (`logo_id`, `logo_url`, `is_internal`, `address_2`) to the clients table

### Technical Improvements

- Migration system now uses `SHOW COLUMNS` check with error suppression for more reliable column detection
- Added `fs_force_schema_check` option that triggers comprehensive schema verification

---

## Version 4.0.14 <!-- @hidden -->

### Bug Fixes

- **Fixed Custom Sections Display**: Custom sections now properly display in:
  - Admin proposal preview/view page
  - Public client-facing proposal view
  - PDF exports
- **Fixed PHP Deprecation Warning**: Resolved `json_decode(): Passing null to parameter #1` deprecation warning when loading proposals without custom sections
- **Fixed WYSIWYG Editor for Dynamic Sections**: Added proper WordPress editor (TinyMCE) initialization for dynamically added custom sections via JavaScript
- **Improved Migration Reliability**: Migration now uses `SHOW COLUMNS` check and error suppression to prevent duplicate column errors on concurrent requests

---

## Version 4.0.13 <!-- @hidden -->

### New Features

- **Proposal Custom Sections**: Add flexible content sections to proposals that appear before or after pricing
  - Add custom sections with title and WYSIWYG editor to any proposal
  - Position sections either "Before Pricing" (for content like Project Overview, Our Process, Timeline) or "After Pricing" (for Terms, What's Included, FAQ)
  - **Save sections as templates** for reuse across proposals - click the star icon to save a section to your library
  - **Insert from template library** - use dropdown to quickly add previously saved sections
  - Drag-and-drop reordering within each position group
  - Custom sections display on the public proposal view with proper formatting
  - New database table `fs_proposal_section_templates` stores your reusable section templates

### Technical Details

- Added `custom_sections` column to proposals table (longtext JSON)
- Added `fs_proposal_section_templates` table for saved templates
- Created `FS_Proposal_Section` model class
- AJAX endpoints for saving/deleting/loading section templates
- CSS styling for custom sections in both admin and public views

---

## Version 4.0.12 <!-- @hidden -->

### Bug Fixes

- **Quarterly Tax Estimates Now Include Manual Income**: The Quarterly Tax Estimate section in Reporting now properly accounts for manually added income entries
  - Previously, only invoice income was included in quarterly tax calculations
  - Now both paid invoices AND manual income entries are summed for accurate tax estimates
  - This ensures quarterly tax estimates reflect all 1099/freelance income sources

---

## Version 4.0.11 <!-- @hidden -->

### Improvements

- **Activity Type Selector on New Projects**: When creating a new project, the Services section now includes the activity type dropdown when "From tracked time" is selected
  - Previously this option was only available when editing existing projects
  - Users can now specify which activity type (Design, Development, etc.) each service should pull tracked hours from
  - Matches the functionality available in the project edit/view screens
  - JavaScript updated to show/hide the activity dropdown based on hourly source selection

---

## Version 4.0.10 <!-- @hidden -->

### New Features

- **Project Archive System**: Archive projects without deleting them
  - Added "Active" and "Archived" tabs on the Projects list page
  - Archive option available from the row menu on project list and from the "More" dropdown on project view
  - Archived projects display an "Archived" badge on the project view page
  - Restore archived projects with "Restore from Archive" option
  - Archived status is separate from Completed status - archive projects that ended for any reason

### Improvements

- **Archived Projects Excluded from Dropdowns**: Archived projects no longer appear in project selectors throughout the plugin
  - Time entry forms show only active projects
  - Invoice creation shows only active projects
  - Expense forms show only active projects
  - Task/milestone creation shows only active projects
  - Dashboard widgets exclude archived projects
  - Overdue and due soon calculations exclude archived projects

- **Meeting Type Footer Buttons**: Updated button styling to match standard plugin button classes (fs-btn fs-btn-secondary fs-btn-small)

- **Tab Count Badges**: Added styling for count badges on tabs (shows active/archived project counts)

### Database

- Added `archived` column to projects table with migration

---

## Version 4.0.8 <!-- @hidden -->

### Improvements

- **Unified Time Entry Modal Form**: Created a single, reusable time entry modal form partial
  - All time entry modals now use the same form with consistent fields
  - Week view "Add Time Entry" modal now includes: Project, Task (dynamic), Activity, Duration, Time Range, Hourly Rate, Description, and Billable
  - Task dropdown loads dynamically when a project is selected
  - Form can be reused across week view, dashboard, and any other modal context
  - Duration and time range fields auto-calculate each other

- **Filter Form Layout Fix**: Added `display: flex` to `.fs-filters form` for proper horizontal alignment of filter elements

- **Week View Task Display**: Time entries in week view now display the associated task name if set

---

## Version 4.0.7 <!-- @hidden -->

### New Features

- **Task Field in Time Entries**: Added Task dropdown to time entry add/edit form
  - Select a task from the project's task list when logging time
  - Task dropdown dynamically loads when a project is selected
  - Task field is optional - time can still be logged without assigning to a specific task

### Improvements

- **Income Calculations Include Other Income**: Dashboard stats and tax calculations now include Other Income
  - YTD Revenue stat card now shows total of invoice revenue + other income
  - Monthly revenue includes both invoice payments and other income
  - Tax liability calculations use combined income for accurate estimates
  - Financial summary card reflects true gross income from all sources

- **Other Income View Styling**: Fixed styling inconsistencies in Other Income list view
  - Wrapped table in `.fs-form-section` container for proper styling
  - Changed to `fs-table widefat` class matching other list views
  - Improved filter form alignment
  - Consistent dropdown menu styling without dashicons
  - Fixed text muted styling using CSS variables

---

## Version 4.0.6 <!-- @hidden -->

### Improvements

- **Other Income Moved to Invoices**: Relocated the "Other Income" feature from Expenses to Invoices page
  - More logical placement: income belongs with income (invoices)
  - Tab navigation between Invoices and Other Income
  - Renamed from "Manual Income" to "Other Income" for clarity
  
- **Income Category Badges**: Added styled status badges for income categories
  - Distinct colors for Etsy, Art Fair, Commission, Royalty, Consulting, Workshop, Affiliate, Freelance, Refund, Other
  - Consistent badge styling matching existing status badges throughout the plugin

- **Income List Styling**: Updated income list view to match other plugin list views
  - Proper column widths and alignment
  - Consistent row menu styling
  - Improved filter layout

---

## Version 4.0.5 <!-- @hidden -->

### New Features

- **Other Income Tracking**: New feature to track income from sources outside of invoiced projects
  - Access via Invoices > Other Income tab
  - Track Etsy sales, art fair income, commissions, royalties, workshops, affiliates, and more
  - Categorize entries for better organization
  - Optional reference/order number field
  - Automatically included in reporting totals and tax calculations
  - Full CRUD operations (create, read, update, delete)

### Improvements

- **View Toggle Buttons**: Updated view toggle buttons (Tasks list/kanban, Bookings filter) to use standard `.fs-btn` and `.fs-btn-primary`/`.fs-btn-secondary` classes for consistent styling

- **Sticky Sidebar Spacing**: Added 30px additional top margin to sticky sidebars on form pages so they don't touch the edge of the screen (now 62px from top instead of 32px)

- **Time Tracking Task Column**: Added "Task" column to the Time Tracking entries list to show which task each time entry is associated with

- **Expenses & Income Navigation**: Added tab navigation between Expenses and Manual Income views

---

## Version 4.0.4 <!-- @hidden -->

### Bug Fixes

- **Escaped Slashes Prevention (Global)**: Added `wp_unslash()` wrapper to ALL form field sanitization throughout the entire plugin to prevent slash escaping issues (e.g., "Can\'t" appearing instead of "Can't")
  - Fixed in AJAX handler (81 instances)
  - Fixed in all controllers (140+ instances)  
  - Fixed in frontend router
  - Fixed in admin settings
  - Fixed in license handler

- **Row Action Dropdown Menus**: Fixed dropdown menus being cut off/covered by table elements
  - Added z-index layering to `.fs-row-menu` with increased z-index on hover/focus
  - Set `overflow: visible` on `.fs-table` to prevent clipping
  - Improved table responsive wrapper to not clip dropdowns on desktop

### Improvements

- **Back Button on Booking Details**: Reformatted the back arrow to be a proper button with "Back" text using the standard `.fs-btn .fs-btn-secondary .fs-btn-small` styling

- **Sticky Sidebar on Proposal/Contract Forms**: Made the sidebar on add/edit proposal and contract pages sticky when scrolling
  - Stays fixed at top of viewport (accounting for WordPress admin bar)
  - Scrollable if content exceeds viewport height
  - Helps keep Actions, Status, and Client Link visible while editing long proposals/contracts

- **Page Header Component**: Added new `.fs-page-header` CSS component for pages with back navigation

---

## Version 4.0.3 <!-- @hidden -->

### Bug Fixes

- **Booking Cancellation Reason**: Fixed escaped slashes appearing in cancellation reasons (e.g., "Can\'t" instead of "Can't")
  - Added `wp_unslash()` when processing cancellation reason input from both admin and client-facing forms
  - Added `stripslashes()` when displaying cancellation reason for existing data

### Improvements

- **Booking Details View Layout**: Redesigned the single booking view for better visual organization
  - Fixed excessive spacing between icons and content in booking details
  - Added proper `.fs-view-columns` two-column layout for booking view
  - Tightened up spacing throughout the booking details card
  - Responsive layout collapses to single column on screens below 900px

---

## Version 4.0.2 <!-- @hidden -->

### Improvements

- **Comprehensive Responsive Design Overhaul**: Added extensive mobile and tablet optimizations across all views:

  **Tables & Lists**
  - Added horizontal scroll wrappers to all data tables for better mobile viewing
  - Tables now scroll horizontally instead of breaking layout on small screens
  - Less important columns (like "Ends", "Project") are hidden on mobile to prioritize key information
  - Touch-friendly row menu triggers with larger tap targets

  **Layout & Navigation**
  - List headers with buttons now stack vertically on mobile
  - Filter panels stack into a vertical layout on smaller screens
  - Tab navigation becomes scrollable horizontally on mobile
  - Two-column form layouts (main + sidebar) stack on tablets

  **Forms & Modals**
  - Form rows stack vertically on mobile instead of side-by-side
  - Modals expand to nearly full-width on small screens
  - Modal footers stack buttons vertically on very small screens
  - Touch-friendly minimum button sizes (44px tap targets)

  **Dashboard & Stats**
  - Stats grid adapts from 4 columns → 2 columns → 1 column based on screen size
  - Dashboard cards stack properly on mobile
  - Calendar cells reduce in size on mobile

  **Specific Views**
  - Projects list: Responsive table with horizontal scroll
  - Invoices list: Responsive table, batch actions wrap properly
  - Proposals/Contracts list: Responsive tables
  - Clients list: Responsive table
  - Time tracking: Responsive table, timer/quick-add sections stack
  - Bookings: Responsive table, sidebar widgets wrap on tablet

  **Utility Classes Added**
  - `.fs-table-responsive` - Horizontal scroll wrapper for tables
  - `.fs-hide-mobile` - Hide elements on screens below 782px
  - `.fs-hide-desktop` - Hide elements on screens above 782px
  - `.fs-mobile-only` - Show elements only on screens below 600px

---

## Version 4.0.1 <!-- @hidden -->

### Bug Fixes

- **Fixed Duplicate Booking Cancellation Emails**: Clients were receiving two separate cancellation confirmation emails when a booking was cancelled. The issue was that both `FS_Booking::cancel()` and the AJAX handler were triggering emails. Now only the model method sends the email, eliminating duplicates.

- **Fixed Recurring Invoice Auto-Send Not Emailing**: When setting up a recurring invoice with "Auto-send invoice when generated" enabled, the invoice was being marked as "sent" but the email was never actually being sent to the client. The email is now properly sent when invoices are generated with auto-send enabled.

---

## Version 3.10.13 <!-- @hidden -->

### Bug Fixes

- **Fixed Contract Section Migration Error**: Fixed database error "Unknown column 'key'" - the correct column name is `section_key`. Migration now runs properly to update contract templates.

---

## Version 3.10.12 <!-- @hidden -->

### Changes

- **Renamed Task Assignee from "Designer" to "Freelancer"**: The internal value for "Me" tasks has been changed from `designer` to `freelancer` for clarity and consistency. A database migration automatically updates existing tasks.

- **Updated Contract Templates**: All contract section templates now use "Freelancer" instead of "Designer" throughout. The migration updates existing contract sections in the database. New installations will have "Freelancer" as the default.

- **Placeholder Update**: The contract placeholder `[Designer's State/Country]` has been changed to `[Freelancer's State/Country]`. Both placeholders are supported for backward compatibility.

### Bug Fixes

- Fixed task assignee badge not reflecting the correct assignment type (was always showing "Me" badge for non-client tasks)
- Fixed My Role dropdown selection not persisting properly in task edit modals
- Fixed filter dropdown `selected()` mismatches in task views

---

## Version 3.10.11 <!-- @hidden -->

### Bug Fixes

- **Fixed Assignee Badge Logic**: Task assignee badges now properly distinguish between "Me" (teal), "Client" (purple), and "Team Member" (purple with groups icon) assignments
- **Fixed Dropdown Values**: Task assignee dropdowns now correctly save and load the selected assignee type

### Improvements

- Added new `.fs-kanban-card-assignee.team` CSS class for team member badge styling
- Assignee tooltips now show the specific role (e.g., "Me (Developer)", "Designer", "Contractor")

---

## Version 3.10.5 <!-- @hidden -->

### Bug Fixes

- **Fixed Fatal Error on Admin Pages**: Fixed "Class FS_Activator not found" error that occurred on admin pages. The activator class is now properly loaded as a dependency.

---

## Version 3.10.4 <!-- @hidden -->

### Bug Fixes

- **Admin Booking Cancellation Now Works**: Fixed issue where clicking "Cancel" on a booking from the admin bookings list did nothing. The AJAX handler was not registered. Bookings can now be cancelled from the admin interface and the guest will receive a cancellation email.

### New Features

- **Email Notification When Booking is Made**: The freelancer/business owner now receives an email notification when someone books a meeting. The email includes guest details (name, email, phone), meeting details (type, date, time, duration), and any notes the guest provided. This is sent automatically - no setup required.

- **Automatic Permalink Change Detection**: The plugin now monitors when WordPress permalink structure changes and automatically flushes rewrite rules. This ensures booking URLs continue to work if you switch between plain and pretty permalinks.

### Improvements

- Added booking confirmation email to guests when they book (uses the existing "Send confirmation email" setting in Bookings > Settings)
- Added cancellation notification emails (to guest when owner cancels, to owner when guest cancels)

---

## Version 3.10.3 <!-- @hidden -->

### Bug Fixes

- **Booking Pages Now Work with Plain Permalinks**: Fixed booking page 404 errors when WordPress is set to use plain permalinks (`?p=123` style). The booking system now:
  - Generates the correct booking URL based on permalink settings (query parameter format for plain, `/book/` for pretty)
  - Handles both URL formats in the frontend router
  - Updates all internal booking links (back buttons, cancel links, confirmation links) to use the correct format
  
---

## Version 3.10.2 <!-- @hidden -->

### Bug Fixes

- **Database Schema Auto-Repair**: Added automatic schema verification that runs once per version update. This fixes the "Unknown column 'logo_url'" error for users who update via FTP/SFTP without reactivating the plugin. The schema check now ensures all required database columns exist regardless of how the plugin was updated.

---

## Version 3.10.1 <!-- @hidden -->

### Bug Fixes

- **PDF Generation Fix**: Fixed "headers already sent" error when viewing Contract, Proposal, or Invoice PDFs. Added output buffer cleanup before PDF generation to handle stray WordPress output.

---

## Version 3.10

### New Features

- **Client-Level Tasks**: Tasks can now be created directly under a client without requiring a project. This is useful for ongoing client relationship tasks (follow-ups, check-ins, general consulting).
  - New Tasks tab on client view page showing both client-level and project tasks
  - Tasks list view now includes "Task Type" and "Client" filters to find client-level tasks
  - Full support for creating, editing, and completing client tasks
  
- **Internal Tasks Dashboard Widget**: When Internal Project Tracking is enabled, a new "Internal Tasks" widget appears on the dashboard showing pending tasks for your internal client.

- **Enhanced Task Model**: Tasks now support `client_id` field for direct client association independent of projects.

### Improvements

- Task list view now shows client name below project name for better context
- Client tasks display with "Client Task" indicator to distinguish from project tasks
- Internal tasks display with "Internal" badge for quick identification
- Renamed "Generic" task labels to "Internal" throughout the UI for clearer terminology
- New `get_by_client()` and `get_internal_tasks()` helper methods in Task model

### Bug Fixes

- Fixed SQL ambiguity error in `count_by_client()` method where `status` column was not prefixed with table alias

---

## Version 3.9.19 <!-- @hidden -->

### Bug Fixes

- **Client Task Creation**: Added JavaScript handler for the "New Task" button on client view Tasks tab, completing the client-level tasks feature.
- Task toggle checkboxes now work correctly on client Tasks tab to mark tasks complete/incomplete.

---

## Version 3.9.18 <!-- @hidden -->

### Improvements

- **Database Schema Verification**: Added comprehensive schema verification that runs on every plugin activation. This checks all required columns exist in all tables and adds any that are missing, eliminating issues from skipped version-based migrations.
- **Prepared for Client Tasks**: Added `client_id` column to tasks table to support future client-level tasks feature.

---

## Version 3.9.16 <!-- @hidden -->

### Bug Fixes

- **Database Migration Fix**: Added `is_internal` column migration for users who already updated to 3.9.15 (which only added `logo_url`).

---

## Version 3.9.15 <!-- @hidden -->

### Bug Fixes

- **Database Migration Fix**: Added `logo_url` column migration that may have been missed in earlier updates. This fixes the "Unknown column 'logo_url'" error when creating internal clients.

---

## Version 3.9.14 <!-- @hidden -->

### Bug Fixes

- **Internal Tracking Auto-Save**: Added `internal_tracking_enabled` to the auto-save system so the toggle saves immediately via AJAX (like other settings fields) instead of requiring a form submit.

---

## Version 3.9.12 <!-- @hidden -->

### Bug Fixes

- **Internal Tracking Setting Not Persisting**: Fixed issue where the "Enable Internal Tracking" setting was being reset when saving other settings tabs. The setting now only updates when saving the Business tab (where the checkbox exists).

---

## Version 3.9.11 <!-- @hidden -->

### Bug Fixes

- **Fixed Database Migration**: Added proper version-based upgrade path for `is_internal` column. The migration now runs via the standard upgrade mechanism when updating the plugin (not just on activation).
- **Fixed Column Check**: `get_internal_client()` method now checks if `is_internal` column exists before querying, preventing fatal errors during upgrade.

---

## Version 3.9.10 <!-- @hidden -->

### Bug Fixes

- **Fixed Fatal Error**: Resolved duplicate `count()` method declaration in FS_Client model that caused fatal error on activation

---

## Version 3.9.9 <!-- @hidden -->

### New Features

- **Internal Project Tracking**: New feature to track admin tasks, internal projects, and non-client work separately from client projects.
  - **Settings Toggle**: Enable/disable in Settings > Business > Internal Project Tracking
  - **Auto-Create Internal Client**: When enabled, automatically creates a "[Your Business] - Internal" client using your business settings
  - **Internal Client Flag**: Clients can be marked as "internal" via checkbox on the client form
  - **Dashboard Filtering**: Internal clients are excluded from active client counts in dashboard metrics
  - **Reporting Filter**: Added "Include internal projects" checkbox in Reporting page to include/exclude internal project data
  - **Visual Badge**: Internal clients show an "Internal" badge in the clients list for easy identification

### Database Changes

- Added `is_internal` column to clients table (tinyint, default 0)
- Added index on `is_internal` column for efficient filtering

---

## Version 3.9.8 <!-- @hidden -->

### Bug Fixes

- **PDF Spacing After Section Headers**: Changed from CSS-based spacer divs to explicit `<br>` tags after h2 section headers in PDFs for better TCPDF compatibility. This should now properly add spacing below the green divider lines.

### Notes

- **Apostrophe Fix (v3.9.7)**: The fix for apostrophes in service descriptions will work for newly created/edited proposals and contracts. If you have existing documents with `\'` showing instead of apostrophes, simply edit and re-save them to fix the data.
- **Designer → Contractor (v3.9.7)**: The PDF and public contract views now use "Contractor" instead of "Designer". Note that your existing contract Terms & Conditions content stored in Settings may still contain "Designer" references - you can update those manually in Settings > Contract Sections if desired.

---

## Version 3.9.7 <!-- @hidden -->

### Bug Fixes

- **Apostrophe Escaping in Services**: Fixed issue where apostrophes in proposal/contract service names and descriptions were being saved with backslashes. Added `wp_unslash()` to service name and description fields in both proposal and contract controllers.
- **Client Logo URL Not Saving**: Fixed bug where client logos entered via URL were not persisting after save. Added `logo_url` field to client controller and model create/update methods.
- **PDF Spacing After Dividers**: Added explicit spacer divs after h2 section headers in contract PDFs for better visual separation since TCPDF has limited CSS margin support.

### Improvements

- **Contract PDF Terminology**: Changed "DESIGNER" to "CONTRACTOR" in the Parties section and signature area of contract PDFs to be more inclusive of all freelancer types.

---

## Version 3.9.6 <!-- @hidden -->

### CSS Improvements

- **Contract Services Table**: Added styling for `.fs-preview-services-table` with full width, left-aligned headers in primary color, proper padding and borders

### PDF Improvements

- **Smaller Document Titles**: Reduced PROPOSAL, INVOICE, and CONTRACT titles from 24-28pt to 18pt for a cleaner look
- **Smaller Project Titles**: Reduced h1 (project names) from 22pt to 18pt
- **Increased Spacing After Dividers**: Added more margin (15px) after green h2 divider lines for better visual separation
- **Fixed Signature Transparency**: PNG signatures with transparent backgrounds now render correctly with white backgrounds instead of inverted/black. Added `signature_to_white_background()` helper that converts transparent PNGs using PHP's GD library.

---

## Version 3.9.5 <!-- @hidden -->

### Improvements

- **PDFs Open in Browser Tab**: All PDFs (proposals, invoices, contracts) now open in a new browser tab instead of automatically downloading, making it easier to preview before saving
- **Logo Margin Improved**: Added proper spacing below logos on all PDF documents
- **Smaller Company Name Font**: Business name now uses 12pt font (down from 16pt) for a cleaner, more professional layout

### Bug Fixes

- **Contract PDF Missing Terms & Conditions**: Fixed issue where Terms & Conditions were not included in contract PDFs. Now properly builds terms from enabled contract sections if custom terms content is empty
- **Contract PDF Missing Signatures**: Fixed signature rendering - actual signature images are now displayed in the PDF when contracts have been signed
- **Service Description Line Breaks**: Fixed line breaks not rendering in contract service descriptions - newlines now properly convert to `<br>` tags

---

## Version 3.9.4 <!-- @hidden -->

### Bug Fixes

- **Fixed Income Summary Report Showing No Payments**: The Income Summary PDF was incorrectly querying the `paid_date` column which is often NULL. Changed to use `paid_at` column which is automatically set when invoices are marked as paid, matching how the Annual Tax Summary calculates gross income.

---

## Version 3.9.3 <!-- @hidden -->

### New Feature: True PDF Generation with TCPDF

The plugin now generates true PDF files using the TCPDF library, providing professional-quality documents without requiring external tools like wkhtmltopdf.

- **Bundled TCPDF Library**: Included a minimal TCPDF installation (~1.5MB) with core PDF fonts
  - Uses standard PDF fonts (Helvetica, Times, Courier) that render consistently everywhere
  - No external dependencies required - works on any PHP hosting
  
- **Improved PDF Templates**: Completely rewritten PDF generation for Proposals, Invoices, and Contracts
  - Clean, professional layouts using HTML tables for maximum compatibility
  - Proper business logo embedding
  - Color-coded status badges
  - Signature image support for signed documents
  
- **Income Summary Report**: Updated to use TCPDF for consistent PDF generation

### Bug Fixes

- **Fixed Notes Field Escaping Issue**: Resolved a bug where apostrophes and other special characters in proposal and contract notes were being escaped with backslashes on each save
  - **Root Cause**: WordPress adds "magic quotes" to all form data, and the code wasn't using `wp_unslash()` before sanitizing
  - Added `wp_unslash()` calls before `sanitize_textarea_field()` in:
    - Proposals controller (notes and internal_notes fields)
    - Contracts controller (notes and internal_notes fields)
    - Frontend router (revision_notes field)
    - AJAX handler (various notes fields)
  - Notes now display correctly without accumulating backslashes

- **Fixed PDF Print Dialog Showing WordPress Sidebar**: Resolved issue where the PDF fallback (browser print dialog) was including the WordPress admin sidebar in Firefox and other browsers
  - Added output buffer clearing at the start of PDF generation in controllers
  - Ensures clean output even when TCPDF is not available

### Technical Notes

- TCPDF library located at `includes/libraries/tcpdf/`
- PDF generation automatically downloads when clicking the PDF button
- Falls back to browser print dialog if TCPDF fails to load (rare edge case)

---

## Version 3.9.2 <!-- @hidden -->

### Bug Fixes

- **Retainer Service Data Now Saves Correctly**: Fixed critical bug where retainer fields weren't persisting
  - **Root Cause**: Form processing happens in `early_save_project()` in class-fs-admin.php, NOT in the controller
  - Updated the correct location (`class-fs-admin.php`) to process retainer-specific fields
  - All retainer fields (Monthly Amount, Hours Included, Overage Rate, Rollover) now save properly

---

## Version 3.9.1 <!-- @hidden -->

### Bug Fixes

- **Retainer Service Data Not Saving**: Fixed service processing to include retainer fields (partial fix - wrong file)
  - Fixed variable name bug (`$services_array` → `$services`) in backwards compatibility code

---

## Version 3.9.0

### Major Feature: Retainer as Service Pricing Type

This release refactors the retainer feature to be a **service-level pricing type** instead of a project-level toggle. This provides a cleaner architecture where retainer settings live alongside your other service pricing options.

### Changes

- **Retainer Service Pricing**: Added "Monthly Retainer" as a pricing type option for services
  - Select "Monthly Retainer" from the pricing type dropdown
  - Configure: Monthly Amount, Hours Included per month, Overage Rate
  - Toggle: "Allow unused hours to roll over to next month"
  - All retainer settings now stored within the service, not at project level

- **Removed Project-Level Retainer Section**: The separate "This is a retainer project" checkbox in the Billing section has been removed
  - Simply add a service with "Monthly Retainer" pricing type instead
  - Legacy `is_retainer` field auto-populated from services for backwards compatibility

- **Reusable Retainer Widget**: Created `includes/views/partials/retainer-widget.php`
  - Single source of truth for retainer status display
  - Used on both project Terms tab and Timer tab
  - Can be included in other views as needed

- **Helper Methods**: Added new methods to `FS_Project` model
  - `get_retainer_service($project)` - Get retainer service data from a project
  - `has_retainer_service($project)` - Check if project has a retainer service
  - `is_retainer_project($project)` - Check if project is retainer (checks both new and legacy approach)

### UI/UX

- Retainer service fields appear inline with other pricing options
- Clean teal background with organized layout
- Responsive grid layout for retainer fields
- Checkbox for rollover setting directly visible

### Backwards Compatibility

- Legacy `is_retainer`, `retainer_amount`, `retainer_hours`, and `retainer_rollover` fields are automatically populated from service data when saving
- Existing retainer projects using old approach will continue to work
- `FS_Project::is_retainer_project()` checks both new service-based and legacy approaches

---

## Version 3.8.8 <!-- @hidden -->

### Bug Fixes

- **Database Resilience**: Added defensive checks in get_project_summary() to handle missing retainer_periods table gracefully
  - Now returns basic summary from project data if table doesn't exist
  - Prevents fatal errors when database tables are missing or have prefix mismatches

- **Timer Tab Progress Bar**: Verified correct HTML structure for progress bar
  - Uses three-level structure: `.fs-retainer-progress` > `.fs-retainer-progress-bar` > `.fs-retainer-progress-fill`

### Notes

- If you're seeing database errors about table prefix mismatches (e.g., looking for `wp_fs_projects` but table is `wpbg_fs_projects`), check your `wp-config.php` to ensure `$table_prefix` matches your actual database table prefixes

---

## Version 3.8.7 <!-- @hidden -->

### Bug Fixes

- **Timer Tab Progress Bar**: Fixed progress bar not showing on the retainer widget in the Timer tab
  - Updated HTML structure to match main widget (added `fs-retainer-progress-fill` div inside `fs-retainer-progress-bar`)
  - Progress bar now correctly displays usage percentage with pink-coral gradient

---

## Version 3.8.6 <!-- @hidden -->

### UI/UX Improvements

- **Retainer Summary Redesign**: Updated retainer status widget with new gradient background
  - Background now uses teal-purple gradient (`var(--fs-teal-purple)`)
  - Text colors changed to white for better contrast
  - Progress bar now uses pink-coral gradient (`var(--fs-pink-coral)`)
  - Rollover badge updated with semi-transparent white background
  - Same styling applied to both main view and timer tab widgets

- **Project Services Table**: Added proper styling for services table
  - Added header row with uppercase labels
  - Better cell padding and spacing
  - Styled tracked hours indicator badge

- **Form Button Updates**: 
  - Changed "Add service" button to use `fs-btn fs-btn-secondary` classes
  - Changed Edit Project submit buttons to `fs-btn-primary` and `fs-btn-secondary`

### Bug Fixes

- **Rollover Checkbox**: Simplified loading logic using explicit int casting
  - `(int)$project->retainer_rollover !== 0` handles all type variations

---

## Version 3.8.5 <!-- @hidden -->

### Bug Fixes

- **Retainer Form Layout Redesign**: Completely rebuilt the retainer input fields with new HTML structure
  - Currency input now uses a proper input group with $ prefix in a gray background box
  - Hours input now uses matching input group with "hrs" suffix in gray background box
  - Both fields properly display side-by-side using CSS grid

- **Rollover Checkbox Persistence**: Fixed "Allow unused hours to roll over" checkbox not persisting
  - More defensive loading logic that checks for multiple falsy value types (0, '0', false)
  - Handles edge cases where database column may not exist

### Technical

- New CSS classes: `.fs-retainer-inputs`, `.fs-retainer-input-group`, `.fs-currency-input`, `.fs-currency-symbol`, `.fs-hours-input`, `.fs-hours-suffix`
- Removed spinner buttons from number inputs for cleaner appearance

---

## Version 3.8.4 <!-- @hidden -->

### Bug Fixes

- **Rollover Checkbox Persistence**: Fixed "Allow unused hours to roll over" checkbox not persisting when unchecked
  - Now properly checks if value is explicitly 0 vs null/missing
  - Handles edge case where database column may not exist yet

- **Currency Prefix Overlap**: Fixed the $ symbol still covering input text in Monthly Retainer Amount field
  - Increased input padding and added text-indent for better spacing

- **Hours Field Missing**: Fixed Hours Included Per Month field not displaying next to Monthly Retainer Amount
  - Added `!important` to grid CSS to override conflicting styles
  - Used more specific CSS selector for retainer form row

---

## Version 3.8.3 <!-- @hidden -->

### Bug Fixes

- **Retainer Form Layout**: Fixed Monthly Retainer Amount and Hours Included fields to display side by side instead of stacked
- **Currency Prefix Overlap**: Fixed the $ symbol overlapping input text in the Monthly Retainer Amount field
- **PHP Fatal Error**: Fixed "Cannot use object of type stdClass as array" error when viewing retainer projects on the Timer tab

---

## Version 3.8.2 <!-- @hidden -->

### New Features

- **Retainer Rollover Option**: Added checkbox to control whether unused hours roll over to next month
  - Enabled by default for new retainer projects
  - When disabled, unused hours expire at end of each billing period
  - Badge shown on retainer widgets when rollover is enabled

- **Retainer Hours Service Type**: Added "Retainer Hours" option to the service pricing type dropdown
  - When selected, hides pricing fields since hours are tracked against retainer allowance
  - Shows informational note explaining that hours will count toward monthly allowance
  - Auto-fills "Retainer Hours" as the service name

- **Retainer Widget on Timer Tab**: Retainer status widget now also appears on the project's Timer tab
  - Shows same information as the main project view widget
  - Displays rollover hours if rollover is enabled
  - Progress bar with color warnings at 75% and 100% usage

### Bug Fixes

- Fixed pricing fields visibility when switching between service types

### Technical

- Added `retainer_rollover` column to projects table
- Updated migration to add rollover column for existing installations

---

## Version 3.8.1 <!-- @hidden -->

### New Features

- **Retainer Project Setup UI**: Added the missing retainer configuration to the project form
  - New "This is a retainer project" checkbox in Billing section
  - Monthly Retainer Amount field (the fee you charge per month)
  - Hours Included Per Month field (hours included in the retainer)
  - Automatically suggests Monthly billing frequency when enabled
  - Migration ensures retainer columns exist for existing installations

### Bug Fixes

- **Orphaned Tasks Cleanup**: Fixed issue where tasks from deleted projects still appeared on the dashboard
  - Added `exclude_orphaned` parameter to task queries to filter out tasks whose projects were deleted
  - Dashboard task widget now automatically excludes orphaned tasks
  - Migration automatically cleans up existing orphaned tasks and milestones

### Improvements

- **Export Section Moved to Sidebar**: Export & Reports section moved from top of Reporting page to the right sidebar, above the Top Clients card for better layout

- **Income Summary Report Renamed**: Changed "1099 Summary" to "Income Summary" for clarity
  - The report shows income YOU received from clients (not payments made to others)
  - Report now includes ALL clients, not just those with $600+ payments
  - Updated PDF design with clearer "For Your Tax Records" messaging
  - Better suited for freelancers tracking their own income for tax purposes

### Technical

- Added `cleanup_orphaned_records()` migration function
- Added `ensure_retainer_columns()` migration function
- Updated task model with `exclude_orphaned` filter option

---

## Version 3.8.0

### New Features

- **1099 Year-End Report**: Generate a professional PDF summary of all clients paid $600 or more during the tax year
  - Includes client company name, contact name, address, and total paid
  - Tax disclaimer and important filing information
  - Available in both PDF and CSV formats
  - Access from the Reporting page under "Export & Reports"

- **Duplicate Project**: One-click copy a project with all its tasks and milestones
  - Creates a new project with "(Copy)" suffix
  - Copies all milestones with their billing percentages
  - Copies all tasks with their assignments and priorities
  - New project starts in "Pending" status
  - Available from the "More" dropdown on project view page

- **Batch Mark Invoices Paid**: Select multiple invoices and mark them as paid at once
  - Checkboxes on invoice list for multi-select
  - "Select All" option for quick selection
  - Set payment date for all selected invoices
  - Batch action bar appears when invoices are selected

- **Export to CSV**: Export your data for use in spreadsheets or other software
  - **Clients**: ID, company, contact, email, phone, address
  - **Invoices**: Invoice #, client, project, dates, amounts, status
  - **Time Entries**: Date, client, project, activity, hours, rate, amount
  - **Expenses**: Date, vendor, category, amount, project, tax deductible
  - **1099 Data**: Clients with $600+ payments for tax filing

- **Retainer Tracking System**: Comprehensive retainer/subscription management
  - New database table for tracking monthly retainer periods
  - Visual retainer status widget on project view
  - Tracks hours included, used, and remaining
  - Rollover hours calculation between periods
  - Progress bar with warning colors (75%+, 90%+, over 100%)
  - Sync hours automatically from time entries

### UI Improvements

- Added "Export CSV" button to Invoices list page
- New "Export & Reports" section on Reporting page with quick-access buttons
- Batch actions bar for invoice list with count display
- Retainer summary widget with progress visualization

### Database Changes

- New `fs_retainer_periods` table for tracking retainer usage and rollover

---

## Version 3.7.2 <!-- @hidden -->

### Bug Fixes
- **PDF Logo Display Fixed**: Business logo now displays correctly in emailed PDF contracts, proposals, and invoices
  - Added `image_to_base64()` helper that converts logo URLs to base64 data URIs for proper PDF embedding
  - Applies to all PDF types: contracts, proposals, and invoices

- **Contract Services Total Updates Dynamically**: The "Services Total" in the contract form sidebar now updates in real-time as services are added, removed, or modified
  - Added `#fs-sidebar-total` to the `calculateServicesTotal()` function

- **Contract Email Contact Name Fixed**: When sending a contract, the email now correctly uses the actual contact's name instead of the company name
  - Added `get_recipients_with_info()` helper function that returns both email and name for each recipient
  - Emails are now personalized per recipient with their actual name

- **Contract Expiration Date Fixed**: The expiration date in contract emails now correctly displays the "Valid Until" date instead of showing "N/A"
  - Changed from non-existent `expires_at` field to the correct `valid_until` field

---

## Version 3.7.1 <!-- @hidden -->

### Improvements
- **Income/Expense Chart Enhancement**: Changed the Monthly Income vs Expenses bar chart to split at the zero line
  - Income bars now display above zero (positive, teal)
  - Expense bars now display below zero (negative, coral)
  - Zero line is emphasized with a darker/thicker gridline
  - Tooltips show absolute values for clarity

---

## Version 3.7.0

### New Features
- **Invoice Wizard Button on Time Tracking Page**: Added a prominent "Invoice Wizard" button to the Time Tracking page for quick access to create invoices from tracked time
- **Interactive Reporting Charts**:
  - Replaced Monthly Income vs Expenses table with a stacked bar chart using site colors
  - Replaced Annual Deductions by Category table with an interactive doughnut chart with clickable legend

### Improvements
- **Activity Name Capitalization**: Activity names are now properly capitalized (first letter uppercase) throughout the Invoice Wizard preview and final invoices
  - Applies to all grouping methods: Entry, Activity, Task, Date, and Project
  - Both JavaScript preview and PHP backend consistently capitalize activity names
- **Updated Color Styling**:
  - Changed `.fs-badge.primary` background to `--fs-primary-xtralight`
  - Changed `.fs-portal-link-section` background to `--fs-primary-xtralight`
  - Changed `.fs-summary-bar` background to `--fs-highlight-light`
  - Changed Quarterly Payments Made and Remaining Tax Liability values to `--fs-accent` color

### Bug Fixes
- **Project Deletion Fixed**: Projects can now be properly deleted after their invoices have been removed
  - Fixed `FS_Invoice::count()` method to properly filter by `project_id`
- **Dashboard Task Completion Fixed**: Marking a task as complete from the dashboard now persists correctly
  - Fixed AJAX handler to accept both `id` and `task_id` parameters

### Technical Details
- Added Invoice Wizard modal include and button handler to Time Tracking list view
- Updated `groupTimeEntries()` JavaScript function to capitalize activities
- Updated `build_time_entry_items()` PHP method to use `ucfirst()` on activity names
- Added Chart.js integration for reporting page visualizations
- Added `.fs-text-accent` CSS utility class
- Added chart container and interactive legend CSS styles

---

## Version 3.6.22 <!-- @hidden -->

### New Features
- **Invoice Wizard: Enhanced Grouping Options**
  - Added "Group by Date" option for time entries - groups entries by the date they were logged
  - Added "Show Descriptions" checkbox for Activity and Task groupings - includes entry descriptions in the invoice line items
  - Added conditional checkboxes for Date grouping:
    - "Show Activity" - includes activity type in date-grouped line items
    - "Show Task" - includes task names in date-grouped line items
    - "Show Descriptions" - includes entry descriptions in date-grouped line items
  
- **Invoice Wizard: No Tasks Warning**
  - When "Group by Task" is selected, a helpful info notice appears if the project has no tasks defined
  - Informs users that all time entries will be grouped under "General Work"

### Improvements
- Invoice wizard grouping options now have dedicated panels that show/hide based on the selected grouping method
- Better visual organization of grouping options with styled option rows

### Technical Details
- Updated `FS_Invoice::build_time_entry_items()` to accept additional options array parameter for show_descriptions and date-specific display options
- Added `GROUP_BY_DATE` constant to FS_Invoice model
- Added helper functions `updateGroupingOptionsVisibility()` and `checkTasksAvailability()` in JavaScript
- AJAX handlers now pass the new options to the backend

---

## Version 3.6.21 <!-- @hidden -->

### Bug Fixes
- **Invoice Wizard: Task Grouping Mismatch Fixed**: The "Group by Task" option now produces consistent results between the live preview (step 2) and the review invoice (step 3)
  - JavaScript preview was incorrectly grouping by the description text field instead of actual task records
  - Fixed to use task_id and task_name from the tasks dropdown for grouping
  - Fixed PHP model using wrong field name (`name` instead of `title`) when looking up task names
  - Both preview and server-side now properly group by linked task records, with "General Work" for entries without a task assigned

---

## Version 3.6.20 <!-- @hidden -->

### Bug Fixes
- **Forms List: Action Menu Alignment**: Fixed the dropdown menu positioning on the Forms list page
  - Actions column now has proper width and centering
  - Dropdown menu correctly aligns with the trigger button

---

## Version 3.6.19 <!-- @hidden -->

### Bug Fixes
- **Critical: Fixed "Headers Already Sent" Redirect Issue**: Resolved the blank page issue that occurred after saving clients, projects, invoices, contracts, proposals, expenses, time entries, and other records
  - Added `safe_redirect()` helper method that properly cleans output buffers before redirecting
  - Updated all controllers to use the new safe redirect method
  - Modified early POST handler to keep output buffer open for controllers that handle their own redirects
  - This fix addresses the PHP warnings: "Cannot modify header information - headers already sent"

### Technical Details
The issue occurred because WordPress admin pages output HTML before the plugin's form submission handlers could redirect. The fix ensures all output buffers are properly cleaned before any `wp_redirect()` call.

---

## Version 3.6.8 <!-- @hidden -->

### Improvements
- **Project Templates**: Enhanced reorder animation for milestones and tasks
  - Items now visually slide to their new positions when using up/down arrows
  - Moving item gets highlighted with shadow and teal border
  - Both items animate simultaneously showing the swap
  - Prevents double-clicking during animation

---

## Version 3.6.7 <!-- @hidden -->

### Improvements
- **Project Templates**: UI refinements for task headers
  - Task headers now shorter (5px padding) and darker (gray-300 background)
  - Milestone headers maintain full height (12px padding) with teal-purple gradient
  - Added animation framework for move actions

---

## Version 3.6.6 <!-- @hidden -->

### Bug Fixes
- **Project Templates**: Removed unwanted left border from nested task items
- **Project Templates**: Reverted task headers to simple grey (removed colorful gradient)

---

## Version 3.6.5 <!-- @hidden -->

### New Features
- **Template Library UI**: Added the missing UI for the Template Library feature
  - New "Template Library" button on Settings → Templates page
  - Modal popup displays all 10 pre-built templates with descriptions and metadata
  - Select multiple templates and import them with one click
  - Already-imported templates are marked and disabled
  - Each template card shows: name, description, milestone count, task count, estimated hours, and price range

### Access
Go to **Freelance Studio → Settings → Templates tab** and click the "Template Library" button to browse and import pre-built project templates.

---

## Version 3.6.4 <!-- @hidden -->

### New Features
- **Template Library**: Added a pre-built template library with 10 common freelance project templates
  - Website Design & Development (60 hrs, 6 milestones, 18 tasks)
  - Brand Identity Package (25 hrs, 5 milestones, 13 tasks)
  - WordPress Website (30 hrs, 4 milestones, 15 tasks)
  - Landing Page (12 hrs, 3 milestones, 9 tasks)
  - Logo Design (10 hrs, 4 milestones, 10 tasks)
  - E-commerce Website (80 hrs, 5 milestones, 18 tasks)
  - Mobile App UI Design (40 hrs, 4 milestones, 13 tasks)
  - Marketing Collateral (15 hrs, 4 milestones, 10 tasks)
  - Social Media Design Package (20 hrs, 3 milestones, 11 tasks)
  - Website Maintenance Retainer (5 hrs/month, 1 milestone, 8 tasks)
  - Access via "Template Library" button on Project Templates page
  - Templates can be selectively imported and customized after import
  - Already-imported templates are visually marked

- **Milestone Modal Editor**: Improved milestone add/edit interface
  - New modal dialog for adding and editing milestones (replaces simple prompt)
  - Full editing of milestone name, description, due date
  - Invoice on completion settings with percentage option
  - Edit button on milestone cards now fully functional

### Bug Fixes
- **Fixed: Milestone Edit Button**: Edit button on milestone cards now opens a proper edit modal instead of doing nothing
- **Fixed: Project Templates - Empty Dropdowns**: Fixed issue where "Default Billing Type" and "Task Priority" dropdowns appeared empty in the Project Templates form
  - Added fallback definitions for `$billing_types` and `$priorities` variables

### UI Improvements
- **Project Templates - Reorder Buttons**: Replaced drag handles with up/down arrow buttons for reordering milestones and tasks
  - More intuitive reordering without requiring drag-and-drop
  - Move up/down buttons automatically renumber items after reordering

---

## Version 3.6.3 <!-- @hidden -->

### Bug Fixes
- **Fixed: Time Tracker Timezone Issue**: Time entries recorded via the timer now display in your local timezone instead of UTC. If you're in EST and track time at 3:08 PM, it now correctly shows as 3:08 PM (was incorrectly showing as 8:08 PM UTC)
  - Added `utc_to_local()` and `get_local_date()` helper functions to FS_Time_Entry model
  - Fixed date grouping in time entry list views to use local dates
  - Fixed time display in Timer View list to show local time
  - Fixed invoice wizard time entry dates to display in local timezone
  - Duration calculations remain accurate using UTC timestamps internally

### New Features
- **Booking URL Repair Button**: Added a "Repair URLs" button in Bookings > Settings that fixes 404 errors on the booking page
  - Click this button if your booking page shows a 404 error
  - Automatically re-registers URL rewrite rules and flushes permalinks

### Improvements
- **More Robust Rewrite Rule Handling**: Improved the automatic flushing of rewrite rules on plugin updates
  - Added `flush_rewrite_rules(true)` for hard flush
  - Added activation hook to ensure rules are registered on fresh installs

---

## Version 3.6.2 <!-- @hidden -->

### UI Improvements
- **Wider Invoice Wizard Modal**: Increased the invoice wizard modal width from 800px to 1000px on desktop for better readability of time entry lists

---

## Version 3.6.1 <!-- @hidden -->

### New Features
- **Live Invoice Preview in Wizard Step 2**: Added a dynamic preview panel on the right side of the "Select Items" step that shows exactly how line items will appear on the final invoice
  - Preview updates in real-time when changing grouping options (Activity Type, Task, Individual Entry, Project Total)
  - Preview updates when toggling "Show dates on line items" checkbox
  - Preview updates when selecting/deselecting time entries and services
  - Preview updates when changing service billing options (full amount, percentage, custom)
  - Shows grouped entries with proper formatting based on selected options
  - Displays running subtotals per project and grand total
  - Includes a visual indicator showing current grouping mode

### UI Improvements
- **Two-Column Layout for Step 2**: Invoice wizard Step 2 now uses a responsive two-column layout with item selection on the left and live preview on the right
- **Sticky Preview Panel**: The preview panel stays visible while scrolling through long lists of time entries
- **Responsive Design**: Preview panel moves above item selection on smaller screens (under 900px) for better mobile experience

---

## Version 3.6.0

### Improvements
- **Invoice Wizard Time Entry Grouping**: When using "Individual Entry" grouping, invoice line items now show both the activity type AND the description (e.g., "01/05 - Development - Circle landing page" instead of just "01/05 - Circle landing page")

### Bug Fixes
- **Fixed: "Headers Already Sent" Error**: Comprehensive fix for redirect issues that caused blank pages after saving settings, clients, projects, etc. Added early output buffering via `admin_init` hook to capture PHP 8.1+ deprecation warnings before redirects
- **Fixed: PHP 8.1+ Deprecation Warnings**: Added null checks to `str_replace()` and `strpos()` calls in email templates and email service to prevent deprecation warnings that were breaking redirects

### Grouping Options Reference
- **Activity Type**: Groups all entries by activity, shows activity name with date range if enabled
- **Task**: Groups entries by task, shows task name with date range if enabled  
- **Individual Entry**: Each time entry as a separate line item, shows activity type + description
- **Project Total**: Single line item summarizing all hours for the project

### Contract View Updates
- **Changed:** "Create Invoice" button on Contract view now opens the Invoice Wizard modal instead of the old dropdown menu

---

## Version 3.5.26 <!-- @hidden -->

### Bug Fixes
- **Fixed:** Contract and Proposal "Send to Client" now properly reads selected contacts from the modal form (was only reading from saved record data, causing "no valid recipients" errors)
- **Fixed:** Email recipient lookup now checks contacts table for primary contact when client doesn't have a legacy email field (supports v3.5.0+ contact-based architecture)
- **Fixed:** Both `send_proposal` and `send_contract` email service methods now accept `$include_primary` parameter for consistent behavior with invoice sending

### UI Changes
- **Added:** `margin-right: 8px` to `.fs-contact-checkbox-item .fs-contact-email` for better spacing in send modal

---

## Version 3.5.25 <!-- @hidden -->

### Bug Fixes
- **Fixed:** Dropdown menus on Proposals and Contracts list pages now display properly (removed `overflow: hidden` from `.fs-table`, increased z-index to 1000)
- **Fixed:** "Send to Client" button on Proposal view now includes `data-client-id` attribute, enabling contact selection in the send modal
- **Fixed:** "Send to Client" button on Contract view now includes `data-client-id` attribute, enabling contact selection in the send modal

### UI Changes
- **Updated:** "Back to Contracts" button on Contract view now uses `fs-btn fs-btn-secondary` class for consistent styling

---

## Version 3.5.24 <!-- @hidden -->

### Bug Fixes
- **Fixed:** Proposal update method now handles partial updates properly - no more "Undefined array key" errors when approving proposals
- **Fixed:** Added null check for client object in proposal view to prevent "Attempt to read property on null" errors
- **Fixed:** Removed duplicate closing `</a>` tag in proposal view

### UI Changes
- **Updated:** `.status-approved` badge background changed from `--fs-primary-light` to `--fs-primary-xtralight` for better visibility
- **Updated:** `.fs-invoice-total-row.balance-due` now uses `color: var(--gray-50)` and `background: var(--primary-color)` for a filled primary color style
- **Fixed:** Proposal view buttons now use correct classes:
  - Delete: `fs-btn fs-btn-danger`
  - Back to Proposals: `fs-btn fs-btn-secondary`
  - Convert to Contract: `fs-btn fs-btn-deposit`

---

## Version 3.5.23 <!-- @hidden -->

### Bug Fixes
- **Fixed:** Custom primary color now correctly applies to public invoice, proposal, and contract pages
  - Changed `primary_color` key to `invoice_primary_color` in frontend router's `get_business_settings()` method to match view expectations
- **Fixed:** Added `margin-right: 10px` to `.fs-contact-checkbox-item input[type="checkbox"]` for proper checkbox spacing

### UI Changes
- **Removed:** "Send To" section from proposal creation form - proposals don't send on creation, so users will use "Send to Client" button after creating the proposal

---

## Version 3.5.22 <!-- @hidden -->

### Bug Fixes
- **Fixed:** Contact checkbox display spacing completely reworked:
  - Reduced container gap from 12px to 4px for tighter element spacing
  - Added explicit 8px margin-right on checkbox input
  - Both `.fs-contact-title` and `.fs-contact-email` now have `::before` separators with middle dot (·) and 6px margins on each side
  - Display format: `Name · Title · email@example.com`

### Settings
- Primary Color setting added to **Settings > Invoices** tab (also still available in Settings > Business > Branding)

---

## Version 3.5.21 <!-- @hidden -->

### Bug Fixes
- **Fixed:** Contact element spacing - changed from `::before` pseudo-elements to simple `margin-left` for cleaner separation
- Name has `margin-right: 6px`, email and title have `margin-left: 6px`
- Only email retains the dot separator (`::before` with `·`)

### Settings
- **Added:** Primary Color setting now also appears in **Settings > Invoices** tab for easier access
- Setting remains in Business > Branding as well for backwards compatibility

---

## Version 3.5.20 <!-- @hidden -->

### Bug Fixes
- **Fixed:** Contact checkbox elements (name, email, title) now have proper spacing with middle dot (·) separators between them
- Added `margin-right: 8px` to contact name and title elements
- Added `::before` pseudo-elements with dot separators to email and title

### UI Updates
- **Pay Now Button:** Changed public invoice "Pay Now" button to use teal-purple gradient (`linear-gradient(90deg, #20bba6 0%, #77d5c9 46.8%, #81a5df 100%)`)

---

## Version 3.5.19 <!-- @hidden -->

### Bug Fixes
- **Fixed:** Contact checkbox spacing now correctly targets `.fs-contact-checkbox` class (was incorrectly targeting `.fs-contact-checkbox-item`)
- Added `margin-bottom: 10px` to `.fs-contact-checkbox` for proper spacing between contact items in send modals

### UI Updates
- Removed house emoji/icon from "View Your Client Dashboard" links in emails and public invoice page - now displays as plain text link

---

## Version 3.5.18 <!-- @hidden -->

### CSS Updates
- Changed `.fs-preview-project` background to `var(--fs-gray-50)`
- Added `margin-top: 30px` to `.fs-invoice-preview`
- Added `margin-bottom: 10px` to `.fs-contact-checkbox-item` for 10px spacing between items

### Client Dashboard Link in Emails
- **New:** Added "View Your Client Dashboard" link to invoice, proposal, and contract emails
- Link only appears for clients with portal access enabled
- Uses primary color from settings and includes a house emoji icon

---

## Version 3.5.17 <!-- @hidden -->

### CSS Updates
- **Stat Icons:** Changed from background fill to border style with `border: 2px solid var(--fs-primary-dark)`
- Updated variant stat icons (completed, pending, designer, client) to use border-color instead of background
- Task and milestone overview card stat icons use semi-transparent white border on gradient backgrounds

### Color Customization
- **Fixed:** Primary color setting now properly applies to public invoices, proposals, and contracts
- Added description to primary color setting: "Used for buttons and accents on public invoices, proposals, and contracts"
- All three document types now consistently use `invoice_primary_color` from Settings > Invoicing

### Client Dashboard Link
- **New:** Added "View Your Client Dashboard" link to bottom of public invoices
- Link only displays for clients who have portal access enabled
- Link hidden when printing invoice
- Styled to match invoice theme with hover effect

---

## Version 3.5.16 <!-- @hidden -->

### UI/Button Standardization
- **Deposit Button:** Changed background to `var(--fs-warning)` (coral/orange)
- **Invoice Wizard Button:** Background set to `var(--fs-secondary)` (purple), paired with `.fs-btn` class
- **Dropdown Triggers:** All `.fs-dropdown-trigger` now paired with `fs-btn fs-btn-secondary`
- **List Header Primary Buttons:** First action link/button in `.fs-list-header` now uses `fs-btn fs-btn-primary` classes
  - Updated: contracts list, proposals list, projects list, clients list, expenses list, recurring-invoices list, project-templates list
- **Detail View Buttons:** Updated contracts, proposals, invoices, recurring-invoices view pages with fs-btn classes
- **Add Service Button:** Updated to `background: var(--fs-primary-light); color: var(--fs-gray-700)`
- **Submit Buttons:** Added `.fs-btn-submit` class with `background: var(--fs-teal-purple)` gradient
  - Updated major forms: contracts, proposals, clients, time-tracking, expenses, project-templates, recurring-invoices
- **Cancel Buttons:** Updated to `fs-btn fs-btn-secondary` in form pages

---

## Version 3.5.15 <!-- @hidden -->

### Bug Fixes
- **Timer Card Styling:** Added border and border-radius (`border: 1px solid #e5e7eb; border-radius: 8px`)
- **Tab Header Buttons:** Changed all `.fs-tab-header` link/button classes to `fs-btn fs-btn-primary`
  - Project view: Timer "New Activity", "Invoice Time", "New Invoice", "Add Expense"
  - Client view: "New Project", "New Proposal", "New Invoice"
- **Quick Add Form:** Changed submit button to `fs-btn fs-btn-secondary`
- **Estimated Tax Card:** Now correctly pulls total from quarterly tax estimates table instead of separate calculation

---

## Version 3.5.14 <!-- @hidden -->

### Bug Fixes
- **Task Card Styling:** Updated to match screenshot with full colored borders
  - White background with 2px colored border around entire card
  - Teal border for "Me" tasks (primary-light color)
  - Pink border (#F2B6F6) for "Client" tasks
  - Red border for overdue tasks
  - Gray border for completed tasks
  - Green checkmark badge for completed tasks
- **Added Missing Styles:**
  - `.fs-task-card-actions`: Added display:flex with gap
  - `.fs-tab-header`: Added display:flex with space-between alignment
- **Assignee Badge Colors:**
  - "Me" badge: Teal background with teal text
  - "Client" badge: Pink background with magenta text

---

## Version 3.5.13 <!-- @hidden -->

### Improvements
- **Task Overview Card:** Updated to match milestone overview card
  - Changed to teal-lime gradient with dark text
  - Progress bar uses highlight-light fill
  - Stat icons use semi-transparent white background
- **Task Cards in Project View:** Now match kanban task cards
  - Sticky-note style with colored backgrounds based on priority
  - Priority colors: gray (low), teal (medium), coral (high), red (urgent)
  - Client tasks have purple/lavender background
  - Hover effect with subtle rotation
  - Hidden priority bar (using background colors instead)
  - Added data-priority attribute for CSS styling
- **Button Updates:**
  - Add Task button changed to fs-btn-primary style
- **Checkbox Colors:** Updated to teal instead of green

---

## Version 3.5.12 <!-- @hidden -->

### Improvements
- **Milestone Overview Card Redesign:**
  - Changed background to teal-lime gradient with dark text
  - Stat numbers and labels now use gray-700 for readability
  - Progress fill uses highlight-light color
  - Stat icons use semi-transparent white background
- **Milestone Cards:**
  - Upcoming milestones now have white background with no colored border
  - Milestone number badges use pink-purple gradient
- **Button Updates:**
  - Add Milestone button changed to fs-btn-primary style
  - Apply Template dropdown changed to fs-btn-secondary style
  - Removed "button" class from Mark Complete button

---

## Version 3.5.11 <!-- @hidden -->

### Bug Fixes
- **Fixed Missing CSS Variables:** Added gradient CSS variables to :root that were referenced but undefined
  - Added `--fs-teal-purple`, `--fs-teal-lime`, `--fs-pink-purple`, `--fs-pink-coral` gradients
  - Added `--fs-primary-xtralight` color
  - Updated color palette to match UI components reference (secondary, accent, highlight colors)
- **Fixed Milestone Overview Card:** Gradient now displays correctly with proper CSS variable
- **Fixed Task Overview Card:** Updated from blue gradient to teal-purple gradient to match FS palette
- **Fixed Stat Icons:** 
  - Default stat icons (on white backgrounds) use appropriate colored backgrounds
  - Stat icons on gradient cards (task/milestone overview) use white semi-transparent backgrounds
  - Stat content text shows white on gradient backgrounds, teal on white backgrounds

---

## Version 3.5.10 <!-- @hidden -->

### Improvements
- **Recolored Milestones Page:** Updated entire milestones section to use Freelance Studio color palette
  - Overview card header now uses teal-purple gradient
  - Milestone number badges use secondary purple (or teal when completed, red when overdue)
  - Completed milestones use teal theme instead of green
  - Upcoming milestones use coral/accent theme instead of yellow
  - Status dots, due dates, and invoice badges updated to match palette
  - Mark Complete button hover state now uses teal

---

## Version 3.5.9 <!-- @hidden -->

### Bug Fixes
- **Fixed Invoice Deposit Button Style:** Updated to use teal-lime gradient per UI components reference (was incorrectly using yellow/amber style)
- Changed button class from WordPress `button` to `fs-btn fs-btn-deposit` for consistent styling

---

## Version 3.5.8 <!-- @hidden -->

### Bug Fixes
- **Fixed Modal Form Layout:** Labels in modal forms now properly stack above their input fields instead of appearing side-by-side (affected Task modal and other modals using `fs-form-row` class)

---

## Version 3.5.7 <!-- @hidden -->

### Bug Fixes
- **Fixed Cancel Button in Contact Modal:** Removed conflicting `fs-modal-close` class that was overriding button styles with X-button dimensions (32x32px). Changed to use `fs-modal-cancel` class instead
- Updated JavaScript handlers to properly close modals when clicking Cancel buttons with `fs-modal-cancel` class
- Added explicit button reset styles and improved border visibility for secondary buttons

---

## Version 3.5.6 <!-- @hidden -->

### Bug Fixes
- **Fixed Cancel Button Styling:** Changed base `.fs-btn` border from `none` to `transparent` so secondary button borders display correctly
- **Fixed Time Tracking Report Cards:**
  - Updated stat card values from purple to teal (primary-dark) for brand consistency
  - Fixed stat icons to be visible on white backgrounds (was white-on-white)
  - Added subtle border and flex layout to stat cards
  - Updated icon color variants for better visibility on white backgrounds

---

## Version 3.5.5 <!-- @hidden -->

### Bug Fixes
- **Fixed Client Save Errors:** 
  - Removed `logo_url` column reference from client model (column doesn't exist in database)
  - Added proper isset() checks for deprecated form fields (name, email, phone) in client controller
- **Fixed Modal Footer Button Display:** 
  - Added proper styling for `.fs-btn` buttons in modal footers
  - Fixed form layout inside modals to prevent button clipping
  - Added flex-shrink: 0 to prevent buttons from being compressed

---

## Version 3.5.4 <!-- @hidden -->

### Improvements
- **Updated Modal Button Styles:** All modal dialogs now use the Freelance Studio button styles (`.fs-btn`) for visual consistency across the plugin
  - Updated: Contact edit modal, Task modals, Invoice wizard, Email send modal, Preset service modal, Quick add time entry modal

---

## Version 3.5.3 <!-- @hidden -->

### Bug Fixes
- **Fixed Missing Project Contact Option:** Added the "Project Contact" checkbox to the Edit/Add Contact modal on client view page
  - Modal now includes Primary, Billing, and Project contact type options
  - AJAX handler updated to save/load is_project_contact field
  - JavaScript updated to populate checkbox when editing existing contacts

---

## Version 3.5.2 <!-- @hidden -->

### Bug Fixes
- **Fixed Contact Type Checkboxes:** Contact type flags (Primary, Billing, Project) were not being saved correctly when editing clients
  - Fixed `is_primary` hidden field value processing (was checking if set, now checks actual value)
  - Changed checkbox processing from `isset()` to `!empty()` for more reliable detection
- **Removed non-functional Booking checkbox:** Removed Booking contact type checkbox that was added but had no database support

---

## Version 3.5.1 <!-- @hidden -->

### Bug Fixes
- **Fixed Contact Migration:** The v3.5.0 migration wasn't running properly for existing installations
  - Migration now runs on every activation until complete (idempotent)
  - Fixed logic to properly detect clients needing migration
  - Added detailed logging to track migration progress
  - Migration now handles clients that already have some contacts but no primary contact

---

## Version 3.5.0

### Breaking Changes
- **Client Contact Architecture Overhaul:** The client record no longer stores a built-in "primary contact". All contacts are now managed through the Contacts list.
  - The "Contact Name", "Email", and "Phone" fields have been removed from the main client form
  - Company/Client Name is now the primary identifier for clients
  - All contact management is done through the Contacts section

### Migration
- **Automatic Data Migration:** When upgrading to v3.5.0, the plugin automatically migrates existing primary contact data:
  - For each client that has name/email/phone fields but no primary contact in the contacts table
  - A new contact record is created with "Primary Contact" and "Billing" flags enabled
  - No data is lost during this migration

### New Features
- **Unified Contacts Management:** All contacts (including the primary contact) are now managed in one place
  - Primary Contact is now a radio button selection (only one contact can be primary)
  - Contact types include: Primary, Billing, Project, and Booking
  - Added visual empty state when no contacts exist
  - First contact added is automatically set as Primary
- **New Helper Methods:** Added `FS_Client::get_display_name()`, `get_primary_contact()`, and `get_document_info()` for consistent client info retrieval across the plugin

### Improvements
- Client form reorganized with "Company Information" as the primary section
- Better visual distinction for contact type badges
- Improved contact form UX with radio buttons for primary selection

---

## Version 3.4.24 <!-- @hidden -->

### New Features
- **Invoice Sales Tax Settings:** New section in Settings > Tax to configure default sales tax rate for invoices
  - Set a default sales tax rate (e.g., 7.25%)
  - Option to auto-apply sales tax on new invoices
  - Shows suggested state base rate based on your configured state (with note about local taxes)
  - States with no sales tax (OR, MT, NH, DE, AK) are properly indicated
- **Auto-populate Sales Tax on Invoices:** When enabled, new invoices will automatically have the default tax rate pre-filled
  - Works for invoices created via the form, wizard, from time entries, and from contracts
  - Tax rate remains fully editable on each individual invoice
  - Tax is calculated automatically when rate is changed

---

## Version 3.4.23 <!-- @hidden -->

### New Features
- **Tax Comparison View:** Settings > Tax > Quarterly Tax Payments now shows a clear before/after comparison:
  - **Tax Before Deductions:** What you'd owe on gross income without any deductions
  - **Tax After Deductions:** Your actual estimated tax liability after expenses and deductions
  - **Your Tax Savings:** The total amount saved through your deductions
- This helps users understand the value of tracking expenses and deductions

### Improvements
- Added sublabel styling for quarterly summary cards
- Separated Expenses and Annual Deductions in the Income Summary for clarity

---

## Version 3.4.22 <!-- @hidden -->

### Bug Fixes
- **Fixed date range comparisons for datetime fields:** Income calculations were potentially missing invoices paid later in the day due to datetime vs date comparison issues
  - `FS_Invoice::get_total_revenue()` now properly includes the full end date by adding `23:59:59` time component
  - `FS_Quarterly_Tax::calculate_quarter()` updated with same fix for quarterly income calculations
  - This could have caused quarterly tax estimates and annual tax summaries to show $0 or lower than actual income

### Improvements
- **Added Income Summary to Settings > Tax > Quarterly Tax Payments:** Now displays Gross Income, Expenses, and Total Deductions above the summary cards so you can see what numbers are being used for calculations
- **Added clarifying note:** Explains that income is calculated from **paid invoices only** - invoices that are sent but not yet marked as paid are not included in tax calculations

---

## Version 3.4.21 <!-- @hidden -->

### New Features
- **Quarterly Tax Estimates Section:** Added a comprehensive quarterly tax breakdown to the Reporting page
  - Shows income, expenses, and calculated taxes broken down by quarter (Q1-Q4)
  - Displays SE Tax + Federal and State Tax columns separately
  - Editable "Actual Payment" field to track what you've actually paid
  - Variance column shows difference between estimated and actual payments
  - Paid rows are highlighted for easy visual tracking
  - Tax rates info banner shows current Self-Employment (15.3%), Federal bracket, and State rates
  - Quarterly due dates reminder at bottom of section
  - AJAX-powered payment updates without page refresh
- **FS_Quarterly_Tax Model:** New model class for managing quarterly tax records and calculations
  - Calculates quarterly income from paid invoices
  - Calculates quarterly expenses
  - Computes SE Tax (15.3%), Federal Tax (based on bracket), and State Tax
  - Tracks actual payments made per quarter

### Technical
- Added `class-fs-quarterly-tax.php` model
- Added `quarterly-tax-section.php` view partial
- Added `fs_update_quarterly_payment` AJAX handler
- Added quarterly tax section CSS styles

---

## Version 3.4.18 <!-- @hidden -->

### New Features
- **Syntax highlighting in email template editor:** Code editor now displays different colors for different elements:
  - **Blue** - HTML tag names (`<p>`, `<table>`, `<strong>`, etc.)
  - **Light blue** - Attribute names (`style`, `href`, `class`)
  - **Orange** - Attribute values (`"value"`)
  - **Teal with highlight** - Template variables (`{client_name}`, `{amount}`)
  - **Blue underlined** - URLs
  - **Gray** - Punctuation (`<`, `>`, `=`)
- Uses VS Code Dark+ inspired color scheme for familiar developer experience
- Highlighting updates in real-time as you type

---

## Version 3.4.17 <!-- @hidden -->

### New Features
- **Split-screen email template editor with live preview:** Completely redesigned the email template editing experience
  - Left panel: HTML code editor with dark theme for easy reading
  - Right panel: Live preview that updates as you type with sample data
  - Toolbar with quick-insert buttons for common HTML (paragraphs, line breaks, bold, italic, links)
  - Click-to-insert variable tags directly into the code
  - Preview shows exactly how the email will look with placeholder data
- HTML is preserved exactly as written - no more TinyMCE corruption

---

## Version 3.4.16 <!-- @hidden -->

### Bug Fixes
- **Disabled TinyMCE Visual editor for email templates:** Root cause found - TinyMCE was stripping `<p>` tags when switching between Visual and Code modes
- Email template editor now uses Code/Text mode only, which preserves HTML exactly as written
- Added helper text explaining HTML editing
- This prevents future template corruption from TinyMCE's HTML "cleaning"
- **Action Required:** Reset your email templates to default to fix existing corruption, then re-customize

---

## Version 3.4.15 <!-- @hidden -->

### Bug Fixes
- **Fixed TinyMCE stripping HTML tags when switching modes:** The email template editor was stripping `<p>` tags and other HTML when switching between Visual and Code modes
- Configured TinyMCE with `valid_elements`, `verify_html`, and other settings to preserve all HTML structure
- This was the root cause of email template corruption that caused extra line breaks and lost paragraph formatting
- **Important:** After updating, reset your invoice email template to default, then re-customize it

---

## Version 3.4.14 <!-- @hidden -->

### Bug Fixes
- **Restored exact v3.4.4 email processing code:** Reverted to the proven working code for all email template processing
- Email processing includes:
  - Cleanup of empty TinyMCE paragraphs (`<p>&nbsp;</p>` and `<p><br></p>`)
  - Collapsing of 3+ consecutive `<br>` tags to just 2 (should fix extra space above tables)
  - Conditional `nl2br()` only applied if template has no `<p>` tags (plain text templates only)
- Applied consistently to invoice, proposal, and contract emails

---

## Version 3.4.13 <!-- @hidden -->

### Bug Fixes
- **Reverted to simpler v3.4.4-style email processing:** Uses `nl2br()` for line breaks which was working correctly
- **Fixed extra whitespace above tables:** Added cleanup for empty `<p>` tags and `<p><br></p>` patterns that TinyMCE adds
- Removed overly complex paragraph margin injection that was causing issues
- Simplified save function to use `wp_kses_post()` which properly handles HTML

---

## Version 3.4.12 <!-- @hidden -->

### Bug Fixes
- **Fixed root cause of email paragraph spacing issue:** Newlines were being stripped when saving email templates because `wp_kses()` sanitization removed them
- Now converts newlines to `<br>` tags before sanitization, ensuring line breaks are preserved in the database
- Added `<tbody>` to allowed HTML tags for proper table support
- **Important:** After updating, you must re-save your email templates for the fix to take effect (simply open the template editor and click Save)

---

## Version 3.4.11 <!-- @hidden -->

### Bug Fixes
- **Fixed paragraph spacing in emails:** Now properly handles mixed content (HTML elements + plain text with newlines)
- Always converts newlines to `<br>` tags AND adds margin to any `<p>` tags
- Double line breaks (blank lines) convert to 16px spacers for clear paragraph separation
- Works correctly regardless of whether content was edited in Visual or Code mode

---

## Version 3.4.10 <!-- @hidden -->

### Bug Fixes
- **Fixed paragraph spacing in emails:** Email templates edited in Visual mode (TinyMCE) now properly display paragraph spacing
- Added inline margin styles to `<p>` tags generated by the visual editor
- Emails now properly handle both Visual editor (paragraph-based) and Text/Code editor (newline-based) content
- Works automatically - users simply type in the Visual editor and paragraph breaks are preserved in emails

---

## Version 3.4.9 <!-- @hidden -->

### Bug Fixes
- **Fixed paragraph spacing in emails:** Blank lines in email templates now create proper visual paragraph spacing
- Single line breaks still create `<br>` tags for line breaks within paragraphs
- Double (or more) line breaks now convert to a 16px spacer for clear paragraph separation
- Users can simply press Enter twice in the email template editor to create paragraph breaks - no HTML knowledge required

---

## Version 3.4.8 <!-- @hidden -->

### Bug Fixes
- **Reverted to nl2br for email line breaks:** Restored `nl2br()` for converting newlines to `<br>` tags in email templates, as `wpautop()` was not preserving single line breaks properly
- **Maintains TinyMCE cleanup:** Still removes empty paragraphs and excessive line breaks that may be introduced by the visual editor

---

## Version 3.4.7 <!-- @hidden -->

### Bug Fixes
- **Fixed email paragraph formatting:** Now using WordPress's `wpautop()` function which properly handles mixed HTML and plain text content in email templates
- **Proper line breaks:** Plain text paragraphs in email templates are now correctly wrapped in `<p>` tags with proper spacing, while existing HTML elements (tables, styled paragraphs, links) are preserved
- **Applied to all email types:** Invoice, proposal, and contract emails all use the improved processing

---

## Version 3.4.6 <!-- @hidden -->

### Bug Fixes
- **Fixed excessive whitespace in emails:** Email templates edited with the WordPress visual editor (TinyMCE) now render correctly without extra blank lines or spacing
- **Email processing improvements:** 
  - Removes empty paragraphs (`<p>&nbsp;</p>`) that TinyMCE sometimes adds
  - Removes `<p><br></p>` artifacts from the editor
  - Limits consecutive line breaks to a maximum of two
  - Only applies `nl2br()` to plain text templates (not to templates that already have HTML paragraph formatting)
- **Applied to all email types:** Invoice, proposal, and contract emails all use the improved processing

### Style Updates
- Changed `.fs-email-tips` background color to `--fs-highlight-light` (softer yellow)

---

## Version 3.4.5 <!-- @hidden -->

### Email Personal Message Improvements
- **Personal message now appears at the top:** When adding a personal message to invoices, proposals, or contracts, the message now appears at the beginning of the email (after the greeting) rather than at the bottom
- **Styled in italics:** Personal messages are now displayed in italics with a subtle gray color to visually differentiate them from the standard email content
- **Applied to all document types:** This improvement applies to invoice, proposal, and contract emails

---

## Version 3.4.4 <!-- @hidden -->

### Invoice Sending Flow Improvements
- **New Invoice Creation:** After clicking "Create Invoice", invoices now stay in "Draft" status instead of incorrectly showing as "Sent"
- **Send Modal Auto-Trigger:** After creating a new invoice, the Send Invoice modal automatically opens prompting the user to send the invoice to their client
- **Contact Selection in Send Modal:** The Send Invoice modal now shows a list of client contacts with checkboxes, allowing you to choose exactly who receives the invoice:
  - Primary client email is shown first and checked by default
  - Additional contacts from the client's contact list are shown with their roles
  - Select multiple recipients to send to everyone at once
- **Resend to Client:** The "Resend to Client" button now opens the same modal with contact selection, so you can choose different recipients when resending
- **Clearer Messaging:** Success message after creation now reads "Invoice created successfully. Ready to send to client."
- **Updated Form Labels:** The "Date Invoice Sent" field description is now clearer about its purpose

### Technical Changes
- Invoice status no longer auto-sets to "Sent" based on the invoice_date field
- Added new AJAX handler `fs_get_client_contacts_for_send` for loading contacts in the send modal
- Updated `send_invoice_with_message` to accept selected contacts from the modal
- Updated `FS_Email_Service::send_invoice()` and `get_recipients()` to support explicit primary email inclusion

---

## Version 3.4.3 <!-- @hidden -->

### UI Improvements
- **Kanban Column Background:** Changed column background to lighter gray (`--fs-gray-50`)
- **Kanban Add Task Button:** Changed to white color for better visibility against colored headers
- **Task Card Priority Colors:** Updated priority backgrounds - Low (gray-200), Medium (primary-light), High (accent-light)
- **Client Task Cards:** Changed background to `--fs-secondary-light` for consistency
- **Project Badge:** Changed background to white for cleaner appearance
- **Combined Task Stats Card:** Merged "Open Tasks", "My Tasks", and "Client Tasks" into a single card with breakdown display

### New Features
- **Archived Project Task Filtering:** Tasks from completed or archived projects are now automatically hidden from the Tasks list and Kanban board. Generic tasks (not associated with any project) remain visible.

### Technical Details
- Added `exclude_archived_projects` parameter to `FS_Task::get_all()` method
- Tasks list and Kanban board now use this parameter by default

---

## Version 3.4.2 <!-- @hidden -->

### UI Improvements
- **Overdue Task Indicator:** Replaced the red border on overdue task cards with a cleaner flag icon badge in the card header. The date still displays in red for emphasis.
- **Completed Task Indicator:** Task cards in the "Done" column now display with a visual completed state: muted colors, strikethrough title, and a teal checkmark badge in the corner.

---

## Version 3.4.1 <!-- @hidden -->

### New Features
- **Kanban Task Completion Sync:** Moving a task card to the "Completed" column now automatically marks the task as complete with the completion date set. Moving a task out of the "Completed" column clears the completion date, marking it as incomplete.
- **Customizable Kanban Column Headers:** Column titles on the Tasks Kanban board can now be customized. Hover over a column title and click the edit icon to rename it. Custom names are saved automatically.

### Technical Details
- Added `fs_kanban_column_labels` option for storing custom column names
- Updated `FS_Task::update()` to properly clear `completed_date` when moving tasks out of completed status
- Added `save_kanban_column_label` AJAX handler for inline column name editing

---

## Version 3.4.0

### New Features
- **Invoice Wizard:** New multi-step wizard for creating invoices from the Invoices list or Project view
  - **Step 1 - Select Type:** Choose to bill Time Entries, Project Services, or a Combined Invoice. Filter by client and select one or multiple projects.
  - **Step 2 - Select Items:** 
    - For time entries: Choose grouping method (by Activity, Task, Individual Entry, or Project Total) with optional date display. Select/deselect individual time entries with "Select All" button per project.
    - For services: Select which services to include with checkboxes. Each service has its own billing option (Full Amount, Percentage, or Custom Amount) with milestone/deposit labeling.
  - **Step 3 - Review:** Preview line items grouped by project with subtotals before creating. Set due date and add optional notes.
- **Multi-Project Invoices:** Create a single invoice that includes items from multiple projects
- **Time Entry Grouping Options:**
  - By Activity: Groups time by activity type (Design, Development, etc.)
  - By Task: Groups time by assigned task
  - By Entry: Shows each time entry as a separate line item
  - By Project: Shows single total per project
- **Service Selection:** Choose exactly which services to bill - no longer forced to include all services
- **Service Partial Billing:** Bill a percentage or custom amount of each service individually for deposits or milestone payments
- **Invoice Editing Rules:**
  - Draft invoices are freely editable
  - Sent/Viewed invoices can be edited with a prompt to resend
  - Paid invoices are locked from editing

### Bug Fixes
- **Orphaned Time Entries:** Fixed issue where deleting an invoice left time entries marked as "invoiced", preventing them from being billed again. Invoice deletion now properly resets linked time entries to unbilled status.
- **Automatic Data Repair:** Plugin update automatically detects and repairs any existing orphaned time entries from previous invoice deletions
- **Container ID Consistency:** Fixed multiple HTML/JS container ID mismatches that prevented time entries and services from rendering in the wizard

### Database Changes
- Added `invoice_source` column to invoices table to track creation method
- Added `time_grouping` column to invoices table
- Added `show_entry_dates` column to invoices table
- New `fs_invoice_items` table for detailed line item storage
- New `fs_invoice_projects` junction table for multi-project invoices

### UI Improvements
- Invoice Wizard button added to Invoices list page
- Project view Invoice button now opens the wizard
- Invoice form shows locked notice for paid invoices
- Invoice form shows resend warning for sent invoices that have been edited
- Improved wizard step 2 styling with clear section headers and checkbox lists

---

## Version 3.2.0

### New Features
- **Invoice Wizard:** New multi-step wizard for creating invoices from the Invoices list or Project view
  - **Step 1 - Select Type:** Choose to bill Time Entries, Project Services, or a Combined Invoice. Select one or multiple projects.
  - **Step 2 - Select Items:** 
    - For time entries: Choose grouping method (by Activity, Task, Individual Entry, or Project Total) with optional date display
    - For services: Choose billing option (Full Amount, Percentage, or Custom Amount) with milestone labeling
  - **Step 3 - Review:** Preview line items with project headers and subtotals before creating
- **Multi-Project Invoices:** Create a single invoice that includes items from multiple projects
- **Time Entry Grouping Options:**
  - By Activity: Groups time by activity type (Design, Development, etc.)
  - By Task: Groups time by assigned task
  - By Entry: Shows each time entry as a separate line item
  - By Project: Shows single total per project
- **Service Partial Billing:** Bill a percentage or custom amount of a service for deposits or milestone payments
- **Invoice Editing Rules:**
  - Draft invoices are freely editable
  - Sent/Viewed invoices can be edited with a prompt to resend
  - Paid invoices are locked from editing

### Database Changes
- Added `invoice_source` column to invoices table to track creation method
- Added `time_grouping` column to invoices table
- Added `show_entry_dates` column to invoices table
- New `fs_invoice_items` table for detailed line item storage
- New `fs_invoice_projects` junction table for multi-project invoices

### UI Improvements
- Invoice Wizard button added to Invoices list page
- Project view Invoice button now opens the wizard
- Invoice form shows locked notice for paid invoices
- Invoice form shows resend warning for sent invoices that have been edited

## Version 3.1.35 <!-- @hidden -->

### Improvements
- **Timer Widget Enhanced:** The timer widget now matches the manual time entry form layout with:
  - Project dropdown (row 1, left)
  - Task dropdown with "(optional)" label (row 1, right)
  - Activity dropdown (row 2, left)  
  - Description field (row 2, right)
- Timer widget is consistent across Dashboard and Timer page
- Activity field now properly saves when starting timer

### Bug Fixes
- Added task_id support to timer start function
- Fixed timer JavaScript to work with new widget structure on Dashboard

## Version 3.1.34 <!-- @hidden -->

### Bug Fixes
- Fixed fatal error: Changed `FS_PLUGIN_PATH` to correct constant `FS_PLUGIN_DIR` in dashboard timer widget

## Version 3.1.33 <!-- @hidden -->

### Improvements
- **Dashboard Invoices Widget:** Now displays project title instead of invoice number for better context
- **Dashboard Timer Widget:** Now uses the same unified timer widget as the Timer page, with project AND task selection dropdowns for consistency across the site
- **Upcoming Events:** Google Calendar event times now display in 12-hour format (e.g., "2:30 PM") instead of 24-hour military time
- **Manual Time Entry - Seconds Support:** Added seconds field to duration input (hours, minutes, seconds)
- **Manual Time Entry - Time Range with Seconds:** Start and end time inputs now support seconds (HH:MM:SS format)
- **Manual Time Entry - Auto-Calculate End Time:** When you set a duration and then set the start time, the end time is automatically calculated. Works both directions:
  - Setting start + end time → calculates duration
  - Setting duration + start time → calculates end time

### Bug Fixes
- **Quick Time Entry Billable Bug:** Fixed issue where unchecking "Billable" checkbox still saved the entry as billable. Non-billable entries now save correctly.

## Version 3.1.32 <!-- @hidden -->

### New Features
- **Logo Upload Consistency:** Both Settings > Business and Clients now support both Media Library upload and direct URL input for logos
  - Settings: Added Media Library upload button alongside existing URL field
  - Clients: Added URL input field alongside existing Media Library upload
  - Both options work together - using one clears the other to avoid conflicts

### Improvements
- **Calendar Colors Updated:** All calendar event colors now match the Freelance Studio design system
  - Projects: #81A5DF (--fs-secondary)
  - Milestones: #3ECFB2 (--fs-primary)
  - Tasks: Priority-based colors using --fs-danger, --fs-accent, --fs-accent-light, --fs-gray-500
  - Bookings: #77D5C9 (--fs-primary-light)
  - Invoices: #FFA26E (--fs-accent), overdue uses #E85A5A (--fs-danger)
  - Expiring Contracts/Proposals: #E85A5A (--fs-danger)
  - Tax Deadlines: #5D4E80 (--fs-secondary-dark)

### Bug Fixes
- **Google Calendar Events:** Fixed blank link issue - clicking Google Calendar events in the Upcoming Events widget now navigates to the calendar page for that month instead of a blank link

### Database Changes
- Added `logo_url` column to clients table for URL-based logo support

## Version 3.1.31 <!-- @hidden -->

### Bug Fixes
- **CSS Syntax Fix:** Removed extra closing brace at line 6189 that was breaking CSS parsing for calendar and other styles

### Improvements
- **Invoice Status Badges:** Restyled inline status dropdown on Invoices list to match designed status badge style
  - Pill-shaped badges with uppercase text and appropriate color coding
  - Dynamic color updates when status is changed via dropdown
  - Colors match standard badge system: gray (draft), coral (pending/partial), purple (sent/viewed), teal (paid), red (overdue)

## Version 3.1.30 <!-- @hidden -->

### Improvements
- **Unified Action Buttons:** Standardized all list views to use consistent ⋯ dropdown menu pattern
  - **Projects, Clients, Proposals** - Already used correct pattern (no changes)
  - **Contracts** - Fixed trigger button class from `fs-row-menu-toggle` to `fs-row-menu-trigger`
  - **Invoices** - Converted from inline buttons to dropdown menu with View, Edit, Download PDF, Duplicate, Delete
  - **Expenses** - Converted from plain text links to dropdown menu with Edit, Duplicate, Delete
  - **Time Tracking** - Consolidated inline buttons + separate dropdown into single dropdown menu
  - **Bookings** - Converted from WordPress buttons to dropdown menu with View, Cancel
  - **Recurring Invoices** - Converted from icon-only buttons to dropdown menu
  - **Forms** - Converted from WordPress buttons to dropdown menu
  - **Project Templates** - Converted from icon-only buttons to dropdown menu
  - **Tasks** - Converted from single icon button to dropdown menu
- Delete links now consistently use `text-danger` class for red styling
- Action columns now use empty header text for cleaner appearance
- Reduced action column widths to 60px to match ⋯ button size

---

## Version 3.1.27 <!-- @hidden -->

### Improvements
- **"Your Week at a Glance" Widget:** Redesigned to show a 7-day grid with full-color event bars
  - Renamed from "Week View" to "Your Week at a Glance"
  - Shows all 7 days in a grid layout with day names and numbers
  - Events appear as colored bars with truncated title text (no icons)
  - Colors match event type (from calendar)
  - Today is highlighted with a teal border
  - Up to 3 events per day with "+X" indicator for more
  - Weekend days have a slightly darker background

- **Upcoming Events Widget:** Now excludes task deadlines
  - Only shows project deadlines, milestones, invoice due dates, bookings/meetings, and imported Google Calendar events
  - Tasks are shown in the "Your Week at a Glance" and "Tasks" widgets instead
  - Prevents duplicate task visibility across multiple widgets

---

## Version 3.1.26 <!-- @hidden -->

### Improvements
- **Dashboard Week View Widget:** Completely redesigned to show actual event/task text instead of just icons
  - Now displays as a list of days with events, only showing days that have events
  - Each event shows an icon and the event title (truncated to 5 words)
  - Color-coded left border indicates event type (purple=project, coral=invoice, teal=task, lime=milestone)
  - Today's events are highlighted with a teal left border
  - Shows "No events this week" message with calendar icon when the week is empty
  - Up to 3 events per day with "+X more" indicator for additional events
  - Click any event to navigate directly to it

---

## Version 3.1.25 <!-- @hidden -->

### Bug Fixes
- **Dashboard Mini Calendar Fatal Error:** Fixed "Call to undefined method FS_Calendar_Controller::get_events_for_range()" error. Rewrote the AJAX handler to fetch calendar events directly from the database instead of relying on a non-existent controller method.

---

## Version 3.1.24 <!-- @hidden -->

### Bug Fixes
- **Dashboard Mini Calendar:** Fixed JavaScript error "Can't find variable: fs_admin" - changed to use correct localized script variable `fs_ajax`
- **Dashboard Tasks Widget:** Fixed styling conflict where quick add task form had coral/orange gradient background instead of proper light gray form styling. Renamed calendar page button class to `fs-calendar-quick-add-btn` to avoid conflict.

### Improvements
- **Dashboard Mini Calendar:** Added proper padding and spacing for mini calendar within widget body
- **Quick Add Form:** Improved input field styling with proper focus states using the new color palette

---

## Version 3.1.23 <!-- @hidden -->

### Bug Fixes
- **Task Modal Layout:** Fixed misaligned labels/inputs in the Add Task modal (Assigned To field). Changed nested `.fs-form-row` elements inside `.fs-form-row-2col` to use `.fs-form-group` for proper layout consistency.
- **Task Modal Cancel Button:** Added `button-secondary` class for proper styling

### Improvements
- **Dashboard Mini Calendar:** Unified CSS classes between dashboard and calendar page sidebar mini calendars for visual consistency. Both now use the same `fs-sidebar-mini-cal` styling.
- **Dashboard Mini Calendar Navigation:** Rewrote AJAX navigation to work with the unified component structure

---

## Version 3.1.22 <!-- @hidden -->

### Bug Fixes
- **Bookings 404:** Added automatic flush of rewrite rules on plugin update to fix booking page 404 errors (users no longer need to manually reset permalinks)
- **Contact Edit Error:** Fixed "Failed to save contact" error when editing secondary contacts
- **Contact Modal:** Fixed Cancel button styling in the contact edit modal

### New Features
- **Task Selection in Time Tracking:** Added task dropdown to Quick Add time entry and Timer widget, allowing time to be tracked against specific tasks within projects
- **Dashboard Calendar Navigation:** Added AJAX-powered month navigation for the dashboard mini calendar widget (arrows now work)

### Improvements
- Timer widget gradient colors updated to match new color palette
- Reorganized Quick Add form layout for better UX with task selection

---

## Version 3.1.21 <!-- @hidden -->

### Complete UI Redesign - New Color Palette

Major visual overhaul of all backend admin pages with a fresh, modern color palette.

#### New Color System
- **Primary (Teal/Mint):** #3ECFB2 - Used for primary buttons, links, and success states
- **Secondary (Purple):** #7B68A6 - Used for headers, secondary elements, and info states
- **Accent (Coral):** #F5896D - Used for CTAs, warnings, and attention elements
- **Highlight (Lime):** #C5E946 - Used for success badges and highlights

#### Updated Components
- **Buttons:** New gradient styles with teal primary, coral accent, and lime success variants
- **Cards:** Removed borders, increased border-radius (12px), refined shadows
- **Tables:** Cleaner header styling with gray backgrounds instead of blue
- **Forms:** Improved input focus states with teal glow, softer label colors
- **Modals:** Purple-tinted backdrop blur, gradient header backgrounds
- **Tabs:** Teal underline for active state, improved hover transitions
- **Status Badges:** Updated colors to match new palette (teal for success, purple for active, coral for pending)
- **Kanban Board:** Column headers match new palette, card sticky-note colors updated
- **Calendar:** Event cards and sidebar use new color scheme
- **Dashboard:** Cards with refined shadows and gradient warning states

#### Design Principles
- Clean, modern aesthetic with ample whitespace
- Soft shadows (purple-tinted) for depth
- Larger border-radius throughout (8px-16px)
- Gradient buttons for primary actions
- Consistent use of CSS variables for easy customization

---

## Version 3.1.20 <!-- @hidden -->

### Task Kanban Styling Update
- Column headers now have full pastel background colors matching their status
- Task cards styled as sticky notes with priority-based background colors
- Improved hover animations and visual feedback

### Bug Fix
- Fixed task URL routing: tasks without projects now link to the Tasks page instead of showing "Project not found" error

---

## Version 3.1.19 <!-- @hidden -->

### New Features

#### Calendar UI Redesign
- Completely redesigned calendar page with modern, clean aesthetic inspired by professional scheduling apps
- Events now display as pastel-colored cards instead of left-bordered items
- Each event type has its own color scheme (projects: blue, milestones: yellow, tasks: gray, invoices: orange, expiring: red, tax: purple, google: green)
- Added mini calendar widget in sidebar showing current month with event indicators
- Added prominent "Add New Task" button with coral/salmon gradient styling
- Reorganized sidebar into clean card sections for better visual hierarchy
- Updated week view with softer colors and improved event card styling
- Today's date now highlighted with coral/orange circle in both main calendar and mini calendar
- Events have subtle hover animations and shadows for better interactivity
- Improved responsive layout - sidebar displays in horizontal grid on medium screens

### UI/UX Improvements
- Rounded corners (16px) on calendar grid and sidebar cards for softer appearance
- Removed heavy header bar from weekday row in favor of subtle text styling
- Legend items now displayed in white card with subtle shadow
- Upcoming events list redesigned with improved icon display and spacing
- Better visual separation between calendar sections

---

## Version 3.1.1 <!-- @hidden -->

### Changed
- Time Tracking table column widths adjusted for better display of status badges and action buttons
- Time Tracking action buttons updated to match invoice list style (Edit, Delete, More dropdown)

---

## Version 3.1.0

### New Features

#### Recurring Expenses
- Added ability to set up recurring expenses for subscriptions (Adobe, hosting, etc.)
- Support for monthly, quarterly, and yearly recurring frequencies
- Option to specify the day of month for recurring charges
- Option to set an end date for recurring expenses
- Recurring expenses are displayed with a 🔄 badge in the expense list
- Monthly recurring total shown in expense summary
- Automatic generation of expense entries via daily cron job

#### External Calendar Integration (iCal)
- Added support for linking external calendars via public iCal/ICS URLs
- Compatible with Office 365, Outlook, Apple iCloud Calendar, and any iCal-compatible service
- Option to block availability for meeting booking based on external calendar events
- Automatic hourly sync of external calendars
- Manual sync option available in settings
- Setup instructions included for major calendar providers

#### Time Tracking Status Labels
- Added text labels to status indicators in time tracking list (Invoiced, Billable, Non-billable)
- Colored status badges for better visibility and intuitive understanding
- Improved accessibility over color-only indicators

### Bug Fixes

#### Dashboard Tasks Widget
- Fixed issue where tasks were not showing in the admin dashboard
- Changed query from invalid 'incomplete' status to valid ['pending', 'in_progress'] statuses
- Tasks now properly display in the dashboard Tasks widget

### Technical Changes
- Added new database columns for recurring expenses (is_recurring, recurring_frequency, recurring_day, recurring_end_date, parent_expense_id)
- Added new database table for external calendars (fs_external_calendars)
- Added cron job for recurring expense generation (daily)
- Added cron job for external calendar sync (hourly)
- Database version updated to 3.1.0

---

## Version 3.0.0

### Added
- **Invoice Project Modal** - New modal interface when creating invoices from projects
  - Shows summary of project services with totals
  - Option to include unbilled tracked time
  - Clear breakdown of what will be invoiced
  - Prevents double-billing when including tracked time with hourly services
- **Project Name on Invoices** - Project name now prominently displayed on all invoice views
  - Public invoice: Project name as main title, "Invoice" as subtitle
  - Admin preview: Project name in meta table
  - Invoice emails: Project name in details box and email subject line
- **SEO Protection for All Public Pages** - Added `noindex, nofollow` meta tags to all client-facing pages
  - Client portal (dashboard and project views)
  - Booking pages (all steps)
  - Public forms
  - Invoices, proposals, contracts already had protection

### Changed
- **Simplified Hourly Rate Architecture** - Removed redundant "Default Hourly Rate" field from projects
  - Hourly rate now derived from project's hourly services
  - Single source of truth for billing rates
  - One less field during project creation
  - Database migration automatically removes deprecated column
- **Invoice Creation from Projects** - Completely redesigned workflow
  - Services now pulled as individual line items (not generic single item)
  - Tracked time grouped by activity type when included
  - Time entries automatically marked as invoiced
- **Invoice Email Subject** - Now includes project name: "Invoice #X for Project Name from Business"

### Fixed
- Invoice line items not displaying on public invoice view (field name mismatch)
- Time tracking calculations now correctly use project service rates
- Quick Add form pre-fills correct hourly rate from project services
- Timer widget applies correct rate when starting timer on project

### Security
- All public-facing pages now include `noindex, nofollow` meta tags
- Token-based access verified for all client portal and document views
- Links are only accessible to those with the direct URL

---

## Version 2.14.0

### Changed
- **Simplified Hourly Rate Management** - Removed redundant "Default Hourly Rate" field from projects
  - Hourly rate for time tracking is now derived from the project's hourly services
  - Eliminates confusion between project rate and service rate
  - One less field to fill out when creating projects
  - Rate priority: Project's hourly service rate → Time entry stored rate → System default rate

### Removed
- **Project Default Hourly Rate Field** - No longer needed as services define the rates
  - Database migration removes the `hourly_rate` column from projects table
  - All rate lookups now query project services for hourly rate

### Fixed
- Time tracking stats (Billable Amount, Uninvoiced) now correctly use the project's service rate
- Quick Add form correctly pre-fills rate based on project's hourly service
- Timer widget correctly applies project service rate when starting timer
- Invoice creation from project uses correct hourly rate from services

---

## Version 2.13.11 <!-- @hidden -->

### Added
- **Project Services on Creation Form** - Services can now be added directly when creating a new project
- **Invoice Deposit Button** - "Invoice Deposit" button appears on project view when deposit is required but unpaid
- **Automatic Deposit Tracking** - When a deposit invoice is marked as paid, project's deposit_paid status updates automatically
- **Client Portal Deposit Status** - Deposit status now shown in client portal billing section

### Changed
- **Project Form Redesigned** - Simplified and reorganized project creation/edit form
- **Deposit Section in Project View** - Removed manual deposit_paid checkbox from edit mode

### Fixed
- Timer page filters now display horizontally in a row

---

## Version 2.13.9 <!-- @hidden -->

### Added
- **Quick Add Time Entry** - New inline form on Timer page for adding manual time entries without leaving the page

### Changed
- Timer page now displays two side-by-side cards: Timer on left, Quick Add on right
- Cleaner card-based UI with headers for Timer and Quick Add sections

---

## Version 2.13.8 <!-- @hidden -->

### Changed
- **Client Dropdowns** - All client selection dropdowns now show company name instead of contact name

---

## Version 2.13.7 <!-- @hidden -->

### Changed
- **Email Signoffs** - Different signoff based on recipient (internal vs external emails)

---

## Version 2.13.6 <!-- @hidden -->

### Changed
- **Invoice Status Updates** - All invoice status changes now logged to activity feed

---

## Version 2.13.5 <!-- @hidden -->

### Added
- **Booking Reminder Emails** - Automatic reminder emails sent before scheduled meetings

---

## Version 2.13.4 <!-- @hidden -->

### Fixed
- **Booking Time Display** - Meeting times now correctly display in 12-hour format with AM/PM

---

## Version 2.13.3 <!-- @hidden -->

### Added
- **Meeting Booking System** - Complete client scheduling solution

---

## Version 2.13.2 <!-- @hidden -->

### Added
- **Forms System** - Create custom intake forms and questionnaires

---

## Version 2.13.1 <!-- @hidden -->

### Fixed
- **Contract Section Modal** - Fixed modal not opening when clicking "Add Section"

---

## Version 2.13.0

### Added
- **Contract Sections Library** - Reusable legal sections for contracts

---

## Version 2.12.0

### Added
- **Recurring Invoices** - Automatically generate invoices on a schedule

---

## Version 2.11.0

### Added
- **Payment Reminders** - Automated email reminders for unpaid invoices

---

## Version 2.10.0

### Added
- **Project Templates** - Save and reuse project configurations

---

## Version 2.9.0

### Added
- **Time Tracking Reports** - Detailed time analysis

---

## Version 2.8.0

### Added
- **Task Management** - Track project tasks and to-dos

---

## Version 2.7.0

### Added
- **Milestones** - Track project progress with milestones

---

## Version 2.6.0

### Added
- **Google Calendar Integration** - Sync deadlines and events

---

## Version 2.5.0

### Added
- **Email Templates** - Customizable email templates

---

## Version 2.4.0

### Added
- **Annual Tax Deductions** - Track yearly deductible expenses

---

## Version 2.3.0

### Added
- **Client Portal** - Secure area for clients to view their information

---

## Version 2.2.0

### Added
- **Proposals** - Create and send professional proposals

---

## Version 2.1.0

### Added
- **Contracts** - Digital contract management

---

## Version 2.0.0

### Added
- **Time Tracking** - Track billable hours

---

## Version 1.0.0

### Added
- Initial release
- Client management with multiple contacts per client
- Project management with hourly, fixed-price, and retainer billing types
- Invoice generation with line items, discounts, and tax calculations
- Expense tracking with categories and receipt uploads
- Dashboard with YTD revenue, expenses, and tax summary
- Reporting with monthly income/expense breakdown and tax estimates
- Settings page for business info, branding, and tax configuration
- PDF invoice generation
- Tax calculator with federal and state tax rate support
