{"id":778,"date":"2026-06-24T11:28:28","date_gmt":"2026-06-24T11:28:28","guid":{"rendered":"https:\/\/silcom.io\/?page_id=778"},"modified":"2026-06-24T11:28:28","modified_gmt":"2026-06-24T11:28:28","slug":"insight-studio","status":"publish","type":"page","link":"https:\/\/silcom.io\/insight-studio\/","title":{"rendered":"Insight Studio"},"content":{"rendered":"\t<section class=\"silcom-bi-tool\" data-silcom-bi-tool>\n\t\t<div class=\"silcom-bi-tool__inner\">\n\t\t\t<nav class=\"silcom-bi-tool__breadcrumb\" aria-label=\"Breadcrumb\">\n\t\t\t\t<a href=\"https:\/\/silcom.io\/\">Home<\/a><span aria-hidden=\"true\">\/<\/span><span>Insight Studio<\/span>\n\t\t\t<\/nav>\n\t\t\t<div class=\"silcom-bi-tool__header\">\n\t\t\t\t<p>Silcom Tools<\/p>\n\t\t\t\t<h1>Insight Studio<\/h1>\n\t\t\t\t<small class=\"silcom-bi-mobile-label\">Mobile summary<\/small>\n\t\t\t\t<span>A guided data visualisation space for turning raw numbers into plain-English snapshots, charts and useful context.<\/span>\n\t\t\t<\/div>\n\t\t\t<section class=\"silcom-bi-panel\">\n\t\t\t\t<div class=\"silcom-bi-panel__top\">\n\t\t\t\t\t<label>\n\t\t\t\t\t\t<span>What do you want to analyse?<\/span>\n\t\t\t\t\t\t<select data-bi-mode>\n\t\t\t\t\t\t\t<option value=\"stocks\">Stocks \/ shares<\/option>\n\t\t\t\t\t\t\t<option value=\"profit\">Profit analysis<\/option>\n\t\t\t\t\t\t<\/select>\n\t\t\t\t\t<\/label>\n\t\t\t\t\t<p data-bi-mode-note>Start with a company or stock idea. More analysis categories can be added here later.<\/p>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"silcom-bi-stock\" data-bi-stock-panel>\n\t\t\t\t\t<div data-bi-stock-branch>\n\t\t\t\t\t\t<div class=\"silcom-bi-stock__intro\">\n\t\t\t\t\t\t\t<p>Stock analysis starter<\/p>\n\t\t\t\t\t\t\t<h2>Investment analysis<\/h2>\n\t\t\t\t\t\t\t<span>Search by company name or ticker, choose the correct market listing, then add up to five entry points to estimate DCA and build a plain-English stock snapshot.<\/span>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<div class=\"silcom-investment-tool\" data-investment-tool data-search-url=\"https:\/\/silcom.io\/?rest_route=\/silcom\/v1\/investment-search\" data-chart-url=\"https:\/\/silcom.io\/?rest_route=\/silcom\/v1\/investment-chart\">\n\t\t\t\t\t\t\t<div class=\"silcom-investment-search\">\n\t\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t\t<span>Company name or ticker<\/span>\n\t\t\t\t\t\t\t\t\t<input type=\"search\" placeholder=\"Apple, AAPL, NVIDIA, CBA...\" autocomplete=\"off\" data-investment-query>\n\t\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t\t<button type=\"button\" data-investment-search>Search ticker<\/button>\n\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t<div class=\"silcom-investment-results\" data-investment-results hidden><\/div>\n\t\t\t\t\t\t\t<div class=\"silcom-investment-memory\" data-investment-memory hidden><\/div>\n\t\t\t\t\t\t\t<div class=\"silcom-investment-selected\" data-investment-selected>\n\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t<span>Selected listing<\/span>\n\t\t\t\t\t\t\t\t\t<strong data-investment-name>No ticker selected yet<\/strong>\n\t\t\t\t\t\t\t\t\t<small data-investment-market>Search and choose the correct market listing.<\/small>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t<span>Current price<\/span>\n\t\t\t\t\t\t\t\t\t<strong data-investment-price>-<\/strong>\n\t\t\t\t\t\t\t\t\t<small data-investment-currency>Live quote source loads after selection.<\/small>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t<span>1-year movement<\/span>\n\t\t\t\t\t\t\t\t\t<strong data-investment-move>-<\/strong>\n\t\t\t\t\t\t\t\t\t<small data-investment-range>Trend range appears here.<\/small>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t<div class=\"silcom-investment-positions\">\n\t\t\t\t\t\t\t\t<div class=\"silcom-investment-positions__head\">\n\t\t\t\t\t\t\t\t\t<span>Date entered<\/span><span>Entry price<\/span><span>Quantity<\/span><span>Cost<\/span>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t<div class=\"silcom-investment-position\"><input type=\"date\" data-position-date><input type=\"number\" min=\"0\" step=\"0.01\" placeholder=\"190.00\" data-position-price><input type=\"number\" min=\"0\" step=\"0.0001\" placeholder=\"5\" data-position-qty><strong data-position-cost>$0<\/strong><\/div>\n\t\t\t\t\t\t\t\t<div class=\"silcom-investment-position\"><input type=\"date\" data-position-date><input type=\"number\" min=\"0\" step=\"0.01\" placeholder=\"175.00\" data-position-price><input type=\"number\" min=\"0\" step=\"0.0001\" placeholder=\"3\" data-position-qty><strong data-position-cost>$0<\/strong><\/div>\n\t\t\t\t\t\t\t\t<div class=\"silcom-investment-position\"><input type=\"date\" data-position-date><input type=\"number\" min=\"0\" step=\"0.01\" placeholder=\"210.00\" data-position-price><input type=\"number\" min=\"0\" step=\"0.0001\" placeholder=\"2\" data-position-qty><strong data-position-cost>$0<\/strong><\/div>\n\t\t\t\t\t\t\t\t<div class=\"silcom-investment-position\"><input type=\"date\" data-position-date><input type=\"number\" min=\"0\" step=\"0.01\" placeholder=\"225.00\" data-position-price><input type=\"number\" min=\"0\" step=\"0.0001\" placeholder=\"1\" data-position-qty><strong data-position-cost>$0<\/strong><\/div>\n\t\t\t\t\t\t\t\t<div class=\"silcom-investment-position\"><input type=\"date\" data-position-date><input type=\"number\" min=\"0\" step=\"0.01\" placeholder=\"240.00\" data-position-price><input type=\"number\" min=\"0\" step=\"0.0001\" placeholder=\"1\" data-position-qty><strong data-position-cost>$0<\/strong><\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t<div class=\"silcom-investment-summary\">\n\t\t\t\t\t\t\t\t<article><span>Total invested<\/span><strong data-investment-total>$0<\/strong><\/article>\n\t\t\t\t\t\t\t\t<article><span>Total quantity<\/span><strong data-investment-qty>0<\/strong><\/article>\n\t\t\t\t\t\t\t\t<article><span>DCA average<\/span><strong data-investment-dca>-<\/strong><\/article>\n\t\t\t\t\t\t\t\t<article><span>Unrealised result<\/span><strong data-investment-result>-<\/strong><\/article>\n\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t<div class=\"silcom-investment-modules\">\n\t\t\t\t\t\t\t\t<section class=\"silcom-investment-chart-card\">\n\t\t\t\t\t\t\t\t\t<div><span>Technical trend<\/span><strong data-investment-trend-label>Waiting for ticker<\/strong><\/div>\n\t\t\t\t\t\t\t\t\t<canvas width=\"960\" height=\"320\" data-investment-chart><\/canvas>\n\t\t\t\t\t\t\t\t<\/section>\n\t\t\t\t\t\t\t\t<section class=\"silcom-investment-insights\" data-investment-insights>\n\t\t\t\t\t\t\t\t\t<article><span>Company profile<\/span><p>Search and select a ticker to load company, ticker, market, sector and industry context.<\/p><\/article>\n\t\t\t\t\t\t\t\t\t<article><span>Quote pulse<\/span><p>Current price, day range, market and quote metadata appear after selection.<\/p><\/article>\n\t\t\t\t\t\t\t\t\t<article><span>Share movement<\/span><p>One-year change and position inside the observed range update from live chart data.<\/p><\/article>\n\t\t\t\t\t\t\t\t\t<article><span>Technical trend<\/span><p>Moving-average and momentum-style readouts update from the selected ticker.<\/p><\/article>\n\t\t\t\t\t\t\t\t\t<article><span>Range context<\/span><p>The high-low range card helps show whether price is near the top, middle or lower end.<\/p><\/article>\n\t\t\t\t\t\t\t\t\t<article><span>Volatility \/ drawdown<\/span><p>Daily volatility and worst drawdown are estimated from historical closes.<\/p><\/article>\n\t\t\t\t\t\t\t\t\t<article><span>Volume activity<\/span><p>Latest volume is compared with recent average volume when the source provides it.<\/p><\/article>\n\t\t\t\t\t\t\t\t\t<article><span>Position \/ DCA mirror<\/span><p>Saved position rows on this device feed the DCA and unrealised movement view.<\/p><\/article>\n\t\t\t\t\t\t\t\t\t<article><span>Valuation \/ fair value<\/span><p>P\/E, P\/S, EV\/EBITDA and fair value assumptions can plug into this widget next.<\/p><\/article>\n\t\t\t\t\t\t\t\t\t<article><span>Factor grades<\/span><p>Momentum is available now; value, growth and profitability grades need fundamentals.<\/p><\/article>\n\t\t\t\t\t\t\t\t\t<article><span>Analyst \/ earnings<\/span><p>Targets, EPS surprises, upgrades and earnings dates are reserved for analyst feeds.<\/p><\/article>\n\t\t\t\t\t\t\t\t\t<article><span>Insider \/ alternative signals<\/span><p>Insider trades and alternative datasets can be connected later without a local ticker database.<\/p><\/article>\n\t\t\t\t\t\t\t\t\t<article><span>Peer \/ sector context<\/span><p>Peer comparison and sector ranking can be added through an on-demand screener source.<\/p><\/article>\n\t\t\t\t\t\t\t\t\t<article><span>Plain-English summary<\/span><p>The summary updates after quote, trend, volume and position data loads.<\/p><\/article>\n\t\t\t\t\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t<div class=\"silcom-company\" data-company-insights data-api=\"https:\/\/silcom.io\/\" data-chart-src=\"https:\/\/silcom.io\/wp-content\/themes\/villavista\/assets\/js\/chart.umd-4.4.8.js\">\n\t\t<section class=\"silcom-company-section\" aria-labelledby=\"silcom-financial-heading\">\n\t\t\t<div class=\"silcom-company-heading\"><h3 id=\"silcom-financial-heading\">Business performance<\/h3><label>Reporting period<select data-company-period><option value=\"annual\">Annual<\/option><option value=\"quarterly\">Quarterly<\/option><\/select><\/label><\/div>\n\t\t\t<p data-company-financial-status role=\"status\">Select a company above to view its financial results.<\/p>\n\t\t\t<div data-company-financial-body hidden>\n\t\t\t\t<div class=\"silcom-company-metrics\" data-company-metrics><\/div>\n\t\t\t\t<div class=\"silcom-company-charts\">\n\t\t\t\t\t<figure><figcaption>Revenue, profit &amp; EBITDA<\/figcaption><div class=\"silcom-company-chart\"><canvas data-company-chart=\"results\" role=\"img\" aria-label=\"Revenue, net income and EBITDA by reporting period. Exact values in the financial table below.\"><\/canvas><\/div><\/figure>\n\t\t\t\t\t<figure><figcaption>Profit margins<\/figcaption><div class=\"silcom-company-chart\"><canvas data-company-chart=\"margins\" role=\"img\" aria-label=\"Gross, operating and net profit margins by reporting period. Exact values in the financial table below.\"><\/canvas><\/div><\/figure>\n\t\t\t\t<\/div>\n\t\t\t\t<p data-company-growth><\/p>\n\t\t\t\t<details><summary>Financial figures &amp; calculation notes<\/summary><div class=\"silcom-company-table\" tabindex=\"0\" role=\"region\" aria-label=\"Financial figures\" data-company-table><\/div><p>Margins = profit \/ revenue. Effective tax rate = tax expense \/ positive pre-tax income; this is not tax paid in cash. Return on assets\/equity = net income \/ average opening and closing balance, for matching periods. Quarterly returns are not annualised. Missing figures stay unavailable. Period-end dates follow the data provider and may differ from fiscal filing dates.<\/p><\/details>\n\t\t\t<\/div>\n\t\t<\/section>\n\t\t<section class=\"silcom-company-section\" aria-labelledby=\"silcom-capital-heading\">\n\t\t\t<h3 id=\"silcom-capital-heading\">Investments &amp; capital allocation<\/h3>\n\t\t\t<div class=\"silcom-company-metrics\" data-company-capital><p>Select a company to see its reported investment and spending figures.<\/p><\/div>\n\t\t\t<p>Investment balances and cash flows describe the company's finances, not your share-price return. They do not identify every subsidiary or portfolio holding. Negative cash-flow values indicate outflows.<\/p>\n\t\t\t<div data-company-reports><\/div>\n\t\t<\/section>\n\t\t<section class=\"silcom-company-section\" aria-labelledby=\"silcom-events-heading\">\n\t\t\t<div class=\"silcom-company-heading\"><h3 id=\"silcom-events-heading\">Company events<\/h3><label>Event type<select data-company-event-type><option value=\"all\">All events<\/option><option>Leadership<\/option><option>Acquisitions<\/option><option>Results<\/option><option>Shareholder returns<\/option><option>Company disclosures<\/option><\/select><\/label><\/div>\n\t\t\t<p data-company-events-status role=\"status\">Select a company to load its dated disclosures and shareholder events.<\/p>\n\t\t\t<div data-company-events-body hidden>\n\t\t\t\t<p data-company-profile><\/p>\n\t\t\t\t<div class=\"silcom-company-event-layout\">\n\t\t\t\t\t<div class=\"silcom-company-calendar\"><div class=\"silcom-company-calendar-head\"><button type=\"button\" data-company-month-step=\"-1\" aria-label=\"Previous month\" title=\"Previous month\">&#8592;<\/button><label>Month<input type=\"month\" data-company-month><\/label><button type=\"button\" data-company-month-step=\"1\" aria-label=\"Next month\" title=\"Next month\">&#8594;<\/button><\/div><div class=\"silcom-company-calendar-grid\" data-company-calendar><\/div><button type=\"button\" data-company-all-events>All recorded dates<\/button><\/div>\n\t\t\t\t\t<div><p data-company-event-scope><\/p><ol class=\"silcom-company-event-list\" data-company-event-list><\/ol><\/div>\n\t\t\t\t<\/div>\n\t\t\t<\/div>\n\t\t\t<p>SEC dates are filing dates. Item 5.02 can concern appointments, departures or compensation; open the filing for names and effective dates. The calendar shows recorded events, not a forecast of future earnings dates. Coverage depends on the listing and source.<\/p>\n\t\t<\/section>\n\t<\/div>\n\t\t\t\t\t\t\t\t<p class=\"silcom-investment-note\">Market data is requested through a WordPress proxy and cached briefly; financial results can be cached for up to six hours. Recent tickers and position rows are remembered only in this browser on this device. This is educational information, not financial advice.<\/p>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<div class=\"silcom-bi-stock__grid\">\n\t\t\t\t\t\t\t<article>\n\t\t\t\t\t\t\t\t<span>Company profile<\/span>\n\t\t\t\t\t\t\t\t<strong>Market cap, employees, sector, headquarters and countries served.<\/strong>\n\t\t\t\t\t\t\t<\/article>\n\t\t\t\t\t\t\t<article>\n\t\t\t\t\t\t\t\t<span>Share movement<\/span>\n\t\t\t\t\t\t\t\t<strong>Current price, selected-period change, high\/low range and volume context.<\/strong>\n\t\t\t\t\t\t\t<\/article>\n\t\t\t\t\t\t\t<article>\n\t\t\t\t\t\t\t\t<span>Financial picture<\/span>\n\t\t\t\t\t\t\t\t<strong>Revenue, profit\/loss, quarterly movement and year-on-year comparison.<\/strong>\n\t\t\t\t\t\t\t<\/article>\n\t\t\t\t\t\t\t<article>\n\t\t\t\t\t\t\t\t<span>Plain-English readout<\/span>\n\t\t\t\t\t\t\t\t<strong>Charts explained in simple language without buy\/sell recommendations.<\/strong>\n\t\t\t\t\t\t\t<\/article>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"silcom-bi-profit\" data-bi-profit-panel hidden>\n\t\t\t\t\t\t<div class=\"silcom-bi-stock__intro\">\n\t\t\t\t\t\t\t<p>Profit analysis<\/p>\n\t\t\t\t\t\t\t<h2>How much money is the business really making?<\/h2>\n\t\t\t\t\t\t\t<span>Start with revenue, expenses and profit, then drill into customers, products, employees, locations, cash flow, refunds, taxes, inventory and time trends.<\/span>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<div class=\"silcom-bi-profit__inputs\" data-bi-profit-form>\n\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t<span>Customers \/ clients<\/span>\n\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"2000\" data-profit-input=\"clients\">\n\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t<span>Average spend per client<\/span>\n\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"200\" data-profit-input=\"avgSpend\">\n\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t<span>Sales shown in system<\/span>\n\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"290000\" data-profit-input=\"visibleSales\">\n\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t<span>Total expenses \/ bills<\/span>\n\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"120000\" data-profit-input=\"expenses\">\n\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t<span>Refunds \/ returns<\/span>\n\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"8000\" data-profit-input=\"refunds\">\n\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t<span>Unpaid invoices<\/span>\n\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"35000\" data-profit-input=\"unpaid\">\n\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t<span>Cash in bank<\/span>\n\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"50000\" data-profit-input=\"cash\">\n\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t<span>Tax collected \/ owed<\/span>\n\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"26000\" data-profit-input=\"tax\">\n\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t<span>Inventory value<\/span>\n\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"45000\" data-profit-input=\"inventory\">\n\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t<span>Number of sales<\/span>\n\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"420\" data-profit-input=\"salesCount\">\n\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t<button type=\"button\" data-bi-profit-demo>Use demo profit data<\/button>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<div class=\"silcom-bi-profit__mirror\" data-bi-profit-mirror>\n\t\t\t\t\t\t\t<h3>Business mirror<\/h3>\n\t\t\t\t\t\t\t<ul data-bi-profit-warnings>\n\t\t\t\t\t\t\t\t<li>Enter whatever numbers you know. The tool will calculate what it can and flag mismatches.<\/li>\n\t\t\t\t\t\t\t<\/ul>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<div class=\"silcom-bi-profit__kpis\">\n\t\t\t\t\t\t\t<article><span>Total revenue<\/span><strong data-bi-profit-revenue>$0<\/strong><small>Money coming in<\/small><\/article>\n\t\t\t\t\t\t\t<article><span>Total expenses<\/span><strong data-bi-profit-expenses>$0<\/strong><small>Money going out<\/small><\/article>\n\t\t\t\t\t\t\t<article><span>Net profit<\/span><strong data-bi-profit-net>$0<\/strong><small>Revenue minus expenses<\/small><\/article>\n\t\t\t\t\t\t\t<article><span>Profit margin<\/span><strong data-bi-profit-margin>0%<\/strong><small>Net profit \/ revenue<\/small><\/article>\n\t\t\t\t\t\t\t<article><span>Sales<\/span><strong data-bi-profit-sales>0<\/strong><small>Completed transactions<\/small><\/article>\n\t\t\t\t\t\t\t<article><span>Customers<\/span><strong data-bi-profit-customers>0<\/strong><small>Unique customer count<\/small><\/article>\n\t\t\t\t\t\t\t<article><span>Expected revenue<\/span><strong data-bi-profit-expected>$0<\/strong><small>Clients multiplied by average spend<\/small><\/article>\n\t\t\t\t\t\t\t<article><span>Revenue gap<\/span><strong data-bi-profit-gap>$0<\/strong><small>Expected revenue minus visible sales<\/small><\/article>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<div class=\"silcom-bi-years\" data-bi-year-panel>\n\t\t\t\t\t\t\t<div class=\"silcom-bi-years__top\">\n\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t<p>Year-over-year mirror<\/p>\n\t\t\t\t\t\t\t\t\t<h3>Compare up to five years<\/h3>\n\t\t\t\t\t\t\t\t\t<span>Enter the yearly numbers you know. Empty fields are treated as unknown, and the graphs update locally in the browser.<\/span>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t\t<span>Years to analyse<\/span>\n\t\t\t\t\t\t\t\t\t<select data-bi-year-count>\n\t\t\t\t\t\t\t\t\t\t<option value=\"1\">1 year<\/option>\n\t\t\t\t\t\t\t\t\t\t<option value=\"2\">2 years<\/option>\n\t\t\t\t\t\t\t\t\t\t<option value=\"3\">3 years<\/option>\n\t\t\t\t\t\t\t\t\t\t<option value=\"4\">4 years<\/option>\n\t\t\t\t\t\t\t\t\t\t<option value=\"5\" selected>5 years<\/option>\n\t\t\t\t\t\t\t\t\t<\/select>\n\t\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t<div class=\"silcom-bi-years__table\" data-bi-year-form>\n\t\t\t\t\t\t\t\t<div class=\"silcom-bi-years__head\">\n\t\t\t\t\t\t\t\t\t<span>Year<\/span><span>Revenue<\/span><span>Expenses<\/span><span>Profit<\/span><span>Customers<\/span><span>Sales<\/span>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t<div class=\"silcom-bi-years__row\" data-year-row=\"1\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" step=\"1\" placeholder=\"2022\" data-year-input=\"year\" data-year-index=\"1\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"250000\" data-year-input=\"revenue\" data-year-index=\"1\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"160000\" data-year-input=\"expenses\" data-year-index=\"1\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" step=\"1\" placeholder=\"90000\" data-year-input=\"profit\" data-year-index=\"1\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"900\" data-year-input=\"customers\" data-year-index=\"1\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"1100\" data-year-input=\"sales\" data-year-index=\"1\">\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t<div class=\"silcom-bi-years__row\" data-year-row=\"2\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" step=\"1\" placeholder=\"2023\" data-year-input=\"year\" data-year-index=\"2\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"290000\" data-year-input=\"revenue\" data-year-index=\"2\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"185000\" data-year-input=\"expenses\" data-year-index=\"2\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" step=\"1\" placeholder=\"105000\" data-year-input=\"profit\" data-year-index=\"2\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"1100\" data-year-input=\"customers\" data-year-index=\"2\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"1360\" data-year-input=\"sales\" data-year-index=\"2\">\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t<div class=\"silcom-bi-years__row\" data-year-row=\"3\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" step=\"1\" placeholder=\"2024\" data-year-input=\"year\" data-year-index=\"3\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"340000\" data-year-input=\"revenue\" data-year-index=\"3\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"220000\" data-year-input=\"expenses\" data-year-index=\"3\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" step=\"1\" placeholder=\"120000\" data-year-input=\"profit\" data-year-index=\"3\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"1450\" data-year-input=\"customers\" data-year-index=\"3\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"1710\" data-year-input=\"sales\" data-year-index=\"3\">\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t<div class=\"silcom-bi-years__row\" data-year-row=\"4\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" step=\"1\" placeholder=\"2025\" data-year-input=\"year\" data-year-index=\"4\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"390000\" data-year-input=\"revenue\" data-year-index=\"4\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"255000\" data-year-input=\"expenses\" data-year-index=\"4\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" step=\"1\" placeholder=\"135000\" data-year-input=\"profit\" data-year-index=\"4\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"1800\" data-year-input=\"customers\" data-year-index=\"4\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"2200\" data-year-input=\"sales\" data-year-index=\"4\">\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t<div class=\"silcom-bi-years__row\" data-year-row=\"5\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" step=\"1\" placeholder=\"2026\" data-year-input=\"year\" data-year-index=\"5\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"430000\" data-year-input=\"revenue\" data-year-index=\"5\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"280000\" data-year-input=\"expenses\" data-year-index=\"5\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" step=\"1\" placeholder=\"150000\" data-year-input=\"profit\" data-year-index=\"5\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"2000\" data-year-input=\"customers\" data-year-index=\"5\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" min=\"0\" step=\"1\" placeholder=\"2500\" data-year-input=\"sales\" data-year-index=\"5\">\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t<div class=\"silcom-bi-years__summary\" data-bi-year-summary>Fill at least two years to see trend warnings.<\/div>\n\t\t\t\t\t\t\t<div class=\"silcom-bi-years__charts\">\n\t\t\t\t\t\t\t\t<article><span>Revenue<\/span><canvas width=\"360\" height=\"180\" data-year-chart=\"revenue\"><\/canvas><\/article>\n\t\t\t\t\t\t\t\t<article><span>Expenses<\/span><canvas width=\"360\" height=\"180\" data-year-chart=\"expenses\"><\/canvas><\/article>\n\t\t\t\t\t\t\t\t<article><span>Profit<\/span><canvas width=\"360\" height=\"180\" data-year-chart=\"profit\"><\/canvas><\/article>\n\t\t\t\t\t\t\t\t<article><span>Profit margin<\/span><canvas width=\"360\" height=\"180\" data-year-chart=\"margin\"><\/canvas><\/article>\n\t\t\t\t\t\t\t\t<article><span>Customers \/ sales<\/span><canvas width=\"360\" height=\"180\" data-year-chart=\"customers\"><\/canvas><\/article>\n\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<div class=\"silcom-bi-profit__drill\" data-bi-profit-drill>\n\t\t\t\t\t\t\t<article data-focus=\"revenue\"><span>Revenue<\/span><strong>Total, hourly, customer, product, employee, location, channel and payment method.<\/strong><\/article>\n\t\t\t\t\t\t\t<article data-focus=\"expenses\"><span>Expenses<\/span><strong>Rent, wages, contractors, inventory, utilities, fuel, marketing, insurance, software, taxes and fees.<\/strong><\/article>\n\t\t\t\t\t\t\t<article data-focus=\"profit\"><span>Profit<\/span><strong>Gross profit, net profit, operating profit, margin and profit trend.<\/strong><\/article>\n\t\t\t\t\t\t\t<article data-focus=\"sales\"><span>Sales<\/span><strong>Sales count, completed, pending, returned, cancelled and average order value.<\/strong><\/article>\n\t\t\t\t\t\t\t<article data-focus=\"customers\"><span>Customers<\/span><strong>New, returning, lost, high-value, low-value, average spend and purchase frequency.<\/strong><\/article>\n\t\t\t\t\t\t\t<article data-focus=\"products\"><span>Products \/ services<\/span><strong>Best sellers, worst sellers, most profitable, least profitable, refunded and seasonal items.<\/strong><\/article>\n\t\t\t\t\t\t\t<article data-focus=\"employees\"><span>Employees<\/span><strong>Sales, revenue, profit, jobs completed, hours worked, conversion and productivity.<\/strong><\/article>\n\t\t\t\t\t\t\t<article data-focus=\"time\"><span>Time<\/span><strong>Today vs yesterday, week vs week, month vs month, year over year and seasonal peaks.<\/strong><\/article>\n\t\t\t\t\t\t\t<article data-focus=\"cash\"><span>Cash flow<\/span><strong>Money received, money paid, invoices, bills due, cash in bank and daily movement.<\/strong><\/article>\n\t\t\t\t\t\t\t<article data-focus=\"outstanding\"><span>Outstanding money<\/span><strong>Unpaid invoices, late payments, customer debt, supplier debt and overdue invoices.<\/strong><\/article>\n\t\t\t\t\t\t\t<article data-focus=\"inventory\"><span>Inventory<\/span><strong>Inventory value, fast-moving items, slow stock, dead stock, shortages and turnover.<\/strong><\/article>\n\t\t\t\t\t\t\t<article data-focus=\"pricing\"><span>Pricing<\/span><strong>Average selling price, discounts, coupons, margin after discount and price changes.<\/strong><\/article>\n\t\t\t\t\t\t\t<article data-focus=\"refunds\"><span>Refunds \/ returns<\/span><strong>Refund amount, refund rate, return reasons, trends and products with highest refunds.<\/strong><\/article>\n\t\t\t\t\t\t\t<article data-focus=\"taxes\"><span>Taxes<\/span><strong>GST\/VAT collected, tax payable, tax paid, payroll tax and income tax estimate.<\/strong><\/article>\n\t\t\t\t\t\t\t<article data-focus=\"forecasting\"><span>Forecasting<\/span><strong>Revenue, expenses, profit, cash flow and inventory forecast starters.<\/strong><\/article>\n\t\t\t\t\t\t\t<article data-focus=\"kpis\"><span>Business health KPIs<\/span><strong>Monthly revenue, expenses, profit, growth, churn, burn rate, runway and break-even.<\/strong><\/article>\n\t\t\t\t\t\t\t<article data-focus=\"dimensions\"><span>Drill-down dimensions<\/span><strong>Time, customer, employee, product, service, supplier, location, channel, campaign and region.<\/strong><\/article>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<div class=\"silcom-bi-lab\" data-bi-lab>\n\t\t\t\t\t\t<div class=\"silcom-bi-lab__header\">\n\t\t\t\t\t\t\t<p>Client-side data visualiser<\/p>\n\t\t\t\t\t\t\t<h2>Upload data, map columns, generate insight<\/h2>\n\t\t\t\t\t\t\t<span>CSV files and API links are processed in your browser. Nothing is sent to a Silcom server.<\/span>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<div class=\"silcom-bi-lab__source\">\n\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t<span>Upload CSV<\/span>\n\t\t\t\t\t\t\t\t<input type=\"file\" accept=\".csv,text\/csv\" data-bi-file>\n\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t<span>CSV or JSON API link<\/span>\n\t\t\t\t\t\t\t\t<input type=\"url\" placeholder=\"https:\/\/example.com\/data.csv\" data-bi-api>\n\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t<button type=\"button\" data-bi-load-api>Load API<\/button>\n\t\t\t\t\t\t\t<button type=\"button\" data-bi-demo>Use demo stock data<\/button>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<div class=\"silcom-bi-quality\" data-bi-quality>\n\t\t\t\t\t\t\t<article><span>Rows<\/span><strong data-bi-quality-rows>0<\/strong><\/article>\n\t\t\t\t\t\t\t<article><span>Columns<\/span><strong data-bi-quality-columns>0<\/strong><\/article>\n\t\t\t\t\t\t\t<article><span>Numeric<\/span><strong data-bi-quality-numeric>0<\/strong><\/article>\n\t\t\t\t\t\t\t<article><span>Dates<\/span><strong data-bi-quality-dates>0<\/strong><\/article>\n\t\t\t\t\t\t\t<article><span>Categories<\/span><strong data-bi-quality-categories>0<\/strong><\/article>\n\t\t\t\t\t\t\t<article><span>Missing cells<\/span><strong data-bi-quality-missing>0<\/strong><\/article>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<div class=\"silcom-bi-suggestions\" data-bi-suggestions>\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<p>Suggested views<\/p>\n\t\t\t\t\t\t\t\t<span>Load data and the tool will suggest sensible charts from the columns it detects.<\/span>\n\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t<div data-bi-suggestion-list><\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<div class=\"silcom-bi-lab__mapper\">\n\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t<span>Chart type<\/span>\n\t\t\t\t\t\t\t\t<select data-bi-chart-type>\n\t\t\t\t\t\t\t\t\t<option value=\"line\">Line chart<\/option>\n\t\t\t\t\t\t\t\t\t<option value=\"bar\">Bar chart<\/option>\n\t\t\t\t\t\t\t\t\t<option value=\"scatter\">Scatter plot<\/option>\n\t\t\t\t\t\t\t\t\t<option value=\"doughnut\">Doughnut chart<\/option>\n\t\t\t\t\t\t\t\t<\/select>\n\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t<span>X column<\/span>\n\t\t\t\t\t\t\t\t<select data-bi-x><\/select>\n\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t<span>Y column<\/span>\n\t\t\t\t\t\t\t\t<select data-bi-y><\/select>\n\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t<span>Aggregation<\/span>\n\t\t\t\t\t\t\t\t<select data-bi-agg>\n\t\t\t\t\t\t\t\t\t<option value=\"none\">No aggregation<\/option>\n\t\t\t\t\t\t\t\t\t<option value=\"count\">Count rows<\/option>\n\t\t\t\t\t\t\t\t\t<option value=\"sum\">Sum<\/option>\n\t\t\t\t\t\t\t\t\t<option value=\"avg\">Average<\/option>\n\t\t\t\t\t\t\t\t\t<option value=\"min\">Minimum<\/option>\n\t\t\t\t\t\t\t\t\t<option value=\"max\">Maximum<\/option>\n\t\t\t\t\t\t\t\t<\/select>\n\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t<button type=\"button\" data-bi-render>Generate insight<\/button>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<div class=\"silcom-bi-lab__workspace\">\n\t\t\t\t\t\t\t<section class=\"silcom-bi-chart-card\">\n\t\t\t\t\t\t\t\t<canvas data-bi-chart width=\"960\" height=\"420\" aria-label=\"Generated data chart\"><\/canvas>\n\t\t\t\t\t\t\t<\/section>\n\t\t\t\t\t\t\t<aside class=\"silcom-bi-insights\" data-bi-insights>\n\t\t\t\t\t\t\t\t<h3>Insights<\/h3>\n\t\t\t\t\t\t\t\t<ul data-bi-insight-list>\n\t\t\t\t\t\t\t\t\t<li>Load CSV data, an API link, or the demo dataset to begin.<\/li>\n\t\t\t\t\t\t\t\t<\/ul>\n\t\t\t\t\t\t\t<\/aside>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<div class=\"silcom-bi-preview\" data-bi-preview>\n\t\t\t\t\t\t\t<h3>Data preview<\/h3>\n\t\t\t\t\t\t\t<div data-bi-preview-table>Waiting for data.<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t<\/section>\n\t\t<\/div>\n\t<\/section>\n\t<script>\n\t(function initialiseDesktopWorkspace(){\n\t\tvar mobile=window.matchMedia('(max-width: 767px)');\n\t\tif(mobile.matches){\n\t\t\tvar resume=function(){if(!mobile.matches){mobile.removeEventListener('change',resume);initialiseDesktopWorkspace();}};\n\t\t\tmobile.addEventListener('change',resume);\n\t\t\treturn;\n\t\t}\n\t\tvar root=document.querySelector('[data-silcom-bi-tool]'); if(!root || root.dataset.desktopReady)return;\n\t\troot.dataset.desktopReady='true';\n\t\tvar mode=root.querySelector('[data-bi-mode]'), note=root.querySelector('[data-bi-mode-note]'), stockPanel=root.querySelector('[data-bi-stock-panel]'), stockBranch=root.querySelector('[data-bi-stock-branch]'), profitPanel=root.querySelector('[data-bi-profit-panel]');\n\t\tvar fileInput=root.querySelector('[data-bi-file]'), apiInput=root.querySelector('[data-bi-api]'), loadApi=root.querySelector('[data-bi-load-api]'), demoButton=root.querySelector('[data-bi-demo]'), profitDemoButton=root.querySelector('[data-bi-profit-demo]');\n\t\tvar profitForm=root.querySelector('[data-bi-profit-form]'), profitDrill=root.querySelector('[data-bi-profit-drill]'), profitWarnings=root.querySelector('[data-bi-profit-warnings]');\n\t\tvar chartType=root.querySelector('[data-bi-chart-type]'), xSelect=root.querySelector('[data-bi-x]'), ySelect=root.querySelector('[data-bi-y]'), aggSelect=root.querySelector('[data-bi-agg]'), renderButton=root.querySelector('[data-bi-render]');\n\t\tvar preview=root.querySelector('[data-bi-preview-table]'), insightList=root.querySelector('[data-bi-insight-list]'), canvas=root.querySelector('[data-bi-chart]'), suggestionList=root.querySelector('[data-bi-suggestion-list]');\n\t\tvar qualityRows=root.querySelector('[data-bi-quality-rows]'), qualityColumns=root.querySelector('[data-bi-quality-columns]'), qualityNumeric=root.querySelector('[data-bi-quality-numeric]'), qualityDates=root.querySelector('[data-bi-quality-dates]'), qualityCategories=root.querySelector('[data-bi-quality-categories]'), qualityMissing=root.querySelector('[data-bi-quality-missing]');\n\t\tvar profitRevenue=root.querySelector('[data-bi-profit-revenue]'), profitExpenses=root.querySelector('[data-bi-profit-expenses]'), profitNet=root.querySelector('[data-bi-profit-net]'), profitMargin=root.querySelector('[data-bi-profit-margin]'), profitSales=root.querySelector('[data-bi-profit-sales]'), profitCustomers=root.querySelector('[data-bi-profit-customers]'), profitExpected=root.querySelector('[data-bi-profit-expected]'), profitGap=root.querySelector('[data-bi-profit-gap]');\n\t\tvar yearCount=root.querySelector('[data-bi-year-count]'), yearForm=root.querySelector('[data-bi-year-form]'), yearSummary=root.querySelector('[data-bi-year-summary]');\n\t\tvar rows=[], columns=[], profile={numeric:[],date:[],category:[],missing:0}, storageKey='silcom.biTool.v1';\n\t\tfunction updateMode(){var active=mode.value||'stocks'; stockPanel.hidden=false; stockBranch.hidden=active!=='stocks'; profitPanel.hidden=active!=='profit'; note.textContent=active==='stocks'?'Stocks \/ shares now includes live ticker lookup, current price, trend context and DCA position tracking.':'Profit analysis connects revenue, expenses, profit, customers, products, employees, time, cash flow and KPIs.'; renderProfitDashboard(); renderYearAnalysis();}\n\t\tfunction esc(text){return String(text).replace(\/[&<>\"']\/g,function(c){return({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#039;'})[c];});}\n\t\tfunction money(value){return '$'+Number(value||0).toLocaleString(undefined,{maximumFractionDigits:0});}\n\t\tfunction percentText(value){return Number(value||0).toLocaleString(undefined,{maximumFractionDigits:1})+'%';}\n\t\tfunction profitInput(name){var input=root.querySelector('[data-profit-input=\"'+name+'\"]'); if(!input)return 0; var n=numericValue(input.value); return Number.isFinite(n)?n:0;}\n\t\tfunction profitInputMap(){var data={}; Array.prototype.forEach.call(root.querySelectorAll('[data-profit-input]'),function(input){data[input.getAttribute('data-profit-input')]=input.value;}); return data;}\n\t\tfunction restoreProfitInputs(data){if(!data)return; Object.keys(data).forEach(function(key){var input=root.querySelector('[data-profit-input=\"'+key+'\"]'); if(input)input.value=data[key];});}\n\t\tfunction yearInputMap(){var data={}; Array.prototype.forEach.call(root.querySelectorAll('[data-year-input]'),function(input){data[input.getAttribute('data-year-index')+'-'+input.getAttribute('data-year-input')]=input.value;}); return data;}\n\t\tfunction restoreYearInputs(data){if(!data)return; Object.keys(data).forEach(function(key){var parts=key.split('-'), input=root.querySelector('[data-year-index=\"'+parts[0]+'\"][data-year-input=\"'+parts[1]+'\"]'); if(input)input.value=data[key];});}\n\t\tfunction yearNumber(index,name){var input=root.querySelector('[data-year-index=\"'+index+'\"][data-year-input=\"'+name+'\"]'); if(!input)return 0; var n=numericValue(input.value); return Number.isFinite(n)?n:0;}\n\t\tfunction yearLabel(index){var input=root.querySelector('[data-year-index=\"'+index+'\"][data-year-input=\"year\"]'); return input?input.value||String(index):String(index);}\n\t\tfunction updateYearRows(){var count=parseInt(yearCount.value||'5',10); Array.prototype.forEach.call(root.querySelectorAll('[data-year-row]'),function(row){var index=parseInt(row.getAttribute('data-year-row')||'0',10); row.hidden=index>count;});}\n\t\tfunction collectYearData(){var count=parseInt(yearCount.value||'5',10), data=[]; for(var i=1;i<=count;i++){var revenue=yearNumber(i,'revenue'), expenses=yearNumber(i,'expenses'), profit=yearNumber(i,'profit'), customers=yearNumber(i,'customers'), sales=yearNumber(i,'sales'); if(!profit?revenue||expenses:false)profit=revenue-expenses; data.push({label:yearLabel(i),revenue:revenue,expenses:expenses,profit:profit,margin:revenue?profit\/revenue*100:0,customers:customers,sales:sales});} return data;}\n\t\tfunction parseCsv(text){var out=[], row=[], cell='', quote=false; for(var i=0;i<text.length;i++){var ch=text[i], next=text[i+1]; if(ch==='\"'){if(quote){if(next==='\"'){cell+='\"'; i++;}else{quote=false;}}else{quote=true;}}else if((ch===','? !quote : false)){row.push(cell); cell='';}else if(((ch==='\\n'||ch==='\\r')? !quote : false)){if((ch==='\\r'?next==='\\n':false))i++; row.push(cell); if(row.some(function(v){return String(v).trim()!=='';}))out.push(row); row=[]; cell='';}else{cell+=ch;}} row.push(cell); if(row.some(function(v){return String(v).trim()!=='';}))out.push(row); if(!out.length)return[]; var headers=out.shift().map(function(h,index){return String(h||('Column '+(index+1))).trim()||('Column '+(index+1));}); return out.map(function(values){var item={}; headers.forEach(function(header,index){item[header]=values[index]!==undefined?values[index]:'';}); return item;});}\n\t\tfunction normaliseJson(data){if(Array.isArray(data))return data; if(data){if(Array.isArray(data.data))return data.data; if(Array.isArray(data.results))return data.results; if(Array.isArray(data.values))return data.values;} return[];}\n\t\tfunction numericValue(value){var cleaned=String(value).replace(\/[$,%\\s,]\/g,''); if(!\/^[-+]?(\\d+(\\.\\d+)?|\\.\\d+)$\/.test(cleaned))return NaN; var n=parseFloat(cleaned); return Number.isFinite(n)?n:NaN;}\n\t\tfunction looksDate(value){var text=String(value||'').trim(); if(!text)return false; if(!\/[\\\\\/-]\/.test(text))return false; var time=Date.parse(text); return Number.isFinite(time);}\n\t\tfunction profileData(){var missing=0,numeric=[],date=[],category=[]; columns.forEach(function(col){var seen=0,num=0,dates=0,unique={}; rows.forEach(function(row){var value=row[col]; if(value===''||value===null||value===undefined){missing++; return;} seen++; unique[String(value)]=1; if(Number.isFinite(numericValue(value)))num++; if(looksDate(value))dates++;}); if(seen?num\/seen>.75:false)numeric.push(col); else if(seen?dates\/seen>.65:false)date.push(col); else category.push(col);}); profile={numeric:numeric,date:date,category:category,missing:missing};}\n\t\tfunction numericColumns(){return profile.numeric||[];}\n\t\tfunction findColumn(patterns){for(var i=0;i<patterns.length;i++){var rx=patterns[i]; for(var j=0;j<columns.length;j++){if(rx.test(columns[j]))return columns[j];}} return '';}\n\t\tfunction sumColumn(patterns){var col=findColumn(patterns); if(!col)return 0; return rows.reduce(function(total,row){var n=numericValue(row[col]); return total+(Number.isFinite(n)?n:0);},0);}\n\t\tfunction uniqueCount(patterns){var col=findColumn(patterns), seen={}; if(!col)return 0; rows.forEach(function(row){var value=String(row[col]||'').trim(); if(value)seen[value]=1;}); return Object.keys(seen).length;}\n\t\tfunction completedSalesCount(){var statusCol=findColumn([\/status\/i]), salesCol=findColumn([\/sale|order|transaction\/i]); if(statusCol){return rows.filter(function(row){return !\/cancel|return|refund|pending\/i.test(String(row[statusCol]||''));}).length;} if(salesCol){return rows.reduce(function(total,row){var n=numericValue(row[salesCol]); return total+(Number.isFinite(n)?n:1);},0);} return rows.length;}\n\t\tfunction renderProfitDashboard(){if(!profitPanel)return; var clients=profitInput('clients'), avgSpend=profitInput('avgSpend'), manualSales=profitInput('visibleSales'), manualExpenses=profitInput('expenses'), refunds=profitInput('refunds'), unpaid=profitInput('unpaid'), cash=profitInput('cash'), tax=profitInput('tax'), inventory=profitInput('inventory'), manualSalesCount=profitInput('salesCount'); var csvRevenue=sumColumn([\/revenue|sales amount|sale amount|amount received|money in\/i]), csvExpenses=sumColumn([\/expense|cost|money out|paid|fee\/i]), csvProfit=sumColumn([\/^profit$|net profit|gross profit|operating profit\/i]); var expected=clients*avgSpend, revenue=manualSales||csvRevenue||expected, expenses=manualExpenses||csvExpenses, profit=csvProfit||revenue-expenses-refunds-tax, margin=revenue?profit\/revenue*100:0, gap=(expected?revenue:false)?expected-revenue:0, gapPercent=expected?gap\/expected*100:0, sales=manualSalesCount||completedSalesCount(), customers=clients||uniqueCount([\/customer|client\/i]); profitRevenue.textContent=money(revenue); profitExpenses.textContent=money(expenses); profitNet.textContent=money(profit); profitMargin.textContent=percentText(margin); profitSales.textContent=sales.toLocaleString(); profitCustomers.textContent=customers.toLocaleString(); profitExpected.textContent=money(expected); profitGap.textContent=money(gap); renderProfitWarnings({clients:clients,avgSpend:avgSpend,expected:expected,revenue:revenue,expenses:expenses,profit:profit,margin:margin,gap:gap,gapPercent:gapPercent,refunds:refunds,unpaid:unpaid,cash:cash,tax:tax,inventory:inventory,sales:sales}); renderYearAnalysis();}\n\t\tfunction renderProfitWarnings(data){var items=[]; if(data.expected?data.revenue:false){if(data.gap>0?data.gapPercent>=10:false)items.push('Expected revenue is '+money(data.expected)+' but visible sales are '+money(data.revenue)+'. That is a '+percentText(data.gapPercent)+' gap, so money may be missing, discounted, refunded, unpaid, or not captured in the sales system.'); else if(data.gap<0)items.push('Visible sales are higher than the customer-spend estimate by '+money(Math.abs(data.gap))+'. That can be fine, but it means average spend or customer count may be understated.'); else items.push('Expected revenue and visible sales are close. The top-line sales story looks internally consistent.');}else if(data.clients||data.avgSpend){items.push('Add both customer count and average spend to estimate expected revenue.');}else{items.push('Add customer count, average spend, and visible sales to check whether the top-line revenue makes sense.');} if(data.revenue?data.expenses:false){var expenseRate=data.expenses\/data.revenue*100; items.push('Expenses are '+percentText(expenseRate)+' of visible sales. After expenses, estimated profit is '+money(data.profit)+'.'); if(expenseRate>75)items.push('Expenses are very high against revenue. This can crush profit even when sales look healthy.');}else if(data.revenue){items.push('Add expenses or bills to calculate realistic profit. Revenue alone can hide the real picture.');} if(data.refunds?data.revenue:false){items.push('Refunds represent '+percentText(data.refunds\/data.revenue*100)+' of sales. High refunds can make reported revenue look better than real revenue.');} if(data.unpaid?data.revenue:false){items.push('Unpaid invoices equal '+percentText(data.unpaid\/data.revenue*100)+' of sales. The business may look profitable but still have cash stuck outside the bank.');} if(data.cash?data.expenses:false){var runway=data.expenses?data.cash\/data.expenses:0; items.push('Cash in bank covers about '+runway.toLocaleString(undefined,{maximumFractionDigits:1})+' periods of the entered expenses.'); if(runway<1)items.push('Cash runway is below one expense cycle. That is a cash-flow warning even if the profit number is positive.');} if(data.tax?data.revenue:false){items.push('Tax entered is '+percentText(data.tax\/data.revenue*100)+' of sales. Keep it separated from spendable cash.');} if(data.inventory?data.revenue:false){items.push('Inventory value is '+percentText(data.inventory\/data.revenue*100)+' of sales. If stock is slow-moving, cash may be sitting on shelves.');} if(data.margin<0?data.revenue:false)items.push('Estimated profit is negative. The business is losing money on the numbers entered.'); profitWarnings.innerHTML=items.map(function(item){return '<li>'+esc(item)+'<\/li>';}).join('');}\n\t\tfunction drawYearMini(canvas,data,key,color){var ctx=canvas.getContext('2d'), w=canvas.width, h=canvas.height, pad=28, values=data.map(function(item){return Number(item[key]||0);}); ctx.clearRect(0,0,w,h); ctx.fillStyle='#020617'; ctx.fillRect(0,0,w,h); ctx.strokeStyle='rgba(148,163,184,.25)'; ctx.lineWidth=1; for(var g=0;g<4;g++){var gy=pad+(h-pad*2)*g\/3; ctx.beginPath(); ctx.moveTo(pad,gy); ctx.lineTo(w-pad,gy); ctx.stroke();} if(!values.some(function(value){return value!==0;})){ctx.fillStyle='#94a3b8'; ctx.font='13px system-ui'; ctx.fillText('Add yearly values',pad,54); return;} var min=Math.min.apply(null,values), max=Math.max.apply(null,values); if(min>0)min=0; if(max===min)max=min+1; ctx.strokeStyle=color; ctx.lineWidth=3; ctx.beginPath(); values.forEach(function(value,index){var x=pad+(w-pad*2)*(data.length===1?0.5:index\/Math.max(data.length-1,1)), y=h-pad-(value-min)*(h-pad*2)\/(max-min); if(index===0)ctx.moveTo(x,y); else ctx.lineTo(x,y);}); ctx.stroke(); values.forEach(function(value,index){var x=pad+(w-pad*2)*(data.length===1?0.5:index\/Math.max(data.length-1,1)), y=h-pad-(value-min)*(h-pad*2)\/(max-min); ctx.fillStyle=color; ctx.beginPath(); ctx.arc(x,y,4,0,Math.PI*2); ctx.fill();}); ctx.fillStyle='#cbd5e1'; ctx.font='11px system-ui'; ctx.fillText(data[0]?data[0].label:'',pad,h-8); ctx.fillText(data[data.length-1]?data[data.length-1].label:'',w-pad-34,h-8);}\n\t\tfunction renderYearAnalysis(){if(!yearForm)return; updateYearRows(); var data=collectYearData(), real=data.filter(function(item){return item.revenue||item.expenses||item.profit||item.customers||item.sales;}); drawYearMini(root.querySelector('[data-year-chart=\"revenue\"]'),data,'revenue','#38bdf8'); drawYearMini(root.querySelector('[data-year-chart=\"expenses\"]'),data,'expenses','#f59e0b'); drawYearMini(root.querySelector('[data-year-chart=\"profit\"]'),data,'profit','#22c55e'); drawYearMini(root.querySelector('[data-year-chart=\"margin\"]'),data,'margin','#a78bfa'); drawYearMini(root.querySelector('[data-year-chart=\"customers\"]'),data,'customers','#14b8a6'); if(real.length<2){yearSummary.textContent='Fill at least two years to see year-over-year trend warnings.'; return;} var first=real[0], last=real[real.length-1], revenueChange=first.revenue?((last.revenue-first.revenue)\/Math.abs(first.revenue))*100:0, profitChange=first.profit?((last.profit-first.profit)\/Math.abs(first.profit))*100:0, marginMove=last.margin-first.margin, customerChange=first.customers?((last.customers-first.customers)\/Math.abs(first.customers))*100:0, notes=[]; notes.push('From '+first.label+' to '+last.label+', revenue moved '+(revenueChange>=0?'+':'')+revenueChange.toFixed(1)+'%.'); notes.push('Profit moved '+(profitChange>=0?'+':'')+profitChange.toFixed(1)+'%, and margin moved '+(marginMove>=0?'+':'')+marginMove.toFixed(1)+' percentage points.'); if(revenueChange>0?profitChange<revenueChange\/2:false)notes.push('Revenue is growing faster than profit. Costs, discounts, refunds, or delivery overhead may be eating the growth.'); if(revenueChange>0?marginMove<0:false)notes.push('Sales are up but margin is down. That usually means the business is working harder for less profit per dollar.'); if(customerChange>0?revenueChange<customerChange\/2:false)notes.push('Customers are growing faster than revenue. Average spend may be falling.'); yearSummary.textContent=notes.join(' ');}\n\t\tfunction setRows(nextRows){rows=nextRows.filter(function(row){return (row?typeof row==='object':false);}); columns=rows.length?Object.keys(rows[0]):[]; profileData(); populateColumns(); renderQuality(); renderSuggestions(); renderPreview(); renderChart(); renderProfitDashboard(); renderYearAnalysis(); remember();}\n\t\tfunction remember(){try{localStorage.setItem(storageKey,JSON.stringify({mode:mode.value,chartType:chartType.value,x:xSelect.value,y:ySelect.value,agg:aggSelect.value,profitInputs:profitInputMap(),yearCount:yearCount.value,yearInputs:yearInputMap(),rows:rows.slice(0,250)}));}catch(e){}}\n\t\tfunction restore(){try{var saved=JSON.parse(localStorage.getItem(storageKey)||'{}'); if(saved.mode)mode.value=saved.mode; if(saved.chartType)chartType.value=saved.chartType; if(saved.agg)aggSelect.value=saved.agg; if(saved.yearCount)yearCount.value=saved.yearCount; restoreProfitInputs(saved.profitInputs); restoreYearInputs(saved.yearInputs); if(Array.isArray(saved.rows)){rows=saved.rows; columns=rows.length?Object.keys(rows[0]):[]; profileData(); populateColumns(); if(saved.x)xSelect.value=saved.x; if(saved.y)ySelect.value=saved.y; renderQuality(); renderSuggestions(); renderPreview(); renderChart(); renderProfitDashboard(); renderYearAnalysis();}}catch(e){}}\n\t\tfunction populateColumns(){var numeric=numericColumns(), opts=columns.map(function(col){return '<option value=\"'+esc(col)+'\">'+esc(col)+'<\/option>';}).join(''); xSelect.innerHTML=opts; ySelect.innerHTML=(numeric.length?numeric:columns).map(function(col){return '<option value=\"'+esc(col)+'\">'+esc(col)+'<\/option>';}).join(''); var xGuess=(profile.date||[])[0]||(profile.category||[])[0]||columns[0]||'', yGuess=numeric.find(function(col){return \/close|price|value|revenue|profit|sales|amount|volume|age\/i.test(col);})||numeric[0]||columns[1]||columns[0]||''; if(xGuess)xSelect.value=xGuess; if(yGuess)ySelect.value=yGuess; if((profile.category||[]).length?numeric.length:false)aggSelect.value='avg'; else aggSelect.value='none'; if((profile.date||[]).length?numeric.length:false)aggSelect.value='none';}\n\t\tfunction renderQuality(){qualityRows.textContent=rows.length; qualityColumns.textContent=columns.length; qualityNumeric.textContent=(profile.numeric||[]).length; qualityDates.textContent=(profile.date||[]).length; qualityCategories.textContent=(profile.category||[]).length; qualityMissing.textContent=profile.missing||0;}\n\t\tfunction renderSuggestions(){var suggestions=[], numeric=profile.numeric||[], dates=profile.date||[], cats=profile.category||[]; if(dates.length?numeric.length:false)suggestions.push({label:'Trend over time',type:'line',x:dates[0],y:numeric[0],agg:'none'}); if(cats.length?numeric.length:false)suggestions.push({label:'Compare groups',type:'bar',x:cats[0],y:numeric[0],agg:'avg'}); if(numeric.length>1)suggestions.push({label:'Relationship between numbers',type:'scatter',x:numeric[0],y:numeric[1],agg:'none'}); if(cats.length)suggestions.push({label:'Count by category',type:'doughnut',x:cats[0],y:numeric[0]||cats[0],agg:'count'}); if(!suggestions.length){suggestionList.innerHTML='<span>No suggestions yet. Load more structured data.<\/span>'; return;} suggestionList.innerHTML=suggestions.map(function(s){return '<button type=\"button\" data-bi-suggest data-type=\"'+esc(s.type)+'\" data-x=\"'+esc(s.x)+'\" data-y=\"'+esc(s.y)+'\" data-agg=\"'+esc(s.agg)+'\">'+esc(s.label)+'<\/button>';}).join('');}\n\t\tfunction renderPreview(){if(!rows.length){preview.textContent='Waiting for data.'; return;} var head=columns.slice(0,8), body=rows.slice(0,6); preview.innerHTML='<table><thead><tr>'+head.map(function(col){return '<th>'+esc(col)+'<\/th>';}).join('')+'<\/tr><\/thead><tbody>'+body.map(function(row){return '<tr>'+head.map(function(col){return '<td>'+esc(row[col])+'<\/td>';}).join('')+'<\/tr>';}).join('')+'<\/tbody><\/table>';}\n\t\tfunction drawEmpty(message){var ctx=canvas.getContext('2d'); ctx.clearRect(0,0,canvas.width,canvas.height); ctx.fillStyle='#020617'; ctx.fillRect(0,0,canvas.width,canvas.height); ctx.fillStyle='#94a3b8'; ctx.font='18px system-ui'; ctx.fillText(message,28,60);}\n\t\tfunction scale(value,min,max,start,end){if(max===min)return(start+end)\/2; return start+(value-min)*(end-start)\/(max-min);}\n\t\tfunction aggregateRows(xCol,yCol,agg){var grouped={}, order=[]; rows.forEach(function(row,index){var label=String(row[xCol]!==undefined?row[xCol]:index+1)||'Blank'; if(!grouped[label]){grouped[label]={label:label,values:[],count:0}; order.push(label);} grouped[label].count++; var n=numericValue(row[yCol]); if(Number.isFinite(n))grouped[label].values.push(n);}); return order.slice(0,80).map(function(label){var group=grouped[label], values=group.values, value=0; if(agg==='count')value=group.count; else if(values.length){if(agg==='sum')value=values.reduce(function(a,b){return a+b;},0); else if(agg==='min')value=Math.min.apply(null,values); else if(agg==='max')value=Math.max.apply(null,values); else value=values.reduce(function(a,b){return a+b;},0)\/values.length;} return{label:label,value:value,x:NaN,count:group.count};}).filter(function(point){return Number.isFinite(point.value);});}\n\t\tfunction rawRows(xCol,yCol){return rows.map(function(row,index){return{label:String(row[xCol]!==undefined?row[xCol]:index+1),value:numericValue(row[yCol]),x:numericValue(row[xCol])};}).filter(function(point){return Number.isFinite(point.value);}).slice(0,80);}\n\t\tfunction chartData(){var type=chartType.value, agg=aggSelect.value, xCol=xSelect.value||columns[0], yCol=ySelect.value||numericColumns()[0]||columns[0]; if(agg!=='none'||type==='doughnut')return aggregateRows(xCol,yCol,agg==='none'?'sum':agg); return rawRows(xCol,yCol);}\n\t\tfunction renderChart(){if(!rows.length||!columns.length){drawEmpty('Load data to generate a chart.'); return;} var type=chartType.value, xCol=xSelect.value||columns[0], yCol=ySelect.value||numericColumns()[0]||columns[0], agg=aggSelect.value, data=chartData(); var ctx=canvas.getContext('2d'), w=canvas.width, h=canvas.height, pad=52; ctx.clearRect(0,0,w,h); ctx.fillStyle='#020617'; ctx.fillRect(0,0,w,h); if(!data.length){drawEmpty(agg==='count'?'Choose a category column.':'Choose a numeric Y column.'); renderInsights([]); return;} var values=data.map(function(d){return d.value;}), min=Math.min.apply(null,values), max=Math.max.apply(null,values); if(min>0)min=0; ctx.strokeStyle='rgba(148,163,184,.28)'; ctx.lineWidth=1; for(var g=0;g<5;g++){var y=scale(g,0,4,h-pad,pad); ctx.beginPath(); ctx.moveTo(pad,y); ctx.lineTo(w-pad,y); ctx.stroke();} ctx.fillStyle='#94a3b8'; ctx.font='12px system-ui'; ctx.fillText((agg==='count'?'Count':yCol)+(agg!=='none'?' \u00b7 '+agg:''),pad,24); if(type==='doughnut'){drawDoughnut(ctx,data,w,h); renderInsights(data); return;} if(type==='bar'){var barW=(w-pad*2)\/data.length*.72; data.forEach(function(d,i){var x=scale(i,0,Math.max(data.length-1,1),pad,w-pad), y=scale(d.value,min,max,h-pad,pad); ctx.fillStyle='#38bdf8'; ctx.fillRect(x-barW\/2,y,barW,h-pad-y);});}else{var xValues=data.map(function(p){return Number.isFinite(p.x)?p.x:0;}), xMin=Math.min.apply(null,xValues), xMax=Math.max.apply(null,xValues); ctx.strokeStyle='#38bdf8'; ctx.lineWidth=3; ctx.beginPath(); data.forEach(function(d,i){var x=(type==='scatter'?Number.isFinite(d.x):false)?scale(d.x,xMin,xMax,pad,w-pad):scale(i,0,Math.max(data.length-1,1),pad,w-pad), y=scale(d.value,min,max,h-pad,pad); if(i===0)ctx.moveTo(x,y); else ctx.lineTo(x,y); if(type==='scatter'){ctx.fillStyle='#38bdf8'; ctx.beginPath(); ctx.arc(x,y,4,0,Math.PI*2); ctx.fill();}}); if(type==='line')ctx.stroke();} ctx.fillStyle='#cbd5e1'; ctx.fillText(String(data[0].label),pad,h-18); ctx.fillText(String(data[data.length-1].label),w-pad-90,h-18); renderInsights(data);}\n\t\tfunction drawDoughnut(ctx,data,w,h){var total=data.reduce(function(sum,d){return sum+Math.max(d.value,0);},0), angle=-Math.PI\/2, cx=w\/2, cy=h\/2, radius=Math.min(w,h)*.32, colors=['#38bdf8','#22c55e','#f59e0b','#ef4444','#a78bfa','#14b8a6']; if(!total){drawEmpty('Doughnut needs positive values.'); return;} data.slice(0,8).forEach(function(d,i){var slice=Math.max(d.value,0)\/total*Math.PI*2; ctx.beginPath(); ctx.moveTo(cx,cy); ctx.fillStyle=colors[i%colors.length]; ctx.arc(cx,cy,radius,angle,angle+slice); ctx.closePath(); ctx.fill(); angle+=slice;}); ctx.globalCompositeOperation='destination-out'; ctx.beginPath(); ctx.arc(cx,cy,radius*.55,0,Math.PI*2); ctx.fill(); ctx.globalCompositeOperation='source-over'; ctx.fillStyle='#e5e7eb'; ctx.font='14px system-ui'; ctx.fillText('Top '+Math.min(data.length,8)+' groups',cx-46,cy+5);}\n\t\tfunction renderInsights(data){if(!rows.length){insightList.innerHTML='<li>Load CSV data, an API link, or the demo dataset to begin.<\/li>'; return;} var xCol=xSelect.value, yCol=ySelect.value, agg=aggSelect.value, numeric=numericColumns(), items=['Rows loaded: '+rows.length+'.','Columns detected: '+columns.length+'.','Numeric columns: '+numeric.length+', date columns: '+(profile.date||[]).length+', category columns: '+(profile.category||[]).length+'.','Missing cells found: '+(profile.missing||0)+'.']; if(data.length){var sorted=data.slice().sort(function(a,b){return b.value-a.value;}), values=data.map(function(d){return d.value;}), min=Math.min.apply(null,values), max=Math.max.apply(null,values), avg=values.reduce(function(a,b){return a+b;},0)\/values.length, first=values[0], last=values[values.length-1], change=first?((last-first)\/Math.abs(first))*100:0; if(agg==='none')items.push('This view plots '+yCol+' against '+xCol+' without aggregation.'); else items.push('This view shows '+agg+' of '+(agg==='count'?'rows':yCol)+' by '+xCol+'.'); items.push('Highest visible group\/value: '+sorted[0].label+' at '+sorted[0].value.toLocaleString()+'.'); items.push('Lowest visible group\/value: '+sorted[sorted.length-1].label+' at '+sorted[sorted.length-1].value.toLocaleString()+'.'); items.push('Average visible value is about '+avg.toLocaleString(undefined,{maximumFractionDigits:2})+'.'); if(chartType.value==='line')items.push('First-to-last movement is '+(change>=0?'+':'')+change.toFixed(2)+'%.'); if(chartType.value==='scatter')items.push('Scatter plots are useful for checking whether two numeric fields move together.');} insightList.innerHTML=items.map(function(item){return '<li>'+esc(item)+'<\/li>';}).join('');}\n\t\tfunction loadText(text){var trimmed=String(text||'').trim(); if(!trimmed)return; if(trimmed[0]==='{'||trimmed[0]==='['){try{setRows(normaliseJson(JSON.parse(trimmed))); return;}catch(e){}} setRows(parseCsv(trimmed));}\n\t\tfunction loadDemo(){loadText('Date,Close,Volume,Revenue,Profit\\n2026-01-01,100,1200000,5000000,620000\\n2026-02-01,108,1320000,5400000,700000\\n2026-03-01,104,1500000,5100000,610000\\n2026-04-01,116,1680000,5900000,830000\\n2026-05-01,121,1750000,6250000,910000\\n2026-06-01,118,1600000,6100000,870000');}\n\t\tfunction clearProfitInputs(){Array.prototype.forEach.call(root.querySelectorAll('[data-profit-input]'),function(input){input.value='';});}\n\t\tfunction loadProfitDemo(){mode.value='profit'; clearProfitInputs(); updateMode(); loadText('Date,Revenue,Expenses,Profit,Customer,Product,Employee,Location,Sales Channel,Payment Method,Status,Invoice Status,Inventory Value,Refund Amount,Tax Collected\\n2026-06-01,1850,920,930,Apex Dental,Firewall setup,Mia,Sydney,Phone,Card,Completed,Paid,4200,0,168\\n2026-06-02,620,210,410,Home Studio,Router install,Noah,Auckland,Online,Card,Completed,Paid,3900,0,56\\n2026-06-03,2400,1240,1160,Harbour Foods,Switch upgrade,Mia,Melbourne,In-store,Bank transfer,Completed,Unpaid,6100,0,218\\n2026-06-04,780,360,420,North Clinic,Service desk,Liam,Wellington,Online,Card,Completed,Paid,6050,80,71\\n2026-06-05,1450,690,760,Metro Legal,VPN rollout,Ava,Perth,Marketplace,Card,Completed,Overdue,5800,0,132\\n2026-06-06,980,410,570,Apex Dental,Endpoint security,Noah,Sydney,Online,Card,Completed,Paid,5600,0,89\\n2026-06-07,310,160,150,Home Studio,Cable kit,Liam,Christchurch,In-store,Cash,Returned,Paid,5520,120,28\\n2026-06-08,2200,980,1220,Harbour Foods,Wi-Fi audit,Ava,Melbourne,Phone,Bank transfer,Completed,Paid,5400,0,200\\n2026-06-09,1660,730,930,Metro Legal,Backup setup,Mia,Perth,Online,Card,Pending,Unpaid,5200,0,151\\n2026-06-10,720,280,440,North Clinic,Remote support,Liam,Wellington,Online,Card,Completed,Paid,5100,0,65');}\n\t\tmode.addEventListener('change',function(){updateMode(); remember();});\n\t\tfileInput.addEventListener('change',function(){var file=(fileInput.files?fileInput.files[0]:null); if(!file)return; var reader=new FileReader(); reader.onload=function(){loadText(reader.result||'');}; reader.readAsText(file);});\n\t\tloadApi.addEventListener('click',function(){var url=apiInput.value.trim(); if(!url){insightList.innerHTML='<li>Add a CSV or JSON API link first.<\/li>'; return;} fetch(url).then(function(response){if(!response.ok)throw new Error('Could not load that link.'); return response.text();}).then(loadText).catch(function(error){insightList.innerHTML='<li>'+esc(error.message||'Could not load API data.')+'<\/li>';});});\n\t\tdemoButton.addEventListener('click',loadDemo);\n\t\tprofitDemoButton.addEventListener('click',loadProfitDemo);\n\t\tprofitForm.addEventListener('input',function(){renderProfitDashboard(); remember();});\n\t\tyearCount.addEventListener('change',function(){renderYearAnalysis(); remember();});\n\t\tyearForm.addEventListener('input',function(){renderYearAnalysis(); remember();});\n\t\trenderButton.addEventListener('click',function(){renderChart(); remember();});\n\t\tchartType.addEventListener('change',function(){renderChart(); remember();});\n\t\txSelect.addEventListener('change',function(){renderChart(); remember();});\n\t\tySelect.addEventListener('change',function(){renderChart(); remember();});\n\t\taggSelect.addEventListener('change',function(){renderChart(); remember();});\n\t\troot.addEventListener('click',function(event){\n\t\t\tvar button=event.target.closest('[data-bi-suggest]');\n\t\t\tif(!button)return;\n\t\t\tif(!root.contains(button))return;\n\t\t\tchartType.value=button.getAttribute('data-type')||chartType.value;\n\t\t\txSelect.value=button.getAttribute('data-x')||xSelect.value;\n\t\t\tySelect.value=button.getAttribute('data-y')||ySelect.value;\n\t\t\taggSelect.value=button.getAttribute('data-agg')||aggSelect.value;\n\t\t\trenderChart();\n\t\t\tremember();\n\t\t});\n\t\trestore();\n\t\tupdateMode();\n\t\tif(!rows.length)drawEmpty('Load data to generate a chart.');\n\t}());\n\t<\/script>\n\t<link rel=\"stylesheet\" href=\"https:\/\/silcom.io\/wp-content\/themes\/villavista\/assets\/css\/silcom-company.css?ver=1.1.0\">\n\t<script src=\"https:\/\/silcom.io\/wp-content\/themes\/villavista\/assets\/js\/silcom-company.js?ver=1.1.0\"><\/script>\n\t<script src=\"https:\/\/silcom.io\/wp-content\/themes\/villavista\/assets\/js\/silcom-investment.js?ver=1.0.5\"><\/script>\n\t\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":0,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_uag_custom_page_level_css":"","footnotes":""},"class_list":["post-778","page","type-page","status-publish","hentry"],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false,"woocommerce_thumbnail":false,"woocommerce_single":false,"woocommerce_gallery_thumbnail":false,"dgwt-wcas-product-suggestion":false},"uagb_author_info":{"display_name":"","author_link":"https:\/\/silcom.io\/author\/"},"uagb_comment_info":0,"uagb_excerpt":null,"_links":{"self":[{"href":"https:\/\/silcom.io\/wp-json\/wp\/v2\/pages\/778","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/silcom.io\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/silcom.io\/wp-json\/wp\/v2\/types\/page"}],"replies":[{"embeddable":true,"href":"https:\/\/silcom.io\/wp-json\/wp\/v2\/comments?post=778"}],"version-history":[{"count":0,"href":"https:\/\/silcom.io\/wp-json\/wp\/v2\/pages\/778\/revisions"}],"wp:attachment":[{"href":"https:\/\/silcom.io\/wp-json\/wp\/v2\/media?parent=778"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}