FinSpark: Calculator Suite
Technical Documentation
Version documented: 2.0.0

======================================================================
1. Purpose of this document
======================================================================

This file explains how the plugin is built today, how the PHP, JS, CSS,
shortcodes, settings, and Elementor integration work together, and where to
change things when the plugin evolves.

Important scope note:
- This is a line-following technical guide, not a marketing manual.
- It explains the code in the order the plugin is written.
- "Every single line" is handled by documenting every block, method, and
  repeated pattern with line ranges, so you can track changes without reading
  the whole codebase from zero every time.
- Line numbers below refer to the current version of the files at the time this
  documentation was written. If you edit the plugin later, line numbers can
  move.

======================================================================
2. Plugin architecture at a glance
======================================================================

The plugin has 4 main responsibilities:

1. Register itself with WordPress.
2. Output calculator HTML through shortcodes and Elementor widgets.
3. Load CSS and JavaScript assets.
4. Let JavaScript perform all calculator math and UI updates in the browser.

There is no custom database table.
There is no AJAX endpoint.
There is no REST API endpoint.
There is no server-side calculator math.

The only persistent data saved by the plugin is the settings option:
- Option name: fc_settings

======================================================================
3. File map
======================================================================

Main files:

- finance-calculators.php
  Main plugin bootstrap, admin settings, shortcode registration, Elementor
  registration, and shortcode markup rendering.

- assets/js/finance-calculators.js
  Frontend calculator engine. Reads form values, performs formulas, builds
  charts/tables, exports CSV, and supports Elementor dynamic rendering.

- assets/css/finance-calculators.css
  Shared visual style for all calculators.

- includes/class-finsparkcs-elementor-calculator-widget.php
  Generic Elementor widget wrapper. One widget instance is registered per
  calculator definition.

- readme.txt
  WordPress.org style plugin readme.

- documentation.txt
  This technical guide.

- assets/js/vendor/chart.umd.min.js
  Bundled local Chart.js 4.5.1 runtime used by the frontend calculators.

- assets/js/vendor/chart.js
  Bundled human-readable Chart.js module reference copy kept with the package
  for review.

- assets/js/vendor/LICENSE-chartjs.txt
  Chart.js MIT license and attribution text shipped with the bundled vendor
  files.

- includes/index.php and assets/js/vendor/index.php
  Silence-is-golden guard files.

======================================================================
4. Runtime flow
======================================================================

Normal WordPress page flow:

1. WordPress loads finance-calculators.php.
2. The plugin creates a singleton instance with finsparkcs_get_plugin_instance().
3. The constructor registers hooks.
4. On frontend page loads, CSS and JS are enqueued.
5. Shortcodes or Elementor widgets render HTML containers.
6. The JS file binds submit handlers to those containers.
7. When a calculator form is submitted, JS computes results in the browser.
8. Results, charts, tables, and CSV buttons are injected into the calculator UI.

Elementor flow:

1. Elementor loads the plugin page/editor.
2. The plugin registers a custom Elementor category called finspark.
3. The plugin registers one widget per calculator definition.
4. The widget renders the exact same calculator markup as the shortcode.
5. JS listens for Elementor's frontend/element_ready/global hook and initializes
   newly rendered calculator blocks.

======================================================================
5. finance-calculators.php walkthrough
======================================================================

----------------------------------------------------------------------
5.1 Plugin header and bootstrap guards
----------------------------------------------------------------------

Lines 4-12:
- Standard WordPress plugin header.
- Visible plugin name is now "FinSpark: Calculator Suite".
- Text domain is finspark-calculator-suite so it matches the submission slug.

Lines 15-17:
- ABSPATH guard.
- Prevents direct file execution outside WordPress.

Lines 19-26:
- Defines global constants only once:
  - FC_PLUGIN_FILE: absolute path to this main plugin file.
  - FC_PLUGIN_DIR: plugin directory path.
  - FC_PLUGIN_URL: plugin base URL.
  - FC_PLUGIN_VERSION: current version string.
  - FC_BRAND_NAME: short visible brand, "FinSpark".
  - FC_PLUGIN_LABEL: full visible plugin label.

Why this matters:
- Paths and labels are centralized.
- Reduces repeated hardcoded values.

----------------------------------------------------------------------
5.2 Main class: FinSparkCS_Calculators
----------------------------------------------------------------------

Line 28:
- Declares the main plugin class.

Line 30:
- Stores the WordPress option name in $option_name.
- Current option key is fc_settings.

----------------------------------------------------------------------
5.3 Constructor and hooks
----------------------------------------------------------------------

Lines 32-41:
- The constructor connects plugin methods to WordPress and Elementor hooks.

Hook list:
- register_activation_hook(...)
  Calls on_activate() once when the plugin is activated.

- add_action('init', load_textdomain)
  Loads translation files.

- add_action('admin_menu', admin_menu)
  Adds the settings screen under Settings.

- add_action('admin_init', register_settings)
  Registers the option and fields.

- add_action('wp_enqueue_scripts', enqueue_assets)
  Loads frontend CSS and JS.

- add_action('init', register_shortcodes)
  Registers all shortcode tags.

- add_action('elementor/elements/categories_registered', register_elementor_category)
  Creates a FinSpark category in Elementor.

- add_action('elementor/widgets/register', register_elementor_widgets)
  Registers all calculator widgets.

Design note:
- The plugin keeps most integration points in one constructor, which makes the
  startup sequence easy to inspect.

----------------------------------------------------------------------
5.4 Internationalization
----------------------------------------------------------------------

Lines 43-45:
- load_textdomain() calls load_plugin_textdomain().
- It tells WordPress where translation files would live.
- The plugin currently uses the finspark-calculator-suite text domain.

----------------------------------------------------------------------
5.5 Activation behavior
----------------------------------------------------------------------

Lines 47-54:
- on_activate() makes sure these folders exist:
  - assets/css/
  - assets/js/
  - assets/js/vendor/
- If the settings option does not exist yet, it writes defaults.

Why this matters:
- Activation is lightweight.
- No custom database table is created.
- No remote download is attempted.

----------------------------------------------------------------------
5.6 Default settings
----------------------------------------------------------------------

Lines 56-77:
- default_settings() returns the initial settings array.

Structure:
- enabled
  Map of calculator keys to 1/0.

- currency_symbol
  Default "$".

- currency_position
  before or after.

- decimals
  Default 2.

- locale
  Defaults to get_locale().

- chart_theme
  light or dark.

These values are later sent into JS through wp_localize_script().

----------------------------------------------------------------------
5.7 Local Chart.js helpers
----------------------------------------------------------------------

Lines 79-89:
- get_chartjs_path()
  Returns the local filesystem path for the bundled Chart.js runtime.

- get_chartjs_url()
  Returns the public URL for that same file.

- has_local_chart_library()
  Returns true if the expected bundled chart file exists in the installation.

Important review detail:
- The plugin no longer loads Chart.js from a CDN.
- The packaged build is expected to include the local vendor file.
- This is safer for WordPress.org review.

----------------------------------------------------------------------
5.8 Central calculator registry
----------------------------------------------------------------------

Lines 91-171:
- get_calculator_definitions() is the central registry for all calculators.
- Each calculator is described once.

Each definition contains:
- shortcode
  The shortcode tag users place in posts/pages.

- method
  The PHP render method that outputs the calculator markup.

- label
  Human label used in the admin settings checkbox list.

- widget_title
  Human label used in Elementor.

- keywords
  Elementor keyword search helpers.

Current calculator keys:
- investment
- position_size
- loan
- compound
- savings
- sip
- debt
- rentbuy
- inflation
- car
- creditcard

Why this function is important:
- Shortcode registration is generated from it.
- Elementor widget registration is generated from it.
- The settings page checkbox list is generated from it.
- This is the first place to edit when adding or renaming a calculator.

----------------------------------------------------------------------
5.9 Generic calculator renderer
----------------------------------------------------------------------

Lines 173-187:
- render_calculator($key, $atts) receives a calculator key.
- It looks up the correct PHP method from get_calculator_definitions().
- It verifies that the method exists.
- Then it calls that method and returns the HTML.

Why this exists:
- Elementor widgets reuse shortcode render methods instead of duplicating markup.

----------------------------------------------------------------------
5.10 Frontend asset loading
----------------------------------------------------------------------

Lines 189-228:
- enqueue_assets() loads CSS and JS for the frontend.

CSS behavior:
- Lines 193-197
  If assets/css/finance-calculators.css exists, register and enqueue it.
- Lines 198-201
  If the CSS file is missing, register an inline fallback stylesheet so the UI
  does not become fully unstyled.

Chart.js behavior:
- Lines 204-214
  Register the `finsparkcs-chartjs` handle first.
  If the bundled local file exists, the handle points to
  assets/js/vendor/chart.umd.min.js.
  If the file is unexpectedly missing from an installation, the handle is
  still registered as a no-op alias so the main calculator script can load and
  JavaScript can fall back to non-chart results.

Main JS behavior:
- Lines 211-226
  Register and enqueue assets/js/finance-calculators.js.
  Localize the settings into window.finsparkcsSettings.

Localized settings sent to JS:
- currency_symbol
- currency_position
- decimals
- locale
- chart_theme
- export_csv_label

Security details:
- Currency symbol and locale are sanitized.
- Currency position and chart theme are allowlisted.
- Decimals are cast to int.

----------------------------------------------------------------------
5.11 Admin menu
----------------------------------------------------------------------

Lines 230-232:
- admin_menu() adds a settings page under Settings.
- Visible menu label is FC_BRAND_NAME, so the menu appears as "FinSpark".
- The page title uses FC_PLUGIN_LABEL.

----------------------------------------------------------------------
5.12 Settings registration
----------------------------------------------------------------------

Lines 234-245:
- register_settings() registers one option:
  - fc_settings
- It then declares one settings section and six fields:
  - enabled calculators
  - currency symbol
  - currency position
  - decimals
  - locale
  - chart theme

----------------------------------------------------------------------
5.13 Settings sanitization
----------------------------------------------------------------------

Lines 247-271:
- sanitize_settings($input) is the only server-side validator for saved settings.

What it does:
- Starts from defaults.
- Rebuilds the enabled calculator map using only known keys.
- Sanitizes currency_symbol.
- Restricts currency_position to before/after.
- Restricts decimals to 0..6.
- Sanitizes locale.
- Restricts chart_theme to light/dark.

Why this matters:
- Even if the settings form is tampered with, unknown calculator keys and invalid
  values are discarded.

----------------------------------------------------------------------
5.14 Field render methods
----------------------------------------------------------------------

Lines 274-287:
- field_enabled()
- Renders one checkbox per calculator definition.

Lines 289-296:
- field_currency_symbol()
- Renders a text input for the currency symbol.

Lines 298-307:
- field_currency_position()
- Renders a select field with before/after.

Lines 309-316:
- field_decimals()
- Renders the numeric decimals field.

Lines 318-325:
- field_locale()
- Renders a locale input and helper description.

Lines 327-336:
- field_chart_theme()
- Renders the light/dark theme select.

----------------------------------------------------------------------
5.15 Settings page UI
----------------------------------------------------------------------

Lines 338-381:
- settings_page() outputs the admin page HTML.

Important details:
- Lines 339-341
  Capability guard. Only manage_options users can access it.

- Lines 344-350
  Standard WordPress settings form using settings_fields(),
  do_settings_sections(), and submit_button().

- Lines 353-360
  Asset checklist for the required CSS and JS files, the bundled Chart.js
  runtime, the readable source copy, and the vendor license file.

- Lines 361-378
  If the Chart.js runtime is unexpectedly missing from an installation, the
  page tells the user to reinstall the packaged plugin instead of adding files
  manually.

Why this matters:
- The admin stays accurate for the packaged build and does not ask site owners
  to add files manually after installation.

----------------------------------------------------------------------
5.16 Shortcode registration
----------------------------------------------------------------------

Lines 383-387:
- register_shortcodes() loops through get_calculator_definitions().
- For each definition, it binds the shortcode tag to the render method.

Effect:
- Adding a calculator to the definitions map and creating its render method is
  enough to make shortcode registration automatic.

----------------------------------------------------------------------
5.17 Elementor category registration
----------------------------------------------------------------------

Lines 389-401:
- register_elementor_category($elements_manager) safely adds a category:
  - slug: finspark
  - title: FinSpark
  - icon: fa fa-plug

Safety:
- It checks the manager class exists.
- It checks add_category() exists.

----------------------------------------------------------------------
5.18 Elementor widget registration
----------------------------------------------------------------------

Lines 403-426:
- register_elementor_widgets($widgets_manager) loads and registers one widget per
  calculator definition.

Behavior:
- Returns early if Elementor is not loaded.
- Requires includes/class-finsparkcs-elementor-calculator-widget.php only when needed.
- Creates a widget instance for each calculator.
- Supports both register() and register_widget_type() for broader Elementor
  version compatibility.

Why this matters:
- The plugin does not hard-fail if Elementor is inactive.

----------------------------------------------------------------------
5.19 Calculator enabled-state helper
----------------------------------------------------------------------

Lines 428-431:
- is_enabled($key) reads the saved settings and returns whether the calculator is
  enabled.

----------------------------------------------------------------------
5.20 Shortcode render methods
----------------------------------------------------------------------

General pattern used by all shortcode methods:

1. Check whether that calculator is enabled.
2. If disabled:
   - show an admin-only notice for users with manage_options
   - return an empty string for public visitors
3. Generate a unique DOM id with uniqid().
4. Start output buffering with ob_start().
5. Echo the calculator HTML.
6. Return the buffered markup.

Important design detail:
- PHP only renders fields and placeholders.
- All formulas run in JavaScript, not PHP.

Calculator method ranges:

Lines 435-465:
- sc_investment()
- Fields: initial, monthly, rate, years
- Sets data-autorun="1", so JS auto-calculates on load.

Lines 467-500:
- sc_position_size()
- Fields: account, risk_pct, risk_amount, entry, stop

Lines 502-532:
- sc_loan()
- Fields: principal, rate, years, payments_per_year

Lines 534-570:
- sc_compound()
- Fields: principal, rate, freq, years
- Includes compounding-frequency select.
- Sets data-autorun="1".

Lines 572-602:
- sc_savings()
- Fields: target, years, rate, current
- Sets data-autorun="1".

Lines 604-631:
- sc_sip()
- Fields: monthly, rate, years
- Sets data-autorun="1".

Lines 633-665:
- sc_debt()
- Fields: monthly_budget, method
- Includes empty .fc-debt-list container for dynamic debt rows.
- Includes "Add Debt" button.

Lines 667-718:
- sc_rentbuy()
- Fields:
  - home_price
  - down_payment_pct
  - mortgage_rate
  - mortgage_term
  - holding_years
  - rent_month
  - appreciation
  - property_tax_rate
  - maintenance_rate
  - selling_cost_pct
  - rent_inflation

Lines 720-748:
- sc_inflation()
- Fields: amount, from_year, to_year, inflation_rate

Lines 750-786:
- sc_car()
- Fields: price, down, rate, term_years, term, residual

Lines 788-816:
- sc_creditcard()
- Fields: balance, rate, payment

Implementation rule:
- If you rename a field name in these render methods, you must also update the
  matching selector in assets/js/finance-calculators.js.

----------------------------------------------------------------------
5.21 Singleton bootstrap and plugin action link
----------------------------------------------------------------------

Lines 818-828:
- finsparkcs_get_plugin_instance() creates one shared plugin instance and returns it.
- This avoids multiple instantiations.

Line 828:
- The singleton is created immediately.

Lines 833-838:
- Adds a Settings link on the Installed Plugins screen.
- The link points to options-general.php?page=finsparkcs_settings.

======================================================================
6. Elementor widget class walkthrough
File: includes/class-finsparkcs-elementor-calculator-widget.php
======================================================================

Lines 1-13:
- File header and ABSPATH guard.
- Returns early if Elementor's Widget_Base is unavailable.

Line 16:
- Declares FinSparkCS_Elementor_Calculator_Widget.

Lines 23-34:
- The constructor stores the calculator definition passed from the main plugin
  file and then calls the parent Elementor constructor.

Lines 37-41:
- get_name()
- Returns a stable widget slug like finspark-investment.

Lines 43-45:
- get_title()
- Returns the widget title from the calculator definition.

Lines 47-49:
- get_icon()
- Uses Elementor's calculator icon.

Lines 51-53:
- get_categories()
- Places the widget under the finspark category.

Lines 55-57:
- get_keywords()
- Returns keyword search terms for the Elementor panel.

Lines 59-65:
- get_style_depends() and get_script_depends()
- Tell Elementor to enqueue the same CSS and JS handles used by shortcodes.

Lines 67-95:
- register_controls()
- Adds an informational control showing which shortcode the widget corresponds to.
- This does not expose calculator settings yet; it only documents the shortcode.

Lines 97-108:
- render()
- Retrieves the plugin singleton.
- Calls render_calculator($key).
- Echoes the same HTML used by the shortcode renderer.

Design advantage:
- One source of truth for calculator markup.
- Shortcodes and Elementor widgets stay synchronized.

======================================================================
7. JavaScript engine walkthrough
File: assets/js/finance-calculators.js
======================================================================

The JS file is the real calculator engine.
PHP outputs forms.
JS reads inputs and produces results.

----------------------------------------------------------------------
7.1 Boot data and globals
----------------------------------------------------------------------

Lines 5-16:
- Wraps everything in an IIFE.
- Enables strict mode.
- Reads window.finsparkcsSettings from PHP localization.
- Provides fallback defaults if localization is missing.

Lines 18-19:
- fcCharts stores active Chart.js instances by canvas id.
- elementorHookBound prevents duplicate Elementor hook registration.

----------------------------------------------------------------------
7.2 Core helper functions
----------------------------------------------------------------------

Lines 22-24:
- getCssVar(name, fallback)
- Reads CSS variables from :root.

Line 27:
- applyTheme()
- Writes data-fc-theme on the html element.

Lines 29-42:
- moneyFormat(val)
- Formats numbers using Intl.NumberFormat and the plugin currency settings.

Lines 44-49:
- parseNum(v)
- Removes commas, trims, parses numeric input safely.

Lines 51-53:
- destroyChartById(id)
- Destroys an existing Chart.js instance before redrawing.

Lines 55-58:
- toggleChartWrap(canvas, isVisible)
- Hides chart wrappers when charts are unavailable.

Lines 60-89:
- chartOptions()
- Builds shared Chart.js options from CSS theme colors.

Lines 91-103:
- createLineChart(...)
- Draws a line chart if Chart.js exists.
- Hides the chart area if Chart.js is not available.

Lines 105-116:
- createPieChart(...)
- Same idea, but for pie charts.

Lines 118-125:
- hexToRgba(hex, alpha)
- Converts theme colors for chart fills.

Lines 127-159:
- ensureResultNodes(container)
- Ensures each calculator has:
  - .fc-results
  - .fc-summary
  - .fc-chart-wrap
  - .fc-export-wrap
  - canvas with a stable id

This function is central because every calculator handler depends on it.

----------------------------------------------------------------------
7.3 CSV export layer
----------------------------------------------------------------------

Lines 162-203:
- downloadCSVFromSchedule(schedule, filename)
- Converts schedule arrays into CSV text.
- Includes nested helper csvEscape(cell).

Lines 208-223:
- createExportButton(nodes, schedule, filename)
- Adds an Export CSV button only when schedule data exists.

Note:
- CSV export is only meaningful for calculators that generate schedules or
  amortization tables.

----------------------------------------------------------------------
7.4 Calculator handlers
----------------------------------------------------------------------

Each handler:
- receives a calculator container
- reads input values by name
- computes formulas
- writes summary HTML
- optionally creates a chart
- optionally builds a table
- optionally adds an Export CSV button

Lines 231-277:
- handleInvestment(container)
- Future value of lump sum + monthly contributions.
- Outputs FV, total invested, ROI, CAGR, and a line chart.

Lines 280-299:
- handlePositionSize(container)
- Trading risk sizing.
- Outputs risk amount, risk/share, share count, total position value.
- No chart, no CSV.

Lines 302-349:
- handleLoan(container)
- Standard amortized loan.
- Builds amortization schedule.
- Outputs payment, total interest, total paid.
- Builds table, pie chart, and CSV export.

Lines 352-377:
- First handleCompound(container) definition.
- Compound interest chart and summary.

Lines 380-384:
- denomToFVFactor(mRate, n)
- Helper for savings-goal math.

Lines 385-436:
- handleSavingsGoal(container)
- Calculates required monthly savings to hit a target.
- Outputs required monthly amount, total invested, estimated FV, interest.
- Draws two series: contributed vs value.

Lines 439-476:
- handleSIP(container)
- Calculates SIP future value.
- Outputs FV, total invested, profit.
- Draws invested vs portfolio value.

Lines 479-532:
- simulateDebtPayoff(debts, budget, method)
- Core debt repayment algorithm.
- Supports:
  - snowball
  - avalanche
- Returns a schedule summary object used by the UI renderer.

Lines 534-561:
- handleDebtPayoff(container)
- Reads all debt rows.
- Calls simulateDebtPayoff().
- Draws a balance-over-time chart.
- Builds a table.
- Adds CSV export.

Lines 564-568:
- remainingMortgageBalance(...)
- Helper used by rent-vs-buy calculations.

Lines 570-622:
- handleRentVsBuy(container)
- Compares buy net cost vs rent cost across holding years.
- Draws two lines: buy cost and rent cost.

Lines 625-646:
- Second handleCompound(container) definition.
- This overrides the earlier definition because JS uses the latest function with
  the same name.
- Behavior is effectively the same.
- This duplication does not currently change output, but it is worth knowing when
  maintaining the file.

Lines 649-684:
- handleInflation(container)
- Adjusts an amount forward or backward in time using a constant inflation rate.

Lines 687-743:
- handleCarLoan(container)
- Handles car financing with an optional residual/balloon value.
- Builds schedule, pie chart, and CSV export.

Lines 746-794:
- handleCreditCard(container)
- Simulates payoff by month for a fixed monthly payment.
- Builds line chart, amortization table, and CSV export.

Lines 797-820:
- addDebtRow(container, name, balance, rate, minPayment)
- Dynamically adds one debt row to the debt calculator UI.

----------------------------------------------------------------------
7.5 Form submission and initialization
----------------------------------------------------------------------

Lines 822-834:
- handleCalculatorSubmit(container)
- Router that chooses the right calculator handler based on CSS class names.

Lines 836-841:
- submitCalculator(container)
- Dispatches a submit event, with fallback direct execution.

Lines 843-864:
- getCalculatorContainers(scope)
- Returns calculator containers inside a given DOM scope.
- Supports both full-document scans and scoped Elementor rendering.

Lines 866-902:
- initCalculator(container)
- Prevents double-binding with data-fc-bound.
- Attaches form submit listeners.
- Initializes default debt rows if needed.
- Supports autorun calculators.

Lines 904-909:
- initCalculators(scope)
- Applies theme and initializes all containers in scope.

Lines 911-916:
- reinitCalculators(scope)
- Re-initializes and re-submits calculators in scope.

----------------------------------------------------------------------
7.6 Elementor support in JS
----------------------------------------------------------------------

Lines 918-924:
- bindElementorHooks()
- Hooks into Elementor's frontend/element_ready/global event.
- Initializes calculator markup inserted dynamically by Elementor.

Lines 926-933:
- onReady()
- Initializes existing calculators on page load.
- Binds Elementor hooks immediately or on elementor/frontend/init.

Lines 936-940:
- Standard DOM ready guard.

Lines 943-947:
- Public API exposed as window.finsparkcsCalculators:
  - setTheme(theme)
  - init(scope)
  - reinit(scope)

Why this API matters:
- External code or future integrations can force calculator refreshes.

======================================================================
8. CSS walkthrough
File: assets/css/finance-calculators.css
======================================================================

Top comment:
- Documents where the CSS file belongs inside the plugin folder.

Theme variables:
- :root defines light theme colors and sizing variables.
- html[data-fc-theme="dark"] overrides those variables for dark mode.

Main component rules:
- .fc-calculator
  Card wrapper style.

- .fc-calculator h3
  Calculator heading style.

- .fc-form, .fc-grid, input/select rules
  Form layout and field appearance.

- .fc-submit
  Shared primary action button.

- .fc-results, .fc-table, .fc-chart
  Shared result layout, table layout, and chart size.

- .fc-debt-list, .fc-debt-row, .fc-add-debt, .fc-debt-remove
  Debt calculator-specific UI styling.

- .fc-export-wrap and button.fc-export-csv
  CSV export button layout.

- @media (max-width:640px)
  Switches the grid to one column on small screens.

Maintenance note:
- Debt-related selectors appear twice near the bottom of the file.
- The later declarations are effectively duplicates, so behavior stays the same.
- If you refactor CSS later, that duplication can be cleaned safely.

======================================================================
9. Current calculator inventory
======================================================================

Key: investment
- Shortcode: [finsparkcs_investment]
- PHP renderer: sc_investment()
- JS handler: handleInvestment()
- Chart: yes
- CSV: no

Key: position_size
- Shortcode: [finsparkcs_position_size]
- PHP renderer: sc_position_size()
- JS handler: handlePositionSize()
- Chart: no
- CSV: no

Key: loan
- Shortcode: [finsparkcs_loan]
- PHP renderer: sc_loan()
- JS handler: handleLoan()
- Chart: yes
- CSV: yes

Key: compound
- Shortcode: [finsparkcs_compound]
- PHP renderer: sc_compound()
- JS handler: handleCompound()
- Chart: yes
- CSV: no

Key: savings
- Shortcode: [finsparkcs_savings_goal]
- PHP renderer: sc_savings()
- JS handler: handleSavingsGoal()
- Chart: yes
- CSV: no

Key: sip
- Shortcode: [finsparkcs_sip]
- PHP renderer: sc_sip()
- JS handler: handleSIP()
- Chart: yes
- CSV: no

Key: debt
- Shortcode: [finsparkcs_debt_payoff]
- PHP renderer: sc_debt()
- JS handler: handleDebtPayoff()
- Chart: yes
- CSV: yes

Key: rentbuy
- Shortcode: [finsparkcs_rent_vs_buy]
- PHP renderer: sc_rentbuy()
- JS handler: handleRentVsBuy()
- Chart: yes
- CSV: no

Key: inflation
- Shortcode: [finsparkcs_inflation]
- PHP renderer: sc_inflation()
- JS handler: handleInflation()
- Chart: yes
- CSV: no

Key: car
- Shortcode: [finsparkcs_car_loan]
- PHP renderer: sc_car()
- JS handler: handleCarLoan()
- Chart: yes
- CSV: yes

Key: creditcard
- Shortcode: [finsparkcs_credit_card]
- PHP renderer: sc_creditcard()
- JS handler: handleCreditCard()
- Chart: yes
- CSV: yes

======================================================================
10. Security, validation, and review-sensitive details
======================================================================

Current good practices in the code:

- Direct access is blocked with ABSPATH guards.
- Settings values are sanitized before saving.
- Allowed values are restricted for settings like theme and currency position.
- Output labels in PHP are escaped with esc_html__, esc_attr, esc_url, or wp_kses
  where needed.
- Elementor integration is optional and guarded by class/method existence checks.
- Chart.js is bundled locally with a vendor license file; no CDN dependency is loaded.

Important boundary to remember:
- Calculator math happens in JavaScript, so users can modify frontend values in
  the browser. This is fine because these calculators are informational tools,
  not payment or transaction processors.

Maintenance caution:
- Several result summaries and tables are assembled with innerHTML in JS.
- Right now the values inserted into those strings are produced by the plugin's
  own numeric calculations, which lowers risk.
- If future changes insert raw user text into those HTML strings, escape or
  sanitize it before injecting it.

======================================================================
11. How to change the plugin safely
======================================================================

If you rename the plugin:
- Update the visible labels in finance-calculators.php.
- Update readme.txt.
- Update documentation.txt.
- Keep the text domain and option key stable unless you intentionally want a
  breaking change.

If you add a new calculator:
1. Add a new entry to get_calculator_definitions().
2. Add a new shortcode render method in finance-calculators.php.
3. Add a new JS handler in assets/js/finance-calculators.js.
4. Add a new routing condition inside handleCalculatorSubmit().
5. If needed, add styles in finance-calculators.css.
6. Update readme.txt and documentation.txt.

If you rename a field:
- Update the input name in PHP markup.
- Update every JS selector that reads that field.
- Test shortcode rendering and Elementor rendering.

If you change chart behavior:
- Update assets/js/finance-calculators.js.
- Keep the local Chart.js-only rule if the plugin is intended for WordPress.org.

If you change the Elementor widgets:
- Most widget metadata comes from get_calculator_definitions().
- The widget class itself is generic, so many changes can be done without adding
  new widget classes.

======================================================================
12. Best places to inspect when debugging
======================================================================

If the calculator does not appear at all:
- Check register_shortcodes() or Elementor widget registration.
- Check whether the calculator is disabled in settings.

If the calculator appears but does not calculate:
- Check assets/js/finance-calculators.js was enqueued.
- Check the form field names match the JS selectors.
- Check the container class matches the router in handleCalculatorSubmit().

If charts do not appear:
- Check whether assets/js/vendor/chart.umd.min.js exists in the installed plugin.
- Check the browser console for the FinSpark Chart.js fallback warning.
- Check the canvas is present inside .fc-chart-wrap.
- Check createLineChart(), createPieChart(), or createChart() is being called.

If Elementor widget appears but stays inactive:
- Check bindElementorHooks().
- Check Elementor actually fires frontend/element_ready/global.
- Check get_script_depends() returns fc-main and the handle is registered.

If settings do not save:
- Check sanitize_settings().
- Check the option name remains fc_settings.

======================================================================
13. Recommended future cleanup items
======================================================================

The plugin works, but these are sensible cleanup targets later:

- Remove the duplicate handleCompound() definition in the JS file.
- Reduce repeated CSS blocks for debt UI selectors.
- Consider splitting the large JS file by calculator or feature.
- Consider splitting each shortcode renderer into smaller reusable template
  helpers if the PHP file keeps growing.

======================================================================
14. Final mental model
======================================================================

Think of the plugin like this:

- finance-calculators.php
  WordPress integration + calculator HTML output

- class-finsparkcs-elementor-calculator-widget.php
  Elementor wrapper around the same calculator HTML

- finance-calculators.js
  Calculator brain

- finance-calculators.css
  Calculator skin

When you follow future changes, start here:

1. Did the calculator registry change?
2. Did the PHP markup fields change?
3. Did the JS selectors and formulas change?
4. Did the CSS or Elementor hooks change?

If those four pieces still agree with each other, the plugin will usually stay
healthy.
