You can get the standalone HTML file in two ways:
## **Option 1: Copy the Complete File Content**
Action: file_editor view /app/solar-calculator-standalone.html
Observation: Here's the result of running `cat -n` on /app/solar-calculator-standalone.html:
1|
2|
3|
4|
5|
6|
Solar Calculator Pro - Calculate Your Solar Savings
7|
8| * {
9| margin: 0;
10| padding: 0;
11| box-sizing: border-box;
12| }
13|
14| body {
15| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
16| -webkit-font-smoothing: antialiased;
17| -moz-osx-font-smoothing: grayscale;
18| line-height: 1.6;
19| }
20|
21| .min-h-screen { min-height: 100vh; }
22| .bg-gradient-to-br { background: linear-gradient(to bottom right, #eff6ff, #f0fdf4); }
23|
24| /* Header Styles */
25| .header {
26| background: white;
27| box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
28| }
29|
30| .header-content {
31| max-width: 1280px;
32| margin: 0 auto;
33| padding: 1rem 1.5rem;
34| }
35|
36| .header-flex {
37| display: flex;
38| align-items: center;
39| }
40|
41| .logo-icon {
42| width: 2rem;
43| height: 2rem;
44| background: #facc15;
45| border-radius: 50%;
46| display: flex;
47| align-items: center;
48| justify-content: center;
49| margin-right: 0.75rem;
50| }
51|
52| .logo-text {
53| font-size: 1.5rem;
54| font-weight: bold;
55| color: #111827;
56| }
57|
58| /* Hero Section */
59| .hero {
60| position: relative;
61| padding: 5rem 0;
62| overflow: hidden;
63| }
64|
65| .hero-bg {
66| position: absolute;
67| inset: 0;
68| z-index: 1;
69| }
70|
71| .hero-img {
72| width: 100%;
73| height: 100%;
74| object-fit: cover;
75| opacity: 0.2;
76| }
77|
78| .hero-overlay {
79| position: absolute;
80| inset: 0;
81| background: linear-gradient(to right, rgba(37, 99, 235, 0.7), rgba(22, 163, 74, 0.7));
82| }
83|
84| .hero-content {
85| position: relative;
86| z-index: 10;
87| max-width: 1280px;
88| margin: 0 auto;
89| padding: 0 1.5rem;
90| text-align: center;
91| }
92|
93| .hero-title {
94| font-size: 3rem;
95| font-weight: bold;
96| color: white;
97| margin-bottom: 1.5rem;
98| }
99|
100| .hero-subtitle {
101| font-size: 1.25rem;
102| color: rgba(255, 255, 255, 0.9);
103| margin-bottom: 2rem;
104| max-width: 48rem;
105| margin-left: auto;
106| margin-right: auto;
107| }
108|
109| /* Main Content */
110| .main-content {
111| padding: 5rem 0;
112| }
113|
114| .container {
115| max-width: 1280px;
116| margin: 0 auto;
117| padding: 0 1.5rem;
118| }
119|
120| .grid {
121| display: grid;
122| gap: 3rem;
123| }
124|
125| @media (min-width: 1024px) {
126| .grid {
127| grid-template-columns: 1fr 1fr;
128| }
129| .hero-title {
130| font-size: 4rem;
131| }
132| }
133|
134| /* Form Styles */
135| .form-card, .results-card {
136| background: white;
137| border-radius: 1rem;
138| box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
139| padding: 2rem;
140| }
141|
142| .card-title {
143| font-size: 1.5rem;
144| font-weight: bold;
145| color: #111827;
146| margin-bottom: 2rem;
147| }
148|
149| .form-group {
150| margin-bottom: 1.5rem;
151| }
152|
153| .label {
154| display: block;
155| font-size: 0.875rem;
156| font-weight: 600;
157| color: #374151;
158| margin-bottom: 0.5rem;
159| }
160|
161| .input, .select {
162| width: 100%;
163| padding: 0.75rem 1rem;
164| border: 1px solid #d1d5db;
165| border-radius: 0.5rem;
166| font-size: 1rem;
167| transition: all 0.2s;
168| }
169|
170| .input:focus, .select:focus {
171| outline: none;
172| border-color: #3b82f6;
173| box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
174| }
175|
176| .input-with-icon {
177| position: relative;
178| }
179|
180| .input-icon {
181| position: absolute;
182| left: 1rem;
183| top: 0.75rem;
184| color: #6b7280;
185| }
186|
187| .input-with-icon .input {
188| padding-left: 2rem;
189| }
190|
191| /* Slider Styles */
192| .slider {
193| width: 100%;
194| height: 6px;
195| background: #e5e7eb;
196| border-radius: 3px;
197| outline: none;
198| -webkit-appearance: none;
199| appearance: none;
200| }
201|
202| .slider::-webkit-slider-thumb {
203| -webkit-appearance: none;
204| appearance: none;
205| width: 20px;
206| height: 20px;
207| background: linear-gradient(to right, #2563eb, #16a34a);
208| border-radius: 50%;
209| cursor: pointer;
210| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
211| }
212|
213| .slider::-moz-range-thumb {
214| width: 20px;
215| height: 20px;
216| background: linear-gradient(to right, #2563eb, #16a34a);
217| border-radius: 50%;
218| cursor: pointer;
219| border: none;
220| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
221| }
222|
223| .slider-labels {
224| display: flex;
225| justify-content: space-between;
226| font-size: 0.875rem;
227| color: #6b7280;
228| margin-top: 0.25rem;
229| }
230|
231| /* Button Styles */
232| .btn-primary {
233| width: 100%;
234| background: linear-gradient(to right, #2563eb, #16a34a);
235| color: white;
236| font-weight: bold;
237| padding: 1rem 1.5rem;
238| border-radius: 0.5rem;
239| border: none;
240| cursor: pointer;
241| transition: all 0.2s;
242| font-size: 1rem;
243| }
244|
245| .btn-primary:hover:not(:disabled) {
246| background: linear-gradient(to right, #1d4ed8, #15803d);
247| transform: scale(1.05);
248| }
249|
250| .btn-primary:disabled {
251| opacity: 0.5;
252| cursor: not-allowed;
253| transform: none;
254| }
255|
256| .btn-secondary {
257| background: linear-gradient(to right, #2563eb, #16a34a);
258| color: white;
259| font-weight: 600;
260| padding: 0.5rem 1.5rem;
261| border-radius: 0.5rem;
262| border: none;
263| cursor: pointer;
264| transition: all 0.2s;
265| }
266|
267| .btn-secondary:hover {
268| background: linear-gradient(to right, #1d4ed8, #15803d);
269| }
270|
271| /* Results Styles */
272| .results-grid {
273| display: grid;
274| grid-template-columns: 1fr 1fr;
275| gap: 1rem;
276| margin-bottom: 1.5rem;
277| }
278|
279| .metric-card {
280| border-radius: 0.5rem;
281| padding: 1rem;
282| text-align: center;
283| }
284|
285| .metric-card.blue {
286| background: #dbeafe;
287| }
288|
289| .metric-card.green {
290| background: #dcfce7;
291| }
292|
293| .metric-value {
294| font-size: 1.5rem;
295| font-weight: bold;
296| }
297|
298| .metric-value.blue { color: #2563eb; }
299| .metric-value.green { color: #16a34a; }
300|
301| .metric-label {
302| font-size: 0.875rem;
303| margin-top: 0.25rem;
304| }
305|
306| .metric-label.blue { color: #1e40af; }
307| .metric-label.green { color: #15803d; }
308|
309| .results-section {
310| border-top: 1px solid #e5e7eb;
311| padding-top: 1.5rem;
312| margin-top: 1.5rem;
313| }
314|
315| .results-section:first-child {
316| border-top: none;
317| padding-top: 0;
318| margin-top: 0;
319| }
320|
321| .section-title {
322| font-weight: 600;
323| color: #111827;
324| margin-bottom: 1rem;
325| }
326|
327| .results-row {
328| display: flex;
329| justify-content: space-between;
330| margin-bottom: 0.75rem;
331| }
332|
333| .results-row.highlight {
334| font-size: 1.125rem;
335| font-weight: bold;
336| border-top: 1px solid #e5e7eb;
337| padding-top: 0.5rem;
338| }
339|
340| .text-green { color: #16a34a; }
341| .font-semibold { font-weight: 600; }
342|
343| .cta-card {
344| background: linear-gradient(to right, #eff6ff, #f0fdf4);
345| border-radius: 0.5rem;
346| padding: 1.5rem;
347| text-align: center;
348| border-top: 1px solid #e5e7eb;
349| margin-top: 1.5rem;
350| }
351|
352| .cta-title {
353| font-weight: 600;
354| color: #111827;
355| margin-bottom: 0.5rem;
356| }
357|
358| .cta-text {
359| font-size: 0.875rem;
360| color: #6b7280;
361| margin-bottom: 1rem;
362| }
363|
364| /* Loading Styles */
365| .loading-flex {
366| display: flex;
367| align-items: center;
368| justify-content: center;
369| }
370|
371| .spinner {
372| width: 1.25rem;
373| height: 1.25rem;
374| border: 2px solid transparent;
375| border-top: 2px solid white;
376| border-radius: 50%;
377| animation: spin 1s linear infinite;
378| margin-right: 0.5rem;
379| }
380|
381| @keyframes spin {
382| to { transform: rotate(360deg); }
383| }
384|
385| /* Empty State */
386| .empty-state {
387| text-align: center;
388| padding: 3rem 0;
389| }
390|
391| .empty-icon {
392| width: 4rem;
393| height: 4rem;
394| background: #f3f4f6;
395| border-radius: 50%;
396| display: flex;
397| align-items: center;
398| justify-content: center;
399| margin: 0 auto 1rem;
400| }
401|
402| .empty-text {
403| color: #6b7280;
404| }
405|
406| /* Footer */
407| .footer {
408| background: #111827;
409| color: white;
410| padding: 3rem 0;
411| }
412|
413| .footer-content {
414| max-width: 1280px;
415| margin: 0 auto;
416| padding: 0 1.5rem;
417| text-align: center;
418| }
419|
420| .footer-logo {
421| display: flex;
422| align-items: center;
423| justify-content: center;
424| margin-bottom: 1rem;
425| }
426|
427| .footer-title {
428| font-size: 1.25rem;
429| font-weight: bold;
430| }
431|
432| .footer-text {
433| color: #9ca3af;
434| margin-bottom: 1rem;
435| }
436|
437| .footer-disclaimer {
438| font-size: 0.875rem;
439| color: #6b7280;
440| }
441|
442| /* Responsive */
443| @media (max-width: 768px) {
444| .hero-title { font-size: 2rem; }
445| .hero-subtitle { font-size: 1rem; }
446| .main-content { padding: 2rem 0; }
447| .form-card, .results-card { padding: 1.5rem; }
448| }
449|
450|
451|
452|
453|
454|
466|
467|
468|
469|
470|

471|
472|
473|
474|
Calculate Your Solar Savings
475|
476| Get an instant estimate of how much you can save with solar panels.
477| Find out your system size, costs, and 25-year savings in minutes.
478|
479|
480|
481|
482|
483|
484|
485|
486|
487|
488|
548|
549|
550|
551|
Your Solar Report
552|
553|
554|
555|
556|
557|
558|
559|
Fill out the form to see your personalized solar estimate
560|
561|
562|
563|
564|
565|
566|
0
567|
Solar Panels
568|
569|
570|
0 kW
571|
System Size
572|
573|
574|
575|
576|
577|
Financial Summary
578|
579| Gross System Cost:
580| $0
581|
582|
583| Federal Tax Credit (30%):
584| -$0
585|
586|
587| Net System Cost:
588| $0
589|
590|
591|
592|
593|
594|
Energy & Savings
595|
596| Annual Energy Usage:
597| 0 kWh
598|
599|
600| Annual Solar Production:
601| 0 kWh
602|
603|
604| Energy Offset:
605| 0%
606|
607|
608| Monthly Savings:
609| $0
610|
611|
612| Annual Savings:
613| $0
614|
615|
616|
617|
618|
619|
Long-term Analysis
620|
621| Payback Period:
622| 0 years
623|
624|
625| 25-Year Net Savings:
626| $0
627|
628|
629|
630|
631|
632|
Ready to Go Solar?
633|
Get personalized quotes from pre-screened local installers
634|
635|
636|
637|
638|
639|
640|
641|
642|
643|
659|
660|
661|
662| // Global variables
663| let isCalculating = false;
664|
665| // DOM elements
666| const form = document.getElementById('solarForm');
667| const zipCodeInput = document.getElementById('zipCode');
668| const monthlyBillInput = document.getElementById('monthlyBill');
669| const roofDirectionSelect = document.getElementById('roofDirection');
670| const utilityInput = document.getElementById('utility');
671| const desiredOffsetSlider = document.getElementById('desiredOffset');
672| const offsetValueSpan = document.getElementById('offsetValue');
673| const roofPitchSelect = document.getElementById('roofPitch');
674| const calculateBtn = document.getElementById('calculateBtn');
675| const emptyState = document.getElementById('emptyState');
676| const resultsContent = document.getElementById('resultsContent');
677|
678| // Result elements
679| const panelsResult = document.getElementById('panelsResult');
680| const systemSizeResult = document.getElementById('systemSizeResult');
681| const grossCostResult = document.getElementById('grossCostResult');
682| const taxCreditResult = document.getElementById('taxCreditResult');
683| const netCostResult = document.getElementById('netCostResult');
684| const annualUsageResult = document.getElementById('annualUsageResult');
685| const annualProductionResult = document.getElementById('annualProductionResult');
686| const offsetResult = document.getElementById('offsetResult');
687| const monthlySavingsResult = document.getElementById('monthlySavingsResult');
688| const annualSavingsResult = document.getElementById('annualSavingsResult');
689| const paybackResult = document.getElementById('paybackResult');
690| const netSavings25Result = document.getElementById('netSavings25Result');
691|
692| // Event listeners
693| desiredOffsetSlider.addEventListener('input', function() {
694| offsetValueSpan.textContent = this.value;
695| });
696|
697| // Form validation
698| function validateForm() {
699| const zipCode = zipCodeInput.value.trim();
700| const monthlyBill = monthlyBillInput.value.trim();
701|
702| if (zipCode && monthlyBill && !isCalculating) {
703| calculateBtn.disabled = false;
704| } else {
705| calculateBtn.disabled = true;
706| }
707| }
708|
709| zipCodeInput.addEventListener('input', validateForm);
710| monthlyBillInput.addEventListener('input', validateForm);
711|
712| // Form submission
713| form.addEventListener('submit', function(e) {
714| e.preventDefault();
715| calculateSolar();
716| });
717|
718| // Solar calculation function
719| function calculateSolar() {
720| if (isCalculating) return;
721|
722| isCalculating = true;
723| calculateBtn.innerHTML = `
724|
725|
726| Calculating...
727|
728| `;
729| calculateBtn.disabled = true;
730|
731| // Get form data
732| const formData = {
733| zipCode: zipCodeInput.value.trim(),
734| monthlyBill: parseFloat(monthlyBillInput.value) || 0,
735| roofDirection: roofDirectionSelect.value,
736| utility: utilityInput.value.trim(),
737| desiredOffset: parseInt(desiredOffsetSlider.value) || 100,
738| roofPitch: roofPitchSelect.value
739| };
740|
741| // Simulate API call delay
742| setTimeout(() => {
743| const results = performCalculations(formData);
744| displayResults(results);
745|
746| isCalculating = false;
747| calculateBtn.innerHTML = 'Calculate My Solar Savings';
748| validateForm();
749| }, 1500);
750| }
751|
752| // Solar calculation logic
753| function performCalculations(data) {
754| const monthlyBill = data.monthlyBill;
755| const desiredOffset = data.desiredOffset;
756|
757| // Basic calculations using industry standards
758| const avgElectricityRate = 0.14; // $0.14 per kWh average
759| const monthlyUsage = monthlyBill / avgElectricityRate; // kWh per month
760| const annualUsage = monthlyUsage * 12; // kWh per year
761|
762| // Solar irradiance factors by region (simplified)
763| function getIrradianceFactor(zipCode) {
764| const zip = parseInt(zipCode) || 0;
765| if (zip >= 90000) return 1.4; // California
766| if (zip >= 80000) return 1.3; // Mountain states
767| if (zip >= 70000) return 1.2; // South
768| if (zip >= 60000) return 1.0; // Midwest
769| if (zip >= 40000) return 0.9; // Northeast
770| return 1.1; // Default
771| }
772|
773| const irradianceFactor = getIrradianceFactor(data.zipCode);
774|
775| // Roof direction efficiency
776| const roofEfficiency = {
777| 'south': 1.0,
778| 'southeast': 0.95,
779| 'southwest': 0.95,
780| 'east': 0.85,
781| 'west': 0.85,
782| 'north': 0.65
783| };
784|
785| // Roof pitch efficiency
786| const pitchEfficiency = {
787| 'flat': 0.85,
788| 'low': 0.9,
789| 'medium': 1.0,
790| 'steep': 0.95
791| };
792|
793| const systemEfficiency = roofEfficiency[data.roofDirection] * pitchEfficiency[data.roofPitch];
794|
795| // Calculate system size needed
796| const targetProduction = (annualUsage * desiredOffset / 100);
797| const systemSizeKW = targetProduction / (1400 * irradianceFactor * systemEfficiency); // 1400 kWh per kW average
798|
799| // Equipment specifications
800| const panelWattage = 400; // Modern panels are typically 400W
801| const numberOfPanels = Math.ceil((systemSizeKW * 1000) / panelWattage);
802| const actualSystemSize = (numberOfPanels * panelWattage) / 1000;
803|
804| // Financial calculations
805| const costPerWatt = 3.50; // Average installed cost
806| const grossSystemCost = actualSystemSize * 1000 * costPerWatt;
807| const federalTaxCredit = grossSystemCost * 0.30; // 30% federal tax credit
808| const netSystemCost = grossSystemCost - federalTaxCredit;
809|
810| // Savings calculations
811| const annualProduction = actualSystemSize * 1400 * irradianceFactor * systemEfficiency;
812| const annualSavings = annualProduction * avgElectricityRate;
813| const monthlyProductionSavings = annualSavings / 12;
814| const paybackPeriod = netSystemCost / annualSavings;
815|
816| // 25 year calculations (with 3% electricity rate inflation)
817| let totalSavings = 0;
818| let currentRate = avgElectricityRate;
819| for (let year = 1; year <= 25; year++) {
820| totalSavings += annualProduction * currentRate;
821| currentRate *= 1.03; // 3% annual increase
822| }
823| const netSavings25Year = totalSavings - netSystemCost;
824|
825| return {
826| numberOfPanels,
827| systemSizeKW: actualSystemSize,
828| annualUsage,
829| annualProduction,
830| grossSystemCost,
831| federalTaxCredit,
832| netSystemCost,
833| monthlyProductionSavings,
834| annualSavings,
835| paybackPeriod,
836| netSavings25Year,
837| offsetPercentage: Math.min((annualProduction / annualUsage) * 100, 100)
838| };
839| }
840|
841| // Display results
842| function displayResults(results) {
843| // Hide empty state and show results
844| emptyState.style.display = 'none';
845| resultsContent.style.display = 'block';
846|
847| // Update result values
848| panelsResult.textContent = results.numberOfPanels;
849| systemSizeResult.textContent = formatNumber(results.systemSizeKW, 1) + ' kW';
850| grossCostResult.textContent = formatCurrency(results.grossSystemCost);
851| taxCreditResult.textContent = '-' + formatCurrency(results.federalTaxCredit);
852| netCostResult.textContent = formatCurrency(results.netSystemCost);
853| annualUsageResult.textContent = formatNumber(results.annualUsage, 0) + ' kWh';
854| annualProductionResult.textContent = formatNumber(results.annualProduction, 0) + ' kWh';
855| offsetResult.textContent = formatNumber(results.offsetPercentage, 1) + '%';
856| monthlySavingsResult.textContent = formatCurrency(results.monthlyProductionSavings);
857| annualSavingsResult.textContent = formatCurrency(results.annualSavings);
858| paybackResult.textContent = formatNumber(results.paybackPeriod, 1) + ' years';
859| netSavings25Result.textContent = formatCurrency(results.netSavings25Year);
860| }
861|
862| // Utility functions
863| function formatCurrency(amount) {
864| return new Intl.NumberFormat('en-US', {
865| style: 'currency',
866| currency: 'USD',
867| minimumFractionDigits: 0,
868| maximumFractionDigits: 0,
869| }).format(amount);
870| }
871|
872| function formatNumber(number, decimals = 1) {
873| return new Intl.NumberFormat('en-US', {
874| minimumFractionDigits: decimals,
875| maximumFractionDigits: decimals,
876| }).format(number);
877| }
878|
879| // Initialize
880| validateForm();
881|
882|
883|