{"id":862,"date":"2023-06-07T09:27:59","date_gmt":"2023-06-07T07:27:59","guid":{"rendered":"https:\/\/moisolar.com\/?page_id=862"},"modified":"2026-01-14T15:04:38","modified_gmt":"2026-01-14T13:04:38","slug":"fixture-calculator","status":"publish","type":"page","link":"https:\/\/moisolar.com\/fi\/kalustelaskuri\/","title":{"rendered":"Kiinnikkeiden Laskin"},"content":{"rendered":"<div data-elementor-type=\"wp-page\" data-elementor-id=\"862\" class=\"elementor elementor-862\" data-elementor-post-type=\"page\">\n\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-20e7271 elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no\" data-id=\"20e7271\" data-element_type=\"section\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t\t\t<div class=\"elementor-background-overlay\"><\/div>\n\t\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-f6b6fab\" data-id=\"f6b6fab\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-ba45c04 elementor-widget-mobile__width-initial elementor-widget elementor-widget-heading\" data-id=\"ba45c04\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Aurinkopaneelien Kiinnitysj\u00e4rjestelm\u00e4n Laskin<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-861e802 elementor-widget-mobile__width-initial elementor-widget elementor-widget-text-editor\" data-id=\"861e802\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<div class=\"group w-full text-gray-800 dark:text-gray-100 border-b border-black\/10 dark:border-gray-900\/50 bg-gray-50 dark:bg-[#444654]\"><div class=\"flex p-4 gap-4 text-base md:gap-6 md:max-w-2xl lg:max-w-[38rem] xl:max-w-3xl md:py-6 lg:px-0 m-auto\"><div class=\"relative flex w-[calc(100%-50px)] flex-col gap-1 md:gap-3 lg:w-[calc(100%-115px)]\"><div class=\"flex flex-grow flex-col gap-3\"><div class=\"min-h-[20px] flex flex-col items-start gap-4 whitespace-pre-wrap break-words\"><div class=\"markdown prose w-full break-words dark:prose-invert light\"><p>Tervetuloa parannettuun aurinkopaneelien kiinnityslaskuriimme, joka hy\u00f6dynt\u00e4\u00e4 nyt SGS-testituloksia vertaansa vailla olevan tarkkuuden saavuttamiseksi. T\u00e4m\u00e4 intuitiivinen ty\u00f6kalu yksinkertaistaa aurinkopaneeliasennuksen suunnittelua antamalla heti arvion tarvittavista kattokiinnikkeist\u00e4 sy\u00f6tt\u00e4miesi kaupunkitietojen, katon kulman, paneelin koon, kattotyypin ja aurinkopaneelien m\u00e4\u00e4r\u00e4n perusteella. Suunniteltu kaikenkokoisiin projekteihin, se auttaa tehokkaassa resurssien hallinnassa ja sujuvammissa asennuksissa. Aloita aurinkopaneeliasennusprosessisi optimointi t\u00e4n\u00e4\u00e4n tieteellisesti tuetulla laskurillamme.<\/p><\/div><\/div><\/div><\/div><\/div><\/div>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-95303a4 elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no\" data-id=\"95303a4\" data-element_type=\"section\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-5925f62\" data-id=\"5925f62\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-2bf872e elementor-widget elementor-widget-spacer\" data-id=\"2bf872e\" data-element_type=\"widget\" data-widget_type=\"spacer.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-spacer\">\n\t\t\t<div class=\"elementor-spacer-inner\"><\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-04b9f02 elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no\" data-id=\"04b9f02\" data-element_type=\"section\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-a7190c2\" data-id=\"a7190c2\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap\">\n\t\t\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-cf8bb49 elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no\" data-id=\"cf8bb49\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-84143a6\" data-id=\"84143a6\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-0b89350 elementor-widget elementor-widget-html\" data-id=\"0b89350\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\" \/>\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" \/>\n  <title>Moisolar Nordic Slope -kiinnityslaskin<\/title>\n  <style>\n\n:root {\n  --page-bg: #0f172a;\n  --page-bg-secondary: #13213f;\n  --surface: rgba(255, 255, 255, 0.86);\n  --surface-muted: rgba(255, 255, 255, 0.65);\n  --accent: #1b8ef2;\n  --accent-dark: #1363c4;\n  --accent-soft: rgba(27, 142, 242, 0.12);\n  --success: #23a46d;\n  --border-muted: rgba(15, 23, 42, 0.08);\n  --border-strong: rgba(15, 23, 42, 0.18);\n  --text-color: #13213f;\n  --text-muted: #5b657d;\n  --text-invert: #f8fafc;\n  --input-bg: rgba(255, 255, 255, 0.92);\n  --shadow-soft: 0 24px 50px rgba(15, 23, 42, 0.18);\n  --shadow-card: 0 16px 40px rgba(15, 23, 42, 0.12);\n}\n\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  margin: 0;\n  font-family: \"Inter\", \"Segoe UI\", \"Helvetica Neue\", Arial, sans-serif;\n  color: var(--text-color);\n}\n\n.calculator-shell {\n  --layout-max-width: 1100px;\n  min-height: 100vh;\n  background: radial-gradient(circle at top left, #f3f6ff 0%, #e4ecff 40%, #d6e2ff 100%);\n  display: flex;\n  justify-content: center;\n  padding: 3rem 1.5rem;\n  width: 100%;\n}\n\na {\n  color: inherit;\n}\n\nimg {\n  max-width: 100%;\n  height: auto;\n  display: block;\n}\n\nmain.layout {\n  position: relative;\n  width: 100%;\n  max-width: var(--layout-max-width);\n  display: flex;\n  flex-direction: column;\n  gap: 2.25rem;\n  padding: 3rem 3rem 4rem;\n  border-radius: 28px;\n  background: linear-gradient(145deg, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.85));\n  box-shadow: var(--shadow-soft);\n  backdrop-filter: blur(12px);\n}\n\n.layout::before {\n  content: \"\";\n  position: absolute;\n  inset: 0;\n  border-radius: inherit;\n  padding: 1px;\n  background: linear-gradient(135deg, rgba(27, 142, 242, 0.45), rgba(35, 164, 109, 0.35));\n  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);\n  -webkit-mask-composite: xor;\n          mask-composite: exclude;\n}\n\n.intro {\n  position: relative;\n  text-align: center;\n  padding-bottom: 0.5rem;\n}\n\n.intro h1 {\n  margin: 0 0 0.75rem;\n  font-size: 2.4rem;\n  font-weight: 700;\n  letter-spacing: -0.01em;\n}\n\n.intro p {\n  margin: 0;\n  color: var(--text-muted);\n  font-size: 1.05rem;\n  line-height: 1.65;\n}\n\nform#calculatorForm {\n  display: grid;\n  gap: 1.75rem;\n}\n\n.section-card {\n  position: relative;\n  background: var(--surface);\n  border-radius: 16px;\n  padding: 2rem;\n  border: 1px solid var(--border-muted);\n  box-shadow: var(--shadow-card);\n  overflow: hidden;\n}\n\n.section-card::before {\n  content: \"\";\n  position: absolute;\n  inset: -40% 55% auto -20%;\n  height: 320px;\n  background: radial-gradient(circle at center, rgba(27, 142, 242, 0.25), transparent 70%);\n  opacity: 0.4;\n  pointer-events: none;\n}\n\n.section-card h2,\n.section-card h3 {\n  margin: 0 0 1rem;\n  font-size: 1.35rem;\n  letter-spacing: -0.01em;\n}\n\n.field-group + .field-group {\n  margin-top: 1.35rem;\n}\n\n.field-label {\n  display: block;\n  font-size: 0.95rem;\n  font-weight: 600;\n  margin-bottom: 0.4rem;\n  letter-spacing: 0.02em;\n}\n\n.input,\n.input-select {\n  width: 100%;\n  padding: 0.75rem 0.9rem;\n  font-size: 1rem;\n  border: 1px solid var(--border-strong);\n  border-radius: 10px;\n  background: var(--input-bg);\n  color: inherit;\n  transition: border 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;\n}\n\n.input:hover,\n.input-select:hover {\n  border-color: rgba(27, 142, 242, 0.6);\n}\n\n.input:focus,\n.input-select:focus {\n  border-color: var(--accent);\n  box-shadow: 0 0 0 4px var(--accent-soft);\n  outline: none;\n  transform: translateY(-1px);\n}\n\n.help-text {\n  margin: 0.5rem 0 0;\n  color: var(--text-muted);\n  font-size: 0.95rem;\n  line-height: 1.65;\n}\n\n.media-frame {\n  margin: 1.25rem auto 1rem;\n  border-radius: 14px;\n  overflow: hidden;\n  box-shadow: 0 18px 35px rgba(15, 23, 42, 0.12);\n  max-width: 420px;\n}\n\n.media-frame img {\n  max-height: 240px;\n  object-fit: cover;\n}\n\n#roofTypeContainer {\n  display: grid;\n  gap: 1.2rem;\n  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n}\n\n.roof-type-option {\n  position: relative;\n  display: flex;\n  flex-direction: column;\n  gap: 0.95rem;\n  align-items: stretch;\n  padding: 1rem;\n  border: 1px solid var(--border-muted);\n  border-radius: 14px;\n  background: var(--surface-muted);\n  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;\n  cursor: pointer;\n  overflow: hidden;\n}\n\n.roof-type-option img {\n  border-radius: 10px;\n  height: 140px;\n  object-fit: cover;\n  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.35);\n}\n\n.roof-type-option input[type=\"radio\"] {\n  position: absolute;\n  inset: 0;\n  opacity: 0;\n  cursor: pointer;\n}\n\n.roof-type-option span {\n  font-weight: 600;\n  font-size: 0.95rem;\n  letter-spacing: 0.01em;\n}\n\n.roof-type-option:is(:hover, :focus-within) {\n  border-color: rgba(27, 142, 242, 0.6);\n  box-shadow: 0 14px 35px rgba(27, 142, 242, 0.16);\n  transform: translateY(-3px);\n}\n\n.roof-type-option:has(input[type=\"radio\"]:checked) {\n  border-color: var(--accent);\n  box-shadow: 0 18px 40px rgba(27, 142, 242, 0.28);\n  background: #f4f9ff;\n}\n\n#panelTypeContainer {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 1.5rem;\n}\n\n.sr-only {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  margin: -1px;\n  overflow: hidden;\n  clip: rect(0, 0, 0, 0);\n  border: 0;\n}\n\n.panel-config {\n  overflow: visible;\n}\n\n.profile-spacing-block {\n  margin-top: 1.75rem;\n}\n\n.profile-spacing-visual {\n  margin: 1rem auto 1.25rem;\n  max-width: 360px;\n  border-radius: 16px;\n  overflow: hidden;\n  box-shadow: 0 18px 32px rgba(27, 142, 242, 0.18);\n  display: none;\n}\n\n.profile-spacing-visual img {\n  width: 100%;\n  height: auto;\n  display: block;\n}\n\n.panel-config-header {\n  display: flex;\n  flex-direction: column;\n  gap: 0.5rem;\n  margin-bottom: 1.5rem;\n}\n\n.profile-span-options {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 1rem;\n  margin-bottom: 1rem;\n}\n\n.profile-mode-card {\n  position: relative;\n  flex: 1 1 240px;\n  padding: 1rem 1.1rem;\n  border-radius: 14px;\n  border: 1px solid rgba(99, 102, 241, 0.25);\n  background: linear-gradient(140deg, rgba(233, 240, 255, 0.92), rgba(215, 226, 255, 0.9));\n  box-shadow: 0 12px 30px rgba(132, 150, 192, 0.25);\n  cursor: pointer;\n  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;\n  display: grid;\n  gap: 0.4rem;\n}\n\n.profile-mode-card input {\n  position: absolute;\n  inset: 0;\n  opacity: 0;\n  cursor: pointer;\n}\n\n.profile-mode-card strong {\n  font-size: 1rem;\n  letter-spacing: 0.02em;\n}\n\n.profile-mode-card span {\n  font-size: 0.9rem;\n  color: rgba(22, 47, 79, 0.7);\n}\n\n.profile-mode-card:is(:hover, :focus-within) {\n  transform: translateY(-2px);\n  border-color: rgba(99, 102, 241, 0.55);\n  box-shadow: 0 16px 34px rgba(132, 150, 192, 0.35);\n}\n\n.profile-mode-card:has(input:checked) {\n  border-color: rgba(99, 102, 241, 0.8);\n  box-shadow: 0 18px 36px rgba(99, 102, 241, 0.35);\n}\n\n.profile-spacing-block {\n  margin-top: 1.75rem;\n}\n\n.profile-custom-field {\n  display: grid;\n  gap: 0.4rem;\n}\n\n.profile-custom-field.disabled {\n  opacity: 0.55;\n}\n\n.panel-control-bar {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 1rem;\n  background: linear-gradient(145deg, rgba(229, 236, 255, 0.95), rgba(209, 223, 255, 0.9));\n  color: #162f4f;\n  padding: 0.9rem 1.2rem;\n  border-radius: 16px;\n  box-shadow: inset 0 0 0 1px rgba(99, 102, 241, 0.18);\n}\n\n.panel-control-hint {\n  margin: 0;\n  font-size: 0.9rem;\n  color: rgba(22, 47, 79, 0.7);\n}\n\n.panel-stepper {\n  display: inline-flex;\n  align-items: center;\n  gap: 0.55rem;\n  background: rgba(15, 23, 42, 0.08);\n  border-radius: 12px;\n  padding: 0.45rem 0.75rem;\n  box-shadow: inset 0 0 0 1px rgba(99, 102, 241, 0.18);\n}\n\n.panel-stepper-unit {\n  font-size: 0.8rem;\n  letter-spacing: 0.08em;\n  text-transform: uppercase;\n  color: rgba(22, 47, 79, 0.6);\n}\n\n.panel-stepper-btn {\n  width: 36px;\n  height: 36px;\n  border-radius: 10px;\n  border: none;\n  background: linear-gradient(145deg, #f97316, #f0522d);\n  color: #fff;\n  font-size: 1.35rem;\n  font-weight: 600;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  cursor: pointer;\n  transition: transform 0.2s ease, box-shadow 0.2s ease;\n}\n\n.panel-stepper-btn:active {\n  transform: translateY(1px);\n}\n\n.panel-stepper-btn:hover {\n  box-shadow: 0 10px 20px rgba(240, 82, 45, 0.35);\n}\n\n.panel-stepper-btn:focus-visible {\n  outline: 2px solid rgba(255, 255, 255, 0.75);\n  outline-offset: 2px;\n}\n\n.panel-stepper-btn span {\n  pointer-events: none;\n}\n\n.panel-stepper-input {\n  width: 56px;\n  border-radius: 8px;\n  border: 1px solid rgba(99, 102, 241, 0.35);\n  background: rgba(255, 255, 255, 0.9);\n  color: #162f4f;\n  font-weight: 600;\n  font-size: 1rem;\n  text-align: center;\n  padding: 0.3rem 0.35rem;\n  box-shadow: inset 0 1px 3px rgba(15, 23, 42, 0.08);\n}\n\n.panel-stepper-input:focus {\n  outline: none;\n  border-color: rgba(99, 102, 241, 0.65);\n  box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.18);\n}\n\n.panel-grid {\n  margin-top: 1.5rem;\n  display: grid;\n  gap: 1.2rem;\n}\n\n.panel-row-card {\n  background: linear-gradient(150deg, rgba(233, 240, 255, 0.95), rgba(210, 223, 255, 0.92));\n  border-radius: 18px;\n  padding: 1.35rem 1.6rem 1.45rem;\n  color: #162f4f;\n  box-shadow: 0 20px 45px rgba(132, 150, 192, 0.35);\n  border: 1px solid rgba(99, 102, 241, 0.18);\n  display: flex;\n  flex-direction: column;\n  gap: 1rem;\n}\n\n.panel-row-header {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 1rem;\n}\n\n.panel-row-title {\n  font-weight: 600;\n  font-size: 1rem;\n  letter-spacing: 0.04em;\n  text-transform: uppercase;\n}\n\n.panel-row-card .panel-stepper {\n  background: rgba(15, 23, 42, 0.08);\n  border-radius: 10px;\n}\n\n.panel-row-card .panel-stepper-unit {\n  color: rgba(22, 47, 79, 0.55);\n}\n\n\n.panel-preview {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.7rem;\n  background: rgba(236, 242, 255, 0.94);\n  border-radius: 14px;\n  padding: 0.85rem;\n  box-shadow: inset 0 0 0 1px rgba(148, 163, 184, 0.18);\n}\n\n.panel-preview-cell {\n  position: relative;\n  isolation: isolate;\n  border-radius: 11px;\n  border: 1px solid rgba(100, 116, 139, 0.28);\n  flex: 0 0 122px;\n  aspect-ratio: 1134 \/ 1722;\n  box-shadow: 0 14px 24px rgba(99, 102, 241, 0.16);\n  overflow: hidden;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.panel-preview-cell::after {\n  content: \"\";\n  position: absolute;\n  inset: 0;\n  background: url(\"https:\/\/moisolar.com\/wp-content\/uploads\/2025\/10\/panel.jpg\") center\/cover no-repeat;\n  opacity: 0.96;\n  filter: saturate(1.05);\n  transition: transform 0.2s ease;\n}\n\n.panel-preview-more {\n  font-size: 0.85rem;\n  font-weight: 600;\n  background: rgba(249, 115, 22, 0.12);\n  border: 1px solid rgba(249, 115, 22, 0.35);\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  color: rgba(22, 47, 79, 0.9);\n}\n\n.panel-preview-cell span {\n  position: relative;\n  z-index: 1;\n  color: rgba(22, 47, 79, 0.85);\n  font-size: 0.95rem;\n  font-weight: 600;\n  letter-spacing: 0.05em;\n}\n\n.panel-preview-cell:hover::after {\n  transform: scale(1.05);\n}\n\n.panel-preview-more::after {\n  filter: grayscale(0.4) brightness(0.9);\n}\n\n.panel-placeholder {\n  padding: 1.5rem;\n  border-radius: 14px;\n  background: rgba(233, 240, 255, 0.6);\n  text-align: center;\n  font-size: 0.95rem;\n  color: var(--text-muted);\n  border: 1px dashed rgba(99, 102, 241, 0.35);\n}\n\n#buttonContainer {\n  display: flex;\n  align-items: center;\n  gap: 1rem;\n  flex-wrap: wrap;\n}\n\nbutton.button {\n  padding: 0.75rem 1.75rem;\n  border-radius: 12px;\n  border: none;\n  font-size: 1rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: background-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;\n  font-family: inherit;\n  box-shadow: 0 14px 28px rgba(15, 23, 42, 0.12);\n}\n\nbutton.button.primary {\n  background: var(--accent);\n  color: var(--text-invert);\n}\n\nbutton.button.primary:hover {\n  background: var(--accent-dark);\n  transform: translateY(-2px);\n  box-shadow: 0 16px 32px rgba(27, 142, 242, 0.32);\n}\n\nbutton.button.secondary {\n  background: var(--success);\n  color: var(--text-invert);\n}\n\nbutton.button.secondary:hover {\n  background: #1c8257;\n  transform: translateY(-2px);\n}\n\nbutton.button:active {\n  transform: translateY(0);\n}\n\nbutton.button:focus-visible {\n  outline: 3px solid rgba(27, 142, 242, 0.35);\n  outline-offset: 3px;\n}\n\n#saveAsPDF,\n#saveAsCSV,\n#suggestUserToSendEmail,\n#resultTable,\n#resultTable2 {\n  display: none;\n}\n\n#resultsSection {\n  display: none;\n  flex-direction: column;\n  gap: 1.5rem;\n  background: var(--surface);\n  border-radius: 16px;\n  padding: 2rem;\n  border: 1px solid var(--border-muted);\n  box-shadow: var(--shadow-card);\n}\n\n#result,\n#result2 {\n  font-size: 1.05rem;\n  font-weight: 600;\n  gap: 0.85rem;\n  padding: 1.5rem 1.5rem 1.75rem;\n  border-radius: 14px;\n  border: 1px solid rgba(27, 142, 242, 0.18);\n  background: linear-gradient(135deg, rgba(27, 142, 242, 0.12), rgba(35, 164, 109, 0.12));\n  box-shadow: 0 14px 40px rgba(15, 23, 42, 0.08);\n}\n\n#result {\n  display: flex;\n  align-items: center;\n  gap: 1.25rem;\n}\n\n#result img {\n  width: 150px;\n  max-width: 45%;\n  border-radius: 16px;\n  box-shadow: 0 16px 32px rgba(27, 142, 242, 0.22);\n}\n\n#result p {\n  margin: 0;\n  letter-spacing: 0.01em;\n}\n\n#result2 {\n  display: grid;\n  grid-template-columns: minmax(0, 1fr);\n  text-align: center;\n}\n\n#result2 .result-visual-container {\n  display: flex;\n  justify-content: center;\n  padding: 0.15rem 0 0.65rem;\n}\n\n#result2 .result-visual {\n  width: min(420px, 90%);\n  height: auto;\n  border-radius: 20px;\n  box-shadow: 0 20px 35px rgba(27, 142, 242, 0.24);\n}\n\n#result2 .result-text {\n  margin: 0;\n  font-size: 1.1rem;\n  letter-spacing: 0.01em;\n}\n\n.warning {\n  color: #5c3d00;\n  background: rgba(255, 225, 179, 0.55);\n  padding: 1rem 1.2rem;\n  border-radius: 12px;\n  border: 1px solid rgba(157, 95, 0, 0.35);\n  font-size: 0.97rem;\n  line-height: 1.6;\n}\n\n.table-wrapper {\n  border-radius: 16px;\n  border: 1px solid var(--border-muted);\n  background: rgba(255, 255, 255, 0.92);\n  box-shadow: 0 12px 32px rgba(15, 23, 42, 0.08);\n  position: relative;\n  overflow: hidden;\n}\n\n.table-wrapper::after {\n  content: \"\";\n  position: absolute;\n  inset: 0;\n  pointer-events: none;\n  border-radius: inherit;\n  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.35);\n}\n\n.table-scroll {\n  border-radius: inherit;\n  overflow-x: auto;\n  overflow-y: hidden;\n  -webkit-overflow-scrolling: touch;\n  scrollbar-width: thin;\n}\n\n.table-scroll::-webkit-scrollbar {\n  height: 10px;\n}\n\n.table-scroll::-webkit-scrollbar-track {\n  background: rgba(148, 163, 184, 0.12);\n  border-radius: 999px;\n}\n\n.table-scroll::-webkit-scrollbar-thumb {\n  background: rgba(27, 142, 242, 0.35);\n  border-radius: 999px;\n}\n\n.table-scroll::-webkit-scrollbar-thumb:hover {\n  background: rgba(27, 142, 242, 0.55);\n}\n\n.table-header-line {\n  display: block;\n  line-height: 1.2;\n}\n\ntable {\n  border-collapse: collapse;\n  width: max-content;\n  min-width: 100%;\n  background: transparent;\n}\n\nthead th {\n  background: rgba(27, 142, 242, 0.12);\n}\n\n#resultTable td,\n#resultTable th,\n#resultTable2 td,\n#resultTable2 th {\n  padding: 0.75rem 1rem;\n  border-bottom: 1px solid rgba(15, 23, 42, 0.07);\n  text-align: left;\n  font-size: 0.95rem;\n}\n\n#resultTable th,\n#resultTable2 th {\n  background: rgba(27, 142, 242, 0.14);\n  font-weight: 600;\n  letter-spacing: 0.03em;\n}\n\n#resultTable tr:nth-child(even),\n#resultTable2 tr:nth-child(even) {\n  background: rgba(15, 23, 42, 0.025);\n}\n\n#resultTable tr:hover,\n#resultTable2 tr:hover {\n  background: rgba(27, 142, 242, 0.08);\n}\n\n#resultTable img,\n#resultTable2 img {\n  max-width: 82px;\n  border-radius: 10px;\n  box-shadow: 0 10px 18px rgba(15, 23, 42, 0.14);\n}\n\n#suggestUserToSendEmail {\n  color: var(--text-muted);\n  font-size: 0.95rem;\n}\n\n#calculatingIndicator {\n  position: fixed;\n  inset: 0;\n  background: rgba(15, 23, 42, 0.45);\n  color: #fff;\n  display: none;\n  align-items: center;\n  justify-content: center;\n  z-index: 1000;\n  backdrop-filter: blur(8px);\n}\n\n#calculatingIndicator::before {\n  content: \"\";\n  width: 78px;\n  height: 78px;\n  border-radius: 50%;\n  border: 5px solid rgba(255, 255, 255, 0.25);\n  border-top-color: #fff;\n  animation: spinner 1.1s linear infinite;\n  margin-right: 1.5rem;\n}\n\n#calculatingText {\n  font-size: 1.35rem;\n  letter-spacing: 0.08em;\n  animation: pulse 1s ease-in-out infinite;\n}\n\n@keyframes pulse {\n  0%,\n  100% {\n    opacity: 0.4;\n  }\n  50% {\n    opacity: 1;\n  }\n}\n\n@keyframes spinner {\n  to {\n    transform: rotate(360deg);\n  }\n}\n\n@media (max-width: 1024px) {\n  main.layout {\n    padding: 2.5rem 2rem 3.5rem;\n  }\n}\n\n@media (max-width: 768px) {\n  .calculator-shell {\n    padding: 2rem 1rem;\n  }\n\n  main.layout {\n    padding: 2.25rem 1.5rem 3rem;\n  }\n\n  #buttonContainer {\n    flex-direction: column;\n    align-items: stretch;\n  }\n\n  button.button {\n    width: 100%;\n    text-align: center;\n  }\n\n  #result,\n  #result2 {\n    flex-direction: column;\n    text-align: center;\n  }\n\n  .panel-control-bar {\n    flex-direction: column;\n    gap: 0.75rem;\n    align-items: stretch;\n  }\n\n  .panel-row-header {\n    flex-direction: column;\n    align-items: stretch;\n    gap: 0.75rem;\n  }\n\n  .panel-row-card .panel-stepper {\n    justify-content: space-between;\n  }\n}\n\n  <\/style>\n<\/head>\n<body>\n  <div class=\"calculator-shell\">\n    <main class=\"layout\">\n      <header class=\"intro\"><\/header>\n\n    <form id=\"calculatorForm\" novalidate action=\"\">\n      <section id=\"snow-load-info\" class=\"section-card\">\n        <h2>Aseta kaupungin tiedot<\/h2>\n        <p class=\"help-text\">Oikean kaupungin valinta on t\u00e4rke\u00e4\u00e4, koska yleinen lumikuorma vaihtelee sijainnin mukaan. T\u00e4t\u00e4 arvoa k\u00e4ytet\u00e4\u00e4n laskemaan tietyn kuorman teid\u00e4n aurinkopaneeleillenne.<\/p>\n        <figure class=\"media-frame\">\n          <img decoding=\"async\" src=\"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/02\/snow-load-for-cities.png\" alt=\"Snow load heat map for Finland\" loading=\"lazy\" \/>\n        <\/figure>\n        <div class=\"field-group\">\n          <label class=\"field-label\" for=\"city\">Valitse kaupunkisi<\/label>\n          <select id=\"city\" class=\"input-select\">\n<option value=\"Akaa\">Akaa - 2,3 kN\/m\u00b2.<\/option>\n    <option value=\"Alaja\u0308rvi\">Alaj\u00e4rvi - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Alavieska\">Alavieska - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Alavus\">Alavus - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Asikkala\">Asikkala - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Askola\">Askola - 2,7 kN\/m\u00b2.<\/option>\n    <option value=\"Aura\">Aura - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Bra\u0308ndo\u0308\">Br\u00e4nd\u00f6 - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Eckero\u0308\">Ecker\u00f6 - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Enonkoski\">Enonkoski - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Enontekio\u0308\">Enonteki\u00f6 - 3,5 kN\/m\u00b2.<\/option>\n    <option value=\"Espoo\">Espoo - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Eura\">Eura - 2,1 kN\/m\u00b2.<\/option>\n    <option value=\"Eurajoki\">Eurajoki - 2,0 kN\/m\u00b2<\/option>\n    <option value=\"Evija\u0308rvi\">Evij\u00e4rvi - 2.2 kN\/m\u00b2<\/option>\n    <option value=\"Finstro\u0308m\">Finstr\u00f6m - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Forssa\">Forssa - 2,6 kN\/m\u00b2.<\/option>\n    <option value=\"Fo\u0308glo\u0308\">F\u00f6gl\u00f6 - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Geta\">Geta - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Haapaja\u0308rvi\">Haapaj\u00e4rvi - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Haapavesi\">Haapavesi - 2,4 kN\/m\u00b2.<\/option>\n    <option value=\"Hailuoto\">Hailuoto - 2,4 kN\/m\u00b2.<\/option>\n    <option value=\"Halsua\">Halsua - 2,4 kN\/m\u00b2.<\/option>\n    <option value=\"Hamina\">Hamina - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Hammarland\">Hammarland - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Hankasalmi\">Hankasalmi - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Hanko\">Hanko - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Harjavalta\">Harjavalta - 2.0 kN\/m\u00b2<\/option>\n    <option value=\"Hartola\">Hartola - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Hattula\">Hattula - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Hausja\u0308rvi\">Hausj\u00e4rvi - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Heinola\">Heinola - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Heina\u0308vesi\">Hein\u00e4vesi - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Helsinki\">Helsinki - 2,75 kN\/m\u00b2<\/option>\n    <option value=\"Hirvensalmi\">Hirvensalmi - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Hollola\">Hollola - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Honkajoki\">Honkajoki - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Huittinen\">Huittinen - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Humppila\">Humppila - 2,25 kN\/m\u00b2.<\/option>\n    <option value=\"Hyrynsalmi\">Hyrynsalmi - 3,5 kN\/m\u00b2.<\/option>\n    <option value=\"Hyvinka\u0308a\u0308 \">Hyvink\u00e4\u00e4 - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Ha\u0308meenkyro\u0308 \">H\u00e4meenkyr\u00f6 - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Ha\u0308meenlinna \">H\u00e4meenlinna - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Ii  \">Ii - 2,8 kN\/m\u00b2.<\/option>\n    <option value=\"Iisalmi \">Iisalmi - 2,6 kN\/m\u00b2.<\/option>\n    <option value=\"Iitti \">Iitti - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Ikaalinen \">Ikaalinen - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Ilmajoki \">Ilmajoki - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Ilomantsi \">Ilomantsi - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Imatra\">Imatra - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Inari \">Inari - 3,0 kN\/m\u00b2.<\/option>\n    <option value=\"Inkoo\">Inkoo - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Isojoki\">Isojoki - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Isokyro\u0308\">Isokyr\u00f6 - 2,3 kN\/m\u00b2.<\/option>\n    <option value=\"Janakkala\">Janakkala - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Joensuu\">Joensuu - 2,6 kN\/m\u00b2.<\/option>\n    <option value=\"Jokioinen\">Jokioinen - 2,6 kN\/m\u00b2.<\/option>\n    <option value=\"Jomala\">Jomala - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Joroinen\">Joroinen - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Joutsa\">Joutsa - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Juuka\">Juuka - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Juupajoki\">Juupajoki - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Juva\">Juva - 2,5 kN\/m\u00b2 Juva - 2,5 kN\/m\u00b2 Juva - 2,5 kN\/m\u00b2<\/option>\n    <option value=\"Jyva\u0308skyla\u0308\">Jyv\u00e4skyl\u00e4 - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Ja\u0308mija\u0308rvi\">J\u00e4mij\u00e4rvi - 2,5 kN\/m\u00b2<\/option>\n    <option value=\"Ja\u0308msa\u0308\">J\u00e4ms\u00e4 - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Ja\u0308rvenpa\u0308a\u0308\">J\u00e4rvenp\u00e4\u00e4 - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Kaarina\">Kaarina - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Kaavi\">Kaavi - 2,65 kN\/m\u00b2.<\/option>\n    <option value=\"Kajaani\">Kajaani - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Kalajoki\">Kalajoki - 2.0 kN\/m\u00b2<\/option>\n    <option value=\"Kangasala\">Kangasala - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Kangasniemi\">Kangasniemi - 2,5 kN\/m\u00b2<\/option>\n    <option value=\"Kankaanpa\u0308a\u0308\">Kankaanp\u00e4\u00e4 - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Kannonkoski\">Kannonkoski - 2.5 kN\/m\u00b2<\/option>\n    <option value=\"Kannus\">Kannus - 2,1 kN\/m\u00b2.<\/option>\n    <option value=\"Karijoki\">Karijoki - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Karkkila\">Karkkila - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Karstula\">Karstula - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Karvia\">Karvia - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Kaskinen\">Kaskinen - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Kauhajoki\">Kauhajoki - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Kauhava\">Kauhava - 2,3 kN\/m\u00b2.<\/option>\n    <option value=\"Kauniainen\">Kauniainen - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Kaustinen\">Kaustinen - 2,2 kN\/m\u00b2.<\/option>\n    <option value=\"Keitele \">Keitele - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Kemi\">Kemi - 3,0 kN\/m\u00b2.<\/option>\n    <option value=\"Kemija\u0308rvi\">Kemij\u00e4rvi - 3.0 kN\/m\u00b2<\/option>\n    <option value=\"Keminmaa\">Keminmaa - 3,0 kN\/m\u00b2 Keminmaa - 3,0 kN\/m\u00b2.<\/option>\n    <option value=\"Kemio\u0308nsaari\">Kemi\u00f6nsaari - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Kempele\">Kempele - 2,25 kN\/m\u00b2.<\/option>\n    <option value=\"Kerava\">Kerava - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Keuruu\">Keuruu - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Kihnio\u0308\">Kihni\u00f6 - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Kinnula\">Kinnula - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Kirkkonummi\">Kirkkonummi - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Kitee\">Kitee - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Kittila\u0308\">Kittil\u00e4 - 3,0 kN\/m\u00b2<\/option>\n    <option value=\"Kiuruvesi\">Kiuruvesi - 2,55 kN\/m\u00b2.<\/option>\n    <option value=\"Kivija\u0308rvi\">Kivij\u00e4rvi - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Kokema\u0308ki\">Kokem\u00e4ki - 2,1 kN\/m\u00b2.<\/option>\n    <option value=\"Kokkola\">Kokkola - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Kolari\">Kolari - 3,0 kN\/m\u00b2.<\/option>\n    <option value=\"Konnevesi\">Konnevesi - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Kontiolahti\">Kontiolahti - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Korsna\u0308s\">Korsn\u00e4s - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Koski Tl\">Koski Tl - 2,6 kN\/m\u00b2.<\/option>\n    <option value=\"Kotka\">Kotka - 2,65 kN\/m\u00b2.<\/option>\n    <option value=\"Kouvola\">Kouvola - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Kristiinankaupunki\">Kristiinankaupunki - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Kruunupyy\">Kruunupyy - 2,2 kN\/m\u00b2.<\/option>\n    <option value=\"Kuhmo\">Kuhmo - 3,0 kN\/m\u00b2.<\/option>\n    <option value=\"Kuhmoinen\">Kuhmoinen - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Kumlinge\">Kumlinge - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Kuopio\">Kuopio - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Kuortane\">Kuortane - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Kurikka\">Kurikka - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Kustavi\">Kustavi - 2,1 kN\/m\u00b2.<\/option>\n    <option value=\"Kuusamo\">Kuusamo - 3,5 kN\/m\u00b2.<\/option>\n    <option value=\"Kyyja\u0308rvi\">Kyyj\u00e4rvi - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Ka\u0308rko\u0308la\u0308\">K\u00e4rk\u00f6l\u00e4 - 2.75 kN\/m\u00b2<\/option>\n    <option value=\"Ka\u0308rsa\u0308ma\u0308ki\">K\u00e4rs\u00e4m\u00e4ki - 2,5 kN\/m\u00b2<\/option>\n    <option value=\"Ko\u0308kar\">K\u00f6kar - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Lahti\">Lahti - 2,65 kN\/m\u00b2.<\/option>\n    <option value=\"Laihia\">Laihia - 2,3 kN\/m\u00b2.<\/option>\n    <option value=\"Laitila\">Laitila - 2,2 kN\/m\u00b2 Laitila - 2,2 kN\/m\u00b2.<\/option>\n    <option value=\"Lapinja\u0308rvi\">Lapinj\u00e4rvi - 2,5 kN\/m\u00b2<\/option>\n    <option value=\"Lapinlahti\">Lapinlahti - 2,6 kN\/m\u00b2<\/option>\n    <option value=\"Lappaja\u0308rvi\">Lappaj\u00e4rvi - 2,4 kN\/m\u00b2.<\/option>\n    <option value=\"Lappeenranta\">Lappeenranta - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Lapua\">Lapua - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Laukaa \">Laukaa - 2,5 kN\/m\u00b2 Laukaa - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Lemi\">Lemi - 2,7 kN\/m\u00b2.<\/option>\n    <option value=\"Lemland\">Lemland - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Lempa\u0308a\u0308la\u0308\">Lemp\u00e4\u00e4l\u00e4 - 2,4 kN\/m\u00b2<\/option>\n    <option value=\"Leppa\u0308virta\">Lepp\u00e4virta - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Lestija\u0308rvi\">Lestij\u00e4rvi - 2,5 kN\/m\u00b2<\/option>\n    <option value=\"Lieksa\">Lieksa - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Lieto\">Lieto - 2,6 kN\/m\u00b2.<\/option>\n    <option value=\"Liminka\">Liminka - 2,45 kN\/m\u00b2.<\/option>\n    <option value=\"Liperi\">Liperi - 2,55 kN\/m\u00b2.<\/option>\n    <option value=\"Lohja\">Lohja - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Loimaa\">Loimaa - 2,65 kN\/m\u00b2.<\/option>\n    <option value=\"Loppi\">Loppi - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Loviisa\">Loviisa - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Luhanka\">Luhanka - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Lumijoki\">Lumijoki - 2,25 kN\/m\u00b2.<\/option>\n    <option value=\"Lumparland\">Lumparland - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Luoto\">Luoto - 2,0 kN\/m\u00b2 Luoto - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Luuma\u0308ki\">Luum\u00e4ki - 2.75 kN\/m\u00b2<\/option>\n    <option value=\"Maalahti\">Maalahti - 2.0 kN\/m\u00b2<\/option>\n    <option value=\"Maarianhamina\">Maarianhamina - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Marttila\">Marttila - 2,6 kN\/m\u00b2<\/option>\n    <option value=\"Masku\">Masku - 2,3 kN\/m\u00b2.<\/option>\n    <option value=\"Merija\u0308rvi\">Merij\u00e4rvi - 2,0 kN\/m\u00b2<\/option>\n    <option value=\"Merikarvia\">Merikarvia - 2,4 kN\/m\u00b2.<\/option>\n    <option value=\"Miehikka\u0308la\u0308\">Miehikk\u00e4l\u00e4 - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Mikkeli\">Mikkeli - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Muhos\">Muhos - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Multia\">Multia - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Muonio\">Muonio - 3,0 kN\/m\u00b2.<\/option>\n    <option value=\"Mustasaari\">Mustasaari - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Muurame\">Muurame - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Myna\u0308ma\u0308ki\">Myn\u00e4m\u00e4ki - 2,3 kN\/m\u00b2.<\/option>\n    <option value=\"Myrskyla\u0308\">Myrskyl\u00e4 - 2,65 kN\/m\u00b2.<\/option>\n    <option value=\"Ma\u0308ntsa\u0308la\u0308\">M\u00e4nts\u00e4l\u00e4 - 2,75 kN\/m\u00b2<\/option>\n    <option value=\"Ma\u0308ntta\u0308\u2010Vilppula\">M\u00e4ntt\u00e4-Vilppula - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Ma\u0308ntyharju\">M\u00e4ntyharju - 2.5 kN\/m\u00b2<\/option>\n    <option value=\"Naantali\">Naantali - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Nakkila\">Nakkila - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Nivala\">Nivala - 2,35 kN\/m\u00b2.<\/option>\n    <option value=\"Nokia\">Nokia - 2,45 kN\/m\u00b2<\/option>\n    <option value=\"Nousiainen\">Nousiainen - 2,3 kN\/m\u00b2.<\/option>\n    <option value=\"Nurmes\">Nurmes - 3,0 kN\/m\u00b2.<\/option>\n    <option value=\"Nurmija\u0308rvi\">Nurmij\u00e4rvi - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Na\u0308rpio\u0308\">N\u00e4rpi\u00f6 - 2,1 kN\/m\u00b2.<\/option>\n    <option value=\"Orimattila \">Orimattila - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Oripa\u0308a\u0308 \">Orip\u00e4\u00e4 - 2,2 kN\/m\u00b2<\/option>\n    <option value=\"Orivesi\">Orivesi - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Oulainen\">Oulainen - 2,15 kN\/m\u00b2.<\/option>\n    <option value=\"Oulu\">Oulu - 2,45 kN\/m\u00b2.<\/option>\n    <option value=\"Outokumpu\">Outokumpu - 2,6 kN\/m\u00b2.<\/option>\n    <option value=\"Padasjoki\">Padasjoki - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Paimio\">Paimio - 2,6 kN\/m\u00b2.<\/option>\n    <option value=\"Paltamo\">Paltamo - 3,3 kN\/m\u00b2.<\/option>\n    <option value=\"Parainen\">Parainen - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Parikkala\">Parikkala - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Parkano\">Parkano - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Pederso\u0308ren kunta\">Peders\u00f6ren kunta - 2.1 kN\/m\u00b2<\/option>\n    <option value=\"Pelkosenniemi\">Pelkosenniemi - 2,9 kN\/m\u00b2.<\/option>\n    <option value=\"Pello\">Pello - 3,0 kN\/m\u00b2.<\/option>\n    <option value=\"Perho\">Perho - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Pertunmaa\">Pertunmaa - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Peta\u0308ja\u0308vesi\">Pet\u00e4j\u00e4vesi - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Pieksa\u0308ma\u0308ki\">Pieks\u00e4m\u00e4ki - 2,5 kN\/m\u00b2<\/option>\n    <option value=\"Pielavesi\">Pielavesi - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Pietarsaari\">Pietarsaari - 2.0 kN\/m\u00b2<\/option>\n    <option value=\"Pihtipudas\">Pihtipudas - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Pirkkala\">Pirkkala - 2,4 kN\/m\u00b2.<\/option>\n    <option value=\"Polvija\u0308rvi\">Polvij\u00e4rvi - 2,75 kN\/m\u00b2<\/option>\n    <option value=\"Pomarkku\">Pomarkku - 2,4 kN\/m\u00b2.<\/option>\n    <option value=\"Pori\">Pori - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Pornainen\">Pornainen - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Porvoo\">Porvoo - 2,65 kN\/m\u00b2.<\/option>\n    <option value=\"Posio\">Posio - 3,5 kN\/m\u00b2.<\/option>\n    <option value=\"Pudasja\u0308rvi\">Pudasj\u00e4rvi - 3,5 kN\/m\u00b2.<\/option>\n    <option value=\"Pukkila\">Pukkila - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Punkalaidun\">Punkalaidun - 2,1 kN\/m\u00b2.<\/option>\n    <option value=\"Puolanka\">Puolanka - 3,5 kN\/m\u00b2.<\/option>\n    <option value=\"Puumala\">Puumala - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Pyhta\u0308a\u0308\">Pyht\u00e4\u00e4 - 2.5 kN\/m\u00b2<\/option>\n    <option value=\"Pyha\u0308joki\">Pyh\u00e4joki - 2,05 kN\/m\u00b2.<\/option>\n    <option value=\"Pyha\u0308ja\u0308rvi\">Pyh\u00e4j\u00e4rvi - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Pyha\u0308nta\u0308\">Pyh\u00e4nt\u00e4 - 2,75 kN\/m\u00b2 Pyh\u00e4nt\u00e4 - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Pyha\u0308ranta\">Pyh\u00e4ranta - 2,1 kN\/m\u00b2.<\/option>\n    <option value=\"Pa\u0308lka\u0308ne\">P\u00e4lk\u00e4ne - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Po\u0308ytya\u0308\">P\u00f6yty\u00e4 - 2.5 kN\/m\u00b2<\/option>\n    <option value=\"Raahe\">Raahe - 2,1 kN\/m\u00b2.<\/option>\n    <option value=\"Raasepori\">Raasepori - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Raisio\">Raisio - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Rantasalmi\">Rantasalmi - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Ranua\">Ranua - 3,2 kN\/m\u00b2.<\/option>\n    <option value=\"Rauma \">Rauma - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Rautalampi\">Rautalampi - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Rautavaara\">Rautavaara - 3,0 kN\/m\u00b2.<\/option>\n    <option value=\"Rautja\u0308rvi\">Rautj\u00e4rvi - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Reisja\u0308rvi\">Reisj\u00e4rvi - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Riihima\u0308ki\">Riihim\u00e4ki - 2,75 kN\/m\u00b2<\/option>\n    <option value=\"Ristija\u0308rvi\">Ristij\u00e4rvi - 3,5 kN\/m\u00b2<\/option>\n    <option value=\"Rovaniemi\">Rovaniemi - 3,0 kN\/m\u00b2.<\/option>\n    <option value=\"Ruokolahti\">Ruokolahti - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Ruovesi\">Ruovesi - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Rusko\">Rusko - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Ra\u0308a\u0308kkyla\u0308\">R\u00e4\u00e4kkyl\u00e4 - 2,6 kN\/m\u00b2.<\/option>\n    <option value=\"Saarija\u0308rvi\">Saarij\u00e4rvi - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Salla\">Salla - 3,0 kN\/m\u00b2<\/option>\n    <option value=\"Salo\">Salo - 2,7 kN\/m\u00b2.<\/option>\n    <option value=\"Saltvik\">Saltvik - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Sastamala\">Sastamala - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Sauvo\">Sauvo - 2,6 kN\/m\u00b2.<\/option>\n    <option value=\"Savitaipale\">Savitaipale - 2,65 kN\/m\u00b2.<\/option>\n    <option value=\"Savonlinna\">Savonlinna - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Savukoski\">Savukoski - 3.0 kN\/m\u00b2<\/option>\n    <option value=\"Seina\u0308joki\">Sein\u00e4joki - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Sievi\">Sievi - 2,35 kN\/m\u00b2.<\/option>\n    <option value=\"Siikainen\">Siikainen - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Siikajoki\">Siikajoki - 2,25 kN\/m\u00b2.<\/option>\n    <option value=\"Siikalatva\">Siikalatva - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Siilinja\u0308rvi\">Siilinj\u00e4rvi - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Simo\">Simo - 3,0 kN\/m\u00b2.<\/option>\n    <option value=\"Sipoo\">Sipoo - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Siuntio\">Siuntio - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Sodankyla\u0308\">Sodankyl\u00e4 - 3.0 kN\/m\u00b2<\/option>\n    <option value=\"Soini\">Soini - 2,5 kN\/m\u00b2<\/option>\n    <option value=\"Somero\">Somero - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Sonkaja\u0308rvi\">Sonkaj\u00e4rvi - 3,0 kN\/m\u00b2.<\/option>\n    <option value=\"Sotkamo\">Sotkamo - 3,4 kN\/m\u00b2.<\/option>\n    <option value=\"Sottunga\">Sottunga - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Sulkava\">Sulkava - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Sund\">Sund - 2,0 kN\/m\u00b2<\/option>\n    <option value=\"Suomussalmi\">Suomussalmi - 3,5 kN\/m\u00b2.<\/option>\n    <option value=\"Suonenjoki\">Suonenjoki - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Sysma\u0308\">Sysm\u00e4 - 2,5 kN\/m\u00b2<\/option>\n    <option value=\"Sa\u0308kyla\u0308\">S\u00e4kyl\u00e4 - 2,1 kN\/m\u00b2.<\/option>\n    <option value=\"Taipalsaari\">Taipalsaari - 2,7 kN\/m\u00b2.<\/option>\n    <option value=\"Taivalkoski\">Taivalkoski - 3,5 kN\/m\u00b2.<\/option>\n    <option value=\"Taivassalo\">Taivassalo - 2,2 kN\/m\u00b2.<\/option>\n    <option value=\"Tammela\">Tammela - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Tampere\">Tampere - 2,5 kN\/m\u00b2<\/option>\n    <option value=\"Tervo \">Tervo - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Tervola\">Tervola - 3,0 kN\/m\u00b2 Tervola - 3,0 kN\/m\u00b2.<\/option>\n    <option value=\"Teuva\">Teuva - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Tohmaja\u0308rvi\">Tohmaj\u00e4rvi - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Toholampi\">Toholampi - 2,25 kN\/m\u00b2.<\/option>\n    <option value=\"Toivakka\">Toivakka - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Tornio\">Tornio - 3.0 kN\/m\u00b2<\/option>\n    <option value=\"Turku\">Turku - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Tuusniemi\">Tuusniemi - 2,5 kN\/m\u00b2<\/option>\n    <option value=\"Tuusula\">Tuusula - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Tyrna\u0308va\u0308\">Tyrn\u00e4v\u00e4 - 2,4 kN\/m\u00b2.<\/option>\n    <option value=\"Ulvila\">Ulvila - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Urjala\">Urjala - 2,4 kN\/m\u00b2.<\/option>\n    <option value=\"Utaja\u0308rvi\">Utaj\u00e4rvi - 2,85 kN\/m\u00b2.<\/option>\n    <option value=\"Utsjoki\">Utsjoki - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Uurainen\">Uurainen - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Uusikaarlepyy\">Uusikaarlepyy - 2.0 kN\/m\u00b2<\/option>\n    <option value=\"Uusikaupunki\">Uusikaupunki - 2,1 kN\/m\u00b2.<\/option>\n    <option value=\"Vaala\">Vaala - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Vaasa\">Vaasa - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Valkeakoski\">Valkeakoski - 2,4 kN\/m\u00b2.<\/option>\n    <option value=\"Valtimo\">Valtimo - 3,0 kN\/m\u00b2.<\/option>\n    <option value=\"Vantaa\">Vantaa - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Varkaus\">Varkaus - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Vehmaa\">Vehmaa - 2,2 kN\/m\u00b2.<\/option>\n    <option value=\"Vesanto\">Vesanto - 2,5 kN\/m\u00b2 Vesanto - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Vesilahti\">Vesilahti - 2,3 kN\/m\u00b2.<\/option>\n    <option value=\"Veteli\">Veteli - 2,3 kN\/m\u00b2.<\/option>\n    <option value=\"Vierema\u0308\">Vierem\u00e4 - 2,85 kN\/m\u00b2.<\/option>\n    <option value=\"Vihti\">Vihti - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Viitasaari\">Viitasaari - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Vimpeli\">Vimpeli - 2,45 kN\/m\u00b2.<\/option>\n    <option value=\"Virolahti\">Virolahti - 2,75 kN\/m\u00b2.<\/option>\n    <option value=\"Virrat\">Virrat - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Va\u030ardo\u0308\">V\u00e5rd\u00f6 - 2,0 kN\/m\u00b2.<\/option>\n    <option value=\"Vo\u0308yri\">V\u00f6yri - 2,1 kN\/m\u00b2.<\/option>\n    <option value=\"Ylitornio\">Ylitornio - 3,0 kN\/m\u00b2.<\/option>\n    <option value=\"Ylivieska\">Ylivieska - 2,15 kN\/m\u00b2.<\/option>\n    <option value=\"Ylo\u0308ja\u0308rvi\">Yl\u00f6j\u00e4rvi - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"Ypa\u0308ja\u0308\">Yp\u00e4j\u00e4 - 2,6 kN\/m\u00b2<\/option>\n    <option value=\"A\u0308hta\u0308ri\">\u00c4ht\u00e4ri - 2,5 kN\/m\u00b2.<\/option>\n    <option value=\"A\u0308a\u0308nekoski \">\u00c4\u00e4nekoski - 2,5 kN\/m\u00b2.<\/option>\n\n          <\/select>\n        <\/div>\n      <\/section>\n\n      <section id=\"roof-angle-info\" class=\"section-card\">\n        <h2>Aseta katon kulma<\/h2>\n        <p class=\"help-text\">Sy\u00f6t\u00e4 katon kaltevuuskulma suoraan tai anna katon pituus ja korkeus kulman laskemista varten. Katon jyrkkyys vaikuttaa siihen, kuinka paljon lunta kertyy.<\/p>\n        <figure class=\"media-frame\">\n          <img decoding=\"async\" src=\"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/02\/roof-angle-example-2.jpg\" alt=\"Diagram illustrating roof angle measurements\" loading=\"lazy\" \/>\n        <\/figure>\n        <div class=\"field-group\">\n          <label class=\"field-label\" for=\"A\">Kulma asteina (A)<\/label>\n          <input type=\"number\" id=\"A\" class=\"input\" value=\"29\" inputmode=\"decimal\" min=\"0\" max=\"89\" step=\"0.1\" \/>\n        <\/div>\n        <p class=\"help-text\"><strong>TAI<\/strong> Anna katon pituus ja korkeus, jotta kulma voidaan laskea automaattisesti.<\/p>\n        <div class=\"field-group\">\n          <label class=\"field-label\" for=\"roofLength\">Pituus metrein\u00e4 (L)<\/label>\n          <input type=\"number\" id=\"roofLength\" class=\"input\" placeholder=\"Sy\u00f6t\u00e4 pituus metrein\u00e4\" inputmode=\"decimal\" min=\"0\" step=\"0.01\" \/>\n        <\/div>\n        <div class=\"field-group\">\n          <label class=\"field-label\" for=\"roofHeight\">Korkeus metrein\u00e4 (H)<\/label>\n          <input type=\"number\" id=\"roofHeight\" class=\"input\" placeholder=\"Sy\u00f6t\u00e4 korkeus metrein\u00e4\" inputmode=\"decimal\" min=\"0\" step=\"0.01\" \/>\n        <\/div>\n      <\/section>\n\n      <section id=\"solar-panel-dimensions\" class=\"section-card\">\n        <h2>Aseta paneelin mitat<\/h2>\n        <p class=\"help-text\">Sy\u00f6t\u00e4 aurinkopaneelisi leveys ja pituus millimetrein\u00e4. N\u00e4it\u00e4 mittoja tarvitaan laskemaan, kuinka suuren lumikuorman kukin paneeli kest\u00e4\u00e4.<\/p>\n        <figure class=\"media-frame\">\n          <img decoding=\"async\" src=\"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/02\/panel-example.jpg\" alt=\"Example solar panel dimensions\" loading=\"lazy\" \/>\n        <\/figure>\n        <div class=\"field-group\">\n          <label class=\"field-label\" for=\"PW\">Leveys (W):<\/label>\n          <input type=\"number\" id=\"PW\" class=\"input\" value=\"1134\" inputmode=\"numeric\" min=\"0\" \/>\n        <\/div>\n        <div class=\"field-group\">\n          <label class=\"field-label\" for=\"PL\">Pituus (L)<\/label>\n          <input type=\"number\" id=\"PL\" class=\"input\" value=\"1722\" inputmode=\"numeric\" min=\"0\" \/>\n        <\/div>\n      <\/section>\n\n      <section class=\"section-card\">\n        <h2>Aseta kattotyyppi<\/h2>\n        <div id=\"roofTypeContainer\">\n          <label class=\"roof-type-option\" for=\"roofType1\">\n            <img decoding=\"async\" src=\"https:\/\/moisolar.com\/wp-content\/uploads\/2023\/06\/roof_rb01.jpg\" alt=\"Rivikatto, konesauma ja lukkosaumakatto\" loading=\"lazy\" \/>\n            <input type=\"radio\" id=\"roofType1\" name=\"roofType\" value=\"1\" data-image=\"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/02\/1.png\" checked \/>\n            <span>Rivikatto, konesauma ja lukkosaumakatto<\/span>\n          <\/label>\n          <label class=\"roof-type-option\" for=\"roofType2\">\n            <img decoding=\"async\" src=\"https:\/\/moisolar.com\/wp-content\/uploads\/2023\/06\/roof_rb02.jpg\" alt=\"Profiilikatot ja huopakatot\" loading=\"lazy\" \/>\n            <input type=\"radio\" id=\"roofType2\" name=\"roofType\" value=\"2\" data-image=\"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/02\/2.png\" \/>\n            <span>Profiilikatot ja huopakatot<\/span>\n          <\/label>\n          <label class=\"roof-type-option\" for=\"roofType3\">\n            <img decoding=\"async\" src=\"https:\/\/moisolar.com\/wp-content\/uploads\/2023\/06\/roof_rb03.jpg\" alt=\"Tiilikuvioinen peltikatto\" loading=\"lazy\" \/>\n            <input type=\"radio\" id=\"roofType3\" name=\"roofType\" value=\"3\" data-image=\"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/02\/3.png\" \/>\n            <span>Tiilikuvioinen peltikatto<\/span>\n          <\/label>\n          <label class=\"roof-type-option\" for=\"roofType4\">\n            <img decoding=\"async\" src=\"https:\/\/moisolar.com\/wp-content\/uploads\/2023\/06\/roof_rb04.jpg\" alt=\"Aaltopeltikatto\" loading=\"lazy\" \/>\n            <input type=\"radio\" id=\"roofType4\" name=\"roofType\" value=\"4\" data-image=\"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/02\/4.png\" \/>\n            <span>Aaltopeltikatto<\/span>\n          <\/label>\n          <label class=\"roof-type-option\" for=\"roofType5\">\n            <img decoding=\"async\" src=\"https:\/\/moisolar.com\/wp-content\/uploads\/2023\/06\/roof_rb05.jpg\" alt=\"Curved tile roof\" loading=\"lazy\" \/>\n            <input type=\"radio\" id=\"roofType5\" name=\"roofType\" value=\"5\" data-image=\"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/02\/5.png\" \/>\n            <span>Tiilikatto (kaareva)<\/span>\n          <\/label>\n          <label class=\"roof-type-option\" for=\"roofType6\">\n            <img decoding=\"async\" src=\"https:\/\/moisolar.com\/wp-content\/uploads\/2023\/06\/roof_rb05.jpg\" alt=\"Straight tile roof\" loading=\"lazy\" \/>\n            <input type=\"radio\" id=\"roofType6\" name=\"roofType\" value=\"6\" data-image=\"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/02\/6.png\" \/>\n            <span>Tiilikatto (suora)<\/span>\n          <\/label>\n        <\/div>\n\n        <div class=\"profile-spacing-block\">\n          <h3>Aseta profiilien v\u00e4li<\/h3>\n          <p class=\"help-text\">Valitse, haluatko k\u00e4ytt\u00e4\u00e4 laskimen suosittelemaa kattokiinnikkeiden v\u00e4li\u00e4 vai sy\u00f6tt\u00e4\u00e4 oman arvosi. Suurempi v\u00e4li v\u00e4hent\u00e4\u00e4 kiinnikkeiden m\u00e4\u00e4r\u00e4\u00e4, mutta lis\u00e4\u00e4 kuormitusta kutakin kiinnikett\u00e4 kohti.<\/p>\n          <div class=\"profile-spacing-visual\" id=\"profileSpacingVisual\" aria-hidden=\"true\">\n            <img decoding=\"async\" src=\"\" alt=\"Profile spacing illustration\" id=\"profileSpacingImage\" loading=\"lazy\" \/>\n          <\/div>\n          <div class=\"profile-span-options\">\n            <label class=\"profile-mode-card\">\n              <input type=\"radio\" name=\"profileSpanMode\" value=\"auto\" checked>\n              <strong>K\u00e4yt\u00e4 suositeltua arvoa<\/strong>\n              <span>Laskin m\u00e4\u00e4ritt\u00e4\u00e4 v\u00e4lin lumikuorman ja kattotyypin perusteella.<\/span>\n            <\/label>\n            <label class=\"profile-mode-card\">\n              <input type=\"radio\" name=\"profileSpanMode\" value=\"custom\">\n              <strong>Aseta mukautettu v\u00e4li<\/strong>\n              <span>Sy\u00f6t\u00e4 haluamasi et\u00e4isyys kattokiinnikkeiden v\u00e4lill\u00e4 millimetrein\u00e4.<\/span>\n            <\/label>\n          <\/div>\n          <div class=\"profile-custom-field disabled\" id=\"profileSpanCustomField\" aria-hidden=\"true\">\n            <label class=\"field-label\" for=\"profileSpanCustom\">Mukautettu v\u00e4li (mm)<\/label>\n            <input type=\"number\" id=\"profileSpanCustom\" class=\"input\" value=\"1200\" min=\"0\" max=\"5000\" step=\"10\" placeholder=\"1500\" disabled>\n          <\/div>\n        <\/div>\n      <\/section>\n\n      <section class=\"section-card panel-config\">\n        <div class=\"panel-config-header\">\n          <h2>Aseta aurinkopaneelien m\u00e4\u00e4r\u00e4<\/h2>\n          <p class=\"help-text\">S\u00e4\u00e4d\u00e4 rivien m\u00e4\u00e4r\u00e4\u00e4 ja simuloi, kuinka monta paneelia sijoittuu kullekin riville.<\/p>\n        <\/div>\n        <div class=\"panel-control-bar\">\n          <div class=\"panel-stepper\" id=\"panelRowsStepper\" role=\"group\" aria-label=\"Adjust number of panel rows\">\n            <button type=\"button\" class=\"panel-stepper-btn\" data-action=\"decrement\" aria-label=\"Remove row\"><span aria-hidden=\"true\">-<\/span><\/button>\n            <input type=\"number\" class=\"panel-stepper-input\" id=\"panelRowsInput\" value=\"0\" min=\"0\" max=\"30\" step=\"1\" aria-label=\"Number of panel rows\">\n            <span class=\"panel-stepper-unit\">rivit<\/span>\n            <button type=\"button\" class=\"panel-stepper-btn\" data-action=\"increment\" aria-label=\"Lis\u00e4\u00e4 rivi\"><span aria-hidden=\"true\">+<\/span><\/button>\n          <\/div>\n          <p class=\"panel-control-hint\"><\/p>\n        <\/div>\n        <div class=\"sr-only\">\n          <label for=\"numRows\">Kuinka monta rivi\u00e4 aurinkopaneeleja aiot asentaa?<\/label>\n          <select id=\"numRows\" name=\"numRows\" class=\"input-select\">\n          <option value=\"0\">0<\/option>\n          <option value=\"1\">1<\/option>\n          <option value=\"2\">2<\/option>\n          <option value=\"3\">3<\/option>\n          <option value=\"4\">4<\/option>\n          <option value=\"5\">5<\/option>\n          <option value=\"6\">6<\/option>\n          <option value=\"7\">7<\/option>\n          <option value=\"8\">8<\/option>\n          <option value=\"9\">9<\/option>\n          <option value=\"10\">10<\/option>\n          <option value=\"11\">11<\/option>\n          <option value=\"12\">12<\/option>\n          <option value=\"13\">13<\/option>\n          <option value=\"14\">14<\/option>\n          <option value=\"15\">15<\/option>\n          <option value=\"16\">16<\/option>\n          <option value=\"17\">17<\/option>\n          <option value=\"18\">18<\/option>\n          <option value=\"19\">19<\/option>\n          <option value=\"20\">20<\/option>\n          <option value=\"21\">21<\/option>\n          <option value=\"22\">22<\/option>\n          <option value=\"23\">23<\/option>\n          <option value=\"24\">24<\/option>\n          <option value=\"25\">25<\/option>\n          <option value=\"26\">26<\/option>\n          <option value=\"27\">27<\/option>\n          <option value=\"28\">28<\/option>\n          <option value=\"29\">29<\/option>\n          <option value=\"30\">30<\/option>\n          <\/select>\n        <\/div>\n        <div id=\"rowInputs\" class=\"panel-grid\">\n          <div class=\"panel-placeholder\">Use the controls above to add panel rows.<\/div>\n        <\/div>\n      <\/section>\n\n      <section class=\"section-card\">\n        <h2>Asiakastiedot<\/h2>\n        <p class=\"help-text\">Anna yhteystietosi alustavan tarjouksen laatimiseksi. Saat r\u00e4\u00e4t\u00e4l\u00f6idyn tarjouksen lataamalla tarjous-taulukon ja l\u00e4hett\u00e4m\u00e4ll\u00e4 sen s\u00e4hk\u00f6postitse osoitteeseen <a href=\"mailto:info@moisolar.com\">info@moisolar.com<\/a>. Erikoistumme yritysasiakkaille (B2B) tarjottaviin palveluihin.<\/p>\n        <p class=\"help-text\"><strong>Huomio tietosuojasta:<\/strong> Sy\u00f6tt\u00e4m\u00e4ll\u00e4 alla olevat tiedot ja aloittamalla laskennan, hyv\u00e4ksyt meid\u00e4n <a href=\"https:\/\/moisolar.com\/privacy-policy\/\">Tietosuojak\u00e4yt\u00e4nt\u00f6<\/a>.<\/p>\n        <div class=\"field-group\">\n          <label class=\"field-label\" for=\"customerName\">Asiakkaan nimi<\/label>\n          <input type=\"text\" id=\"customerName\" class=\"input\" placeholder=\"Sy\u00f6t\u00e4 asiakkaan nimi\" autocomplete=\"name\" \/>\n        <\/div>\n        <div class=\"field-group\">\n          <label class=\"field-label\" for=\"companyName\">Yrityksen nimi<\/label>\n          <input type=\"text\" id=\"companyName\" class=\"input\" placeholder=\"Sy\u00f6t\u00e4 yrityksen nimi\" autocomplete=\"organization\" \/>\n        <\/div>\n        <div class=\"field-group\">\n          <label class=\"field-label\" for=\"email\">S\u00e4hk\u00f6posti<\/label>\n          <input type=\"email\" id=\"email\" class=\"input\" placeholder=\"Sy\u00f6t\u00e4 s\u00e4hk\u00f6postiosoite\" autocomplete=\"email\" \/>\n        <\/div>\n        <div class=\"field-group\">\n          <label class=\"field-label\" for=\"phone\">Puhelinnumero<\/label>\n          <input type=\"tel\" id=\"phone\" class=\"input\" placeholder=\"Sy\u00f6t\u00e4 puhelinnumero\" autocomplete=\"tel\" \/>\n        <\/div>\n        <div class=\"field-group\">\n          <label class=\"field-label\" for=\"discount\">Alennus (%)<\/label>\n          <input type=\"number\" id=\"discount\" name=\"discount\" class=\"input\" value=\"0\" min=\"0\" max=\"100\" step=\"1\" \/>\n          <p class=\"help-text\">Sy\u00f6t\u00e4 numero v\u00e4lill\u00e4 0-100 saadaksesi arvion tai ota yhteytt\u00e4 saadaksesi r\u00e4\u00e4t\u00e4l\u00f6idyn tarjouksen.<\/p>\n        <\/div>\n        <div id=\"buttonContainer\">\n          <button id=\"calculateButton\" type=\"submit\" class=\"button primary\">Laske<\/button>\n          <button id=\"saveAsPDF\" type=\"button\" class=\"button secondary\">Tallenna tulos PDF-muodossa<\/button>\n        <\/div>\n      <\/section>\n    <input type=\"hidden\" name=\"trp-form-language\" value=\"fi\"\/><\/form>\n\n    <section id=\"resultsSection\" class=\"section-card\">\n      <div id=\"result\"><\/div>\n      <div id=\"result2\"><\/div>\n      <div id=\"warning0\" class=\"warning\" role=\"alert\"><\/div>\n      <div id=\"warningSpan\" class=\"warning\" role=\"alert\"><\/div>\n      <div class=\"table-wrapper\">\n        <div class=\"table-scroll\">\n          <table id=\"resultTable\"><\/table>\n        <\/div>\n      <\/div>\n      <div id=\"suggestUserToSendEmail\" class=\"help-text\"><\/div>\n      <button id=\"saveAsCSV\" type=\"button\" class=\"button secondary\">Tallenna tarjous<\/button>\n      <div class=\"table-wrapper\">\n        <div class=\"table-scroll\">\n          <table id=\"resultTable2\"><\/table>\n        <\/div>\n      <\/div>\n      <div id=\"warning1\" class=\"warning\" role=\"alert\"><\/div>\n      <div id=\"warning2\" class=\"warning\" role=\"alert\"><\/div>\n    <\/section>\n    <\/main>\n  <\/div>\n\n  <div id=\"calculatingIndicator\">\n    <span id=\"calculatingText\">Lasketaan...<\/span>\n  <\/div>\n\n  <script src=\"https:\/\/ajax.googleapis.com\/ajax\/libs\/jquery\/3.5.1\/jquery.min.js\"><\/script>\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jspdf\/2.4.0\/jspdf.umd.min.js\"><\/script>\n  <script>\n\/\/ \/\/ Handle change event for the row count dropdown\n\/\/ $(\"#numRows\").change(function () {\n\/\/   var rowCount = $(this).val();\n\/\/   \/\/ Clear the current panel amount inputs and create new ones based on the selected row count\n\/\/   $(\"#panelAmounts\").html(\"\");\n\/\/   for (var i = 0; i < rowCount; i++) {\n\/\/     $(\"#panelAmounts\").append(\n\/\/       \"Row \" + (i + 1) + ': <input type=\"number\" id=\"panelAmount' + i + '\"><br>'\n\/\/     );\n\/\/   }\n\/\/ });\n\n'use strict';\n\nconst rowCountElement = document.getElementById(\"numRows\");\nconst rowInputsContainer = document.getElementById(\"rowInputs\");\nconst panelRowsStepper = document.getElementById(\"panelRowsStepper\");\nconst panelRowsInput = document.getElementById(\"panelRowsInput\");\nconst saveCsvButton = document.getElementById(\"saveAsCSV\");\nconst profileSpanModeRadios = document.querySelectorAll('input[name=\"profileSpanMode\"]');\nconst profileSpanCustomInput = document.getElementById(\"profileSpanCustom\");\nconst profileSpanCustomField = document.getElementById(\"profileSpanCustomField\");\n\/\/ Start safely grabbing elements (some may not exist yet in all contexts)\nconst profileSpacingBlock = document.querySelector('.profile-spacing-block');\nconst profileSpacingVisual = document.getElementById(\"profileSpacingVisual\");\nconst profileSpacingImage = document.getElementById(\"profileSpacingImage\");\nlet customSpanWarning = \"\";\n\nconst roofTypeRadios = document.querySelectorAll('input[name=\"roofType\"]');\n\nconst currencyFormatter = new Intl.NumberFormat(\"fi-FI\", {\n  minimumFractionDigits: 2,\n  maximumFractionDigits: 2,\n});\n\nconst formatCurrency = (value) => currencyFormatter.format(value);\n\nconst applyDiscount = (price, discountPercent) =>\n  price * (1 - discountPercent \/ 100);\n\nlet recommendedProfileSpan = 1500;\nconst PROFILE_SPAN_MIN = 0;\nconst PROFILE_SPAN_MAX = 5000;\n\nfunction setWarning(elementId, content) {\n  const element = document.getElementById(elementId);\n  if (!element) {\n    return;\n  }\n  if (elementId === \"warningSpan\") {\n    customSpanWarning = content || \"\";\n  }\n\n  if (!content) {\n    element.textContent = \"\";\n    element.style.display = \"none\";\n  } else {\n    element.innerHTML = content;\n    element.style.display = \"block\";\n  }\n}\n\nfunction syncProfileSpanControls() {\n  const mode = document.querySelector('input[name=\"profileSpanMode\"]:checked')?.value || 'auto';\n  if (profileSpanCustomInput) {\n    const isCustom = mode === 'custom';\n    profileSpanCustomInput.disabled = !isCustom;\n    if (profileSpanCustomField) {\n      profileSpanCustomField.classList.toggle('disabled', !isCustom);\n      profileSpanCustomField.style.display = isCustom ? 'grid' : 'none';\n      profileSpanCustomField.setAttribute('aria-hidden', isCustom ? 'false' : 'true');\n    }\n    if (!isCustom) {\n      profileSpanCustomInput.setAttribute('aria-disabled', 'true');\n      profileSpanCustomInput.value = String(recommendedProfileSpan);\n    } else {\n      profileSpanCustomInput.removeAttribute('aria-disabled');\n      if (!profileSpanCustomInput.value) {\n        profileSpanCustomInput.value = recommendedProfileSpan;\n      }\n    }\n    profileSpanCustomInput.placeholder = recommendedProfileSpan;\n  }\n}\n\nfunction getSelectedRoofType() {\n  const selected = document.querySelector('input[name=\"roofType\"]:checked');\n  return selected ? selected.value : null;\n}\n\nfunction updateProfileSpacingVisual(selectedRoofType) {\n  if (!profileSpacingBlock) {\n    return;\n  }\n\n  const roofTypeValue = selectedRoofType || getSelectedRoofType();\n  const selectedRadio = roofTypeValue\n    ? document.querySelector(`input[name=\"roofType\"][value=\"${roofTypeValue}\"]`)\n    : null;\n  const imageUrl = selectedRadio?.dataset?.image ||\n    (roofTypeValue ? `https:\/\/moisolar.com\/wp-content\/uploads\/2024\/02\/${roofTypeValue}.png` : \"\");\n\n  profileSpacingBlock.style.display = 'block';\n\n  if (!roofTypeValue || !imageUrl) {\n    if (profileSpacingVisual) {\n      profileSpacingVisual.style.display = 'none';\n      profileSpacingVisual.setAttribute('aria-hidden', 'true');\n    }\n    return;\n  }\n\n  if (profileSpacingImage && profileSpacingVisual) {\n    profileSpacingImage.src = imageUrl;\n    profileSpacingImage.alt = `Profile spacing illustration for roof type ${roofTypeValue}`;\n    profileSpacingVisual.style.display = 'block';\n    profileSpacingVisual.setAttribute('aria-hidden', 'false');\n  }\n}\n\nfunction syncRowCountDisplay() {\n  if (panelRowsInput) {\n    panelRowsInput.value = rowCountElement.value;\n  }\n}\n\nfunction renderPanelPreview(inputId, total) {\n  const preview = document.getElementById(`${inputId}-preview`);\n  const numberInput = document.getElementById(`${inputId}-input`);\n  const hiddenInput = document.getElementById(inputId);\n  if (!preview || !numberInput || !hiddenInput) {\n    return;\n  }\n\n  const count = Math.max(1, Math.min(100, Number(total) || 1));\n  numberInput.value = String(count);\n  hiddenInput.value = String(count);\n  const maxPreviewTiles = 48;\n  const fragment = document.createDocumentFragment();\n\n  const tilesToRender = Math.min(count, maxPreviewTiles);\n\n  for (let panelIndex = 1; panelIndex <= tilesToRender; panelIndex += 1) {\n    const cell = document.createElement('div');\n    cell.className = 'panel-preview-cell';\n    cell.innerHTML = `<span>${panelIndex}<\/span>`;\n    fragment.appendChild(cell);\n  }\n\n  if (count > maxPreviewTiles) {\n    const moreCell = document.createElement('div');\n    moreCell.className = 'panel-preview-cell panel-preview-more';\n    moreCell.innerHTML = `<span>+${count - maxPreviewTiles}<\/span>`;\n    fragment.appendChild(moreCell);\n  }\n\n  preview.innerHTML = '';\n  preview.appendChild(fragment);\n}\n\nfunction updatePanelRowValue(inputId, delta) {\n  const numberInput = document.getElementById(`${inputId}-input`);\n  const hiddenInput = document.getElementById(inputId);\n  if (!numberInput || !hiddenInput) {\n    return;\n  }\n  const current = Number(numberInput.value || 0);\n  let next = current + delta;\n  next = Math.max(1, Math.min(100, next));\n  if (next === current) {\n    return;\n  }\n  numberInput.value = String(next);\n  hiddenInput.value = String(next);\n  renderPanelPreview(inputId, next);\n}\n\nfunction adjustRowCount(delta) {\n  const current = Number(rowCountElement.value || 0);\n  let next = current + delta;\n  next = Math.max(0, Math.min(30, next));\n  if (next === current) {\n    return;\n  }\n  rowCountElement.value = String(next);\n  updateRowInputs();\n}\n\nfunction updateRowInputs() {\n  const rowCount = Number(rowCountElement.value || 0);\n  syncRowCountDisplay();\n  const existingValues = [];\n  rowInputsContainer\n    .querySelectorAll('input[type=\"hidden\"][id^=\"row\"]')\n    .forEach((input) => {\n      existingValues.push(Number(input.value) || 1);\n    });\n\n  rowInputsContainer.innerHTML = '';\n\n  if (!rowCount) {\n    rowInputsContainer.innerHTML = '<div class=\"panel-placeholder\">Use the controls above to add panel rows.<\/div>';\n    return;\n  }\n\n  for (let index = 0; index < rowCount; index += 1) {\n    const inputId = `row${index}`;\n    const initialValue = existingValues[index] ?? 1;\n    const card = document.createElement('div');\n    card.className = 'panel-row-card';\n    card.innerHTML = `\n      <div class=\"panel-row-header\">\n        <span class=\"panel-row-title\">Row ${index + 1}<\/span>\n        <div class=\"panel-stepper\" data-input=\"${inputId}\">\n          <button type=\"button\" class=\"panel-stepper-btn\" data-action=\"decrement\" aria-label=\"Remove panel from row ${index + 1}\">-<\/button>\n          <input type=\"number\" class=\"panel-stepper-input\" id=\"${inputId}-input\" data-input=\"${inputId}\" value=\"${initialValue}\" min=\"1\" max=\"100\" step=\"1\" aria-label=\"Panels in row ${index + 1}\">\n          <span class=\"panel-stepper-unit\">pcs<\/span>\n          <button type=\"button\" class=\"panel-stepper-btn\" data-action=\"increment\" aria-label=\"Add panel to row ${index + 1}\">+<\/button>\n        <\/div>\n      <\/div>\n      <input type=\"hidden\" id=\"${inputId}\" name=\"${inputId}\" value=\"${initialValue}\">\n      <div class=\"panel-preview\" id=\"${inputId}-preview\"><\/div>\n    `;\n\n    rowInputsContainer.appendChild(card);\n    renderPanelPreview(inputId, initialValue);\n  }\n}\n\nrowInputsContainer.addEventListener('click', (event) => {\n  const button = event.target.closest('.panel-stepper-btn');\n  if (!button) {\n    return;\n  }\n  const wrapper = button.closest('.panel-stepper');\n  if (!wrapper) {\n    return;\n  }\n  const inputId = wrapper.getAttribute('data-input');\n  if (!inputId) {\n    return;\n  }\n  const delta = button.dataset.action === 'increment' ? 1 : -1;\n  updatePanelRowValue(inputId, delta);\n});\n\nrowInputsContainer.addEventListener('input', (event) => {\n  const numberInput = event.target.closest('.panel-stepper-input');\n  if (!numberInput) {\n    return;\n  }\n  const inputId = numberInput.dataset.input;\n  if (!inputId) {\n    return;\n  }\n  let value = Number(numberInput.value);\n  if (!Number.isFinite(value)) {\n    value = 1;\n  }\n  value = Math.round(Math.max(1, Math.min(100, value)));\n  numberInput.value = value;\n  renderPanelPreview(inputId, value);\n});\n\nif (panelRowsStepper) {\n  panelRowsStepper.addEventListener('click', (event) => {\n    const button = event.target.closest('.panel-stepper-btn');\n    if (!button) {\n      return;\n    }\n    const delta = button.dataset.action === 'increment' ? 1 : -1;\n    adjustRowCount(delta);\n  });\n}\n\nif (panelRowsInput) {\n  panelRowsInput.addEventListener('input', () => {\n    let value = Number(panelRowsInput.value);\n    if (!Number.isFinite(value)) {\n      value = 0;\n    }\n    value = Math.round(Math.max(0, Math.min(30, value)));\n    panelRowsInput.value = value;\n    rowCountElement.value = String(value);\n    updateRowInputs();\n  });\n}\n\nprofileSpanModeRadios.forEach((radio) => {\n  radio.addEventListener('change', () => {\n    syncProfileSpanControls();\n    setWarning('warningSpan', '');\n  });\n});\n\nroofTypeRadios.forEach((radio) => {\n  radio.addEventListener('change', () => {\n    updateProfileSpacingVisual(radio.value);\n    syncProfileSpanControls();\n  });\n});\n\nif (profileSpanCustomInput) {\n  profileSpanCustomInput.addEventListener('input', () => {\n    if (profileSpanCustomInput.disabled) {\n      return;\n    }\n    let value = Number(profileSpanCustomInput.value);\n    if (!Number.isFinite(value)) {\n      value = recommendedProfileSpan;\n    }\n    value = Math.round(Math.max(PROFILE_SPAN_MIN, Math.min(PROFILE_SPAN_MAX, value)));\n    profileSpanCustomInput.value = value;\n  });\n}\n\nrowCountElement.addEventListener('change', updateRowInputs);\nsyncRowCountDisplay();\nupdateRowInputs();\nsyncProfileSpanControls();\nupdateProfileSpacingVisual(getSelectedRoofType());\n\nif (saveCsvButton) {\n  saveCsvButton.addEventListener(\"click\", downloadTableAsCSV);\n}\n\n\/\/ Function to validate user input\nfunction validateInput() {\n  const angleInput = $(\"#A\").val();\n  const angle = angleInput === \"\" ? null : Number(angleInput);\n  const angleInvalid =\n    angle === null || Number.isNaN(angle) || angle <= 0 || angle >= 90;\n\n  const lengthInput = $(\"#roofLength\").val();\n  const heightInput = $(\"#roofHeight\").val();\n  const length = lengthInput === \"\" ? null : Number(lengthInput);\n  const height = heightInput === \"\" ? null : Number(heightInput);\n  const lengthInvalid =\n    length === null || Number.isNaN(length) || length <= 0;\n  const heightInvalid =\n    height === null || Number.isNaN(height) || height <= 0;\n\n  if (angleInvalid && (lengthInvalid || heightInvalid)) {\n    alert(\n      \"Sy\u00f6t\u00e4 joko kelvollinen kattokulma tai sek\u00e4 katon pituus ett\u00e4 korkeus.\"\n    );\n    return false;\n  }\n\n  const panelWidthInput = $(\"#PW\").val();\n  const panelLengthInput = $(\"#PL\").val();\n  const panelWidth = Number(panelWidthInput);\n  const panelLength = Number(panelLengthInput);\n\n  if (panelWidthInput === \"\" || Number.isNaN(panelWidth) || panelWidth <= 0) {\n    alert(\"Sy\u00f6t\u00e4 kelvollinen aurinkopaneelin leveys.\");\n    return false;\n  }\n  if (panelLengthInput === \"\" || Number.isNaN(panelLength) || panelLength <= 0) {\n    alert(\"Sy\u00f6t\u00e4 kelvollinen aurinkopaneelin pituus.\");\n    return false;\n  }\n\n  if (!$('input[name=\"roofType\"]:checked').val()) {\n    alert(\"Ole hyv\u00e4 ja valitse kattotyyppi.\");\n    return false;\n  }\n\n  const numRows = Number($(\"#numRows\").val());\n  if (!Number.isFinite(numRows) || numRows <= 0) {\n    alert(\n      \"Ole hyv\u00e4 ja m\u00e4\u00e4rit\u00e4, kuinka monta rivi\u00e4 aurinkopaneeleja aiot asentaa.\"\n    );\n    return false;\n  }\n\n  for (let i = 0; i < numRows; i += 1) {\n    const rowValue = Number($(`#row${i}`).val());\n    if (!Number.isFinite(rowValue) || rowValue <= 0) {\n      alert(`Ole hyv\u00e4 ja m\u00e4\u00e4rit\u00e4 aurinkopaneelien m\u00e4\u00e4r\u00e4 rivill\u00e4 ${i + 1}`);\n      return false;\n    }\n  }\n\n  const name = $(\"#customerName\").val().trim();\n  const company = $(\"#companyName\").val().trim();\n  const email = $(\"#email\").val().trim();\n  const phone = $(\"#phone\").val().trim();\n  const discountInput = $(\"#discount\").val();\n\n  if (!name || !company || !email || !phone || discountInput === \"\") {\n    alert(\"Anna voimassa olevat asiakastiedot.\");\n    return false;\n  }\n\n  return true;\n}\n\nfunction getNextEvenNumber(number) {\n  \/\/ Round the number up to the nearest integer\n  const roundedNumber = Math.ceil(number);\n\n  if (roundedNumber % 2 === 0) {\n    \/\/ If the rounded number is already even, increment it by 2 to get the next even number\n    return roundedNumber + 2;\n  } else {\n    \/\/ If the rounded number is odd, increment it by 1 to make it even, then add another 1 to get the next even number\n    return roundedNumber + 1;\n  }\n}\n\nconst productTotals = {\n  PR11: 0,\n  PR12: 0,\n  CL11: 0,\n  CL02: 0,\n  RB11: 0,\n  RB12: 0,\n  RB13: 0,\n  RB04: 0,\n  RB15: 0,\n  RB06: 0,\n  RB17: 0,\n  BN01: 0,\n  RS01: 0,\n  RS02: 0,\n  SS05: 0,\n  SS06: 0,\n};\n\nlet fixtures = {};\nlet totalPanelAmount = 0;\n\nconst productData = [\n  {\n    code: \"PR11\",\n    total: 0,\n    imageUrl: \"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/03\/PR11.jpg\",\n    url: \"https:\/\/moisolar.com\/fi\/tuote\/pr01\/\",\n    unitText: \"kpl\",\n    unitNumber: 1,\n    unit: \"kpl\",\n    price: 54.95,\n  },\n  {\n    code: \"PR12\",\n    total: 0,\n    imageUrl: \"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/03\/PR12.jpg\",\n    url: \"https:\/\/moisolar.com\/fi\/tuote\/pr02\/\",\n    unitText: \"pussia (10 kpl\/pussi)\",\n    unitNumber: 10,\n    unit: \"pussia\",\n    price: 30,\n  },\n  {\n    code: \"CL11\",\n    total: 0,\n    imageUrl: \"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/03\/CL11.jpg\",\n    url: \"https:\/\/moisolar.com\/fi\/tuote\/cl01\/\",\n    unitText: \"pussia (10 kpl\/pussi)\",\n    unitNumber: 10,\n    unit: \"pussia\",\n    price: 26.15,\n  },\n  {\n    code: \"CL02\",\n    total: 0,\n    imageUrl: \"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/03\/CL02.jpg\",\n    url: \"https:\/\/moisolar.com\/fi\/tuote\/cl02\/\",\n    unitText: \"pussia (8 kpl\/pussi)\",\n    unitNumber: 8,\n    unit: \"pussia\",\n    price: 22.27,\n  },\n  {\n    code: \"RB11\",\n    total: 0,\n    imageUrl: \"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/03\/RB11.jpg\",\n    url: \"https:\/\/moisolar.com\/fi\/tuote\/rb01\/\",\n    unitText: \"kpl\",\n    unitNumber: 1,\n    unit: \"kpl\",\n    price: 9.45,\n  },\n  {\n    code: \"RB12\",\n    total: 0,\n    imageUrl: \"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/03\/RB12.jpg\",\n    url: \"https:\/\/moisolar.com\/fi\/tuote\/rb02\/\",\n    unitText: \"kpl\",\n    unitNumber: 1,\n    unit: \"kpl\",\n    price: 4.95,\n  },\n  {\n    code: \"RB13\",\n    total: 0,\n    imageUrl: \"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/03\/RB13.jpg\",\n    url: \"https:\/\/moisolar.com\/fi\/tuote\/rb03\/\",\n    unitText: \"kpl\",\n    unitNumber: 1,\n    unit: \"kpl\",\n    price: 5.85,\n  },\n  {\n    code: \"RB04\",\n    total: 0,\n    imageUrl: \"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/03\/RB04.jpg\",\n    url: \"https:\/\/moisolar.com\/fi\/tuote\/rb04\/\",\n    unitText: \"kpl\",\n    unitNumber: 1,\n    unit: \"kpl\",\n    price: 7.73,\n  },\n  {\n    code: \"RB15\",\n    total: 0,\n    imageUrl: \"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/03\/RB15.jpg\",\n    url: \"https:\/\/moisolar.com\/fi\/tuote\/rb05\/\",\n    unitText: \"kpl\",\n    unitNumber: 1,\n    unit: \"kpl\",\n    price: 9.85,\n  },\n  {\n    code: \"RB06\",\n    total: 0,\n    imageUrl: \"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/03\/RB06.jpg\",\n    url: \"https:\/\/moisolar.com\/fi\/tuote\/rb06\/\",\n    unitText: \"kpl\",\n    unitNumber: 1,\n    unit: \"kpl\",\n    price: 2.79,\n  },\n  {\n    code: \"RB17\",\n    total: 0,\n    imageUrl: \"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/03\/RB17.jpg\",\n    url: \"https:\/\/moisolar.com\/fi\/tuote\/rb17\/\",\n    unitText: \"kpl\",\n    unitNumber: 1,\n    unit: \"kpl\",\n    price: 9.21,\n  },\n  {\n    code: \"BN01\",\n    total: 0,\n    imageUrl: \"https:\/\/moisolar.com\/wp-content\/uploads\/2023\/08\/BN01.jpg\",\n    url: \"https:\/\/moisolar.com\/fi\/tuote\/bn01\/\",\n    unitText: \"pussia (50 kpl\/pussi)\",\n    unitNumber: 50,\n    unit: \"pussia\",\n    price: 47,\n  },\n  {\n    code: \"RS01\",\n    total: 0,\n    imageUrl: \"https:\/\/moisolar.com\/wp-content\/uploads\/2023\/08\/RS01.jpg\",\n    url: \"https:\/\/moisolar.com\/fi\/tuote\/rs01\/\",\n    unitText: \"pussia (100 kpl\/pussi)\",\n    unitNumber: 100,\n    unit: \"pussia\",\n    price: 30,\n  },\n  {\n    code: \"RS02\",\n    total: 0,\n    imageUrl: \"https:\/\/moisolar.com\/wp-content\/uploads\/2023\/08\/RS02.jpg\",\n    url: \"https:\/\/moisolar.com\/fi\/tuote\/rs02\/\",\n    unitText: \"pussia (50 kpl\/pussi)\",\n    unitNumber: 50,\n    unit: \"pussia\",\n    price: 30,\n  },\n  {\n    code: \"SS05\",\n    total: 0,\n    imageUrl: \"\",\n    url: \"https:\/\/moisolar.com\/fi\/tuote\/ss05\/\",\n    unitText: \"kpl\",\n    unitNumber: 1,\n    unit: \"kpl\",\n    price: 0.448,\n  },\n  {\n    code: \"SS06\",\n    total: 0,\n    imageUrl: \"\",\n    url: \"https:\/\/moisolar.com\/fi\/tuote\/ss06\/\",\n    unitText: \"pussia (100 kpl\/pussi)\",\n    unitNumber: 100,\n    unit: \"pussia\",\n    price: 31.2,\n  },\n];\n\nfunction scrollToResults() {\n  const element = document.getElementById(\"result\");\n  const elementPosition = element.getBoundingClientRect().top + window.scrollY;\n  window.scrollTo({\n    top: elementPosition,\n    behavior: \"smooth\",\n  });\n}\n\n\/\/ Object mapping cities to their snow load values\nconst snowLoadValues = {\n  Akaa: 2.3,\n  Alaja\u0308rvi: 2.5,\n  Alavieska: 2.0,\n  Alavus: 2.5,\n  Asikkala: 2.5,\n  Askola: 2.7,\n  Aura: 2.5,\n  Bra\u0308ndo\u0308: 2.0,\n  Eckero\u0308: 2.0,\n  Enonkoski: 2.5,\n  Enontekio\u0308: 3.5,\n  Espoo: 2.75,\n  Eura: 2.1,\n  Eurajoki: 2.0,\n  Evija\u0308rvi: 2.2,\n  Finstro\u0308m: 2.0,\n  Forssa: 2.6,\n  Fo\u0308glo\u0308: 2.0,\n  Geta: 2.0,\n  Haapaja\u0308rvi: 2.5,\n  Haapavesi: 2.4,\n  Hailuoto: 2.4,\n  Halsua: 2.4,\n  Hamina: 2.75,\n  Hammarland: 2.0,\n  Hankasalmi: 2.5,\n  Hanko: 2.5,\n  Harjavalta: 2.0,\n  Hartola: 2.5,\n  Hattula: 2.5,\n  Hausja\u0308rvi: 2.75,\n  Heinola: 2.5,\n  Heina\u0308vesi: 2.5,\n  Helsinki: 2.75,\n  Hirvensalmi: 2.5,\n  Hollola: 2.75,\n  Honkajoki: 2.5,\n  Huittinen: 2.0,\n  Humppila: 2.25,\n  Hyrynsalmi: 3.5,\n  \"Hyvinka\u0308a\u0308 \": 2.75,\n  \"Ha\u0308meenkyro\u0308 \": 2.5,\n  \"Ha\u0308meenlinna \": 2.5,\n  \"Ii  \": 2.8,\n  \"Iisalmi \": 2.6,\n  \"Iitti \": 2.5,\n  \"Ikaalinen \": 2.5,\n  \"Ilmajoki \": 2.5,\n  \"Ilomantsi \": 2.75,\n  Imatra: 2.75,\n  \"Inari \": 3.0,\n  Inkoo: 2.75,\n  Isojoki: 2.5,\n  Isokyro\u0308: 2.3,\n  Janakkala: 2.75,\n  Joensuu: 2.6,\n  Jokioinen: 2.6,\n  Jomala: 2.0,\n  Joroinen: 2.5,\n  Joutsa: 2.5,\n  Juuka: 2.75,\n  Juupajoki: 2.5,\n  Juva: 2.5,\n  Jyva\u0308skyla\u0308: 2.5,\n  Ja\u0308mija\u0308rvi: 2.5,\n  Ja\u0308msa\u0308: 2.5,\n  Ja\u0308rvenpa\u0308a\u0308: 2.75,\n  Kaarina: 2.5,\n  Kaavi: 2.65,\n  Kajaani: 2.75,\n  Kalajoki: 2.0,\n  Kangasala: 2.5,\n  Kangasniemi: 2.5,\n  Kankaanpa\u0308a\u0308: 2.5,\n  Kannonkoski: 2.5,\n  Kannus: 2.1,\n  Karijoki: 2.5,\n  Karkkila: 2.75,\n  Karstula: 2.5,\n  Karvia: 2.5,\n  Kaskinen: 2.0,\n  Kauhajoki: 2.5,\n  Kauhava: 2.3,\n  Kauniainen: 2.75,\n  Kaustinen: 2.2,\n  \"Keitele \": 2.5,\n  Kemi: 3.0,\n  Kemija\u0308rvi: 3.0,\n  Keminmaa: 3.0,\n  Kemio\u0308nsaari: 2.5,\n  Kempele: 2.25,\n  Kerava: 2.75,\n  Keuruu: 2.5,\n  Kihnio\u0308: 2.5,\n  Kinnula: 2.5,\n  Kirkkonummi: 2.75,\n  Kitee: 2.75,\n  Kittila\u0308: 3.0,\n  Kiuruvesi: 2.55,\n  Kivija\u0308rvi: 2.5,\n  Kokema\u0308ki: 2.1,\n  Kokkola: 2.0,\n  Kolari: 3.0,\n  Konnevesi: 2.5,\n  Kontiolahti: 2.75,\n  Korsna\u0308s: 2.0,\n  \"Koski Tl\": 2.6,\n  Kotka: 2.65,\n  Kouvola: 2.5,\n  Kristiinankaupunki: 2.5,\n  Kruunupyy: 2.2,\n  Kuhmo: 3.0,\n  Kuhmoinen: 2.5,\n  Kumlinge: 2.0,\n  Kuopio: 2.5,\n  Kuortane: 2.5,\n  Kurikka: 2.5,\n  Kustavi: 2.1,\n  Kuusamo: 3.5,\n  Kyyja\u0308rvi: 2.5,\n  Ka\u0308rko\u0308la\u0308: 2.75,\n  Ka\u0308rsa\u0308ma\u0308ki: 2.5,\n  Ko\u0308kar: 2.0,\n  Lahti: 2.65,\n  Laihia: 2.3,\n  Laitila: 2.2,\n  Lapinja\u0308rvi: 2.5,\n  Lapinlahti: 2.6,\n  Lappaja\u0308rvi: 2.4,\n  Lappeenranta: 2.75,\n  Lapua: 2.5,\n  \"Laukaa \": 2.5,\n  Lemi: 2.7,\n  Lemland: 2.0,\n  Lempa\u0308a\u0308la\u0308: 2.4,\n  Leppa\u0308virta: 2.5,\n  Lestija\u0308rvi: 2.5,\n  Lieksa: 2.75,\n  Lieto: 2.6,\n  Liminka: 2.45,\n  Liperi: 2.55,\n  Lohja: 2.75,\n  Loimaa: 2.65,\n  Loppi: 2.75,\n  Loviisa: 2.5,\n  Luhanka: 2.5,\n  Lumijoki: 2.25,\n  Lumparland: 2.0,\n  Luoto: 2.0,\n  Luuma\u0308ki: 2.75,\n  Maalahti: 2.0,\n  Maarianhamina: 2.0,\n  Marttila: 2.6,\n  Masku: 2.3,\n  Merija\u0308rvi: 2.0,\n  Merikarvia: 2.4,\n  Miehikka\u0308la\u0308: 2.75,\n  Mikkeli: 2.5,\n  Muhos: 2.5,\n  Multia: 2.5,\n  Muonio: 3.0,\n  Mustasaari: 2.0,\n  Muurame: 2.5,\n  Myna\u0308ma\u0308ki: 2.3,\n  Myrskyla\u0308: 2.65,\n  Ma\u0308ntsa\u0308la\u0308: 2.75,\n  \"Ma\u0308ntta\u0308\u2010Vilppula\": 2.5,\n  Ma\u0308ntyharju: 2.5,\n  Naantali: 2.5,\n  Nakkila: 2.0,\n  Nivala: 2.35,\n  Nokia: 2.45,\n  Nousiainen: 2.3,\n  Nurmes: 3.0,\n  Nurmija\u0308rvi: 2.75,\n  Na\u0308rpio\u0308: 2.1,\n  \"Orimattila \": 2.75,\n  \"Oripa\u0308a\u0308 \": 2.2,\n  Orivesi: 2.5,\n  Oulainen: 2.15,\n  Oulu: 2.45,\n  Outokumpu: 2.6,\n  Padasjoki: 2.5,\n  Paimio: 2.6,\n  Paltamo: 3.3,\n  Parainen: 2.5,\n  Parikkala: 2.75,\n  Parkano: 2.5,\n  \"Pederso\u0308ren kunta\": 2.1,\n  Pelkosenniemi: 2.9,\n  Pello: 3.0,\n  Perho: 2.5,\n  Pertunmaa: 2.5,\n  Peta\u0308ja\u0308vesi: 2.5,\n  Pieksa\u0308ma\u0308ki: 2.5,\n  Pielavesi: 2.5,\n  Pietarsaari: 2.0,\n  Pihtipudas: 2.5,\n  Pirkkala: 2.4,\n  Polvija\u0308rvi: 2.75,\n  Pomarkku: 2.4,\n  Pori: 2.0,\n  Pornainen: 2.75,\n  Porvoo: 2.65,\n  Posio: 3.5,\n  Pudasja\u0308rvi: 3.5,\n  Pukkila: 2.75,\n  Punkalaidun: 2.1,\n  Puolanka: 3.5,\n  Puumala: 2.5,\n  Pyhta\u0308a\u0308: 2.5,\n  Pyha\u0308joki: 2.05,\n  Pyha\u0308ja\u0308rvi: 2.5,\n  Pyha\u0308nta\u0308: 2.75,\n  Pyha\u0308ranta: 2.1,\n  Pa\u0308lka\u0308ne: 2.5,\n  Po\u0308ytya\u0308: 2.5,\n  Raahe: 2.1,\n  Raasepori: 2.75,\n  Raisio: 2.5,\n  Rantasalmi: 2.5,\n  Ranua: 3.2,\n  \"Rauma \": 2.0,\n  Rautalampi: 2.5,\n  Rautavaara: 3.0,\n  Rautja\u0308rvi: 2.75,\n  Reisja\u0308rvi: 2.5,\n  Riihima\u0308ki: 2.75,\n  Ristija\u0308rvi: 3.5,\n  Rovaniemi: 3.0,\n  Ruokolahti: 2.75,\n  Ruovesi: 2.5,\n  Rusko: 2.5,\n  Ra\u0308a\u0308kkyla\u0308: 2.6,\n  Saarija\u0308rvi: 2.5,\n  Salla: 3.0,\n  Salo: 2.7,\n  Saltvik: 2.0,\n  Sastamala: 2.5,\n  Sauvo: 2.6,\n  Savitaipale: 2.65,\n  Savonlinna: 2.5,\n  Savukoski: 3.0,\n  Seina\u0308joki: 2.5,\n  Sievi: 2.35,\n  Siikainen: 2.5,\n  Siikajoki: 2.25,\n  Siikalatva: 2.5,\n  Siilinja\u0308rvi: 2.5,\n  Simo: 3.0,\n  Sipoo: 2.75,\n  Siuntio: 2.75,\n  Sodankyla\u0308: 3.0,\n  Soini: 2.5,\n  Somero: 2.75,\n  Sonkaja\u0308rvi: 3.0,\n  Sotkamo: 3.4,\n  Sottunga: 2.0,\n  Sulkava: 2.5,\n  Sund: 2.0,\n  Suomussalmi: 3.5,\n  Suonenjoki: 2.5,\n  Sysma\u0308: 2.5,\n  Sa\u0308kyla\u0308: 2.1,\n  Taipalsaari: 2.7,\n  Taivalkoski: 3.5,\n  Taivassalo: 2.2,\n  Tammela: 2.75,\n  Tampere: 2.5,\n  \"Tervo \": 2.5,\n  Tervola: 3.0,\n  Teuva: 2.5,\n  Tohmaja\u0308rvi: 2.75,\n  Toholampi: 2.25,\n  Toivakka: 2.5,\n  Tornio: 3.0,\n  Turku: 2.5,\n  Tuusniemi: 2.5,\n  Tuusula: 2.75,\n  Tyrna\u0308va\u0308: 2.4,\n  Ulvila: 2.0,\n  Urjala: 2.4,\n  Utaja\u0308rvi: 2.85,\n  Utsjoki: 2.5,\n  Uurainen: 2.5,\n  Uusikaarlepyy: 2.0,\n  Uusikaupunki: 2.1,\n  Vaala: 2.75,\n  Vaasa: 2.0,\n  Valkeakoski: 2.4,\n  Valtimo: 3.0,\n  Vantaa: 2.75,\n  Varkaus: 2.5,\n  Vehmaa: 2.2,\n  Vesanto: 2.5,\n  Vesilahti: 2.3,\n  Veteli: 2.3,\n  Vierema\u0308: 2.85,\n  Vihti: 2.75,\n  Viitasaari: 2.5,\n  Vimpeli: 2.45,\n  Virolahti: 2.75,\n  Virrat: 2.5,\n  Va\u030ardo\u0308: 2.0,\n  Vo\u0308yri: 2.1,\n  Ylitornio: 3.0,\n  Ylivieska: 2.15,\n  Ylo\u0308ja\u0308rvi: 2.5,\n  Ypa\u0308ja\u0308: 2.6,\n  A\u0308hta\u0308ri: 2.5,\n  \"A\u0308a\u0308nekoski \": 2.5,\n};\n\nlet snowLoadPerPanel = 0;\nlet snowLoadperProfile = 0;\nlet profileSpan = 1500;\n\nfunction calculateSnowLoad(roofTypeValue) {\n  const city = document.getElementById(\"city\").value;\n  const designSnowLoad = snowLoadValues[city];\n  if (typeof designSnowLoad !== \"number\") {\n    console.warn(`No snow load value configured for city: ${city}`);\n    return;\n  }\n  updateProfileSpacingVisual(roofTypeValue);\n  const roofLengthValue = document.getElementById(\"roofLength\").value;\n  const roofHeightValue = document.getElementById(\"roofHeight\").value;\n  const panelWidth = Number(document.getElementById(\"PW\").value) \/ 1000;\n  const panelLength = Number(document.getElementById(\"PL\").value) \/ 1000;\n\n  const roofLength = roofLengthValue === \"\" ? null : Number(roofLengthValue);\n  const roofHeight = roofHeightValue === \"\" ? null : Number(roofHeightValue);\n\n  let angle = document.getElementById(\"A\").value;\n  if (angle === \"\" && roofLength && roofHeight) {\n    angle = Math.atan(roofHeight \/ roofLength) * (180 \/ Math.PI);\n  } else {\n    angle = Number(angle);\n  }\n\n  const shapeCoefficient = Math.min(0.8, (0.8 * (60 - angle)) \/ 30);\n  const snowLoadOnRoof = shapeCoefficient * designSnowLoad;\n\n  snowLoadPerPanel = snowLoadOnRoof * panelWidth * panelLength;\n  snowLoadperProfile = snowLoadPerPanel \/ 2;\n\n  let maxProfileSpan = PROFILE_SPAN_MAX;\n  if (roofTypeValue === \"6\") {\n    maxProfileSpan = 900;\n  } else if (roofTypeValue === \"2\") {\n    maxProfileSpan = 1200;\n  } else if (roofTypeValue === \"3\" || roofTypeValue === \"4\") {\n    maxProfileSpan = 1500;\n  }\n\n  if (roofTypeValue === \"1\") {\n    if (snowLoadperProfile < 2.3) {\n      profileSpan = 1500;\n    } else if (snowLoadperProfile < 4) {\n      profileSpan = 1000;\n    } else {\n      profileSpan = 500;\n    }\n  } else if (roofTypeValue === \"5\" || roofTypeValue === \"6\") {\n    if (snowLoadperProfile < 3.1) {\n      profileSpan = 1200;\n    } else if (snowLoadperProfile < 3.5) {\n      profileSpan = 1100;\n    } else if (snowLoadperProfile < 4) {\n      profileSpan = 1000;\n    } else if (snowLoadperProfile < 4.7) {\n      profileSpan = 900;\n    } else {\n      profileSpan = 800;\n    }\n  } else {\n    if (snowLoadperProfile < 2.1) {\n      profileSpan = 1600;\n    } else if (snowLoadperProfile < 2.3) {\n      profileSpan = 1500;\n    } else if (snowLoadperProfile < 2.5) {\n      profileSpan = 1400;\n    } else if (snowLoadperProfile < 2.8) {\n      profileSpan = 1300;\n    } else if (snowLoadperProfile < 3.1) {\n      profileSpan = 1200;\n    } else if (snowLoadperProfile < 3.5) {\n      profileSpan = 1100;\n    } else if (snowLoadperProfile < 4) {\n      profileSpan = 1000;\n    } else if (snowLoadperProfile < 4.7) {\n      profileSpan = 900;\n    } else {\n      profileSpan = 800;\n    }\n  }\n\n  recommendedProfileSpan = Math.min(profileSpan, maxProfileSpan);\n\n  const mode = document.querySelector('input[name=\"profileSpanMode\"]:checked')?.value || 'auto';\n  let selectedProfileSpan = recommendedProfileSpan;\n\n  if (mode === 'custom' && profileSpanCustomInput) {\n    let customValue = Number(profileSpanCustomInput.value);\n    if (!Number.isFinite(customValue) || customValue <= 0) {\n      customValue = recommendedProfileSpan;\n      profileSpanCustomInput.value = recommendedProfileSpan;\n    }\n    customValue = Math.round(Math.max(PROFILE_SPAN_MIN, Math.min(PROFILE_SPAN_MAX, customValue)));\n    profileSpanCustomInput.value = customValue;\n    selectedProfileSpan = customValue;\n  } else if (profileSpanCustomInput) {\n    profileSpanCustomInput.placeholder = recommendedProfileSpan;\n  }\n\n  profileSpan = selectedProfileSpan;\n\n  syncProfileSpanControls();\n\n  if (mode === 'custom' && selectedProfileSpan > recommendedProfileSpan) {\n    setWarning(\n      'warningSpan',\n      `* Antamasi kiinnikev\u00e4li ${selectedProfileSpan} mm ylitt\u00e4\u00e4 suosituksen ${recommendedProfileSpan} mm. Varmista, ett\u00e4 kattorakenne ja kiinnikkeet kest\u00e4v\u00e4t kuormituksen.`\n    );\n  } else {\n    setWarning('warningSpan', '');\n  }\n\n  document.getElementById(\"result\").innerHTML =\n    '<img decoding=\"async\" src=\"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/02\/snow_panel.png\" alt=\"Snow Load on Solar Panel\">' +\n    '<p style=\"font-size:20px; font-weight:bold; color:#333;\">Lumikuorma aurinkopaneelia kohden: ' +\n    snowLoadPerPanel.toFixed(2) +\n    \" kN.<\/p>\";\n\n  const selectedRadio = document.querySelector(`input[name=\"roofType\"][value=\"${roofTypeValue}\"]`);\n  const spanLabel = mode === 'custom' ? ' (valitsemasi arvo)' : '';\n  const roofTypeImageSrc = selectedRadio?.dataset?.image || `https:\/\/moisolar.com\/wp-content\/uploads\/2024\/02\/${roofTypeValue}.png`;\n\n  document.getElementById(\"result2\").innerHTML =\n    '<div class=\"result-visual-container\">' +\n    `<img decoding=\"async\" src=\"${roofTypeImageSrc}\" alt=\"Profile Span Image\" class=\"result-visual\">` +\n    '<\/div>' +\n    '<p class=\"result-text\">Kahden kattotelineen v\u00e4linen et\u00e4isyys on <strong>' +\n    profileSpan +\n    ` mm.<\/strong>${spanLabel}<\/p>`;\n\n  if (roofTypeValue === \"5\") {\n    setWarning(\n      \"warning0\",\n      \"* Suosittelemme vahvasti, ett\u00e4 k\u00e4yt\u00e4tte enint\u00e4\u00e4n 1200mm kiinnikev\u00e4li\u00e4 tiilikatoilla. Harkitse lis\u00e4ksi ylim\u00e4\u00e4r\u00e4isten kattotelineiden lis\u00e4\u00e4mist\u00e4 alimpaan profiililinjaan. Suosittelemme asentamaan kattokiinnikkeen mahdollisimman l\u00e4helle aurinkopaneelin keskikiinnikkeit\u00e4 (CL11) v\u00e4hent\u00e4\u00e4ksesi kattotiilen vaurioitumisen riski\u00e4. Alin profiililinja voi ylikuormittua kasaantuvan lumen seurauksena. Huonokuntoinen kattotiili voi t\u00e4ll\u00f6in vaurioitua.\"\n    );\n  } else if (roofTypeValue === \"6\") {\n    setWarning(\n      \"warning0\",\n      \"* Suosittelemme vahvasti, ett\u00e4 k\u00e4yt\u00e4tte enint\u00e4\u00e4n 900mm kiinnikev\u00e4li\u00e4 tiilikatoilla. Harkitse lis\u00e4ksi ylim\u00e4\u00e4r\u00e4isten kattotelineiden lis\u00e4\u00e4mist\u00e4 alimpaan profiililinjaan. Suosittelemme asentamaan kattokiinnikkeen mahdollisimman l\u00e4helle aurinkopaneelin keskikiinnikkeit\u00e4 (CL11) v\u00e4hent\u00e4\u00e4ksesi kattotiilen vaurioitumisen riski\u00e4. Alin profiililinja voi ylikuormittua kasaantuvan lumen seurauksena. Huonokuntoinen kattotiili voi t\u00e4ll\u00f6in vaurioitua.\"\n    );\n  } else {\n    setWarning(\"warning0\", \"\");\n  }\n}\nlet productDiscount = 0;\n\nfunction clearAllData() {\n  productTotals.PR11 = 0;\n  productTotals.PR12 = 0;\n  productTotals.CL11 = 0;\n  productTotals.CL02 = 0;\n  productTotals.RB11 = 0;\n  productTotals.RB12 = 0;\n  productTotals.RB13 = 0;\n  productTotals.RB04 = 0;\n  productTotals.RB15 = 0;\n  productTotals.RB06 = 0;\n  productTotals.RB17 = 0;\n  productTotals.BN01 = 0;\n  productTotals.RS01 = 0;\n  productTotals.RS02 = 0;\n  productTotals.SS05 = 0;\n  productTotals.SS06 = 0;\n  fixtures = {};\n  totalPanelAmount = 0;\n  for (let productx of productData) {\n    productx.total = 0;\n  }\n  eachRowData = [];\n  productDiscount = 0;\n  document.getElementById(\"saveAsCSV\").style.display = \"none\";\n  document.getElementById(\"saveAsPDF\").style.display = \"none\";\n  document.getElementById(\"suggestUserToSendEmail\").style.display = \"none\";\n  setWarning(\"warning0\", \"\");\n  setWarning(\"warningSpan\", \"\");\n  setWarning(\"warning1\", \"\");\n  setWarning(\"warning2\", \"\");\n  document.getElementById(\"resultsSection\").style.display = \"none\";\n  syncProfileSpanControls();\n}\n\nlet eachRowData = [];\n\nfunction animateCalculatingText() {\n  const textElement = document.getElementById(\"calculatingText\");\n  let dotCount = 0; \/\/ Initialize dot count for controlling the number of dots\n\n  \/\/ Clear any existing interval to avoid overlapping animations\n  clearInterval(window.calculatingIntervalId);\n\n  window.calculatingIntervalId = setInterval(() => {\n    let dots = \".\".repeat(dotCount % 4); \/\/ Create a string of dots that cycles from 0 to 3\n    textElement.textContent = `Lasketaan${dots}`; \/\/ Update text content with the current number of dots\n    dotCount++; \/\/ Increment dot count for the next cycle\n  }, 500); \/\/ Update text every 500 milliseconds\n}\n\nfunction checkDiscount() {\n  const discount = $(\"#discount\").val();\n  \/\/ Check if discount is not empty and is a number within the specified range\n  if (discount !== \"\" && !isNaN(discount) && discount >= 0 && discount <= 100) {\n    productDiscount = Number(discount);\n  } else {\n    productDiscount = 0;\n  }\n}\n\nfunction renderResultTable() {\n  const resultTable = document.getElementById(\"resultTable\");\n  resultTable.innerHTML = \"\";\n\n  const headerLabels = [\n    \"Tarvittava tuote\",\n    \"Tarvittava m\u00e4\u00e4r\u00e4\",\n    \"Myyntiyksikk\u00f6\",\n    \"Vaadittu yksikk\u00f6\",\n    \"Yksikk\u00f6hinta (\u20ac)\",\n    \"Alennus(%)\",\n    \"Yksikk\u00f6hinta alennuksen j\u00e4lkeen (\u20ac)\",\n    \"Kokonaishinta tuotteelle (\u20ac)\",\n  ];\n\n  const headerRow = document.createElement(\"tr\");\n  headerLabels.forEach((label) => {\n    const th = document.createElement(\"th\");\n    label.split(\" \").forEach((word) => {\n      const line = document.createElement(\"span\");\n      line.className = \"table-header-line\";\n      line.textContent = word;\n      th.appendChild(line);\n    });\n    headerRow.appendChild(th);\n  });\n  resultTable.appendChild(headerRow);\n\n  fixtures = {};\n  let totalPrice = 0;\n\n  productData.forEach((product) => {\n    const quantity = productTotals[product.code] || 0;\n    if (!quantity) {\n      product.total = 0;\n      return;\n    }\n\n    product.total = quantity;\n    fixtures[product.code] = quantity;\n\n    const packagesNeeded = Math.ceil(quantity \/ product.unitNumber);\n    const discountedUnitPrice = applyDiscount(product.price, productDiscount);\n    const lineTotal = packagesNeeded * discountedUnitPrice;\n    const imageHtml = product.imageUrl\n      ? `<a href=\"${product.url}\"><img decoding=\"async\" src=\"${product.imageUrl}\" alt=\"${product.code}\" style=\"display:block; margin-bottom: 5px;\"><\\\/a>`\n      : \"\";\n    const codeLinkHtml = `<a href=\"${product.url}\">${product.code}<\\\/a>`;\n\n    const row = document.createElement(\"tr\");\n    row.innerHTML = `\n      <td>${imageHtml}${codeLinkHtml}<\\\/td>\n      <td>${quantity} kpl<\\\/td>\n      <td>${product.unitText}<\\\/td>\n      <td>${packagesNeeded}<\\\/td>\n      <td>${formatCurrency(product.price)}<\\\/td>\n      <td>${productDiscount}<\\\/td>\n      <td>${formatCurrency(discountedUnitPrice)}<\\\/td>\n      <td>${formatCurrency(lineTotal)}<\\\/td>\n    `;\n    resultTable.appendChild(row);\n\n    totalPrice += lineTotal;\n  });\n\n  const summaryRow = document.createElement(\"tr\");\n  summaryRow.innerHTML = `\n    <td colspan=\"7\">Kokonaishinta (ALV 0%)<\\\/td>\n    <td>${formatCurrency(totalPrice)}<\\\/td>\n  `;\n  resultTable.appendChild(summaryRow);\n\n  return totalPrice;\n}\n\nfunction downloadTableAsCSV() {\n  const csv = [];\n  const rows = document.querySelectorAll(\"#resultTable tr\");\n\n  for (let i = 0; i < rows.length; i++) {\n    const row = [];\n    const cols = rows[i].querySelectorAll(\"td, th\");\n\n    for (let j = 0; j < cols.length; j++) {\n      \/\/ Clean the text and wrap it in quotes to handle commas correctly\n      const text = cols[j].innerText.replace(\/\"\/g, '\"\"').replace(\".\", \",\");\n      row.push('\"' + text + '\"');\n    }\n    csv.push(row.join(\",\"));\n  }\n\n  \/\/ Create a CSV string from the data\n  const csvString = csv.join(\"\\n\");\n  const blob = new Blob([csvString], { type: \"text\/csv;charset=utf-8;\" });\n  const link = document.createElement(\"a\");\n  const url = URL.createObjectURL(blob);\n  const companyName = document.getElementById(\"companyName\").value.trim();\n  const fileName = (companyName || \"quotation\") + \".csv\";\n\n  link.setAttribute(\"href\", url);\n  link.setAttribute(\"download\", fileName);\n  link.style.visibility = \"hidden\";\n\n  document.body.appendChild(link);\n  link.click();\n  document.body.removeChild(link);\n}\n\ndocument\n  .getElementById(\"calculatorForm\")\n  .addEventListener(\"submit\", function (event) {\n    event.preventDefault();\n\n    if (validateInput()) {\n      \/\/Add a claculating... animation\n      \/\/ Show calculating indicator\n      animateCalculatingText();\n      document.getElementById(\"calculatingIndicator\").style.display = \"flex\";\n\n      \/\/ Simulate calculating for 5 to 10 seconds (using 5000ms as an example)\n      setTimeout(function () {\n        \/\/ After calculating, hide the indicator and proceed\n        document.getElementById(\"calculatingIndicator\").style.display = \"none\";\n        clearInterval(window.calculatingIntervalId);\n        \/\/ Here you can add what should happen after the \"calculating\" phase is over\n      }, 4000); \/\/ Adjust time as needed\n\n      \/\/ Reset the total variables\n      clearAllData();\n\n      const roofType = document\n        .querySelector('input[name=\"roofType\"]:checked')\n        .value;\n      const panelWidth = Number(document.getElementById(\"PW\").value);\n      const numRows = Number(document.getElementById(\"numRows\").value);\n      const panelsPerRow = [];\n\n      calculateSnowLoad(roofType);\n      checkDiscount(); \/\/ Check if need to show user price\n\n      for (let i = 0; i < numRows; i += 1) {\n        panelsPerRow.push(Number(document.getElementById(`row${i}`).value));\n      }\n\n      \/\/ Calculation logic based on roof type\n      for (let i = 0; i < numRows; i += 1) {\n        const numPanels = panelsPerRow[i];\n        if (!numPanels || numPanels <= 0) {\n          continue;\n        }\n        totalPanelAmount += numPanels;\n        const requiredProfileInMM =\n          numPanels * panelWidth + 20 * (numPanels - 1) + 105;\n        const requiredProfileInPCS = requiredProfileInMM \/ 3550;\n        const PR01 = Math.ceil(2 * requiredProfileInPCS); \/\/ panel needs up profile and down profile\n        const PR02 = 2 * Math.floor(requiredProfileInPCS);\n        const CL01 = (numPanels - 1) * 2;\n        const CL02 = 4;\n        const RB0X =\n          Math.ceil((requiredProfileInMM - 400) \/ profileSpan) * 2 + 2;\n\n        eachRowData.push([\n          numPanels,\n          2 * requiredProfileInMM,\n          (2 * requiredProfileInPCS).toFixed(2),\n          PR02,\n          RB0X,\n          CL01,\n          CL02,\n        ]);\n\n        \/\/ console.log(eachRowData);\n\n        if (roofType === \"1\") {\n          \/\/ \"Lock seamed metal roof\"\n          const RB01 = RB0X;\n          const BN01 = RB01;\n\n          productTotals.RB11 += RB01;\n          productTotals.BN01 += BN01;\n        }\n        if (roofType === \"2\") {\n          \/\/ \"Felt roof and metal profile roof\"\n          const RB02 = RB0X;\n          const BN01 = RB02;\n          const SS06 = RB02 * 4;\n\n          productTotals.RB12 += RB02;\n          productTotals.BN01 += BN01;\n          productTotals.SS06 += SS06;\n        }\n        if (roofType === \"3\") {\n          \/\/ \"Tile-shaped metal profile roof\"\n          const RB03 = RB0X;\n          const BN01 = RB03;\n          const RS01 = 2 * RB03;\n          const RS02 = RB03;\n          const SS05 = RB03 * 2;\n\n          productTotals.RB13 += RB03;\n          productTotals.BN01 += BN01;\n          productTotals.RS01 += RS01;\n          productTotals.RS02 += RS02;\n          productTotals.SS05 += SS05;\n        }\n        if (roofType === \"4\") {\n          \/\/ \"Wave-shaped metal profile roof\"\n          const RB04 = RB0X;\n          const BN01 = RB04;\n          const SS05 = RB04 * 2;\n\n          productTotals.RB04 += RB04;\n          productTotals.BN01 += BN01;\n          productTotals.SS05 += SS05;\n        }\n        if (roofType === \"5\") {\n          \/\/ \"Tile roof\"\n          const RB05 = RB0X;\n          const BN01 = RB05;\n          const RB06 = RB05;\n          const SS06 = RB05;\n\n          productTotals.RB15 += RB05;\n          productTotals.BN01 += BN01;\n          productTotals.RB06 += RB06;\n          productTotals.SS06 += SS06;\n        }\n        if (roofType === \"6\") {\n          \/\/ \"Tile roof with flat style\"\n          const RB17 = RB0X;\n          const BN01 = RB17;\n          const SS06 = RB17;\n\n          productTotals.RB17 += RB17;\n          productTotals.BN01 += BN01;\n          productTotals.SS06 += SS06;\n        }\n\n        productTotals.PR11 += PR01;\n        productTotals.PR12 += PR02;\n        productTotals.CL11 += CL01;\n        productTotals.CL02 += CL02;\n      }\n\n      \/\/ Output the results in a table\n      document.getElementById(\"resultTable\").style.display = \"table\";\n      document.getElementById(\"resultTable2\").style.display = \"table\";\n      document.getElementById(\"resultsSection\").style.display = \"flex\";\n\n      renderResultTable();\n\n      buildResultTable2();\n\n      setWarning(\n        \"warning1\",\n        \"* Laskelmat ovat vain suosituksia. Huomioithan rakennuspaikkasi erityisolosuhteet tehdess\u00e4si p\u00e4\u00e4t\u00f6ksi\u00e4.\"\n      );\n\n      setWarning(\n        \"warning2\",\n        \"* T\u00e4m\u00e4 laskin ei ota huomioon joitakin poikkeuksellisia tilanteita, jotka on mainittu Eurokoodissa, kuten ylemm\u00e4t katot. Erityisesti rakennuksen korkeuden ylitt\u00e4ess\u00e4 10 metri\u00e4 ja\/tai sijaitessa hyvin tuulisella ja\/tai lumisella alueella, tullee mitoitus suorittaa riitt\u00e4v\u00e4n ammattitaidon omaavan rakennesuunnittelijan toimesta. Lis\u00e4tietoja varten ole hyv\u00e4 ja konsultoi teknist\u00e4 asiantuntijaamme.\"\n      );\n\n      \/\/ After outputting the results in the table\n      scrollToResults(); \/\/ Call the smooth scroll function here\n\n      document.getElementById(\"saveAsPDF\").style.display = \"block\";\n      document.getElementById(\"saveAsCSV\").style.display = \"block\";\n      document.getElementById(\"suggestUserToSendEmail\").style.display = \"block\";\n\n      \/\/Insert into db\n      const formData = {\n        date: new Date().toISOString().split(\"T\")[0],\n        customer_name: document.getElementById(\"customerName\").value,\n        company_name: document.getElementById(\"companyName\").value,\n        email: document.getElementById(\"email\").value,\n        phone: document.getElementById(\"phone\").value,\n        roof_type: roofType,\n        panel_amount: totalPanelAmount,\n        fixtures: fixtures,\n      };\n\n      fetch(\"https:\/\/api.moisolar.com:3005\/insertRecord\", {\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application\/json\",\n        },\n        body: JSON.stringify(formData),\n      })\n        .then((response) => response.json())\n        .then((data) => {\n          console.log(\"Success\");\n        })\n        .catch((error) => {\n          console.error(\"Error:\", error);\n          \/\/ alert(\"Error inserting record.\");\n        });\n    }\n  });\n\nfunction buildResultTable2() {\n  const resultTable2 = document.getElementById(\"resultTable2\");\n  resultTable2.innerHTML = \"\"; \/\/ Clear previous table contents\n\n  const hasBN = productTotals.BN01 > 0;\n  const hasRS = productTotals.RS01 > 0;\n  const hasSS05 = productTotals.SS05 > 0;\n  const hasSS06 = productTotals.SS06 > 0;\n\n  const headerCells = [\n    { text: \"Rivinumero\" },\n    { lines: [\"Paneelien\", \"m\u00e4\u00e4r\u00e4 (kpl)\"] },\n    { lines: [\"PR11\", \"(mm)\"] },\n    { lines: [\"PR11\", \"(kpl)\"] },\n    { lines: [\"PR12\", \"(kpl)\"] },\n    { lines: [\"Kattokiinnike\", \"(kpl)\"] },\n    { lines: [\"CL11\", \"(kpl)\"] },\n    { lines: [\"CL02\", \"(kpl)\"] },\n  ];\n\n  if (hasSS06) {\n    headerCells.push({ lines: [\"SS06\", \"(kpl)\"] });\n  }\n  if (hasSS05) {\n    headerCells.push({ lines: [\"SS05\", \"(kpl)\"] });\n  }\n  if (hasBN) {\n    headerCells.push({ lines: [\"BN01\", \"(kpl)\"] });\n  }\n  if (hasRS) {\n    headerCells.push({ lines: [\"RS01\", \"(kpl)\"] });\n    headerCells.push({ lines: [\"RS02\", \"(kpl)\"] });\n  }\n\n  const columnCount = headerCells.length;\n  const titleRow = document.createElement(\"tr\");\n  titleRow.innerHTML = `<td colspan=\"${columnCount}\" style=\"text-align:center;\"><strong>Tiedot rivi kerrallaan<\\\/strong><\\\/td>`;\n  resultTable2.appendChild(titleRow);\n\n  const headerRow = document.createElement(\"tr\");\n  headerCells.forEach((cell) => {\n    const th = document.createElement(\"th\");\n    if (cell.lines && cell.lines.length) {\n      cell.lines.forEach((line) => {\n        const div = document.createElement(\"div\");\n        div.textContent = line;\n        th.appendChild(div);\n      });\n    } else {\n      th.textContent = cell.text;\n    }\n    headerRow.appendChild(th);\n  });\n  resultTable2.appendChild(headerRow);\n\n  eachRowData.forEach(function (rowData, index) {\n    const rbPerRow = rowData[4];\n    const rowCells = [`Rivi ${index + 1}`, ...rowData];\n\n    if (hasSS06) {\n      rowCells.push(rbPerRow * 4);\n    }\n    if (hasSS05) {\n      rowCells.push(rbPerRow * 2);\n    }\n    if (hasBN) {\n      rowCells.push(rbPerRow);\n    }\n    if (hasRS) {\n      rowCells.push(2 * rbPerRow);\n      rowCells.push(rbPerRow);\n    }\n\n    const row = document.createElement(\"tr\");\n    rowCells.forEach(function (cell) {\n      const td = document.createElement(\"td\");\n      td.textContent = cell;\n      row.appendChild(td);\n    });\n    resultTable2.appendChild(row);\n  });\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", function () {\n  document\n    .getElementById(\"saveAsPDF\")\n    .addEventListener(\"click\", async function (event) {\n      event.preventDefault();\n\n      try {\n        const { jsPDF } = window.jspdf;\n        const doc = new jsPDF();\n        \/\/ Constants for the table\n        const tableLeft = 10;\n        const tableRight = 201;\n        const tableMiddle = (tableLeft + tableRight) \/ 2;\n        const tableGap1 = 20;\n        const tp1 = tableLeft + tableGap1;\n        const tableGap2 = 35;\n        const tp2 = tp1 + tableGap2;\n        const tableGap3 = 25;\n        const tp3 = tp2 + tableGap3;\n        const tableGap4 = 25;\n        const tp4 = tp3 + tableGap4;\n        const tableGap5 = 18;\n        const tp5 = tp4 + tableGap5;\n        const tableGap6 = 33;\n        const tp6 = tp5 + tableGap6;\n        const tableGap7 = 18;\n        const tp7 = tp6 + tableGap7;\n        const tableGap8 = 18;\n        const tp8 = tp7 + tableGap8;\n        const rowHeight = 4;\n        const rowHeight2 = 9;\n\n        const right = doc.internal.pageSize.width - 40; \/\/ Adjust the -60 as needed\n\n        \/\/ console.log(tp1, tp2, tp3, tp4, tp5, tp6, tp7, tp8);\n\n        let rbType = \"\";\n        const hasBN = productTotals.BN01 > 0;\n        const hasRS = productTotals.RS01 > 0;\n        const hasSS05 = productTotals.SS05 > 0;\n        const hasSS06 = productTotals.SS06 > 0;\n\n        \/\/ console.log(eachRowData);\n\n        let currentY = 0; \/\/ Adjusted starting position to accommodate the logo and title\n        let spaceBetweenLines = 2;\n\n        \/\/ Load the logo\n        const logoWidth = 30;\n        const logoHeight = (422 \/ 1838) * logoWidth; \/\/ Calculating height to maintain aspect ratio\n        const logo = await fetchImageAsBase64(\n          \"https:\/\/moisolar.com\/wp-content\/uploads\/2023\/08\/moisolarlogo.jpg\"\n        );\n        doc.addImage(logo, \"JPEG\", right, 3, logoWidth, logoHeight);\n\n        doc.setFontSize(11);\n        const customerName = $(\"#customerName\").val();\n        const companyName = $(\"#companyName\").val();\n        doc.text(companyName, 10, 10);\n        doc.setFontSize(10);\n        doc.text(customerName, 10, 15);\n\n        currentY += 23;\n\n        \/\/ Add title\n        const title = \"Laskennan tulos\";\n        doc.setFontSize(14); \/\/ Adjust for a bigger title size\n        doc.text(title, doc.internal.pageSize.width \/ 2, currentY, {\n          align: \"center\",\n        }); \/\/ Positioned to be centered\n\n        currentY += 6;\n\n        \/\/Prepare for first table\n        let conclusionText =\n          \"Vaaditun asennuksen tekemiseksi tarvitset seuraavat osat.\";\n\n        doc.setFontSize(10);\n        doc.setLineWidth(0.5);\n        doc.text(conclusionText, tableLeft, currentY);\n        currentY += 4;\n\n        \/\/Draw top line\n        doc.line(tableLeft, currentY, tableRight, currentY);\n        \/\/ Vertical lines for columns\n        doc.line(tableLeft, currentY, tableLeft, currentY + rowHeight2); \/\/ Left\n        doc.line(\n          tableLeft + 47,\n          currentY,\n          tableLeft + 47,\n          currentY + rowHeight2\n        ); \/\/ Middle 1\n        doc.line(\n          tableLeft + 2 * 47,\n          currentY,\n          tableLeft + 2 * 47,\n          currentY + rowHeight2\n        ); \/\/ Middle 2\n        doc.line(\n          tableLeft + 3 * 47,\n          currentY,\n          tableLeft + 3 * 47,\n          currentY + rowHeight2\n        ); \/\/ Middle 3\n        doc.line(tableRight, currentY, tableRight, currentY + rowHeight2); \/\/ Right\n        \/\/ Add head content\n        doc.setFontSize(9);\n        doc.text(\"Tuote\", tableLeft + 5, currentY + 5);\n        doc.text(\"Tarvittava m\u00e4\u00e4r\u00e4\", tableLeft + 47 + 5, currentY + 5);\n        doc.text(\"Myyntiyksikk\u00f6\", tableLeft + 2 * 47 + 5, currentY + 5);\n        doc.text(\"Vaadittu yksikk\u00f6\", tableLeft + 3 * 47 + 5, currentY + 5);\n\n        currentY += rowHeight2 - 1;\n        let isFirstRow = true;\n        const imgWidth = 10; \/\/ Desired width for the image in the PDF\n        const imgHeight = (225 \/ 300) * imgWidth;\n\n        for (let product of productData) {\n          if (product.total > 0) {\n            if (product.code.includes(\"RB\") && !product.code.includes(\"06\")) {\n              rbType = product.code.toLowerCase();\n            }\n\n            let imageAdded = false;\n            if (product.imageUrl) {\n              try {\n                const image = await fetchImageAsBase64(product.imageUrl);\n                doc.addImage(\n                  image,\n                  \"JPEG\",\n                  tableLeft + 5,\n                  currentY + 1,\n                  imgWidth,\n                  imgHeight\n                );\n                imageAdded = true;\n              } catch (error) {\n                console.warn(\"Failed to load product image for PDF:\", product.code, error);\n              }\n            }\n\n            doc.setFontSize(9); \/\/ Set font size\n\n            const textX =\n              tableLeft + 5 + (imageAdded ? imgWidth + 5 : 0);\n            const textY = currentY + 5;\n            const textWidth = doc.getTextWidth(product.code);\n\n            \/\/ Set the hyperlink just for the product code\n            doc.setTextColor(0, 0, 255); \/\/ Set text color to blue\n            doc.text(product.code, textX, textY);\n            const linkX = imageAdded ? tableLeft + 5 : textX;\n            const linkWidth = textWidth + (imageAdded ? imgWidth + 5 : 0);\n            const linkHeight = imageAdded ? Math.max(imgHeight, doc.getFontSize()) : doc.getFontSize();\n            doc.link(linkX, currentY, linkWidth, linkHeight, {\n              url: product.url,\n            });\n\n            \/\/ Draw underline for link\n            doc.setDrawColor(0, 0, 255);\n\n            doc.line(\n              textX,\n              currentY + 6,\n              textX + textWidth,\n              currentY + 6\n            );\n\n            \/\/ Reset text color to black\n            doc.setTextColor(0, 0, 0);\n\n            \/\/ Reset the draw color to black for table lines\n            doc.setDrawColor(0, 0, 0);\n\n            doc.text(`${product.total} kpl`, tableLeft + 47 + 5, currentY + 5);\n\n            doc.text(product.unitText, tableLeft + 2 * 47 + 5, currentY + 5);\n\n            let unitAmount = Math.ceil(product.total \/ product.unitNumber);\n            let content2 = unitAmount + \" \" + product.unit;\n            doc.text(content2, tableLeft + 3 * 47 + 5, currentY + 5);\n\n            \/\/ Draw topmost horizontal line only once when encountering the first product\n            if (isFirstRow) {\n              doc.line(tableLeft, currentY, tableRight, currentY);\n              isFirstRow = false;\n            }\n\n            \/\/ Draw horizontal line for row bottom\n            doc.line(\n              tableLeft,\n              currentY + rowHeight2,\n              tableRight,\n              currentY + rowHeight2\n            );\n\n            \/\/ Vertical lines for columns\n            doc.line(tableLeft, currentY, tableLeft, currentY + rowHeight2); \/\/ Left\n            doc.line(\n              tableLeft + 47,\n              currentY,\n              tableLeft + 47,\n              currentY + rowHeight2\n            ); \/\/ Middle 1\n            doc.line(\n              tableLeft + 2 * 47,\n              currentY,\n              tableLeft + 2 * 47,\n              currentY + rowHeight2\n            ); \/\/ Middle 2\n            doc.line(\n              tableLeft + 3 * 47,\n              currentY,\n              tableLeft + 3 * 47,\n              currentY + rowHeight2\n            ); \/\/ Middle 3\n            doc.line(tableRight, currentY, tableRight, currentY + rowHeight2); \/\/ Right\n\n            \/\/ Move down for next product\n            currentY += rowHeight2;\n          }\n        }\n        currentY += 5;\n        \/\/ Prepare 2nd table\n        doc.setFontSize(10);\n        doc.text(\n          \"Kunkin rivin tiedot n\u00e4kyv\u00e4t alla olevassa taulukossa.\",\n          tableLeft,\n          currentY\n        );\n        currentY += 3;\n\n        const rowColumns = [\n          {\n            label: \"Rivinumero\",\n            weight: 0.9,\n            getValue: (_, rowIndex) => `Rivi ${rowIndex + 1}`,\n          },\n          {\n            label: \"Paneelien m\u00e4\u00e4r\u00e4\",\n            labelLines: [\"Paneelien m\u00e4\u00e4r\u00e4\", \"(kpl)\"],\n            weight: 1.3,\n            getValue: (rowData) => rowData[0],\n          },\n          {\n            label: \"PR11\",\n            labelLines: [\"PR11\", \"(mm)\"],\n            weight: 0.85,\n            getValue: (rowData) => rowData[1],\n          },\n          {\n            label: \"PR11 kpl\",\n            labelLines: [\"PR11\", \"(kpl)\"],\n            weight: 0.8,\n            getValue: (rowData) => rowData[2],\n          },\n          {\n            label: \"PR12\",\n            labelLines: [\"PR12\", \"(kpl)\"],\n            weight: 0.8,\n            getValue: (rowData) => rowData[3],\n          },\n          {\n            label: \"Kattokiinnike\",\n            labelLines: [\"Kattokiinnike\", \"(kpl)\"],\n            weight: 1.05,\n            getValue: (rowData) => rowData[4],\n          },\n          {\n            label: \"CL11\",\n            labelLines: [\"CL11\", \"(kpl)\"],\n            weight: 0.8,\n            getValue: (rowData) => rowData[5],\n          },\n          {\n            label: \"CL02\",\n            labelLines: [\"CL02\", \"(kpl)\"],\n            weight: 0.8,\n            getValue: (rowData) => rowData[6],\n          },\n        ];\n\n        if (hasSS06) {\n          rowColumns.push({\n            label: \"SS06\",\n            labelLines: [\"SS06\", \"(kpl)\"],\n            weight: 0.75,\n            getValue: (rowData) => rowData[4] * 4,\n          });\n        }\n        if (hasSS05) {\n          rowColumns.push({\n            label: \"SS05\",\n            labelLines: [\"SS05\", \"(kpl)\"],\n            weight: 0.75,\n            getValue: (rowData) => rowData[4] * 2,\n          });\n        }\n        if (hasBN) {\n          rowColumns.push({\n            label: \"BN01\",\n            labelLines: [\"BN01\", \"(kpl)\"],\n            weight: 0.75,\n            getValue: (rowData) => rowData[4],\n          });\n        }\n        if (hasRS) {\n          rowColumns.push({\n            label: \"RS01\",\n            labelLines: [\"RS01\", \"(kpl)\"],\n            weight: 0.75,\n            getValue: (rowData) => rowData[4] * 2,\n          });\n          rowColumns.push({\n            label: \"RS02\",\n            labelLines: [\"RS02\", \"(kpl)\"],\n            weight: 0.75,\n            getValue: (rowData) => rowData[4],\n          });\n        }\n\n        const totalWeight = rowColumns.reduce(\n          (sum, column) => sum + column.weight,\n          0\n        );\n        const pageWidth = doc.internal.pageSize.width;\n        const rightMargin = 12;\n        const maxRowTableWidth = pageWidth - tableLeft - rightMargin;\n        let cursorX = tableLeft;\n\n        rowColumns.forEach((column) => {\n          const computedWidth =\n            (column.weight \/ totalWeight) * maxRowTableWidth;\n          const width = Math.max(13, computedWidth);\n          column.start = cursorX;\n          column.end = cursorX + width;\n          column.width = width;\n          cursorX = column.end;\n        });\n\n        const rowTableRight = cursorX;\n\n        const headerLineHeight = 3.2;\n        const maxHeaderLines = rowColumns.reduce(\n          (max, column) => Math.max(max, (column.labelLines?.length || 1)),\n          1\n        );\n        const headerHeight = Math.max(rowHeight, maxHeaderLines * headerLineHeight + 1);\n\n        doc.line(tableLeft, currentY, rowTableRight, currentY); \/\/ Draw top line\n        doc.setFontSize(8);\n\n        rowColumns.forEach((column) => {\n          doc.line(column.end, currentY, column.end, currentY + headerHeight); \/\/ Column borders\n          const labels = column.labelLines || [column.label];\n          labels.forEach((text, index) => {\n            doc.text(text, column.start + 2, currentY + 3 + index * headerLineHeight);\n          });\n        });\n        doc.line(tableLeft, currentY, tableLeft, currentY + headerHeight); \/\/ Left border\n\n        currentY += headerHeight;\n        doc.line(tableLeft, currentY, rowTableRight, currentY);\n\n        eachRowData.forEach(function (rowData, index) {\n          doc.line(tableLeft, currentY, tableLeft, currentY + rowHeight); \/\/ Left border\n          rowColumns.forEach((column) => {\n            const value = column.getValue(rowData, index);\n            doc.text(String(value), column.start + 2, currentY + 3);\n            doc.line(\n              column.end,\n              currentY,\n              column.end,\n              currentY + rowHeight\n            );\n          });\n\n          currentY += rowHeight;\n          doc.line(tableLeft, currentY, rowTableRight, currentY);\n        });\n\n        \/\/ Show Text info\n        currentY += 6;\n\n        doc.setFontSize(10); \/\/ Adjusting the font size for the note\n        doc.text(\n          \"Lumikuorma aurinkopaneelia kohden: \" +\n            snowLoadPerPanel.toFixed(2) +\n            \"kN. Kahden kattotelineen v\u00e4linen et\u00e4isyys on \" +\n            profileSpan +\n            \" mm.\",\n          tableLeft,\n          currentY\n        );\n\n        currentY += 5;\n\n        if (customSpanWarning) {\n          doc.setFontSize(9);\n          const warningLines = doc.splitTextToSize(\n            customSpanWarning,\n            rowTableRight - tableLeft\n          );\n          warningLines.forEach((line, index) => {\n            doc.text(line, tableLeft, currentY + index * 4);\n          });\n          currentY += warningLines.length * 4 + 2;\n        }\n\n        if (rbType === \"rb15\" || rbType === \"rb17\") {\n          const maxSpanText = rbType === \"rb17\" ? \"900\" : \"1200\";\n\n          doc.setFontSize(9);\n          doc.text(\n            `* Suosittelemme vahvasti, ett\u00e4 k\u00e4yt\u00e4tte enint\u00e4\u00e4n ${maxSpanText}mm kiinnikev\u00e4li\u00e4 tiilikatoilla. Harkitse lis\u00e4ksi ylim\u00e4\u00e4r\u00e4isten kattotelineiden lis\u00e4\u00e4mist\u00e4`,\n            tableLeft,\n            currentY\n          );\n          doc.text(\n            \"alimpaan profiililinjaan. Suosittelemme asentamaan kattokiinnikkeen mahdollisimman l\u00e4helle aurinkopaneelin keskikiinnikkeit\u00e4(CL11)\",\n            tableLeft + 2,\n            currentY + 4\n          );\n          doc.text(\n            \"v\u00e4hent\u00e4\u00e4ksesi kattotiilen vaurioitumisen riski\u00e4. Alin profiililinja voi ylikuormittua kasaantuvan lumen seurauksena. Huonokuntoinen\",\n            tableLeft + 2,\n            currentY + 8\n          );\n          doc.text(\n            \"kattotiili voi t\u00e4ll\u00f6in vaurioitua.\",\n            tableLeft + 2,\n            currentY + 12\n          );\n\n          currentY += 17;\n        }\n\n        \/\/ Note after the table\n        doc.setFontSize(9); \/\/ Adjusting the font size for the note\n        doc.text(\n          \"* Laskelmat ovat vain suosituksia. Huomioithan rakennuspaikkasi erityisolosuhteet tehdess\u00e4si p\u00e4\u00e4t\u00f6ksi\u00e4.\",\n          tableLeft,\n          currentY\n        );\n        doc.text(\n          \"* T\u00e4m\u00e4 laskin ei ota huomioon joitakin poikkeuksellisia tilanteita, jotka on mainittu Eurokoodissa, kuten ylemm\u00e4t katot. Erityisesti \",\n          tableLeft,\n          currentY + 7 - 2\n        );\n        doc.text(\n          \"rakennuksen korkeuden ylitt\u00e4ess\u00e4 10 metri\u00e4 ja\/tai sijaitessa hyvin tuulisella ja\/tai lumisella alueella, tullee mitoitus suorittaa riitt\u00e4v\u00e4n \",\n          tableLeft + 2,\n          currentY + 11 - 2\n        );\n        doc.text(\n          \"ammattitaidon omaavan rakennesuunnittelijan toimesta. Lis\u00e4tietoja varten ole hyv\u00e4 ja konsultoi teknist\u00e4 asiantuntijaamme.\",\n          tableLeft + 2,\n          currentY + 15 - 2\n        );\n\n        \/\/ Company information at the bottom\n        const pageHeight = 297; \/\/ A4 size\n        doc.setFontSize(8); \/\/ Adjusting the font size for the footer details\n        doc.text(\"Moisolar Oy\", 10, pageHeight - 10);\n        doc.text(\"Tekniikantie 12, 02150 Espoo, Suomi\", 10, pageHeight - 5);\n\n        const middle = doc.internal.pageSize.width \/ 2 - 20; \/\/ Adjust the -20 as needed\n        doc.text(\"www.moisolar.com\", middle, pageHeight - 13);\n        doc.text(\"info@moisolar.com\", middle, pageHeight - 9);\n        doc.text(\"+358 40 838 2368\", middle, pageHeight - 5);\n\n        doc.text(\"Y-tunnus 3345172-1\", right, pageHeight - 10);\n        doc.text(\"Varasto:01260 Vantaa\", right, pageHeight - 5);\n\n        \/\/ Save the PDF\n        doc.save(\"Laskennan tulos.pdf\");\n      } catch (error) {\n        console.error(\"An error occurred:\", error);\n      }\n    });\n});\n\n\/\/ Fetch the image and return it as a base64 data URI\nasync function fetchImageAsBase64(url) {\n  const response = await fetch(url);\n  const blob = await response.blob();\n  return new Promise((resolve, reject) => {\n    const reader = new FileReader();\n    reader.onloadend = () => resolve(reader.result);\n    reader.onerror = reject;\n    reader.readAsDataURL(blob);\n  });\n}\n\n  <\/script>\n<\/body>\n<\/html>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<\/div>","protected":false},"excerpt":{"rendered":"<p>Solar Panel Fixture Calculator Welcome to our enhanced Solar Panel Fixture Calculator, now leveraging SGS test results for unparalleled accuracy. This intuitive tool simplifies your solar installation planning by instantly estimating the required roof fixtures based on the city information, roof angle, panel size, roof type, and number of solar panels you input. Designed for all project scales, it aids in efficient resource management and smoother installations. Begin optimizing your solar panel installation process today with our scientifically backed calculator. Nordic Slope Fixture Calculator Set City Information Selecting the correct city is important as the general snow load varies by location. This value is used to calculate the specific load on your solar panels. Select Your City Akaa &#8211; 2.3 kN\/m\u00b2Alaja\u0308rvi &#8211; 2.5 kN\/m\u00b2Alavieska &#8211; 2.0 kN\/m\u00b2Alavus &#8211; 2.5 kN\/m\u00b2Asikkala &#8211; 2.5 kN\/m\u00b2Askola &#8211; 2.7 kN\/m\u00b2Aura &#8211; 2.5 kN\/m\u00b2Bra\u0308ndo\u0308 &#8211; 2.0 kN\/m\u00b2Eckero\u0308 &#8211; 2.0 kN\/m\u00b2Enonkoski &#8211; 2.5 kN\/m\u00b2Enontekio\u0308 &#8211; 3.5 kN\/m\u00b2Espoo &#8211; 2.75 kN\/m\u00b2Eura &#8211; 2.1 kN\/m\u00b2Eurajoki &#8211; 2.0 kN\/m\u00b2Evija\u0308rvi &#8211; 2.2 kN\/m\u00b2Finstro\u0308m &#8211; 2.0 kN\/m\u00b2Forssa &#8211; 2.6 kN\/m\u00b2Fo\u0308glo\u0308 &#8211; 2.0 kN\/m\u00b2Geta &#8211; 2.0 kN\/m\u00b2Haapaja\u0308rvi &#8211; 2.5 kN\/m\u00b2Haapavesi &#8211; 2.4 kN\/m\u00b2Hailuoto &#8211; 2.4 kN\/m\u00b2Halsua &#8211; 2.4 kN\/m\u00b2Hamina &#8211; 2.75 kN\/m\u00b2Hammarland &#8211; 2.0 kN\/m\u00b2Hankasalmi &#8211; 2.5 kN\/m\u00b2Hanko &#8211; 2.5 kN\/m\u00b2Harjavalta &#8211; 2.0 kN\/m\u00b2Hartola &#8211; 2.5 kN\/m\u00b2Hattula &#8211; 2.5 kN\/m\u00b2Hausja\u0308rvi &#8211; 2.75 kN\/m\u00b2Heinola &#8211; 2.5 kN\/m\u00b2Heina\u0308vesi &#8211; 2.5 kN\/m\u00b2Helsinki &#8211; 2.75 kN\/m\u00b2Hirvensalmi &#8211; 2.5 kN\/m\u00b2Hollola &#8211; 2.75 kN\/m\u00b2Honkajoki &#8211; 2.5 kN\/m\u00b2Huittinen &#8211; 2.0 kN\/m\u00b2Humppila &#8211; 2.25 kN\/m\u00b2Hyrynsalmi &#8211; 3.5 kN\/m\u00b2Hyvinka\u0308a\u0308 &#8211; 2.75 kN\/m\u00b2Ha\u0308meenkyro\u0308 &#8211; 2.5 kN\/m\u00b2Ha\u0308meenlinna &#8211; 2.5 kN\/m\u00b2Ii &#8211; 2.8 kN\/m\u00b2Iisalmi &#8211; 2.6 kN\/m\u00b2Iitti &#8211; 2.5 kN\/m\u00b2Ikaalinen &#8211; 2.5 kN\/m\u00b2Ilmajoki &#8211; 2.5 kN\/m\u00b2Ilomantsi &#8211; 2.75 kN\/m\u00b2Imatra &#8211; 2.75 kN\/m\u00b2Inari &#8211; 3.0 kN\/m\u00b2Inkoo &#8211; 2.75 kN\/m\u00b2Isojoki &#8211; 2.5 kN\/m\u00b2Isokyro\u0308 &#8211; 2.3 kN\/m\u00b2Janakkala &#8211; 2.75 kN\/m\u00b2Joensuu &#8211; 2.6 kN\/m\u00b2Jokioinen &#8211; 2.6 kN\/m\u00b2Jomala &#8211; 2.0 kN\/m\u00b2Joroinen &#8211; 2.5 kN\/m\u00b2Joutsa &#8211; 2.5 kN\/m\u00b2Juuka &#8211; 2.75 kN\/m\u00b2Juupajoki &#8211; 2.5 kN\/m\u00b2Juva &#8211; 2.5 kN\/m\u00b2Jyva\u0308skyla\u0308 &#8211; 2.5 kN\/m\u00b2Ja\u0308mija\u0308rvi &#8211; 2.5 kN\/m\u00b2Ja\u0308msa\u0308 &#8211; 2.5 kN\/m\u00b2Ja\u0308rvenpa\u0308a\u0308 &#8211; 2.75 kN\/m\u00b2Kaarina &#8211; 2.5 kN\/m\u00b2Kaavi &#8211; 2.65 kN\/m\u00b2Kajaani &#8211; 2.75 kN\/m\u00b2Kalajoki &#8211; 2.0 kN\/m\u00b2Kangasala &#8211; 2.5 kN\/m\u00b2Kangasniemi &#8211; 2.5 kN\/m\u00b2Kankaanpa\u0308a\u0308 &#8211; 2.5 kN\/m\u00b2Kannonkoski &#8211; 2.5 kN\/m\u00b2Kannus &#8211; 2.1 kN\/m\u00b2Karijoki &#8211; 2.5 kN\/m\u00b2Karkkila &#8211; 2.75 kN\/m\u00b2Karstula &#8211; 2.5 kN\/m\u00b2Karvia &#8211; 2.5 kN\/m\u00b2Kaskinen &#8211; 2.0 kN\/m\u00b2Kauhajoki &#8211; 2.5 kN\/m\u00b2Kauhava &#8211; 2.3 kN\/m\u00b2Kauniainen &#8211; 2.75 kN\/m\u00b2Kaustinen &#8211; 2.2 kN\/m\u00b2Keitele &#8211; 2.5 kN\/m\u00b2Kemi &#8211; 3.0 kN\/m\u00b2Kemija\u0308rvi &#8211; 3.0 kN\/m\u00b2Keminmaa &#8211; 3.0 kN\/m\u00b2Kemio\u0308nsaari &#8211; 2.5 kN\/m\u00b2Kempele &#8211; 2.25 kN\/m\u00b2Kerava &#8211; 2.75 kN\/m\u00b2Keuruu &#8211; 2.5 kN\/m\u00b2Kihnio\u0308 &#8211; 2.5 kN\/m\u00b2Kinnula &#8211; 2.5 kN\/m\u00b2Kirkkonummi &#8211; 2.75 kN\/m\u00b2Kitee &#8211; 2.75 kN\/m\u00b2Kittila\u0308 &#8211; 3.0 kN\/m\u00b2Kiuruvesi &#8211; 2.55 kN\/m\u00b2Kivija\u0308rvi &#8211; 2.5 kN\/m\u00b2Kokema\u0308ki &#8211; 2.1 kN\/m\u00b2Kokkola &#8211; 2.0 kN\/m\u00b2Kolari &#8211; 3.0 kN\/m\u00b2Konnevesi &#8211; 2.5 kN\/m\u00b2Kontiolahti &#8211; 2.75 kN\/m\u00b2Korsna\u0308s &#8211; 2.0 kN\/m\u00b2Koski Tl &#8211; 2.6 kN\/m\u00b2Kotka &#8211; 2.65 kN\/m\u00b2Kouvola &#8211; 2.5 kN\/m\u00b2Kristiinankaupunki &#8211; 2.5 kN\/m\u00b2Kruunupyy &#8211; 2.2 kN\/m\u00b2Kuhmo &#8211; 3.0 kN\/m\u00b2Kuhmoinen &#8211; 2.5 kN\/m\u00b2Kumlinge &#8211; 2.0 kN\/m\u00b2Kuopio &#8211; 2.5 kN\/m\u00b2Kuortane &#8211; 2.5 kN\/m\u00b2Kurikka &#8211; 2.5 kN\/m\u00b2Kustavi &#8211; 2.1 kN\/m\u00b2Kuusamo &#8211; 3.5 kN\/m\u00b2Kyyja\u0308rvi &#8211; 2.5 kN\/m\u00b2Ka\u0308rko\u0308la\u0308 &#8211; 2.75 kN\/m\u00b2Ka\u0308rsa\u0308ma\u0308ki &#8211; 2.5 kN\/m\u00b2Ko\u0308kar &#8211; 2.0 kN\/m\u00b2Lahti &#8211; 2.65 kN\/m\u00b2Laihia &#8211; 2.3 kN\/m\u00b2Laitila &#8211; 2.2 kN\/m\u00b2Lapinja\u0308rvi &#8211; 2.5 kN\/m\u00b2Lapinlahti &#8211; 2.6 kN\/m\u00b2Lappaja\u0308rvi &#8211; 2.4 kN\/m\u00b2Lappeenranta &#8211; 2.75 kN\/m\u00b2Lapua &#8211; 2.5 kN\/m\u00b2Laukaa &#8211; 2.5 kN\/m\u00b2Lemi &#8211; 2.7 kN\/m\u00b2Lemland &#8211; 2.0 kN\/m\u00b2Lempa\u0308a\u0308la\u0308 &#8211; 2.4 kN\/m\u00b2Leppa\u0308virta &#8211; 2.5 kN\/m\u00b2Lestija\u0308rvi &#8211; 2.5 kN\/m\u00b2Lieksa &#8211; 2.75 kN\/m\u00b2Lieto &#8211; 2.6 kN\/m\u00b2Liminka &#8211; 2.45 kN\/m\u00b2Liperi &#8211; 2.55 kN\/m\u00b2Lohja &#8211; 2.75 kN\/m\u00b2Loimaa &#8211; 2.65 kN\/m\u00b2Loppi &#8211; 2.75 kN\/m\u00b2Loviisa &#8211; 2.5 kN\/m\u00b2Luhanka &#8211; 2.5 kN\/m\u00b2Lumijoki &#8211; 2.25 kN\/m\u00b2Lumparland &#8211; 2.0 kN\/m\u00b2Luoto &#8211; 2.0 kN\/m\u00b2Luuma\u0308ki &#8211; 2.75 kN\/m\u00b2Maalahti &#8211; 2.0 kN\/m\u00b2Maarianhamina &#8211; 2.0 kN\/m\u00b2Marttila &#8211; 2.6 kN\/m\u00b2Masku &#8211; 2.3 kN\/m\u00b2Merija\u0308rvi &#8211; 2.0 kN\/m\u00b2Merikarvia &#8211; 2.4 kN\/m\u00b2Miehikka\u0308la\u0308 &#8211; 2.75 kN\/m\u00b2Mikkeli &#8211; 2.5 kN\/m\u00b2Muhos &#8211; 2.5 kN\/m\u00b2Multia &#8211; 2.5 kN\/m\u00b2Muonio &#8211; 3.0 kN\/m\u00b2Mustasaari &#8211; 2.0 kN\/m\u00b2Muurame &#8211; 2.5 kN\/m\u00b2Myna\u0308ma\u0308ki &#8211; 2.3 kN\/m\u00b2Myrskyla\u0308 &#8211; 2.65 kN\/m\u00b2Ma\u0308ntsa\u0308la\u0308 &#8211; 2.75 kN\/m\u00b2Ma\u0308ntta\u0308\u2010Vilppula &#8211; 2.5 kN\/m\u00b2Ma\u0308ntyharju &#8211; 2.5 kN\/m\u00b2Naantali &#8211; 2.5 kN\/m\u00b2Nakkila &#8211; 2.0 kN\/m\u00b2Nivala &#8211; 2.35 kN\/m\u00b2Nokia &#8211; 2.45 kN\/m\u00b2Nousiainen &#8211; 2.3 kN\/m\u00b2Nurmes &#8211; 3.0 kN\/m\u00b2Nurmija\u0308rvi &#8211; 2.75 kN\/m\u00b2Na\u0308rpio\u0308 &#8211; 2.1 kN\/m\u00b2Orimattila &#8211; 2.75 kN\/m\u00b2Oripa\u0308a\u0308 &#8211; 2.2 kN\/m\u00b2Orivesi &#8211; 2.5 kN\/m\u00b2Oulainen &#8211; 2.15 kN\/m\u00b2Oulu &#8211; 2.45 kN\/m\u00b2Outokumpu &#8211; 2.6 kN\/m\u00b2Padasjoki &#8211; 2.5 kN\/m\u00b2Paimio &#8211; 2.6 kN\/m\u00b2Paltamo &#8211; 3.3 kN\/m\u00b2Parainen &#8211; 2.5 kN\/m\u00b2Parikkala &#8211; 2.75 kN\/m\u00b2Parkano &#8211; 2.5 kN\/m\u00b2Pederso\u0308ren kunta &#8211; 2.1 kN\/m\u00b2Pelkosenniemi &#8211; 2.9 kN\/m\u00b2Pello &#8211; 3.0 kN\/m\u00b2Perho &#8211; 2.5 kN\/m\u00b2Pertunmaa &#8211; 2.5 kN\/m\u00b2Peta\u0308ja\u0308vesi &#8211; 2.5 kN\/m\u00b2Pieksa\u0308ma\u0308ki &#8211; 2.5 kN\/m\u00b2Pielavesi &#8211; 2.5 kN\/m\u00b2Pietarsaari &#8211; 2.0 kN\/m\u00b2Pihtipudas &#8211; 2.5 kN\/m\u00b2Pirkkala &#8211; 2.4 kN\/m\u00b2Polvija\u0308rvi &#8211; 2.75 kN\/m\u00b2Pomarkku &#8211; 2.4 kN\/m\u00b2Pori &#8211; 2.0 kN\/m\u00b2Pornainen &#8211; 2.75 kN\/m\u00b2Porvoo &#8211; 2.65 kN\/m\u00b2Posio &#8211; 3.5 kN\/m\u00b2Pudasja\u0308rvi &#8211; 3.5 kN\/m\u00b2Pukkila &#8211; 2.75 kN\/m\u00b2Punkalaidun &#8211; 2.1 kN\/m\u00b2Puolanka &#8211; 3.5 kN\/m\u00b2Puumala &#8211; 2.5 kN\/m\u00b2Pyhta\u0308a\u0308 &#8211; 2.5 kN\/m\u00b2Pyha\u0308joki &#8211; 2.05 kN\/m\u00b2Pyha\u0308ja\u0308rvi &#8211; 2.5 kN\/m\u00b2Pyha\u0308nta\u0308 &#8211; 2.75 kN\/m\u00b2Pyha\u0308ranta &#8211; 2.1 kN\/m\u00b2Pa\u0308lka\u0308ne &#8211; 2.5 kN\/m\u00b2Po\u0308ytya\u0308 &#8211; 2.5 kN\/m\u00b2Raahe &#8211; 2.1 kN\/m\u00b2Raasepori &#8211; 2.75 kN\/m\u00b2Raisio &#8211; 2.5 kN\/m\u00b2Rantasalmi &#8211; 2.5 kN\/m\u00b2Ranua &#8211; 3.2 kN\/m\u00b2Rauma &#8211; 2.0 kN\/m\u00b2Rautalampi &#8211; 2.5 kN\/m\u00b2Rautavaara &#8211; 3.0 kN\/m\u00b2Rautja\u0308rvi &#8211; 2.75 kN\/m\u00b2Reisja\u0308rvi &#8211; 2.5 kN\/m\u00b2Riihima\u0308ki &#8211; 2.75 kN\/m\u00b2Ristija\u0308rvi &#8211; 3.5 kN\/m\u00b2Rovaniemi &#8211; 3.0 kN\/m\u00b2Ruokolahti &#8211; 2.75 kN\/m\u00b2Ruovesi &#8211; 2.5 kN\/m\u00b2Rusko &#8211; 2.5 kN\/m\u00b2Ra\u0308a\u0308kkyla\u0308 &#8211; 2.6 kN\/m\u00b2Saarija\u0308rvi &#8211; 2.5 kN\/m\u00b2Salla &#8211; 3.0 kN\/m\u00b2Salo &#8211; 2.7 kN\/m\u00b2Saltvik &#8211; 2.0 kN\/m\u00b2Sastamala &#8211; 2.5 kN\/m\u00b2Sauvo &#8211; 2.6 kN\/m\u00b2Savitaipale &#8211; 2.65 kN\/m\u00b2Savonlinna &#8211; 2.5 kN\/m\u00b2Savukoski &#8211; 3.0 kN\/m\u00b2Seina\u0308joki &#8211; 2.5 kN\/m\u00b2Sievi &#8211; 2.35 kN\/m\u00b2Siikainen &#8211; 2.5 kN\/m\u00b2Siikajoki &#8211; 2.25 kN\/m\u00b2Siikalatva &#8211; 2.5 kN\/m\u00b2Siilinja\u0308rvi &#8211; 2.5 kN\/m\u00b2Simo &#8211; 3.0 kN\/m\u00b2Sipoo &#8211; 2.75 kN\/m\u00b2Siuntio &#8211; 2.75 kN\/m\u00b2Sodankyla\u0308 &#8211; 3.0 kN\/m\u00b2Soini &#8211; 2.5 kN\/m\u00b2Somero &#8211; 2.75 kN\/m\u00b2Sonkaja\u0308rvi &#8211; 3.0 kN\/m\u00b2Sotkamo &#8211; 3.4 kN\/m\u00b2Sottunga &#8211; 2.0 kN\/m\u00b2Sulkava &#8211; 2.5 kN\/m\u00b2Sund &#8211; 2.0 kN\/m\u00b2Suomussalmi &#8211; 3.5 kN\/m\u00b2Suonenjoki &#8211; 2.5 kN\/m\u00b2Sysma\u0308 &#8211; 2.5 kN\/m\u00b2Sa\u0308kyla\u0308 &#8211; 2.1 kN\/m\u00b2Taipalsaari &#8211; 2.7 kN\/m\u00b2Taivalkoski &#8211; 3.5 kN\/m\u00b2Taivassalo &#8211; 2.2 kN\/m\u00b2Tammela &#8211; 2.75 kN\/m\u00b2Tampere &#8211; 2.5 kN\/m\u00b2Tervo &#8211; 2.5 kN\/m\u00b2Tervola &#8211; 3.0 kN\/m\u00b2Teuva &#8211; 2.5 kN\/m\u00b2Tohmaja\u0308rvi &#8211; 2.75 kN\/m\u00b2Toholampi &#8211; 2.25 kN\/m\u00b2Toivakka &#8211; 2.5 kN\/m\u00b2Tornio &#8211; 3.0 kN\/m\u00b2Turku &#8211; 2.5 kN\/m\u00b2Tuusniemi &#8211; 2.5 kN\/m\u00b2Tuusula &#8211; 2.75 kN\/m\u00b2Tyrna\u0308va\u0308 &#8211; 2.4 kN\/m\u00b2Ulvila &#8211; 2.0 kN\/m\u00b2Urjala &#8211; 2.4 kN\/m\u00b2Utaja\u0308rvi &#8211; 2.85 kN\/m\u00b2Utsjoki &#8211; 2.5 kN\/m\u00b2Uurainen &#8211; 2.5 kN\/m\u00b2Uusikaarlepyy &#8211; 2.0 kN\/m\u00b2Uusikaupunki &#8211; 2.1 kN\/m\u00b2Vaala &#8211; 2.75 kN\/m\u00b2Vaasa &#8211; 2.0 kN\/m\u00b2Valkeakoski &#8211; 2.4 kN\/m\u00b2Valtimo &#8211; 3.0 kN\/m\u00b2Vantaa &#8211; 2.75 kN\/m\u00b2Varkaus &#8211; 2.5 kN\/m\u00b2Vehmaa<\/p>","protected":false},"author":1,"featured_media":2024,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_header_footer","meta":{"om_disable_all_campaigns":false,"_mi_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"site-sidebar-layout":"no-sidebar","site-content-layout":"page-builder","ast-site-content-layout":"full-width-container","site-content-style":"unboxed","site-sidebar-style":"unboxed","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"disabled","ast-breadcrumbs-content":"","ast-featured-img":"disabled","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"set","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-862","page","type-page","status-publish","has-post-thumbnail","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.7 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Fixture Calculator - Moisolar<\/title>\n<meta name=\"description\" content=\"The fixture calculator is an intuitive tool simplifies your solar installation planning. Begin optimizing your panel installation today with our calculator. Designed for all project scales, it aids in efficient resource management and smoother installations.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/moisolar.com\/fi\/kalustelaskuri\/\" \/>\n<meta property=\"og:locale\" content=\"fi_FI\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Fixture Calculator - Moisolar\" \/>\n<meta property=\"og:description\" content=\"The fixture calculator is an intuitive tool simplifies your solar installation planning. Begin optimizing your panel installation today with our calculator. Designed for all project scales, it aids in efficient resource management and smoother installations.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/moisolar.com\/fi\/kalustelaskuri\/\" \/>\n<meta property=\"og:site_name\" content=\"Moisolar\" \/>\n<meta property=\"article:modified_time\" content=\"2026-01-14T13:04:38+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/03\/calculatorBackground-1.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1756\" \/>\n\t<meta property=\"og:image:height\" content=\"1144\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"7 minuuttia\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/moisolar.com\/kalustelaskuri\/\",\"url\":\"https:\/\/moisolar.com\/kalustelaskuri\/\",\"name\":\"Fixture Calculator - Moisolar\",\"isPartOf\":{\"@id\":\"https:\/\/moisolar.com\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/moisolar.com\/kalustelaskuri\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/moisolar.com\/kalustelaskuri\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/03\/calculatorBackground-1.jpg\",\"datePublished\":\"2023-06-07T07:27:59+00:00\",\"dateModified\":\"2026-01-14T13:04:38+00:00\",\"description\":\"The fixture calculator is an intuitive tool simplifies your solar installation planning. Begin optimizing your panel installation today with our calculator. Designed for all project scales, it aids in efficient resource management and smoother installations.\",\"breadcrumb\":{\"@id\":\"https:\/\/moisolar.com\/kalustelaskuri\/#breadcrumb\"},\"inLanguage\":\"fi\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/moisolar.com\/kalustelaskuri\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"fi\",\"@id\":\"https:\/\/moisolar.com\/kalustelaskuri\/#primaryimage\",\"url\":\"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/03\/calculatorBackground-1.jpg\",\"contentUrl\":\"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/03\/calculatorBackground-1.jpg\",\"width\":1756,\"height\":1144},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/moisolar.com\/kalustelaskuri\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/moisolar.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Fixture Calculator\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/moisolar.com\/en\/#website\",\"url\":\"https:\/\/moisolar.com\/en\/\",\"name\":\"Moisolar\",\"description\":\"Moisolar Oy | Aurinkopaneelin kiinnikkeet ja tasakattotelineet\",\"publisher\":{\"@id\":\"https:\/\/moisolar.com\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/moisolar.com\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"fi\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/moisolar.com\/en\/#organization\",\"name\":\"Moisolar\",\"url\":\"https:\/\/moisolar.com\/en\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"fi\",\"@id\":\"https:\/\/moisolar.com\/en\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/moisolar.com\/wp-content\/uploads\/2023\/02\/cropped-Screenshot_2023-02-12_at_23.47.03-removebg-preview-2.png\",\"contentUrl\":\"https:\/\/moisolar.com\/wp-content\/uploads\/2023\/02\/cropped-Screenshot_2023-02-12_at_23.47.03-removebg-preview-2.png\",\"width\":1016,\"height\":246,\"caption\":\"Moisolar\"},\"image\":{\"@id\":\"https:\/\/moisolar.com\/en\/#\/schema\/logo\/image\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Kalustelaskuri - Moisolar","description":"The fixture calculator is an intuitive tool simplifies your solar installation planning. Begin optimizing your panel installation today with our calculator. Designed for all project scales, it aids in efficient resource management and smoother installations.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/moisolar.com\/fi\/kalustelaskuri\/","og_locale":"fi_FI","og_type":"article","og_title":"Fixture Calculator - Moisolar","og_description":"The fixture calculator is an intuitive tool simplifies your solar installation planning. Begin optimizing your panel installation today with our calculator. Designed for all project scales, it aids in efficient resource management and smoother installations.","og_url":"https:\/\/moisolar.com\/fi\/kalustelaskuri\/","og_site_name":"Moisolar","article_modified_time":"2026-01-14T13:04:38+00:00","og_image":[{"width":1756,"height":1144,"url":"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/03\/calculatorBackground-1.jpg","type":"image\/jpeg"}],"twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"7 minuuttia"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/moisolar.com\/kalustelaskuri\/","url":"https:\/\/moisolar.com\/kalustelaskuri\/","name":"Kalustelaskuri - Moisolar","isPartOf":{"@id":"https:\/\/moisolar.com\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/moisolar.com\/kalustelaskuri\/#primaryimage"},"image":{"@id":"https:\/\/moisolar.com\/kalustelaskuri\/#primaryimage"},"thumbnailUrl":"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/03\/calculatorBackground-1.jpg","datePublished":"2023-06-07T07:27:59+00:00","dateModified":"2026-01-14T13:04:38+00:00","description":"The fixture calculator is an intuitive tool simplifies your solar installation planning. Begin optimizing your panel installation today with our calculator. Designed for all project scales, it aids in efficient resource management and smoother installations.","breadcrumb":{"@id":"https:\/\/moisolar.com\/kalustelaskuri\/#breadcrumb"},"inLanguage":"fi","potentialAction":[{"@type":"ReadAction","target":["https:\/\/moisolar.com\/kalustelaskuri\/"]}]},{"@type":"ImageObject","inLanguage":"fi","@id":"https:\/\/moisolar.com\/kalustelaskuri\/#primaryimage","url":"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/03\/calculatorBackground-1.jpg","contentUrl":"https:\/\/moisolar.com\/wp-content\/uploads\/2024\/03\/calculatorBackground-1.jpg","width":1756,"height":1144},{"@type":"BreadcrumbList","@id":"https:\/\/moisolar.com\/kalustelaskuri\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/moisolar.com\/"},{"@type":"ListItem","position":2,"name":"Fixture Calculator"}]},{"@type":"WebSite","@id":"https:\/\/moisolar.com\/en\/#website","url":"https:\/\/moisolar.com\/en\/","name":"Moisolar","description":"Moisolar Oy | Aurinkopaneelin kiinnikkeet ja tasakattotelineet","publisher":{"@id":"https:\/\/moisolar.com\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/moisolar.com\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"fi"},{"@type":"Organization","@id":"https:\/\/moisolar.com\/en\/#organization","name":"Moisolar","url":"https:\/\/moisolar.com\/en\/","logo":{"@type":"ImageObject","inLanguage":"fi","@id":"https:\/\/moisolar.com\/en\/#\/schema\/logo\/image\/","url":"https:\/\/moisolar.com\/wp-content\/uploads\/2023\/02\/cropped-Screenshot_2023-02-12_at_23.47.03-removebg-preview-2.png","contentUrl":"https:\/\/moisolar.com\/wp-content\/uploads\/2023\/02\/cropped-Screenshot_2023-02-12_at_23.47.03-removebg-preview-2.png","width":1016,"height":246,"caption":"Moisolar"},"image":{"@id":"https:\/\/moisolar.com\/en\/#\/schema\/logo\/image\/"}}]}},"_links":{"self":[{"href":"https:\/\/moisolar.com\/fi\/wp-json\/wp\/v2\/pages\/862","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/moisolar.com\/fi\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/moisolar.com\/fi\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/moisolar.com\/fi\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/moisolar.com\/fi\/wp-json\/wp\/v2\/comments?post=862"}],"version-history":[{"count":761,"href":"https:\/\/moisolar.com\/fi\/wp-json\/wp\/v2\/pages\/862\/revisions"}],"predecessor-version":[{"id":3675,"href":"https:\/\/moisolar.com\/fi\/wp-json\/wp\/v2\/pages\/862\/revisions\/3675"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/moisolar.com\/fi\/wp-json\/wp\/v2\/media\/2024"}],"wp:attachment":[{"href":"https:\/\/moisolar.com\/fi\/wp-json\/wp\/v2\/media?parent=862"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}