{"id":2975,"date":"2026-07-21T23:35:44","date_gmt":"2026-07-21T21:35:44","guid":{"rendered":"https:\/\/gekkophotonics.com\/narzedzia\/tablica-korelacji\/"},"modified":"2026-07-22T02:28:13","modified_gmt":"2026-07-22T00:28:13","slug":"tablica-korelacji","status":"publish","type":"page","link":"https:\/\/gekkophotonics.com\/en\/narzedzia\/raman-correlation-interactive-band-map\/","title":{"rendered":"Raman correlation table \u2014 interactive band map"},"content":{"rendered":"<style>:root{--page-title-display:none;}<\/style>\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"2975\" class=\"elementor elementor-2975 elementor-bc-flex-widget\" data-elementor-post-type=\"page\">\n\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-52f6fe1 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"52f6fe1\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-no\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-2f4d3a2\" data-id=\"2f4d3a2\" data-element_type=\"column\" data-e-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-63fb008 elementor-widget elementor-widget-html\" data-id=\"63fb008\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<style>\n\/* Gekko Photonics \u2014 Narz\u0119dzia Ramana.\n   Font = kanon Design System (Manrope). Kolory\/cienie\/radius dopasowane do \u017cywej\n   strony (navy #2B2E83, cie\u0144 0 14px 40px, radius 14\/10). Scope pod .gk-tool. *\/\n@import url('https:\/\/fonts.googleapis.com\/css2?family=Manrope:wght@400;500;600;700;800&display=swap');\n\n.gk-tool{\n  --blue:#019FE2; --blue-600:#0088C2; --blue-50:#EDF8FD; --blue-100:#D6EFFA;\n  --navy:#2B2E83; --navy-dark:#192A3D;\n  --ink:#101828; --ink-2:#3A4256; --ink-3:#667085;\n  --line:#E4E7EC; --line-2:#D0D5DD; --surface:#FFFFFF; --surface-2:#F7F8FA;\n  --gold:#F3C969; --ok:#12B76A; --warn:#F3C969; --danger:#E04B4B;\n  --r-card:14px; --r-in:10px; --r-pill:999px;\n  --sh:0 14px 40px rgba(16,24,40,.12); --sh-sm:0 2px 8px rgba(16,24,40,.06);\n  --focus:0 0 0 4px rgba(1,159,226,.14);\n  --ease:cubic-bezier(.22,1,.36,1);\n  --f-body:\"Manrope\",system-ui,-apple-system,\"Segoe UI\",Arial,sans-serif;\n  --f-head:\"Manrope\",system-ui,sans-serif;\n  --mono:ui-monospace,\"SF Mono\",Menlo,Consolas,monospace;\n\n  font-family:var(--f-body); color:var(--ink-2); line-height:1.6; font-size:16px;\n  max-width:1140px; margin:0 auto; padding:clamp(28px,4vw,56px) clamp(16px,3vw,28px) 40px;\n  box-sizing:border-box;\n}\n.gk-tool *,.gk-tool *::before,.gk-tool *::after{box-sizing:border-box;}\n\n\/* typografia *\/\n.gk-tool .eyebrow{font-size:12px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:var(--blue);margin:0 0 14px;}\n.gk-tool h1{font-family:var(--f-head);font-weight:800;line-height:1.12;letter-spacing:-.01em;color:var(--navy);margin:0 0 14px;font-size:clamp(28px,3.2vw,42px);}\n.gk-tool h2{font-family:var(--f-head);font-weight:700;line-height:1.2;color:var(--navy);margin:0 0 8px;font-size:clamp(21px,2.1vw,28px);}\n.gk-tool h3{font-family:var(--f-head);font-weight:700;color:var(--navy);margin:0 0 6px;font-size:18px;}\n.gk-tool p{margin:0 0 12px;}\n.gk-tool .lead{font-size:clamp(16px,1.35vw,18px);color:var(--ink-2);max-width:68ch;line-height:1.6;}\n.gk-tool .muted{color:var(--ink-3);font-size:14px;}\n.gk-tool a{color:var(--blue);text-decoration:none;}\n.gk-tool a:hover{color:var(--blue-600);text-decoration:underline;}\n\n\/* layout *\/\n.gk-tool .head{margin-bottom:clamp(24px,3vw,40px);}\n.gk-tool .grid{display:grid;gap:20px;}\n@media(min-width:720px){.gk-tool .grid.cols-2{grid-template-columns:1fr 1fr;}}\n.gk-tool .card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-card);box-shadow:var(--sh-sm);padding:clamp(20px,2.4vw,28px);}\n.gk-tool .card + .card{margin-top:20px;}\n\n\/* kontrolki *\/\n.gk-tool label.field{display:block;margin-bottom:14px;}\n.gk-tool label.field > span{display:block;font-size:13px;font-weight:600;color:var(--ink);margin-bottom:6px;}\n.gk-tool input[type=number],.gk-tool input[type=text],.gk-tool select{\n  width:100%;font-family:var(--f-body);font-size:16px;color:var(--ink);\n  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-in);\n  padding:11px 13px;transition:border-color var(--ease) .15s,box-shadow var(--ease) .15s;}\n.gk-tool input:focus,.gk-tool select:focus{outline:none;border-color:var(--blue);box-shadow:var(--focus);}\n.gk-tool .chips{display:flex;gap:8px;flex-wrap:wrap;}\n.gk-tool .chip{cursor:pointer;font-size:13px;font-weight:600;padding:8px 15px;border-radius:var(--r-pill);\n  border:1px solid var(--line-2);background:var(--surface);color:var(--ink-2);transition:all var(--ease) .15s;}\n.gk-tool .chip:hover{border-color:var(--blue);color:var(--blue);}\n.gk-tool .chip.active{background:var(--navy);border-color:var(--navy);color:#fff;}\n\n.gk-tool .btn{cursor:pointer;font-family:var(--f-body);font-size:15px;font-weight:700;border:none;\n  border-radius:var(--r-pill);padding:13px 24px;transition:background var(--ease) .15s,transform var(--ease) .15s;}\n.gk-tool .btn-primary{background:var(--blue);color:#fff;}\n.gk-tool .btn-primary:hover{background:var(--blue-600);}\n.gk-tool .btn-ghost{background:var(--surface);color:var(--navy);border:1px solid var(--line-2);}\n.gk-tool .btn-ghost:hover{border-color:var(--blue);color:var(--blue);}\n\n\/* wynik *\/\n.gk-tool .result{background:var(--blue-50);border:1px solid var(--blue-100);border-radius:var(--r-in);padding:18px 20px;margin-top:4px;}\n.gk-tool .result .big{font-family:var(--f-head);font-weight:800;color:var(--navy);font-variant-numeric:tabular-nums;font-size:clamp(28px,3vw,38px);line-height:1.05;}\n.gk-tool .result .unit{font-size:.5em;font-weight:700;color:var(--ink-2);margin-left:6px;}\n.gk-tool .result .sub{font-size:14px;color:var(--ink-2);margin-top:6px;}\n.gk-tool .kv{display:flex;justify-content:space-between;gap:16px;padding:10px 0;border-bottom:1px solid var(--line);font-size:15px;}\n.gk-tool .kv:last-child{border-bottom:none;}\n.gk-tool .kv .v{font-weight:700;color:var(--navy);font-variant-numeric:tabular-nums;}\n.gk-tool .badge{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;padding:4px 11px;border-radius:var(--r-pill);}\n.gk-tool .badge.ok{background:rgba(18,183,106,.12);color:#087443;}\n.gk-tool .badge.no{background:rgba(224,75,75,.10);color:#b42318;}\n\n\/* details \/ obja\u015bnienie *\/\n.gk-tool details{border:1px solid var(--line);border-radius:var(--r-card);padding:2px 18px;background:var(--surface);margin-top:16px;}\n.gk-tool summary{cursor:pointer;font-family:var(--f-head);font-weight:700;color:var(--navy);padding:14px 0;list-style:none;}\n.gk-tool summary::-webkit-details-marker{display:none;}\n.gk-tool summary::before{content:\"+\";color:var(--blue);font-weight:800;margin-right:10px;font-family:var(--f-body);}\n.gk-tool details[open] summary::before{content:\"\u2013\";}\n.gk-tool details .body{padding:0 0 16px;font-size:15px;color:var(--ink-2);line-height:1.65;}\n.gk-tool .formula{font-family:var(--mono);font-size:14px;background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-in);padding:13px 15px;color:var(--ink);overflow-x:auto;line-height:1.7;}\n\n\/* hub kafelki *\/\n.gk-tool .tiles{display:grid;gap:20px;grid-template-columns:1fr;}\n@media(min-width:640px){.gk-tool .tiles{grid-template-columns:1fr 1fr;}}\n@media(min-width:980px){.gk-tool .tiles{grid-template-columns:1fr 1fr 1fr;}}\n.gk-tool .tile{display:flex;flex-direction:column;background:var(--surface);border:1px solid var(--line);\n  border-radius:var(--r-card);box-shadow:var(--sh-sm);padding:26px;transition:transform var(--ease) .18s,box-shadow var(--ease) .18s,border-color var(--ease) .18s;}\n.gk-tool a.tile:hover{text-decoration:none;transform:translateY(-3px);box-shadow:var(--sh);border-color:var(--line-2);}\n.gk-tool .tile .ico{width:48px;height:48px;border-radius:12px;background:var(--blue-50);color:var(--blue);display:flex;align-items:center;justify-content:center;margin-bottom:16px;}\n.gk-tool .tile h3{margin-bottom:8px;}\n.gk-tool .tile p{font-size:14.5px;color:var(--ink-2);margin:0;line-height:1.55;}\n.gk-tool .tile .soon{align-self:flex-start;margin-top:14px;font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:#9a7a1e;background:rgba(243,201,105,.22);padding:4px 10px;border-radius:var(--r-pill);}\n\n\/* expert CTA \u2014 panel \u201eporozmawiajmy o Twoim przypadku\" przy wyniku *\/\n.gk-tool .gk-expert{background:var(--blue-50);border:1px solid var(--blue-100);border-radius:var(--r-in);padding:18px 20px;margin-top:18px;}\n.gk-tool .gk-expert-head{display:flex;gap:14px;align-items:center;justify-content:space-between;flex-wrap:wrap;}\n.gk-tool .gk-expert-copy{margin:0;font-size:15px;color:var(--ink-2);flex:1 1 320px;line-height:1.55;}\n.gk-tool .gk-expert-head .btn{flex:0 0 auto;}\n\n\/* wsp\u00f3lny formularz leadowy + wysuwanie (slide) *\/\n.gk-tool .gk-slide{max-height:0;overflow:hidden;opacity:0;margin-top:0;transition:max-height .35s var(--ease),opacity .25s var(--ease),margin-top .35s var(--ease);}\n.gk-tool .gk-slide.open{max-height:560px;opacity:1;margin-top:16px;}\n.gk-tool .gk-lf-grid{display:grid;gap:10px;grid-template-columns:1fr;}\n@media(min-width:560px){.gk-tool .gk-lf-grid{grid-template-columns:1fr 1fr;}\n  .gk-tool .gk-lf-grid input[type=email]{grid-column:1 \/ -1;}}\n.gk-tool .gk-lf .consent{display:flex;gap:8px;align-items:flex-start;font-size:12px;color:var(--ink-3);margin-top:12px;max-width:64ch;}\n.gk-tool .gk-lf .consent input{margin-top:3px;width:auto;}\n.gk-tool .gk-lf .hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden;}\n.gk-tool .gk-lf .msg{margin-top:12px;font-size:14px;font-weight:600;min-height:1em;}\n.gk-tool .gk-lf .row{display:flex;gap:10px;flex-wrap:wrap;margin-top:14px;}\n\n\/* CTA lead \u2014 jak sekcje navy na stronie *\/\n.gk-tool .cta{background:var(--navy);color:#fff;border-radius:var(--r-card);padding:clamp(26px,3vw,40px);margin-top:44px;box-shadow:var(--sh);}\n.gk-tool .cta h2{color:#fff;}\n.gk-tool .cta p{color:#C7CBE8;}\n.gk-tool .cta .gk-lf-grid{margin-top:18px;}\n.gk-tool .cta input[type=email],.gk-tool .cta input[type=text]{\n  width:100%;background:rgba(255,255,255,.10);border:1px solid rgba(255,255,255,.24);color:#fff;\n  border-radius:var(--r-in);padding:13px 15px;font-size:16px;font-family:var(--f-body);}\n.gk-tool .cta input::placeholder{color:#a6abd6;}\n.gk-tool .cta input:focus{outline:none;border-color:var(--blue);box-shadow:var(--focus);}\n.gk-tool .cta .consent{color:#a6abd6;}\n.gk-tool .cta .consent a{color:#9fd6f2;}\n\n\/* nav *\/\n.gk-tool .back{display:inline-flex;align-items:center;gap:6px;font-size:14px;font-weight:600;margin-bottom:22px;color:var(--ink-3);}\n.gk-tool .back:hover{color:var(--blue);}\n.gk-tool .foot{margin-top:44px;padding-top:20px;border-top:1px solid var(--line);font-size:13px;color:var(--ink-3);}\n\n<\/style>\n<div class=\"gk-tool\" style=\"padding-bottom:0\"><a class=\"back\" href=\"\/narzedzia\/\">\u2190 Narz\u0119dzia Ramana<\/a><p class=\"eyebrow\">Narz\u0119dzia Ramana<\/p><\/div>\n<style>\n\n        :root { color-scheme: light; }\n        @import url('https:\/\/fonts.googleapis.com\/css2?family=Manrope:wght@400;500;600;700;800&display=swap');\n\n        body {\n            font-family: 'Manrope', sans-serif;\n            background-color: #D6EFFA;\n            color: #192A3D;\n            margin: 0;\n            padding: 2rem;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n        }\n\n        \/* Centered header with logo and title *\/\n        .header {\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            gap: 1.5rem; \/* Space between logo and title *\/\n            margin-bottom: 1rem;\n        }\n\n        .header img {\n            height: 50px; \/* Adjust logo size as needed *\/\n        }\n\n        h1, h2 {\n            color: #3A4F66;\n            text-align: center;\n            margin: 0;\n        }\n\n        h1 {\n            font-size: 2.5rem;\n        }\n\n        h2 {\n            font-size: 2rem;\n            margin-top: 3rem;\n            margin-bottom: 1.5rem;\n        }\n\n        p {\n            color: #5E6676;\n            max-width: 800px;\n            text-align: center;\n            margin: 0 auto 2rem auto;\n        }\n\n        .container {\n            width: 100%;\n            max-width: 1400px;\n            background-color: #ffffff;\n            padding: 2rem;\n            border-radius: 12px;\n            box-shadow: 0 8px 16px rgba(0, 0, 0, 0.05);\n            margin-bottom: 2rem;\n        }\n\n        #chart-container {\n            overflow-x: hidden;\n            position: relative;\n        }\n\n        #chart {\n            position: relative;\n            font-size: 12px;\n        }\n\n\n\n        #top-x-axis {\n            position: sticky;\n            top: 0;\n            width: 100%;\n            height: 28px;\n            border-bottom: 2px solid #3A4F66;\n            background: #ffffff;\n            display: block;\n            margin-bottom: 0;\n            z-index: 20;\n            font-size: 12px;\n            user-select: none;\n            box-shadow: 0 3px 6px rgba(0,0,0,0.06);\n        }\n\n        .top-tick-label {\n            position: absolute;\n            font-size: 12px;\n            font-weight: 600;\n            color: #3A4F66;\n            transform: translateX(-50%);\n            top: 5px;\n            pointer-events: none;\n        }\n\n        #grid-lines {\n            position: absolute;\n            top: 55px; \/* just below top axis bottom border *\/\n            left: 32px;\n            width: 100%;\n            \/* Height from bottom of top axis to top of bottom axis *\/\n            height: calc(100% - 55px - 70px);\n            pointer-events: none;\n            z-index: 0; \/* behind chart bars *\/\n        }\n\n        .grid-line-vertical {\n            position: absolute;\n            top: 0;\n            width: 1px;\n            height: 100%;\n            background: #F5F6F8;\n        }\n\n        #x-axis {\n            position: relative;\n            width: 100%;\n            height: 40px;\n            border-top: 2px solid #3A4F66; \/* matching line at bottom *\/\n            margin-top: 0;\n            z-index: 2;\n        }\n\n        .major-tick, .minor-tick {\n            position: absolute;\n            background: #3A4F66;\n        }\n\n        .major-tick {\n            width: 2px;\n            height: 12px;\n            margin-left: -1px; \/* wy\u015brodkuj grubsz\u0105 kresk\u0119 na pozycji *\/\n        }\n\n        .minor-tick {\n            width: 1px;\n            height: 6px;\n            background: #8A93A0;\n        }\n\n        .tick-label {\n            font-size: 12px;\n            font-weight: 600;\n            color: #3A4F66;\n            user-select: none;\n            position: absolute;\n            top: 15px; \/* below bottom axis ticks *\/\n            transform: translateX(-50%);\n        }\n\n        #chart {\n            position: relative;\n            width: 100%;\n            z-index: 10;\n            }\n\n        .group-container {\n            margin-bottom: 25px;\n        }\n\n        .group-title {\n            font-weight: 700;\n            font-size: 1rem;\n            margin-bottom: 10px;\n            color: #3A4F66;\n            padding-left: 10px;\n            border-left: 4px solid;\n        }\n\n        .bar-wrapper {\n            position: relative;\n            height: 22px;\n            margin-bottom: 4px;\n        }\n        .ion-label {\n            position: absolute; left: 2px; top: 3px; z-index: 6;\n            font-size: 11px; font-weight: 700; color: #3A4F66;\n            background: rgba(255,255,255,0.75); padding: 0 3px; border-radius: 3px;\n            pointer-events: none;\n        }\n\n        .bar {\n            position: absolute;\n            height: 100%;\n            border-radius: 4px;\n            cursor: pointer;\n            transition: transform 0.2s ease, box-shadow 0.2s ease;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            font-weight: 600;\n            white-space: nowrap;\n            overflow: hidden;\n            text-overflow: ellipsis;\n            padding: 0 8px;\n            box-sizing: border-box;\n            min-width: 7px;\n        }\n        .bar.point {\n            transform: translateX(-50%);\n            border-radius: 2px;\n        }\n        .bar.point:hover { transform: translateX(-50%) scaleY(1.15); }\n        .bar:hover {\n            transform: scaleY(1.1);\n            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);\n            z-index: 10;\n        }\n\n        #tooltip {\n            position: absolute;\n            background-color: rgba(0, 0, 0, 0.85);\n            color: #fff;\n            padding: 10px 15px;\n            border-radius: 6px;\n            font-size: 14px;\n            pointer-events: none;\n            opacity: 0;\n            visibility: hidden;\n            transition: opacity 0.2s ease;\n            z-index: 100;\n            box-shadow: 0 4px 12px rgba(0,0,0,0.2);\n            white-space: normal;\n            max-width: 260px;\n            text-align: left;\n        }\n        #tooltip .frag {\n            display: block;\n            margin: 0 auto 8px;\n            background: #fff;\n            border-radius: 6px;\n            padding: 4px;\n        }\n\n        \/* Intensity Styles *\/\n        .intensity-strong { height: 22px; }\n        .intensity-medium { height: 16px; margin-top:3px; }\n        .intensity-weak { height: 10px; margin-top:6px; }\n\n        \/* Controls (search & filters) *\/\n        .controls {\n            width: 100%; max-width: 1400px; background: #ffffff; border-radius: 12px;\n            box-shadow: 0 8px 16px rgba(0,0,0,0.05); padding: 1.1rem 1.5rem;\n            margin-bottom: 1.5rem; box-sizing: border-box; display: flex; flex-direction: column; gap: .75rem;\n        }\n        .control-row { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem; }\n        #search-input {\n            flex: 1; min-width: 240px; padding: .6rem .8rem; border: 1px solid #D3D8DF;\n            border-radius: 8px; font-size: .95rem; font-family: 'Manrope', sans-serif;\n        }\n        .control-btn {\n            padding: .5rem .85rem; border: 1px solid #D3D8DF; background: #F5F6F8;\n            border-radius: 8px; cursor: pointer; font-size: .85rem; color: #3A4F66;\n        }\n        .control-btn:hover { background: #E8EBEF; }\n        .control-btn.active { background: #2B2E83; color: #fff; border-color: #2B2E83; }\n        .control-label { font-size: .85rem; font-weight: 600; color: #3A4F66; }\n        .chk { display: inline-flex; align-items: center; gap: .35rem; font-size: .85rem; color: #3A4F66; cursor: pointer; }\n        #match-count { font-size: .85rem; color: #2B2E83; font-weight: 700; margin-left: auto; }\n        .bar.dim { opacity: 0.12; }\n        .bar.hit { outline: 2px solid #2B2E83; box-shadow: 0 0 0 3px rgba(11,61,102,0.25); z-index: 9; }\n\n        #guide-lines { position: absolute; top: 0; left: 0; width: 100%; height: calc(100% + 40px); pointer-events: none; z-index: 12; }\n        #empty-hint { display: none; text-align: center; color: #8a94a6; font-size: .95rem; line-height: 1.6; padding: 2.75rem 1rem; }\n        .guide-line { position: absolute; top: 0; width: 2px; height: 100%; background: rgba(214,40,40,0.85); }\n        .guide-label {\n            position: absolute; top: 2px; transform: translateX(-50%); background: #d62828; color: #fff;\n            font-size: 11px; font-weight: 700; padding: 1px 5px; border-radius: 4px; white-space: nowrap;\n        }\n\n        \/* Legend *\/\n        .legend {\n            width: 100%; max-width: 1400px;\n            background: #ffffff; border-radius: 12px;\n            box-shadow: 0 8px 16px rgba(0,0,0,0.05);\n            padding: 1.1rem 1.5rem; margin-bottom: 1.5rem; box-sizing: border-box;\n        }\n        .legend-title { font-weight: 700; color: #3A4F66; margin: 0 0 .5rem; font-size: .95rem; }\n        .legend-groups { display: flex; flex-wrap: wrap; gap: .35rem .9rem; }\n        .legend-cat { margin: .35rem 0 .7rem; }\n        .legend-cat-name { display: block; font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: #8a94a6; margin-bottom: .3rem; }\n        .legend-intensity { display: flex; flex-wrap: wrap; gap: .5rem 1.5rem; align-items: flex-end; }\n        .legend-item { display: inline-flex; align-items: center; gap: .4rem; font-size: .8rem; color: #3A4F66; }\n        .legend-item.toggle { cursor: pointer; user-select: none; padding: 1px 4px; border-radius: 4px; color: #000; }\n        .legend-item.toggle:hover { background: #F5F6F8; }\n        .legend-item.toggle.selected { background: #EDF8FD; box-shadow: 0 0 0 1px #b8d4f0; font-weight: 600; }\n        .legend-swatch { width: 14px; height: 14px; border-radius: 3px; display: inline-block; }\n        .ikey { display: inline-block; width: 24px; background: #8a94a6; border-radius: 3px; }\n        .ikey-strong { height: 16px; } .ikey-medium { height: 11px; } .ikey-weak { height: 6px; }\n        .ikey-point { width: 12px; height: 12px; transform: rotate(45deg); }\n\n        \/* AI Tool Styles *\/\n        .ai-tool-container {\n            display: flex;\n            flex-direction: column;\n            gap: 1rem;\n            align-items: center;\n        }\n\n        .input-group {\n            display: flex;\n            flex-direction: column;\n            width: 100%;\n            gap: 0.5rem;\n        }\n\n        .input-group label {\n            font-weight: 600;\n            color: #3A4F66;\n            text-align: center;\n        }\n\n        .input-group input,\n        .ai-tool-container textarea {\n            width: 100%;\n            border-radius: 8px;\n            border: 1px solid #D3D8DF;\n            padding: 0.75rem;\n            font-family: 'Manrope', sans-serif;\n            font-size: 1rem;\n            box-sizing: border-box;\n        }\n\n        .ai-tool-container textarea {\n            min-height: 100px;\n            resize: vertical;\n        }\n\n        .ai-button {\n            padding: 0.75rem 1.5rem;\n            font-size: 1rem;\n            font-weight: 600;\n            color: #fff;\n            border: none;\n            border-radius: 8px;\n            cursor: pointer;\n            transition: all 0.3s ease;\n        }\n\n        #interpret-button {\n            background: linear-gradient(45deg, #019FE2, #00b4d8);\n        }\n        #interpret-button:hover {\n            box-shadow: 0 4px 15px rgba(0, 150, 200, 0.3);\n            transform: translateY(-2px);\n        }\n\n        #explore-app-button {\n            background: linear-gradient(45deg, #3a86ff, #8338ec);\n        }\n        #explore-app-button:hover {\n            box-shadow: 0 4px 15px rgba(100, 100, 255, 0.3);\n            transform: translateY(-2px);\n        }\n\n        .result-box {\n            width: 100%;\n            margin-top: 1rem;\n            padding: 1.5rem 1.75rem;\n            border: 1px solid #E8EBEF;\n            border-radius: 8px;\n            background-color: #F5F6F8;\n            min-height: 70px;\n            line-height: 1.65;\n            box-sizing: border-box;\n            color: #192A3D;\n            overflow-wrap: break-word;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            text-align: center;\n        }\n        .result-box.has-answer { display: block; text-align: left; }\n\n        .result-box .placeholder {\n            text-align: center;\n            color: #5E6676;\n            margin: 0;\n        }\n        .result-box .loader { margin: 12px auto; }\n        .result-box h1, .result-box h2, .result-box h3, .result-box h4 {\n            text-align: center;\n            margin: 1rem 0 0.5rem;\n            line-height: 1.3;\n            color: #2B2E83;\n        }\n        .result-box h1 { font-size: 1.4rem; }\n        .result-box h2 { font-size: 1.2rem; }\n        .result-box h3 { font-size: 1.08rem; }\n        .result-box h4 { font-size: 1rem; }\n        .result-box p { margin: 0.5rem 0; }\n        .result-box ul, .result-box ol { margin: 0.4rem 0 0.8rem; padding-left: 1.4rem; }\n        .result-box li { margin: 0.25rem 0; }\n        .result-box strong { font-weight: 700; color: #111; }\n        .result-box em { font-style: italic; }\n        .result-box code {\n            font-family: 'SFMono-Regular', Consolas, monospace;\n            background: #F5F6F8;\n            padding: 0.1em 0.35em;\n            border-radius: 4px;\n            font-size: 0.92em;\n        }\n        .result-box hr { border: none; border-top: 1px solid #E8EBEF; margin: 0.9rem 0; }\n\n        \/* Czytelna prezentacja odpowiedzi AI *\/\n        .result-box .md { text-align: left; }\n        .result-box .md h1, .result-box .md h2, .result-box .md h3, .result-box .md h4 { text-align: left; }\n        .result-box .md h2 {\n            color: #2B2E83; font-size: 1.15rem; margin: 1.15rem 0 .6rem;\n            padding-bottom: .3rem; border-bottom: 2px solid #D6EFFA;\n        }\n        .result-box .md h2:first-child { margin-top: .2rem; }\n        .result-box .md h3 { color: #0b5394; font-size: 1.02rem; margin: .9rem 0 .4rem; }\n        .result-box .md p { margin: .55rem 0; text-align: left; max-width: none; }\n        .result-box .md ul { list-style: none; padding-left: 0; margin: .4rem 0 .9rem; }\n        .result-box .md ul li {\n            position: relative; padding: .4rem .7rem .4rem 1.6rem; margin: .35rem 0;\n            background: #ffffff; border: 1px solid #E8EBEF; border-left: 3px solid #3DB6E8;\n            border-radius: 6px; line-height: 1.5;\n        }\n        .result-box .md ul li::before {\n            content: \"\u2022\"; position: absolute; left: .65rem; top: .38rem; color: #0288D1; font-weight: 700;\n        }\n        .result-box .md ol { padding-left: 1.5rem; margin: .4rem 0 .9rem; }\n        .result-box .md ol li { margin: .35rem 0; padding-left: .2rem; text-align: left; }\n        .result-box .md ul li, .result-box .md li { text-align: left; }\n        .result-box .md strong { color: #2B2E83; }\n\n        .loader {\n            border: 4px solid #F5F6F8;\n            border-top: 4px solid #019FE2;\n            border-radius: 50%;\n            width: 30px;\n            height: 30px;\n            animation: spin 1s linear infinite;\n        }\n\n        @keyframes spin {\n            0% { transform: rotate(0deg); }\n            100% { transform: rotate(360deg); }\n        }\n    \n<\/style>\n\n\n    <div class=\"header\">\n        <svg width=\"50\" height=\"50\" viewBox=\"0 0 50 50\" role=\"img\" aria-label=\"Raman logo\">\n            <rect x=\"1\" y=\"1\" width=\"48\" height=\"48\" rx=\"11\" fill=\"#2B2E83\"\/>\n            <polyline points=\"6,34 13,34 17,16 21,34 27,34 31,10 35,34 44,34\"\n                fill=\"none\" stroke=\"#3DB6E8\" stroke-width=\"2.6\"\n                stroke-linejoin=\"round\" stroke-linecap=\"round\"\/>\n        <\/svg>\n        <h1>Tablica korelacji Ramana<\/h1>\n    <\/div>\n\n    <p>Wykres jest wizualnym przewodnikiem po interpretacji widm Ramana. Najed\u017a na pasek, aby zobaczy\u0107 szczeg\u00f3\u0142y drgania. Kolor paska oznacza grup\u0119 funkcyjn\u0105, a jego wysoko\u015b\u0107 \u2014 wzgl\u0119dn\u0105 intensywno\u015b\u0107 w Ramanie (silna, \u015brednia lub s\u0142aba).<\/p>\n\n    <div id=\"legend\" class=\"legend\"><\/div>\n\n    <div id=\"controls\" class=\"controls\">\n        <div class=\"control-row\">\n            <input id=\"search-input\" type=\"text\" placeholder=\"Szukaj: liczba falowa (np. 1005 lub 2980, 1660) albo tekst (np. pier\u015bcie\u0144, amid, SO\u2084)\">\n            <button id=\"search-clear\" class=\"control-btn\">Wyczy\u015b\u0107<\/button>\n            <span id=\"match-count\"><\/span>\n        <\/div>\n        <div class=\"control-row\" id=\"intensity-filter\">\n            <span class=\"control-label\">Intensywno\u015b\u0107:<\/span>\n            <label class=\"chk\"><input type=\"checkbox\" data-int=\"strong\" checked> Silna<\/label>\n            <label class=\"chk\"><input type=\"checkbox\" data-int=\"medium\" checked> \u015arednia<\/label>\n            <label class=\"chk\"><input type=\"checkbox\" data-int=\"weak\" checked> S\u0142aba<\/label>\n            <span class=\"control-label\" style=\"margin-left:1rem;\">Grupy:<\/span>\n            <button id=\"grp-all\" class=\"control-btn\">Poka\u017c wszystkie<\/button>\n            <button id=\"grp-none\" class=\"control-btn\">Ukryj wszystkie<\/button>\n            <button id=\"reset-all\" class=\"control-btn\">Reset widoku<\/button>\n            <span class=\"control-label\" style=\"opacity:.7;\">(domy\u015blnie wykres jest pusty \u2014 kliknij grup\u0119 w legendzie, aby pokaza\u0107 j\u0105 na wykresie)<\/span>\n        <\/div>\n        <div class=\"control-row\" id=\"zoom-controls\">\n            <span class=\"control-label\">Zakres osi:<\/span>\n            <button id=\"zoom-full\" class=\"control-btn active\">Pe\u0142ny 0\u20134000 cm\u207b\u00b9<\/button>\n            <button id=\"zoom-fp\" class=\"control-btn\">Odcisk palca 400\u20131800 cm\u207b\u00b9<\/button>\n        <\/div>\n    <\/div>\n\n    <div class=\"container\" id=\"chart-container\">\n        <div id=\"top-x-axis\"><\/div>\n        <div id=\"grid-lines\"><\/div>\n        <div id=\"chart\"><\/div>\n        <div id=\"x-axis\"><\/div>\n    <\/div>\n\n    <div id=\"tooltip\"><\/div>\n\n    \n<script>\n\n        document.addEventListener('DOMContentLoaded', () => {\n        const ramanData = [\n            \/\/ ==========================================================================\n            \/\/  \u0179R\u00d3D\u0141A (zwalidowane wg za\u0142\u0105czonych referencji):\n            \/\/  E2  = Edwards, Tabela 2 (Handbook of Vibrational Spectroscopy, Wiley 2006;\n            \/\/        przedruk z Lin-Vien, Colthup, Fateley, Grasselli, Academic Press 1991)\n            \/\/  S1  = Socrates, Tabela 22.1 \"Free inorganic ions and coordinated ions\"\n            \/\/  S24 = Socrates, Tabela 22.24 \"Hydride A-H stretching vibration bands\"\n            \/\/  S26 = Socrates, Tabela 22.26 \/ Wykres 22.5 (tlenki metali; dane orientacyjne, g\u0142\u00f3wnie IR)\n            \/\/  LV  = Lin-Vien i wsp., Handbook (dzia\u0142 organiczny)\n            \/\/  gen = sta\u0142a fizyczna spoza tabel za\u0142\u0105czonych \u017ar\u00f3de\u0142 (zachowana informacyjnie)\n            \/\/ ==========================================================================\n\n            \/\/ --- C-H Vibrations (Aliphatic) ---\n            { group: \"C-H Vibrations (Aliphatic)\", mode: \"C-H rozci\u0105ganie (og\u00f3lne)\", type: \"\u03bd\", range: [2800, 3000], intensity: \"strong\", src: \"E2\" },\n            { group: \"C-H Vibrations (Aliphatic)\", mode: \"CH\u2083 asymetryczne\", type: \"\u03bdasym\", range: [2950, 2975], intensity: \"medium\", src: \"E2\" },\n            { group: \"C-H Vibrations (Aliphatic)\", mode: \"CH\u2083 asymetryczne (n-alkany, ~2967)\", type: \"\u03bdasym\", range: [2965, 2969], intensity: \"medium\", src: \"E2\" },\n            { group: \"C-H Vibrations (Aliphatic)\", mode: \"CH\u2083 asymetryczne (n-alkany, ~2920)\", type: \"\u03bdasym\", range: [2912, 2929], intensity: \"medium\", src: \"E2\" },\n            { group: \"C-H Vibrations (Aliphatic)\", mode: \"CH\u2083 symetryczne\", type: \"\u03bdsym\", range: [2865, 2885], intensity: \"strong\", src: \"LV\" },\n            { group: \"C-H Vibrations (Aliphatic)\", mode: \"CH\u2083 symetryczne (n-alkany)\", type: \"\u03bdsym\", range: [2883, 2884], intensity: \"strong\", src: \"E2\" },\n            { group: \"C-H Vibrations (Aliphatic)\", mode: \"CH\u2082 asymetryczne\", type: \"\u03bdasym\", range: [2915, 2940], intensity: \"strong\", src: \"LV\" },\n            { group: \"C-H Vibrations (Aliphatic)\", mode: \"CH\u2082 symetryczne (n-alkany)\", type: \"\u03bdsym\", range: [2849, 2861], intensity: \"strong\", src: \"E2\" },\n            { group: \"C-H Vibrations (Aliphatic)\", mode: \"CH\u2082 no\u017cycowe \/ CH\u2083,OCH\u2082 deformacja\", type: \"\u03b4\", range: [1440, 1480], intensity: \"medium\", src: \"E2\" },\n            { group: \"C-H Vibrations (Aliphatic)\", mode: \"CH\u2083 deformacja (n-alkany)\", type: \"\u03b4\", range: [1465, 1466], intensity: \"medium\", src: \"E2\" },\n            { group: \"C-H Vibrations (Aliphatic)\", mode: \"CH\u2083 deformacja symetryczna\", type: \"\u03b4sym\", range: [1368, 1385], intensity: \"medium\", src: \"E2\" },\n            { group: \"C-H Vibrations (Aliphatic)\", mode: \"CH deformacja (izopropyl)\", type: \"\u03b4\", range: [1330, 1350], intensity: \"medium\", src: \"E2\" },\n            { group: \"C-H Vibrations (Aliphatic)\", mode: \"CH\u2082 skr\u0119canie i wahanie\", type: \"\u03c4,\u03c1\", range: [1175, 1310], intensity: \"medium\", src: \"E2\" },\n            { group: \"C-H Vibrations (Aliphatic)\", mode: \"CH\u2082 skr\u0119canie w fazie\", type: \"\u03c4\", range: [1295, 1305], intensity: \"medium\", src: \"E2\" },\n            { group: \"Ring & Skeletal Vibrations\", mode: \"Rozci\u0105gania CC (n-alkany)\", type: \"\u03bd\", range: [950, 1150], intensity: \"medium\", src: \"E2\" },\n            { group: \"Ring & Skeletal Vibrations\", mode: \"CC rozci\u0105ganie szkieletowe (n-alkany)\", type: \"\u03bd\", range: [837, 905], intensity: \"medium\", src: \"E2\" },\n\n            \/\/ --- Aldehydes ---\n            { group: \"C-H Vibrations (Aliphatic)\", mode: \"C-H rozci\u0105ganie aldehydowe (grupa CHO)\", type: \"\u03bd\", range: [2700, 2850], intensity: \"medium\", src: \"E2\" },\n\n            \/\/ --- C-H Vibrations (Aromatic & =C-H) ---\n            { group: \"C-H Vibrations (Aromatic)\", mode: \"Rozci\u0105ganie CH aromatyczne\", type: \"\u03bd\", range: [3000, 3100], intensity: \"medium\", src: \"E2\" },\n            { group: \"C-H Vibrations (Aromatic)\", mode: \"Rozci\u0105ganie CH benzenu\", type: \"\u03bd\", range: [3062, 3062], intensity: \"medium\", src: \"E2\" },\n            { group: \"C-H Vibrations (Aromatic)\", mode: \"CH benzen\u00f3w alkilowych\", type: \"\u03bd\", range: [3057, 3057], intensity: \"medium\", src: \"E2\" },\n            { group: \"C=C Vibrations\", mode: \"=CH\u2082 antysym. (C=CH\u2082 poch.)\", type: \"\u03bdasym\", range: [3070, 3095], intensity: \"medium\", src: \"E2\" },\n            { group: \"C=C Vibrations\", mode: \"=CH rozci\u0105ganie (C=CHR poch.)\", type: \"\u03bd\", range: [3000, 3040], intensity: \"medium\", src: \"E2\" },\n            { group: \"C=C Vibrations\", mode: \"=CH\u2082 symetryczne (C=CH\u2082 poch.)\", type: \"\u03bdsym\", range: [2980, 2990], intensity: \"medium\", src: \"E2\" },\n\n            \/\/ --- C=C Vibrations ---\n            { group: \"C=C Vibrations\", mode: \"C=C rozci\u0105ganie etyleny tetraalkilowe\", type: \"\u03bd\", range: [1665, 1680], intensity: \"strong\", src: \"E2\" },\n            { group: \"C=C Vibrations\", mode: \"C=C rozci\u0105ganie etyleny trialkilowe\", type: \"\u03bd\", range: [1664, 1678], intensity: \"strong\", src: \"E2\" },\n            { group: \"C=C Vibrations\", mode: \"C=C rozci\u0105ganie trans-dialkilowe\", type: \"\u03bd\", range: [1665, 1676], intensity: \"strong\", src: \"E2\" },\n            { group: \"C=C Vibrations\", mode: \"C=C rozci\u0105ganie cis-dialkilowe\", type: \"\u03bd\", range: [1654, 1660], intensity: \"strong\", src: \"E2\" },\n            { group: \"C=C Vibrations\", mode: \"C=C rozci\u0105ganie R\u2082C=CH\u2082\", type: \"\u03bd\", range: [1644, 1658], intensity: \"strong\", src: \"E2\" },\n            { group: \"C=C Vibrations\", mode: \"C=C rozci\u0105ganie H\u2082C=CHR\", type: \"\u03bd\", range: [1638, 1648], intensity: \"strong\", src: \"E2\" },\n            { group: \"C=C Vibrations\", mode: \"C=C rozci\u0105ganie pochodne alkilowe\", type: \"\u03bd\", range: [1625, 1649], intensity: \"strong\", src: \"E2\" },\n            { group: \"C=C Vibrations\", mode: \"C=C rozci\u0105ganie (monofluoroalkeny)\", type: \"\u03bd\", range: [1644, 1689], intensity: \"strong\", src: \"E2\" },\n            { group: \"C=C Vibrations\", mode: \"C=C rozci\u0105ganie (chloroalkeny)\", type: \"\u03bd\", range: [1571, 1616], intensity: \"strong\", src: \"E2\" },\n            { group: \"C=C Vibrations\", mode: \"C=C rozci\u0105ganie (bromoalkeny)\", type: \"\u03bd\", range: [1547, 1596], intensity: \"strong\", src: \"E2\" },\n            { group: \"C=C Vibrations\", mode: \"C=C rozci\u0105ganie (jodoalkeny)\", type: \"\u03bd\", range: [1465, 1581], intensity: \"strong\", src: \"E2\" },\n            { group: \"C=C Vibrations\", mode: \"\u22653 sprz\u0119\u017cone rozci\u0105gania C=C (polieny)\", type: \"\u03bd\", range: [1540, 1620], intensity: \"strong\", src: \"E2\" },\n\n            \/\/ --- C\u2261C Vibrations ---\n            { group: \"C\u2261C Vibrations\", mode: \"C\u2261C rozci\u0105ganie alkiny alkilowe\", type: \"\u03bd\", range: [2100, 2160], intensity: \"medium\", src: \"E2\" },\n            { group: \"C\u2261C Vibrations\", mode: \"C\u2261C rozci\u0105ganie R-C\u2261C-CH\u2083\", type: \"\u03bd\", range: [2233, 2316], intensity: \"medium\", src: \"E2\" },\n            { group: \"C\u2261C Vibrations\", mode: \"C\u2261C rozci\u0105ganie R-C\u2261C-R'\", type: \"\u03bd\", range: [2231, 2301], intensity: \"medium\", src: \"E2\" },\n            { group: \"C\u2261C Vibrations\", mode: \"\u2261C-H rozci\u0105ganie acetyleny alkilowe\", type: \"\u03bd\", range: [3300, 3335], intensity: \"medium\", src: \"E2\" },\n            { group: \"C\u2261C Vibrations\", mode: \"Deformacja szkieletowa (monoalkiny alkilowe)\", type: \"\u03b4\", range: [335, 355], intensity: \"weak\", src: \"E2\" },\n\n            \/\/ --- C=O Vibrations ---\n            { group: \"C=O Vibrations\", mode: \"C=O rozci\u0105ganie (zakres og\u00f3lny)\", type: \"\u03bd\", range: [1600, 1800], intensity: \"medium\", src: \"E2\" },\n            { group: \"C=O Vibrations\", mode: \"C=O rozci\u0105ganie (ketony alifatyczne)\", type: \"\u03bd\", range: [1700, 1725], intensity: \"strong\", src: \"E2\" },\n            { group: \"C=O Vibrations\", mode: \"C=O rozci\u0105ganie (aldehydy alifatyczne)\", type: \"\u03bd\", range: [1720, 1740], intensity: \"medium\", src: \"LV\" },\n\n            \/\/ --- Anhydrides & Acid Halides ---\n            { group: \"C=O Vibrations\", mode: \"C=O symetryczne (bezwodnik octowy)\", type: \"\u03bdsym\", range: [1820, 1820], intensity: \"medium\", src: \"E2\" },\n            { group: \"C=O Vibrations\", mode: \"C=O rozci\u0105ganie (halogenki kwasowe)\", type: \"\u03bd\", range: [1788, 1810], intensity: \"medium\", src: \"E2\" },\n            { group: \"C=O Vibrations\", mode: \"C=O symetryczne (bezwodniki niecykliczne)\", type: \"\u03bdsym\", range: [1799, 1805], intensity: \"medium\", src: \"E2\" },\n            { group: \"C=O Vibrations\", mode: \"C=O rozci\u0105ganie (aldehydy halogenowane)\", type: \"\u03bd\", range: [1730, 1770], intensity: \"medium\", src: \"E2\" },\n\n            \/\/ --- Carboxylic Acids ---\n            { group: \"Carboxylic Acids\", mode: \"C=O symetryczne (dimer cykliczny)\", type: \"\u03bdsym\", range: [1649, 1654], intensity: \"medium\", src: \"E2\" },\n            { group: \"Carboxylic Acids\", mode: \"C=O (monomer)\", type: \"\u03bd\", range: [1760, 1760], intensity: \"medium\", src: \"LV\" },\n            { group: \"Carboxylic Acids\", mode: \"C=O (arylowe\/\u03b1,\u03b2-nienasycone)\", type: \"\u03bd\", range: [1660, 1715], intensity: \"medium\", src: \"LV\" },\n            { group: \"Carboxylic Acids\", mode: \"O-H rozci\u0105ganie (dimer)\", type: \"\u03bd\", range: [2500, 3300], intensity: \"weak\", src: \"LV\" },\n            { group: \"Carboxylic Acids\", mode: \"rozci\u0105ganie C-O \/ deform. O-H (dimer)\", type: \"\u03bd\", range: [1395, 1440], intensity: \"medium\", src: \"LV\" },\n            { group: \"Carboxylic Acids\", mode: \"COO\u207b asymetryczne (karboksylan)\", type: \"\u03bdasym\", range: [1540, 1620], intensity: \"weak\", src: \"S1\" },\n            { group: \"Carboxylic Acids\", mode: \"COO\u207b symetryczne (karboksylan)\", type: \"\u03bdsym\", range: [1340, 1440], intensity: \"strong\", src: \"E2\" },\n\n            \/\/ --- Amino Acids ---\n            { group: \"Amino Acids\", mode: \"C=O rozci\u0105ganie (kationowe \u03b1-aminokwasy)\", type: \"\u03bd\", range: [1729, 1743], intensity: \"medium\", src: \"E2\" },\n            { group: \"Amino Acids\", mode: \"COO\u207b symetryczne (dipolarne\/anionowe)\", type: \"\u03bdsym\", range: [1400, 1415], intensity: \"strong\", src: \"E2\" },\n\n            \/\/ --- Esters and Derivatives ---\n            { group: \"Esters and Derivatives\", mode: \"C=O (estry alifatyczne nasycone)\", type: \"\u03bd\", range: [1725, 1750], intensity: \"medium\", src: \"LV\" },\n            { group: \"Esters and Derivatives\", mode: \"C=O (estry \u03b1,\u03b2-nienasycone\/arylowe)\", type: \"\u03bd\", range: [1705, 1740], intensity: \"medium\", src: \"LV\" },\n            { group: \"Esters and Derivatives\", mode: \"C=O (estry \u03b1-halogenowe)\", type: \"\u03bd\", range: [1730, 1770], intensity: \"medium\", src: \"LV\" },\n            { group: \"Esters and Derivatives\", mode: \"C-O-C asymetryczne (alifatyczne)\", type: \"\u03bdasym\", range: [1185, 1275], intensity: \"strong\", src: \"LV\" },\n            { group: \"Esters and Derivatives\", mode: \"C-O-C symetryczne (alifatyczne)\", type: \"\u03bdsym\", range: [1050, 1160], intensity: \"weak\", src: \"LV\" },\n            { group: \"Esters and Derivatives\", mode: \"C-O-C asymetryczne (aromatyczne\/\u03b1,\u03b2-nienasyc.)\", type: \"\u03bdasym\", range: [1250, 1310], intensity: \"strong\", src: \"LV\" },\n            { group: \"Esters and Derivatives\", mode: \"C-O-C symetryczne (aromatyczne\/\u03b1,\u03b2-nienasyc.)\", type: \"\u03bdsym\", range: [1100, 1200], intensity: \"weak\", src: \"LV\" },\n            { group: \"Esters and Derivatives\", mode: \"C-O-C glikozydowe (cukry)\", type: \"\u03bdasym\", range: [1000, 1160], intensity: \"medium\", src: \"LV\" },\n            { group: \"Esters and Derivatives\", mode: \"C=O (tioestry alifatyczne)\", type: \"\u03bd\", range: [1690, 1700], intensity: \"medium\", src: \"LV\" },\n\n            \/\/ --- C-O Vibrations ---\n            { group: \"C-O Vibrations\", mode: \"C-O rozci\u0105ganie (alkohol pierwszorz\u0119dowy, nasyc.)\", type: \"\u03bd\", range: [1000, 1090], intensity: \"strong\", src: \"LV\" },\n            { group: \"C-O Vibrations\", mode: \"C-O (alkohol drugorz\u0119dowy)\", type: \"\u03bd\", range: [1085, 1125], intensity: \"strong\", src: \"LV\" },\n            { group: \"C-O Vibrations\", mode: \"C-O (alkohol trzeciorz\u0119dowy)\", type: \"\u03bd\", range: [1125, 1205], intensity: \"strong\", src: \"LV\" },\n            { group: \"C-O Vibrations\", mode: \"C\u2083-O szkieletowe (alkohol drugorz\u0119dowy)\", type: \"\u03bd\", range: [820, 825], intensity: \"medium\", src: \"E2\" },\n            { group: \"C-O Vibrations\", mode: \"C\u2084-O szkieletowe (alkohol trzeciorz\u0119dowy)\", type: \"\u03bd\", range: [730, 760], intensity: \"medium\", src: \"E2\" },\n            { group: \"C-O Vibrations\", mode: \"C-O-C symetryczne (eter alifatyczny)\", type: \"\u03bdsym\", range: [830, 930], intensity: \"strong\", src: \"E2\" },\n            { group: \"C-O Vibrations\", mode: \"C-O (fenol)\", type: \"\u03bd\", range: [1180, 1260], intensity: \"medium\", src: \"LV\" },\n\n            \/\/ --- Amide Vibrations ---\n            { group: \"Amide Vibrations\", mode: \"Amid I pierwszorz\u0119dowe (cia\u0142a sta\u0142e)\", type: \"\u03bd\", range: [1636, 1686], intensity: \"strong\", src: \"E2\" },\n            { group: \"Amide Vibrations\", mode: \"Amid I drugorz\u0119dowe\", type: \"\u03bd\", range: [1650, 1660], intensity: \"strong\", src: \"E2\" },\n            { group: \"Amide Vibrations\", mode: \"Amid I trzeciorz\u0119dowe\", type: \"\u03bd\", range: [1630, 1670], intensity: \"strong\", src: \"E2\" },\n            { group: \"Amide Vibrations\", mode: \"Amid III drugorz\u0119dowe\", type: \"\u03b4\", range: [1250, 1310], intensity: \"strong\", src: \"E2\" },\n            { group: \"Amide Vibrations\", mode: \"NH\u2082 no\u017cycowe pierwszorz\u0119dowe\", type: \"\u03b4\", range: [1590, 1650], intensity: \"medium\", src: \"E2\" },\n\n            \/\/ --- Aromatic & Heteroaromatic Ring Vibrations ---\n            { group: \"Aromatic Ring Vibrations\", mode: \"Dublet rozci\u0105ga\u0144 pier\u015bcienia\", type: \"\u03bd\", range: [1550, 1630], intensity: \"medium\", src: \"E2\" },\n            { group: \"Aromatic Ring Vibrations\", mode: \"Rozci\u0105ganie pier\u015bcienia (antraceny)\", type: \"\u03bd\", range: [1385, 1415], intensity: \"medium\", src: \"E2\" },\n            { group: \"Aromatic Ring Vibrations\", mode: \"Rozci\u0105ganie pier\u015bcienia (naftaleny)\", type: \"\u03bd\", range: [1370, 1390], intensity: \"medium\", src: \"E2\" },\n            { group: \"Aromatic Ring Vibrations\", mode: \"CC rozci\u0105ganie mostka (bifenyle)\", type: \"\u03bd\", range: [1280, 1300], intensity: \"medium\", src: \"E2\" },\n            { group: \"Aromatic Ring Vibrations\", mode: \"Drganie pier\u015bcienia para-dipodstawiony (wysokie)\", type: \"\u03bd\", range: [1200, 1230], intensity: \"medium\", src: \"E2\" },\n            { group: \"Aromatic Ring Vibrations\", mode: \"Drganie pier\u015bcienia orto-dipodstawiony\", type: \"\u03bd\", range: [1020, 1060], intensity: \"medium\", src: \"E2\" },\n            { group: \"Aromatic Ring Vibrations\", mode: \"Deformacja CH w p\u0142aszczy\u017anie (mono-)\", type: \"\u03b4\", range: [1015, 1030], intensity: \"medium\", src: \"E2\" },\n            { group: \"Aromatic Ring Vibrations\", mode: \"Trygonalne oddychanie pier\u015bcienia (mono\/meta\/1,3,5)\", type: \"\u03bd\", range: [990, 1010], intensity: \"strong\", src: \"E2\" },\n            { group: \"Aromatic Ring Vibrations\", mode: \"Trygonalne oddychanie pier\u015bcienia (pirydyna, mono-\/di-podst.)\", type: \"\u03bd\", range: [985, 1030], intensity: \"strong\", src: \"E2\" },\n            { group: \"Aromatic Ring Vibrations\", mode: \"Drganie pier\u015bcienia para-dipodstawiony (niskie)\", type: \"\u03bd\", range: [720, 830], intensity: \"medium\", src: \"E2\" },\n            { group: \"Aromatic Ring Vibrations\", mode: \"Deformacja pier\u015bcienia monopodstawiony\", type: \"\u03b4\", range: [615, 630], intensity: \"medium\", src: \"E2\" },\n\n            \/\/ --- Ring & Skeletal Vibrations (alicyclic \/ heterocyclic) ---\n            { group: \"Aromatic Ring Vibrations\", mode: \"Rozci\u0105ganie pier\u015bcienia (grupa 2-furfurylowa)\", type: \"\u03bd\", range: [1490, 1515], intensity: \"medium\", src: \"E2\" },\n            { group: \"Aromatic Ring Vibrations\", mode: \"Rozci\u0105ganie pier\u015bcienia (tiofeny 2-podstawione)\", type: \"\u03bd\", range: [1398, 1443], intensity: \"medium\", src: \"E2\" },\n            { group: \"Ring & Skeletal Vibrations\", mode: \"Drganie pier\u015bcienia (cyklopropany 1,1-dialkilowe)\", type: \"\u03bd\", range: [1320, 1320], intensity: \"medium\", src: \"E2\" },\n            { group: \"Ring & Skeletal Vibrations\", mode: \"Rozci\u0105ganie pier\u015bcienia (pochodne epoksydowe)\", type: \"\u03bd\", range: [1240, 1280], intensity: \"medium\", src: \"E2\" },\n            { group: \"Ring & Skeletal Vibrations\", mode: \"Drganie pier\u015bcienia (cyklopropany mono-\/1,2-dialkilowe)\", type: \"\u03bd\", range: [1200, 1220], intensity: \"medium\", src: \"E2\" },\n            { group: \"Ring & Skeletal Vibrations\", mode: \"Drganie pier\u015bcienia (cyklobutany alkilowe)\", type: \"\u03bd\", range: [933, 933], intensity: \"medium\", src: \"E2\" },\n            { group: \"Ring & Skeletal Vibrations\", mode: \"Drganie pier\u015bcienia (cyklopentany alkilowe)\", type: \"\u03bd\", range: [890, 900], intensity: \"medium\", src: \"E2\" },\n            { group: \"Ring & Skeletal Vibrations\", mode: \"Rozci\u0105ganie szkieletowe (grupa izopropylowa)\", type: \"\u03bd\", range: [749, 835], intensity: \"medium\", src: \"E2\" },\n            { group: \"Ring & Skeletal Vibrations\", mode: \"Drganie pier\u015bcienia (cykloheksany alkilowe)\", type: \"\u03bd\", range: [700, 785], intensity: \"medium\", src: \"E2\" },\n            { group: \"Ring & Skeletal Vibrations\", mode: \"Symetryczne rozci\u0105ganie szkieletowe (tert-butyl)\", type: \"\u03bdsym\", range: [650, 760], intensity: \"medium\", src: \"E2\" },\n\n            \/\/ --- N-H Vibrations ---\n            { group: \"N-H Vibrations\", mode: \"NH\u2082 antysymetryczne (aminy pierwszorz\u0119dowe)\", type: \"\u03bdasym\", range: [3330, 3400], intensity: \"medium\", src: \"E2\" },\n            { group: \"N-H Vibrations\", mode: \"NH\u2082 antysymetryczne (amidy pierwszorz\u0119dowe)\", type: \"\u03bdasym\", range: [3325, 3355], intensity: \"medium\", src: \"E2\" },\n            { group: \"N-H Vibrations\", mode: \"NH rozci\u0105ganie (aminy drugorz\u0119dowe)\", type: \"\u03bd\", range: [3300, 3350], intensity: \"weak\", src: \"E2\" },\n            { group: \"N-H Vibrations\", mode: \"NH rozci\u0105ganie (amidy drugorz\u0119dowe)\", type: \"\u03bd\", range: [3290, 3310], intensity: \"weak\", src: \"E2\" },\n            { group: \"N-H Vibrations\", mode: \"NH\u2082 symetryczne (aminy pierwszorz\u0119dowe)\", type: \"\u03bdsym\", range: [3250, 3300], intensity: \"medium\", src: \"E2\" },\n            { group: \"N-H Vibrations\", mode: \"NH\u2082 symetryczne (amidy pierwszorz\u0119dowe)\", type: \"\u03bdsym\", range: [3145, 3190], intensity: \"medium\", src: \"E2\" },\n\n            \/\/ --- O-H Vibrations ---\n            { group: \"O-H Vibrations\", mode: \"OH rozci\u0105ganie (alkohole alifatyczne)\", type: \"\u03bd\", range: [3340, 3380], intensity: \"medium\", src: \"E2\" },\n\n            \/\/ --- C-N Vibrations ---\n            { group: \"C-N Vibrations\", mode: \"C\u2261N rozci\u0105ganie izonitryle alifatyczne\", type: \"\u03bd\", range: [2134, 2161], intensity: \"strong\", src: \"E2\" },\n            { group: \"C-N Vibrations\", mode: \"C\u2261N rozci\u0105ganie (tiocyjaniany)\", type: \"\u03bd\", range: [2140, 2156], intensity: \"strong\", src: \"E2\" },\n            { group: \"C-N Vibrations\", mode: \"C=N rozci\u0105ganie (ketoksymy)\", type: \"\u03bd\", range: [1652, 1666], intensity: \"strong\", src: \"E2\" },\n            { group: \"C-N Vibrations\", mode: \"C=N rozci\u0105ganie (semikarbazony)\", type: \"\u03bd\", range: [1650, 1665], intensity: \"strong\", src: \"E2\" },\n            { group: \"C-N Vibrations\", mode: \"C=N rozci\u0105ganie (aldoksymy)\", type: \"\u03bd\", range: [1649, 1660], intensity: \"strong\", src: \"E2\" },\n            { group: \"C-N Vibrations\", mode: \"C=N symetryczne (aldazyny, ketazyny)\", type: \"\u03bdsym\", range: [1636, 1663], intensity: \"strong\", src: \"E2\" },\n            { group: \"C-N Vibrations\", mode: \"C=N rozci\u0105ganie (tiosemikarbazony)\", type: \"\u03bd\", range: [1642, 1652], intensity: \"strong\", src: \"E2\" },\n            { group: \"C-N Vibrations\", mode: \"C=N rozci\u0105ganie (hydrazony)\", type: \"\u03bd\", range: [1610, 1660], intensity: \"strong\", src: \"E2\" },\n            { group: \"C-N Vibrations\", mode: \"CNC symetryczne (aminy drugorz\u0119dowe)\", type: \"\u03bdsym\", range: [850, 900], intensity: \"strong\", src: \"E2\" },\n\n            \/\/ --- Cumulated Double Bonds (X=Y=Z) ---\n            { group: \"Cumulated Double Bonds\", mode: \"N=C=O pseudoantysym. (izocyjaniany)\", type: \"\u03bdasym\", range: [2250, 2300], intensity: \"strong\", src: \"E2\" },\n            { group: \"Cumulated Double Bonds\", mode: \"N=C=O pseudosym. (izocyjaniany)\", type: \"\u03bdsym\", range: [1400, 1450], intensity: \"medium\", src: \"E2\" },\n            { group: \"Cumulated Double Bonds\", mode: \"C=C=C symetryczne (alleny)\", type: \"\u03bdsym\", range: [1100, 1130], intensity: \"medium\", src: \"E2\" },\n            { group: \"Cumulated Double Bonds\", mode: \"N=C=S pseudosym. (izotiocyjaniany alkilowe)\", type: \"\u03bdsym\", range: [650, 690], intensity: \"medium\", src: \"E2\" },\n\n            \/\/ --- Nitro Vibrations ---\n            { group: \"Nitro Vibrations\", mode: \"NO\u2082 antysymetryczne pierwszorz\u0119dowe\", type: \"\u03bdasym\", range: [1550, 1560], intensity: \"strong\", src: \"E2\" },\n            { group: \"Nitro Vibrations\", mode: \"NO\u2082 antysymetryczne drugorz\u0119dowe\", type: \"\u03bdasym\", range: [1550, 1555], intensity: \"strong\", src: \"E2\" },\n            { group: \"Nitro Vibrations\", mode: \"NO\u2082 antysymetryczne trzeciorz\u0119dowe\", type: \"\u03bdasym\", range: [1535, 1545], intensity: \"strong\", src: \"E2\" },\n            { group: \"Nitro Vibrations\", mode: \"N=O rozci\u0105ganie (azotyny alkilowe)\", type: \"\u03bd\", range: [1640, 1648], intensity: \"medium\", src: \"E2\" },\n            { group: \"Nitro Vibrations\", mode: \"NO\u2082 antysymetryczne (azotany alkilowe)\", type: \"\u03bdasym\", range: [1622, 1634], intensity: \"medium\", src: \"E2\" },\n            { group: \"Nitro Vibrations\", mode: \"NO\u2082 symetryczne pierwszorz\u0119dowe\", type: \"\u03bdsym\", range: [1380, 1395], intensity: \"strong\", src: \"E2\" },\n            { group: \"Nitro Vibrations\", mode: \"NO\u2082 symetryczne drugorz\u0119dowe\", type: \"\u03bdsym\", range: [1360, 1375], intensity: \"strong\", src: \"E2\" },\n            { group: \"Nitro Vibrations\", mode: \"NO\u2082 symetryczne trzeciorz\u0119dowe\", type: \"\u03bdsym\", range: [1345, 1355], intensity: \"strong\", src: \"E2\" },\n            { group: \"Nitro Vibrations\", mode: \"NO\u2082 symetryczne (azotany alkilowe)\", type: \"\u03bdsym\", range: [1275, 1282], intensity: \"strong\", src: \"E2\" },\n\n            \/\/ --- Sulfur Vibrations ---\n            { group: \"Sulfur Vibrations\", mode: \"SH rozci\u0105ganie (tiole)\", type: \"\u03bd\", range: [2560, 2590], intensity: \"weak\", src: \"E2\" },\n            { group: \"Sulfur Vibrations\", mode: \"S-S rozci\u0105ganie disiarczki dialkilowe\", type: \"\u03bd\", range: [510, 525], intensity: \"strong\", src: \"E2\" },\n            { group: \"Sulfur Vibrations\", mode: \"S-S rozci\u0105ganie trisiarczki dialkilowe\", type: \"\u03bd\", range: [480, 510], intensity: \"strong\", src: \"E2\" },\n            { group: \"Sulfur Vibrations\", mode: \"C-S rozci\u0105ganie siarczki alkilowe\", type: \"\u03bd\", range: [585, 740], intensity: \"medium\", src: \"E2\" },\n            { group: \"Sulfur Vibrations\", mode: \"C-S rozci\u0105ganie disiarczki dialkilowe\", type: \"\u03bd\", range: [620, 715], intensity: \"strong\", src: \"E2\" },\n            { group: \"Sulfur Vibrations\", mode: \"C=S rozci\u0105ganie tioamidy \/ tiomoczniki\", type: \"\u03bd\", range: [690, 735], intensity: \"strong\", src: \"E2\" },\n            { group: \"Sulfur Vibrations\", mode: \"SO\u2082 symetryczne siarczany alkilowe\", type: \"\u03bdsym\", range: [1188, 1196], intensity: \"strong\", src: \"E2\" },\n            { group: \"Sulfur Vibrations\", mode: \"SO\u2082 symetryczne sulfoniany alkilowe\", type: \"\u03bdsym\", range: [1165, 1172], intensity: \"strong\", src: \"E2\" },\n            { group: \"Sulfur Vibrations\", mode: \"SO\u2082 symetryczne sulfony dialkilowe\", type: \"\u03bdsym\", range: [1125, 1145], intensity: \"strong\", src: \"E2\" },\n            { group: \"Sulfur Vibrations\", mode: \"S=O rozci\u0105ganie (sulfotlenki)\", type: \"\u03bd\", range: [1040, 1070], intensity: \"strong\", src: \"E2\" },\n\n            \/\/ --- Halogen Vibrations ---\n            { group: \"Halogen Vibrations\", mode: \"C-Cl rozci\u0105ganie pierwszorz\u0119dowe\", type: \"\u03bd\", range: [720, 730], intensity: \"strong\", src: \"E2\" },\n            { group: \"Halogen Vibrations\", mode: \"C-Cl rozci\u0105ganie drugorz\u0119dowe\", type: \"\u03bd\", range: [605, 615], intensity: \"strong\", src: \"E2\" },\n            { group: \"Halogen Vibrations\", mode: \"C-Cl rozci\u0105ganie trzeciorz\u0119dowe\", type: \"\u03bd\", range: [560, 570], intensity: \"strong\", src: \"E2\" },\n            { group: \"Halogen Vibrations\", mode: \"C-Br rozci\u0105ganie pierwszorz\u0119dowe\", type: \"\u03bd\", range: [640, 655], intensity: \"strong\", src: \"E2\" },\n            { group: \"Halogen Vibrations\", mode: \"C-Br rozci\u0105ganie drugorz\u0119dowe\", type: \"\u03bd\", range: [535, 540], intensity: \"strong\", src: \"E2\" },\n            { group: \"Halogen Vibrations\", mode: \"C-Br rozci\u0105ganie trzeciorz\u0119dowe\", type: \"\u03bd\", range: [510, 520], intensity: \"strong\", src: \"E2\" },\n            { group: \"Halogen Vibrations\", mode: \"C-I rozci\u0105ganie pierwszorz\u0119dowe\", type: \"\u03bd\", range: [500, 510], intensity: \"strong\", src: \"E2\" },\n            { group: \"Halogen Vibrations\", mode: \"C-I rozci\u0105ganie drugorz\u0119dowe\", type: \"\u03bd\", range: [485, 495], intensity: \"strong\", src: \"E2\" },\n            { group: \"Halogen Vibrations\", mode: \"C-I rozci\u0105ganie trzeciorz\u0119dowe\", type: \"\u03bd\", range: [485, 495], intensity: \"strong\", src: \"E2\" },\n\n            \/\/ --- Inorganic Ions (Socrates, Tab. 22.1) ---\n            { group: \"Inorganic Ions\", mode: \"CO\u2083\u00b2\u207b w\u0119glan antysymetryczne\", type: \"\u03bdasym\", range: [1320, 1530], intensity: \"medium\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"CO\u2083\u00b2\u207b w\u0119glan symetryczne (\u03bd\u2081)\", type: \"\u03bdsym\", range: [1020, 1100], intensity: \"strong\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"SO\u2084\u00b2\u207b siarczan antysymetryczne (\u03bd\u2083)\", type: \"\u03bdasym\", range: [1080, 1130], intensity: \"medium\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"SO\u2084\u00b2\u207b siarczan symetryczne (\u03bd\u2081)\", type: \"\u03bdsym\", range: [975, 1000], intensity: \"strong\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"SO\u2084\u00b2\u207b siarczan deformacja (\u03bd\u2084)\", type: \"\u03b4\", range: [600, 680], intensity: \"weak\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"SO\u2083\u00b2\u207b siarczyn\", type: \"\u03bd\", range: [955, 1130], intensity: \"strong\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"NO\u2083\u207b azotan symetryczne (\u03bd\u2081)\", type: \"\u03bdsym\", range: [1015, 1070], intensity: \"strong\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"NO\u2082\u207b azotyn\", type: \"\u03bd\", range: [1300, 1400], intensity: \"strong\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"PO\u2084\u00b3\u207b fosforan antysymetryczne\", type: \"\u03bdasym\", range: [1000, 1180], intensity: \"strong\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"PO\u2084\u00b3\u207b fosforan symetryczne (\u03bd\u2081)\", type: \"\u03bdsym\", range: [900, 1000], intensity: \"strong\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"ClO\u2084\u207b nadchloran (\u03bd\u2081)\", type: \"\u03bd\", range: [930, 955], intensity: \"strong\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"ClO\u2083\u207b chloran\", type: \"\u03bd\", range: [910, 980], intensity: \"strong\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"C\u2082O\u2084\u00b2\u207b szczawian (C=O)\", type: \"\u03bd\", range: [1680, 1730], intensity: \"medium\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"C\u2082O\u2084\u00b2\u207b szczawian (C-O\/C-C)\", type: \"\u03bd\", range: [1400, 1490], intensity: \"medium\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"NH\u2084\u207a jon amonowy\", type: \"\u03bd\", range: [3030, 3335], intensity: \"medium\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"AlO\u2082\u207b glinian\", type: \"\u03bd\", range: [800, 920], intensity: \"medium\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"AsO\u2084\u00b3\u207b ortoarsenian\", type: \"\u03bd\", range: [800, 910], intensity: \"strong\", src: \"S1\" },\n\n            \/\/ --- Silicate \/ Silane Vibrations ---\n            { group: \"Silicate\/Oxide Vibrations\", mode: \"Krzemiany \u2014 rozci\u0105ganie Si-O\", type: \"\u03bd\", range: [900, 1100], intensity: \"strong\", src: \"S1\" },\n            { group: \"Silicate\/Oxide Vibrations\", mode: \"Si-O-Si mostek (zginanie)\", type: \"\u03b4\", range: [480, 625], intensity: \"strong\", src: \"S26\" },\n            { group: \"Silicate\/Oxide Vibrations\", mode: \"SiF\u2086\u00b2\u207b heksafluorokrzemian\", type: \"\u03bd\", range: [645, 665], intensity: \"strong\", src: \"S1\" },\n            { group: \"Silicate\/Oxide Vibrations\", mode: \"Si-H (silany)\", type: \"\u03bd\", range: [2100, 2250], intensity: \"strong\", src: \"S24\" },\n\n            \/\/ --- Acid\/Base (protonated) Ions ---\n            { group: \"Acid\/Base Vibrations\", mode: \"HCO\u2083\u207b wodorow\u0119glan (rozc. C-O)\", type: \"\u03bd\", range: [1600, 1700], intensity: \"strong\", src: \"S1\" },\n            { group: \"Acid\/Base Vibrations\", mode: \"HSO\u2084\u207b wodorosiarczan (rozc. S-O)\", type: \"\u03bd\", range: [1160, 1190], intensity: \"strong\", src: \"S1\" },\n            { group: \"Acid\/Base Vibrations\", mode: \"H\u2082PO\u2084\u207b diwodorofosforan\", type: \"\u03bd\", range: [950, 1200], intensity: \"strong\", src: \"S1\" },\n            { group: \"Acid\/Base Vibrations\", mode: \"HPO\u2084\u00b2\u207b wodorofosforan\", type: \"\u03bd\", range: [1000, 1150], intensity: \"strong\", src: \"S1\" },\n            { group: \"Acid\/Base Vibrations\", mode: \"HF\u2082\u207b wodorodifluorek\", type: \"\u03bd\", range: [2050, 2125], intensity: \"medium\", src: \"S1\" },\n\n            \/\/ --- Metal Oxides (Socrates Chart 22.5 - orientacyjne, g\u0142\u00f3wnie IR) ---\n            { group: \"Metal Oxides\", mode: \"M=O rozci\u0105ganie (og\u00f3lne)\", type: \"\u03bd\", range: [825, 1100], intensity: \"strong\", src: \"S26\" },\n            { group: \"Metal Oxides\", mode: \"Ti-O rozci\u0105ganie (TiO\u2082, ditlenek tytanu)\", type: \"\u03bd\", range: [400, 1000], intensity: \"strong\", src: \"S26\" },\n            { group: \"Metal Oxides\", mode: \"SiO\u2082 krzemionka (asym. Si-O-Si)\", type: \"\u03bd\", range: [1000, 1100], intensity: \"strong\", src: \"S26\" },\n            { group: \"Metal Oxides\", mode: \"Fe\u2082O\u2083 tlenek \u017celaza(III)\", type: \"\u03bd\", range: [200, 700], intensity: \"strong\", src: \"S26\" },\n            { group: \"Metal Oxides\", mode: \"CuO tlenek miedzi(II)\", type: \"\u03bd\", range: [400, 700], intensity: \"medium\", src: \"S26\" },\n            { group: \"Metal Oxides\", mode: \"ZnO tlenek cynku\", type: \"\u03bd\", range: [400, 600], intensity: \"medium\", src: \"S26\" },\n\n            \/\/ --- Complex Anions (Socrates, Tab. 22.1) ---\n            { group: \"Pseudohalide Ions\", mode: \"CN\u207b cyjanek\", type: \"\u03bd\", range: [2070, 2240], intensity: \"strong\", src: \"S1\" },\n            { group: \"Pseudohalide Ions\", mode: \"SCN\u207b tiocyjanian (asym.)\", type: \"\u03bdasym\", range: [2030, 2190], intensity: \"strong\", src: \"S1\" },\n            { group: \"Pseudohalide Ions\", mode: \"OCN\u207b cyjanian\", type: \"\u03bd\", range: [2100, 2225], intensity: \"strong\", src: \"S1\" },\n            { group: \"Pseudohalide Ions\", mode: \"N\u2083\u207b azydek, symetryczne (Raman)\", type: \"\u03bdsym\", range: [1175, 1375], intensity: \"strong\", src: \"S1\" },\n            { group: \"Pseudohalide Ions\", mode: \"N\u2083\u207b azydek, antysymetryczne\", type: \"\u03bdasym\", range: [2030, 2170], intensity: \"medium\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"IO\u2083\u207b jodan\", type: \"\u03bd\", range: [690, 830], intensity: \"strong\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"BrO\u2083\u207b bromian\", type: \"\u03bd\", range: [760, 850], intensity: \"strong\", src: \"S1\" },\n            { group: \"Transition Metal Oxoanions\", mode: \"CrO\u2084\u00b2\u207b chromian\", type: \"\u03bd\", range: [770, 960], intensity: \"strong\", src: \"S1\" },\n            { group: \"Transition Metal Oxoanions\", mode: \"Cr\u2082O\u2087\u00b2\u207b dichromian\", type: \"\u03bd\", range: [900, 1000], intensity: \"strong\", src: \"S1\" },\n            { group: \"Transition Metal Oxoanions\", mode: \"MnO\u2084\u207b nadmanganian\", type: \"\u03bd\", range: [820, 950], intensity: \"strong\", src: \"S1\" },\n            { group: \"Transition Metal Oxoanions\", mode: \"MoO\u2084\u00b2\u207b molibdenian\", type: \"\u03bd\", range: [810, 935], intensity: \"strong\", src: \"S1\" },\n            { group: \"Transition Metal Oxoanions\", mode: \"WO\u2084\u00b2\u207b wolframian\", type: \"\u03bd\", range: [780, 960], intensity: \"strong\", src: \"S1\" },\n            { group: \"Transition Metal Oxoanions\", mode: \"VO\u2084\u00b3\u207b ortowanadan\", type: \"\u03bd\", range: [825, 1000], intensity: \"strong\", src: \"S1\" },\n            { group: \"Transition Metal Oxoanions\", mode: \"TiO\u2083\u00b2\u207b tytanian\", type: \"\u03bd\", range: [500, 700], intensity: \"strong\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"P\u2082O\u2087\u2074\u207b pirofosforan\", type: \"\u03bd\", range: [960, 1220], intensity: \"strong\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"PO\u2083\u207b metafosforan\", type: \"\u03bd\", range: [1150, 1305], intensity: \"medium\", src: \"S1\" },\n\n            \/\/ --- Boron Compounds (Socrates, Tab. 22.1) ---\n            { group: \"Boron Compounds\", mode: \"BH\u2084\u207b borowodorek\", type: \"\u03bd\", range: [2195, 2400], intensity: \"strong\", src: \"S1\" },\n            { group: \"Boron Compounds\", mode: \"BF\u2084\u207b antysymetryczne (\u03bd\u2083)\", type: \"\u03bdasym\", range: [1030, 1125], intensity: \"medium\", src: \"S1\" },\n            { group: \"Boron Compounds\", mode: \"BF\u2084\u207b symetryczne (\u03bd\u2081, silne w Ramanie)\", type: \"\u03bdsym\", range: [760, 780], intensity: \"strong\", src: \"S1\" },\n            { group: \"Boron Compounds\", mode: \"BO\u2083\u00b3\u207b boran\", type: \"\u03bd\", range: [1260, 1490], intensity: \"strong\", src: \"S1\" },\n            { group: \"Boron Compounds\", mode: \"B\u2084O\u2087\u00b2\u207b tetraboran\", type: \"\u03bd\", range: [1330, 1380], intensity: \"strong\", src: \"S1\" },\n\n            \/\/ --- Fluorine Compounds ---\n            { group: \"Fluorine Compounds\", mode: \"PF\u2086\u207b antysymetryczne (\u03bd\u2083)\", type: \"\u03bdasym\", range: [840, 915], intensity: \"medium\", src: \"S1\" },\n            { group: \"Fluorine Compounds\", mode: \"PF\u2086\u207b symetryczne (\u03bd\u2081, silne w Ramanie)\", type: \"\u03bdsym\", range: [745, 750], intensity: \"strong\", src: \"S1\" },\n            { group: \"Fluorine Compounds\", mode: \"AsF\u2086\u207b heksafluoroarsenian\", type: \"\u03bd\", range: [680, 705], intensity: \"strong\", src: \"S1\" },\n\n            \/\/ --- Chalcogen Compounds (Socrates, Tab. 22.1 \/ 22.24) ---\n            { group: \"Chalcogen Compounds\", mode: \"SeO\u2084\u00b2\u207b selenian\", type: \"\u03bd\", range: [830, 935], intensity: \"strong\", src: \"S1\" },\n            { group: \"Chalcogen Compounds\", mode: \"SeO\u2083\u00b2\u207b selenin\", type: \"\u03bd\", range: [750, 830], intensity: \"strong\", src: \"S1\" },\n            { group: \"Chalcogen Compounds\", mode: \"TeO\u2084\u00b2\u207b tellurian\", type: \"\u03bd\", range: [730, 750], intensity: \"strong\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"S\u2082O\u2083\u00b2\u207b tiosiarczan\", type: \"\u03bd\", range: [1100, 1200], intensity: \"strong\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"S\u2082O\u2085\u00b2\u207b pirosiarczyn (silne pasmo ramanowskie)\", type: \"\u03bd\", range: [1040, 1100], intensity: \"strong\", src: \"S1\" },\n            { group: \"Chalcogen Compounds\", mode: \"Se-H selenowodory\", type: \"\u03bd\", range: [2200, 2400], intensity: \"strong\", src: \"S24\" },\n            { group: \"Inorganic Ions\", mode: \"NO\u2083\u207b azotan antysymetryczne (\u03bd\u2083)\", type: \"\u03bdasym\", range: [1280, 1520], intensity: \"medium\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"NO\u2083\u207b azotan deformacja (\u03bd\u2082)\", type: \"\u03b4\", range: [800, 860], intensity: \"medium\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"NO\u2083\u207b azotan deformacja (\u03bd\u2084)\", type: \"\u03b4\", range: [700, 770], intensity: \"weak\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"NO\u2082\u207b azotyn antysymetryczne\", type: \"\u03bdasym\", range: [1185, 1285], intensity: \"medium\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"NO\u2082\u207b azotyn deformacja\", type: \"\u03b4\", range: [800, 860], intensity: \"medium\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"PO\u2084\u00b3\u207b fosforan deformacja (\u03bd\u2084)\", type: \"\u03b4\", range: [540, 580], intensity: \"medium\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"ClO\u2084\u207b nadchloran deformacja (\u03bd\u2082)\", type: \"\u03b4\", range: [420, 490], intensity: \"medium\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"ClO\u2084\u207b nadchloran deformacja (\u03bd\u2084)\", type: \"\u03b4\", range: [620, 630], intensity: \"weak\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"ClO\u2083\u207b chloran deformacja (\u03bd\u2084)\", type: \"\u03b4\", range: [480, 510], intensity: \"medium\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"C\u2082O\u2084\u00b2\u207b szczawian (deformacja ~850)\", type: \"\u03bd\", range: [800, 900], intensity: \"medium\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"C\u2082O\u2084\u00b2\u207b szczawian (deformacja ~560)\", type: \"\u03b4\", range: [520, 600], intensity: \"medium\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"NH\u2084\u207a jon amonowy (deformacja)\", type: \"\u03b4\", range: [1325, 1490], intensity: \"weak\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"SO\u2083\u00b2\u207b siarczyn (deformacja \u03bd\u2084)\", type: \"\u03b4\", range: [580, 680], intensity: \"medium\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"IO\u2083\u207b jodan (deformacja)\", type: \"\u03b4\", range: [310, 420], intensity: \"medium\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"BrO\u2083\u207b bromian (deformacja)\", type: \"\u03b4\", range: [430, 450], intensity: \"medium\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"S\u2082O\u2083\u00b2\u207b tiosiarczan (S-S \/ deformacja)\", type: \"\u03bd\", range: [660, 695], intensity: \"medium\", src: \"S1\" },\n            { group: \"Inorganic Ions\", mode: \"S\u2082O\u2085\u00b2\u207b pirosiarczyn (deformacja)\", type: \"\u03b4\", range: [640, 670], intensity: \"medium\", src: \"S1\" },\n            { group: \"Transition Metal Oxoanions\", mode: \"CrO\u2084\u00b2\u207b chromian (deformacje)\", type: \"\u03b4\", range: [300, 420], intensity: \"medium\", src: \"S1\" },\n            { group: \"Transition Metal Oxoanions\", mode: \"Cr\u2082O\u2087\u00b2\u207b dichromian (mostkowe\/def.)\", type: \"\u03bd\", range: [730, 900], intensity: \"medium\", src: \"S1\" },\n            { group: \"Pseudohalide Ions\", mode: \"N\u2083\u207b azydek (deformacja)\", type: \"\u03b4\", range: [410, 680], intensity: \"medium\", src: \"S1\" },\n            { group: \"Boron Compounds\", mode: \"BF\u2084\u207b deformacja (\u03bd\u2084)\", type: \"\u03b4\", range: [510, 560], intensity: \"medium\", src: \"S1\" },\n            { group: \"Boron Compounds\", mode: \"BO\u2083\u00b3\u207b boran (\u03bd ~1020)\", type: \"\u03bd\", range: [1010, 1030], intensity: \"medium\", src: \"S1\" },\n            { group: \"Boron Compounds\", mode: \"BO\u2083\u00b3\u207b boran (\u03bd ~800)\", type: \"\u03bd\", range: [750, 830], intensity: \"strong\", src: \"S1\" },\n            { group: \"Boron Compounds\", mode: \"B\u2084O\u2087\u00b2\u207b tetraboran (\u03bd ~1000)\", type: \"\u03bd\", range: [980, 1020], intensity: \"medium\", src: \"S1\" },\n            { group: \"Fluorine Compounds\", mode: \"PF\u2086\u207b deformacja (\u03bd\u2084)\", type: \"\u03b4\", range: [555, 580], intensity: \"medium\", src: \"S1\" },\n            { group: \"Chalcogen Compounds\", mode: \"SeO\u2084\u00b2\u207b selenian (deformacja)\", type: \"\u03b4\", range: [350, 450], intensity: \"medium\", src: \"S1\" },\n            { group: \"Chalcogen Compounds\", mode: \"TeO\u2084\u00b2\u207b tellurian (~465)\", type: \"\u03bd\", range: [460, 470], intensity: \"medium\", src: \"S1\" },\n            { group: \"Acid\/Base Vibrations\", mode: \"HCO\u2083\u207b wodorow\u0119glan (\u03bd\u2081 ~995)\", type: \"\u03bd\", range: [990, 1000], intensity: \"strong\", src: \"S1\" },\n            { group: \"Acid\/Base Vibrations\", mode: \"HCO\u2083\u207b wodorow\u0119glan (deformacja)\", type: \"\u03b4\", range: [1290, 1420], intensity: \"medium\", src: \"S1\" },\n            { group: \"Acid\/Base Vibrations\", mode: \"HSO\u2084\u207b wodorosiarczan (S-O ~1040)\", type: \"\u03bd\", range: [1000, 1080], intensity: \"medium\", src: \"S1\" },\n            { group: \"Acid\/Base Vibrations\", mode: \"HSO\u2084\u207b wodorosiarczan (~860)\", type: \"\u03bd\", range: [840, 880], intensity: \"medium\", src: \"S1\" },\n            { group: \"Acid\/Base Vibrations\", mode: \"HF\u2082\u207b wodorodifluorek (deformacja)\", type: \"\u03b4\", range: [1200, 1260], intensity: \"medium\", src: \"S1\" },\n            \/\/ --- Wi\u0105zania koordynacyjne (kompleksy) \u2014 Socrates, Tab. 22.14\u201322.20 ---\n            { group: \"Coordination Complexes\", mode: \"M\u2013C\u2261O terminalny (\u03bd CO)\", type: \"\u03bd\", range: [1900, 2170], intensity: \"strong\", src: \"SK\" },\n            { group: \"Coordination Complexes\", mode: \"M\u2013CO\u2013M mostkuj\u0105cy (\u03bd CO)\", type: \"\u03bd\", range: [1700, 1900], intensity: \"strong\", src: \"SK\" },\n            { group: \"Coordination Complexes\", mode: \"M\u2013C rozci\u0105ganie (karbonylki)\", type: \"\u03bd\", range: [340, 640], intensity: \"medium\", src: \"SK\" },\n            { group: \"Coordination Complexes\", mode: \"M\u2013C\u2261N cyjanokompleksy (\u03bd CN)\", type: \"\u03bd\", range: [2000, 2250], intensity: \"strong\", src: \"SK\" },\n            { group: \"Coordination Complexes\", mode: \"M\u2013NCS\/M\u2013SCN tiocyjaniano (\u03bd CN)\", type: \"\u03bd\", range: [2040, 2185], intensity: \"strong\", src: \"SK\" },\n            { group: \"Coordination Complexes\", mode: \"M\u2013NH\u2083 deformacja (aminy koord.)\", type: \"\u03b4\", range: [1550, 1650], intensity: \"weak\", src: \"SK\" },\n            { group: \"Coordination Complexes\", mode: \"COO\u207b bidentny asymetryczne (karboksylan koord.)\", type: \"\u03bdasym\", range: [1515, 1610], intensity: \"strong\", src: \"SK\" },\n            { group: \"Coordination Complexes\", mode: \"COO\u207b bidentny symetryczne (karboksylan koord.)\", type: \"\u03bdsym\", range: [1315, 1495], intensity: \"medium\", src: \"SK\" },\n            { group: \"Coordination Complexes\", mode: \"C=O unidentny (karboksylan koord.)\", type: \"\u03bd\", range: [1575, 1675], intensity: \"medium\", src: \"SK\" },\n            { group: \"Coordination Complexes\", mode: \"M\u2013NO\u2082 nitro (asymetryczne)\", type: \"\u03bdasym\", range: [1370, 1470], intensity: \"medium\", src: \"SK\" },\n            { group: \"Coordination Complexes\", mode: \"M\u2013ONO nitrito (N=O)\", type: \"\u03bd\", range: [1400, 1485], intensity: \"strong\", src: \"SK\" },\n            { group: \"Coordination Complexes\", mode: \"M\u2013O rozci\u0105ganie\", type: \"\u03bd\", range: [290, 550], intensity: \"medium\", src: \"SK\" },\n            { group: \"Coordination Complexes\", mode: \"M\u2013N rozci\u0105ganie\", type: \"\u03bd\", range: [275, 550], intensity: \"medium\", src: \"SK\" },\n        ];\n\n        const colors = {\n            \/\/ W\u0119glowodory i szkielet C\u2013H (intensywno\u015b\u0107 malej\u0105co)\n            \"C-H Vibrations (Aliphatic)\": \"#165CA2\",\n            \"C-H Vibrations (Aromatic)\": \"#2271C1\",\n            \"C=C Vibrations\": \"#3A87D4\",\n            \"C\u2261C Vibrations\": \"#649DD6\",\n            \"Ring & Skeletal Vibrations\": \"#8BB2DA\",\n            \/\/ Grupy tlenowe (intensywno\u015b\u0107 malej\u0105co)\n            \"C=O Vibrations\": \"#1C9C3C\",\n            \"Carboxylic Acids\": \"#25B649\",\n            \"Amino Acids\": \"#2FCE57\",\n            \"Esters and Derivatives\": \"#4ED26F\",\n            \"C-O Vibrations\": \"#6DD587\",\n            \"O-H Vibrations\": \"#8BDA9F\",\n            \/\/ Grupy azotowe (intensywno\u015b\u0107 malej\u0105co)\n            \"Amide Vibrations\": \"#69298E\",\n            \"N-H Vibrations\": \"#8234AF\",\n            \"C-N Vibrations\": \"#9847C7\",\n            \"Cumulated Double Bonds\": \"#AB69D1\",\n            \"Nitro Vibrations\": \"#BD8BDA\",\n            \/\/ Pier\u015bcienie aromatyczne (intensywno\u015b\u0107 malej\u0105co)\n            \"Aromatic Ring Vibrations\": \"#E2A412\",\n            \/\/ Siarka i chlorowce (intensywno\u015b\u0107 malej\u0105co)\n            \"Sulfur Vibrations\": \"#9C1C5C\",\n            \"Halogen Vibrations\": \"#DA8BB2\",\n            \/\/ Jony nieorganiczne (intensywno\u015b\u0107 malej\u0105co)\n            \"Inorganic Ions\": \"#A12017\",\n            \"Acid\/Base Vibrations\": \"#CA3227\",\n            \"Pseudohalide Ions\": \"#D45F57\",\n            \"Transition Metal Oxoanions\": \"#DA908B\",\n            \/\/ Tlenki i krzemiany (intensywno\u015b\u0107 malej\u0105co)\n            \"Silicate\/Oxide Vibrations\": \"#825935\",\n            \"Metal Oxides\": \"#CBB19A\",\n            \/\/ Pierwiastki grup g\u0142\u00f3wnych (intensywno\u015b\u0107 malej\u0105co)\n            \"Boron Compounds\": \"#258A93\",\n            \"Fluorine Compounds\": \"#44BFCA\",\n            \"Chalcogen Compounds\": \"#8BD4DA\",\n            \/\/ Kompleksy koordynacyjne (intensywno\u015b\u0107 malej\u0105co)\n            \"Coordination Complexes\": \"#5C3DB8\"\n        };\n\n        const groupPL = {\n            \"C-H Vibrations (Aliphatic)\": \"Drgania C\u2013H (alifatyczne)\",\n            \"C-H Vibrations (Aromatic)\": \"Drgania C\u2013H (aromatyczne)\",\n            \"C=C Vibrations\": \"Drgania C=C\",\n            \"C\u2261C Vibrations\": \"Drgania C\u2261C\",\n            \"Ring & Skeletal Vibrations\": \"Drgania szkieletu i pier\u015bcieni alicyklicznych\",\n            \"C=O Vibrations\": \"Drgania C=O\",\n            \"Carboxylic Acids\": \"Kwasy karboksylowe\",\n            \"Amino Acids\": \"Aminokwasy\",\n            \"Esters and Derivatives\": \"Estry i pochodne\",\n            \"C-O Vibrations\": \"Drgania C\u2013O\",\n            \"O-H Vibrations\": \"Drgania O\u2013H\",\n            \"Amide Vibrations\": \"Drgania amidowe\",\n            \"Aromatic Ring Vibrations\": \"Drgania pier\u015bcienia aromatycznego\",\n            \"N-H Vibrations\": \"Drgania N\u2013H\",\n            \"C-N Vibrations\": \"Drgania C\u2013N \/ C=N \/ C\u2261N\",\n            \"Cumulated Double Bonds\": \"Skumulowane wi\u0105zania podw\u00f3jne\",\n            \"Nitro Vibrations\": \"Drgania N\u2013O (nitro, azotyny, azotany)\",\n            \"Sulfur Vibrations\": \"Drgania siarkowe\",\n            \"Halogen Vibrations\": \"Drgania z chlorowcami\",\n            \"Inorganic Ions\": \"Jony nieorganiczne\",\n            \"Silicate\/Oxide Vibrations\": \"Krzemiany \/ drgania Si\u2013O\",\n            \"Acid\/Base Vibrations\": \"Jony kwasowo-zasadowe\",\n            \"Metal Oxides\": \"Tlenki metali\",\n            \"Pseudohalide Ions\": \"Jony pseudohalogenkowe (CN\u207b, SCN\u207b, OCN\u207b, N\u2083\u207b)\",\n            \"Transition Metal Oxoanions\": \"Oksoaniony metali przej\u015bciowych\",\n            \"Boron Compounds\": \"Zwi\u0105zki boru\",\n            \"Fluorine Compounds\": \"Zwi\u0105zki fluoru\",\n            \"Chalcogen Compounds\": \"Zwi\u0105zki chalkogen\u00f3w\",\n            \"Coordination Complexes\": \"Wi\u0105zania koordynacyjne (kompleksy)\"\n        };\n\n            let minWavenumber = 0;\n            let maxWavenumber = 4000;\n            const apiKey = \"\"; \/* AI wy\u0142\u0105czone \u2014 faza 2 przez backend *\/ \/\/ Wklej tutaj sw\u00f3j klucz API Gemini. UWAGA: poprzedni klucz usuni\u0119to ze wzgl\u0119d\u00f3w bezpiecze\u0144stwa \u2014 nale\u017cy go uniewa\u017cni\u0107 w konsoli Google.\n            const chart = document.getElementById('chart');\n            const tooltip = document.getElementById('tooltip');\n            const xAxis = document.getElementById('x-axis');\n            const topXAxis = document.getElementById('top-x-axis');\n            const gridLines = document.getElementById('grid-lines');\n            const interpretButton = document.getElementById('interpret-button');\n            const peakInput = document.getElementById('peak-input');\n            const geminiResult = document.getElementById('gemini-result');\n            const exploreButton = document.getElementById('explore-app-button');\n            const compoundNameInput = document.getElementById('compound-name');\n            const functionalGroupInput = document.getElementById('functional-group');\n            const compoundGroupInput = document.getElementById('compound-group');\n            const ramanAppResult = document.getElementById('raman-app-result');\n\n            let shownGroups = new Set(); \/\/ domy\u015blnie pusto => wykres pusty\n            let activeIntensities = new Set(['strong', 'medium', 'weak']);\n\n            function allGroups() {\n                const seen = [];\n                ramanData.forEach(d => { if (!seen.includes(d.group)) seen.push(d.group); });\n                return seen;\n            }\n\n            function buildLegend() {\n                const legend = document.getElementById('legend');\n                if (!legend) return;\n                const macro = [\n                    [\"W\u0119glowodory i szkielet C\u2013H\", [\"C-H Vibrations (Aliphatic)\", \"C-H Vibrations (Aromatic)\", \"C=C Vibrations\", \"C\u2261C Vibrations\", \"Ring & Skeletal Vibrations\"]],\n                    [\"Grupy tlenowe\", [\"C=O Vibrations\", \"Carboxylic Acids\", \"Amino Acids\", \"Esters and Derivatives\", \"C-O Vibrations\", \"O-H Vibrations\"]],\n                    [\"Grupy azotowe\", [\"Amide Vibrations\", \"N-H Vibrations\", \"C-N Vibrations\", \"Cumulated Double Bonds\", \"Nitro Vibrations\"]],\n                    [\"Pier\u015bcienie aromatyczne\", [\"Aromatic Ring Vibrations\"]],\n                    [\"Siarka i chlorowce\", [\"Sulfur Vibrations\", \"Halogen Vibrations\"]],\n                    [\"Jony nieorganiczne\", [\"Inorganic Ions\", \"Acid\/Base Vibrations\", \"Pseudohalide Ions\", \"Transition Metal Oxoanions\"]],\n                    [\"Tlenki i krzemiany\", [\"Silicate\/Oxide Vibrations\", \"Metal Oxides\"]],\n                    [\"Pierwiastki grup g\u0142\u00f3wnych\", [\"Boron Compounds\", \"Fluorine Compounds\", \"Chalcogen Compounds\"]],\n                    [\"Kompleksy koordynacyjne\", [\"Coordination Complexes\"]]\n                ];\n                const present = new Set(ramanData.map(d => d.group));\n                let html = '<div class=\"legend-title\">Grupy drga\u0144 \u2014 kliknij, aby pokaza\u0107 na wykresie<\/div>';\n                macro.forEach(([cat, groups]) => {\n                    const gs = groups.filter(g => present.has(g));\n                    if (gs.length === 0) return;\n                    html += '<div class=\"legend-cat\"><span class=\"legend-cat-name\">' + cat + '<\/span><div class=\"legend-groups\">';\n                    gs.forEach(g => {\n                        html += '<span class=\"legend-item toggle\" data-group=\"' + g + '\"><span class=\"legend-swatch\" style=\"background:' + colors[g] + '\"><\/span>' + (groupPL[g] || g) + '<\/span>';\n                    });\n                    html += '<\/div><\/div>';\n                });\n                html += '<div class=\"legend-title\">Intensywno\u015b\u0107 w Ramanie (wysoko\u015b\u0107 paska)<\/div><div class=\"legend-intensity\">'\n                     + '<span class=\"legend-item\"><span class=\"ikey ikey-strong\"><\/span>Silna<\/span>'\n                     + '<span class=\"legend-item\"><span class=\"ikey ikey-medium\"><\/span>\u015arednia<\/span>'\n                     + '<span class=\"legend-item\"><span class=\"ikey ikey-weak\"><\/span>S\u0142aba<\/span>'\n                     + '<\/div>';\n                legend.innerHTML = html;\n            }\n\n            \/\/ Normalizacja tekstu do wyszukiwania: ma\u0142e litery + indeksy dolne\/g\u00f3rne -> zwyk\u0142e cyfry, usuni\u0119cie \u0142adunk\u00f3w\n            function normalize(s) {\n                s = (s || '').toLowerCase();\n                const sub = '\u2080\u2081\u2082\u2083\u2084\u2085\u2086\u2087\u2088\u2089', sup = '\u2070\u00b9\u00b2\u00b3\u2074\u2075\u2076\u2077\u2078\u2079';\n                for (let i = 0; i < 10; i++) { s = s.split(sub[i]).join(i).split(sup[i]).join(i); }\n                const dia = { '\u0105':'a','\u0107':'c','\u0119':'e','\u0142':'l','\u0144':'n','\u00f3':'o','\u015b':'s','\u017a':'z','\u017c':'z' };\n                s = s.replace(\/[\u0105\u0107\u0119\u0142\u0144\u00f3\u015b\u017a\u017c]\/g, ch => dia[ch]);\n                return s.replace(\/[\u207b\u207a]\/g, '');\n            }\n\n            function buildChart() {\n                chart.innerHTML = '';\n                const ionGroups = new Set(['Inorganic Ions', 'Pseudohalide Ions', 'Transition Metal Oxoanions', 'Acid\/Base Vibrations', 'Silicate\/Oxide Vibrations', 'Metal Oxides', 'Boron Compounds', 'Fluorine Compounds', 'Chalcogen Compounds']);\n                const grouped = ramanData.reduce((acc, item) => { (acc[item.group] = acc[item.group] || []).push(item); return acc; }, {});\n\n                const makeBar = (item) => {\n                    const [start, end] = item.range[0] > item.range[1] ? [item.range[1], item.range[0]] : item.range;\n                    if (end < minWavenumber || start > maxWavenumber) return null;\n                    const cS = Math.max(start, minWavenumber), cE = Math.min(end, maxWavenumber);\n                    const width = ((cE - cS) \/ (maxWavenumber - minWavenumber)) * 100;\n                    const left = ((cS - minWavenumber) \/ (maxWavenumber - minWavenumber)) * 100;\n                    const isPoint = (end - start) === 0;\n                    const bar = document.createElement('div');\n                    bar.className = `bar intensity-${item.intensity}` + (isPoint ? ' point' : '');\n                    bar.style.left = `${left}%`;\n                    bar.style.width = `${width}%`;\n                    bar.style.backgroundColor = colors[item.group];\n                    bar.style.color = '#000000';\n                    bar.dataset.group = item.group;\n                    bar.dataset.intensity = item.intensity;\n                    bar.dataset.mode = normalize(item.mode + ' ' + (groupPL[item.group] || item.group));\n                    bar.dataset.min = start;\n                    bar.dataset.max = end;\n                    bar.textContent = '';\n                    bar.addEventListener('mouseenter', (e) => handleTooltipShow(e, item));\n                    bar.addEventListener('mouseleave', handleTooltipHide);\n                    return bar;\n                };\n\n                \/\/ Klucz rodzaju wi\u0105zania\/fragmentu dla cz\u0119\u015bci organicznej (etykieta po lewej, jak wz\u00f3r jonu)\n                const bondPats = ['M\u2013C\u2261O','M\u2013C\u2261N','M\u2013CO','M\u2013NCS','M\u2013SCN','M\u2013NH\u2083','M\u2013NO\u2082','M\u2013ONO','M\u2013O','M\u2013N','M\u2013C',\n                    '\u2261C-H','=CH\u2082','=CH','C=C=C','C=C','N=C=O','N=C=S','C\u2261C','C\u2261N','C=N','C=S','C=O',\n                    'COO\u207b','C-O-C','C-Cl','C-Br','C-I','C-S','C-O','C-N','CNC',\n                    'S-S','S=O','SO\u2082','S-H','SH','NH\u2082','N-H','NH','N=O','NO\u2082',\n                    'CH\u2083','CH\u2082','C-H','CC','CH','O-H','OH','Amid'];\n                const bondKey = (mode) => {\n                    for (const pat of bondPats) if (mode.includes(pat)) return pat;\n                    if (\/pier\u015bcien|aromat|benzen|cyklo|furan|furfuryl|tiofen|pirydyn|naftalen|antracen|bifenyl|epoksyd\/i.test(mode)) return 'pier\u015bcie\u0144';\n                    if (\/szkielet|izopropyl|tert-butyl\/i.test(mode)) return 'szkielet';\n                    return mode.split(' ')[0];\n                };\n\n                \/\/ Pakowanie w tory: paski nak\u0142adaj\u0105ce si\u0119 trafiaj\u0105 do kolejnego wiersza\n                const span = (maxWavenumber - minWavenumber) || 1;\n                const gap = span * 0.015;\n                const packLanes = (arr) => {\n                    const wr = arr.map(it => { let [s, e] = it.range[0] > it.range[1] ? [it.range[1], it.range[0]] : it.range; return { it, s: Math.max(s, minWavenumber), e: Math.min(e, maxWavenumber) }; })\n                        .filter(x => x.e >= minWavenumber && x.s <= maxWavenumber)\n                        .sort((a, b) => (a.s - b.s) || (a.e - b.e));\n                    const lanes = [];\n                    wr.forEach(x => {\n                        let placed = false;\n                        for (const lane of lanes) { if (x.s >= lane.end + gap) { lane.items.push(x.it); lane.end = x.e; placed = true; break; } }\n                        if (!placed) lanes.push({ end: x.e, items: [x.it] });\n                    });\n                    return lanes.map(l => l.items);\n                };\n\n                Object.entries(grouped).forEach(([groupName, items]) => {\n                    const gc = document.createElement('div');\n                    gc.className = 'group-container';\n                    const title = document.createElement('div');\n                    title.className = 'group-title';\n                    title.textContent = groupPL[groupName] || groupName;\n                    title.style.borderColor = colors[groupName];\n                    gc.appendChild(title);\n\n                    \/\/ scalanie pasm tego samego jonu \/ rodzaju wi\u0105zania w jeden wiersz + etykieta po lewej\n                    const keyFn = ionGroups.has(groupName) ? (it => it.mode.split(' ')[0]) : (it => bondKey(it.mode));\n                    const byKey = {}, order = [];\n                    items.forEach(it => { const k = keyFn(it); if (!byKey[k]) { byKey[k] = []; order.push(k); } byKey[k].push(it); });\n                    order.forEach(k => {\n                        const lanes = packLanes(byKey[k]);\n                        lanes.forEach((laneItems, li) => {\n                            const bars = laneItems.map(makeBar).filter(Boolean);\n                            if (!bars.length) return;\n                            const w = document.createElement('div');\n                            w.className = 'bar-wrapper';\n                            const lab = document.createElement('span');\n                            lab.className = 'ion-label';\n                            lab.textContent = (li === 0) ? k : '';\n                            w.appendChild(lab);\n                            bars.forEach(b => w.appendChild(b));\n                            gc.appendChild(w);\n                        });\n                    });\n                    if (gc.querySelectorAll('.bar-wrapper').length > 0) chart.appendChild(gc);\n                });\n                const guide = document.createElement('div');\n                guide.id = 'guide-lines';\n                chart.appendChild(guide);\n                const hint = document.createElement('div');\n                hint.id = 'empty-hint';\n                hint.innerHTML = 'Wybierz grup\u0119 drga\u0144 z legendy powy\u017cej, aby pokaza\u0107 jej pasma na wykresie.<br>Mo\u017cesz te\u017c wpisa\u0107 liczb\u0119 falow\u0105 (np. 1005) w wyszukiwarce, aby zaznaczy\u0107 j\u0105 pionow\u0105 lini\u0105.';\n                chart.appendChild(hint);\n            }\n            function parseQueryNumbers(q) {\n                const m = q.match(\/\\d+(?:\\.\\d+)?\/g);\n                return m ? m.map(Number).filter(n => n >= 0 && n <= 4000) : [];\n            }\n\n            function drawGuides(nums) {\n                const g = document.getElementById('guide-lines');\n                if (!g) return;\n                g.innerHTML = '';\n                nums.forEach(v => {\n                    if (v < minWavenumber || v > maxWavenumber) return;\n                    const l = ((v - minWavenumber) \/ (maxWavenumber - minWavenumber)) * 100;\n                    const line = document.createElement('div');\n                    line.className = 'guide-line'; line.style.left = l + '%';\n                    const lab = document.createElement('div');\n                    lab.className = 'guide-label'; lab.style.left = l + '%'; lab.textContent = v + ' cm\u207b\u00b9';\n                    g.appendChild(line); g.appendChild(lab);\n                });\n            }\n\n            function applyFilters() {\n                const input = document.getElementById('search-input');\n                const q = (input ? input.value : '').trim();\n                const numericQuery = q !== '' && \/^[\\d\\s.,;]+$\/.test(q);\n                const nums = numericQuery ? parseQueryNumbers(q) : [];\n                const isNum = nums.length > 0;\n                const text = (!isNum && q) ? normalize(q) : '';\n                const searchActive = isNum || !!text;\n                drawGuides(isNum ? nums : []);\n                let matches = 0, visibleGroups = 0;\n                document.querySelectorAll('.group-container').forEach(gc => {\n                    let groupVisible = false;\n                    gc.querySelectorAll('.bar-wrapper').forEach(w => {\n                        let rowVisible = false;\n                        w.querySelectorAll('.bar').forEach(bar => {\n                            let isMatch = false;\n                            if (isNum) isMatch = nums.some(v => v >= +bar.dataset.min && v <= +bar.dataset.max);\n                            else if (text) isMatch = bar.dataset.mode.includes(text) || bar.dataset.group.toLowerCase().includes(text);\n                            const structural = activeIntensities.has(bar.dataset.intensity)\n                                && (searchActive ? (isMatch || shownGroups.has(bar.dataset.group)) : shownGroups.has(bar.dataset.group));\n                            bar.style.display = structural ? '' : 'none';\n                            bar.classList.remove('dim', 'hit');\n                            if (structural) {\n                                rowVisible = true;\n                                if (searchActive) { bar.classList.add(isMatch ? 'hit' : 'dim'); if (isMatch) matches++; }\n                            }\n                        });\n                        w.style.display = rowVisible ? '' : 'none';\n                        if (rowVisible) groupVisible = true;\n                    });\n                    gc.style.display = groupVisible ? '' : 'none';\n                    if (groupVisible) visibleGroups++;\n                });\n                const hint = document.getElementById('empty-hint');\n                if (hint) hint.style.display = (visibleGroups === 0) ? 'block' : 'none';\n                const mc = document.getElementById('match-count');\n                if (mc) mc.textContent = (isNum || text) ? (matches + ' pasm pasuje') : '';\n            }\n            function showPeaksFromInterpreter(opts) {\n                opts = opts || {};\n                const pi = document.getElementById('peak-input');\n                const si = document.getElementById('search-input');\n                if (!si) return false;\n                \/\/ Wsp\u00f3lne \u017ar\u00f3d\u0142o: pole pik\u00f3w interpretera, a je\u015bli puste \u2014 pole wyszukiwarki.\n                const source = ((pi && pi.value.trim()) || si.value.trim());\n                const nums = parseQueryNumbers(source);\n                if (nums.length === 0) {\n                    if (opts.notify) { geminiResult.classList.remove('has-answer'); geminiResult.innerHTML = '<p class=\"placeholder\">Nie znaleziono liczb falowych do pokazania \u2014 wpisz np. 1005 albo 2980, 1660.<\/p>'; }\n                    return false;\n                }\n                si.value = source;\n                applyFilters();\n                const cc = document.getElementById('chart-container');\n                if (cc) cc.scrollIntoView({ behavior: 'smooth', block: 'start' });\n                return true;\n            }\n\n            function setView(min, max, activeId) {\n                minWavenumber = min;\n                maxWavenumber = max;\n                buildChart();\n                buildXAxis();\n                applyFilters();\n                document.querySelectorAll('#zoom-controls .control-btn').forEach(b => b.classList.remove('active'));\n                const a = document.getElementById(activeId);\n                if (a) a.classList.add('active');\n            }\n\n            function setupControls() {\n                const input = document.getElementById('search-input');\n                if (input) input.addEventListener('input', applyFilters);\n                const clr = document.getElementById('search-clear');\n                if (clr) clr.addEventListener('click', () => { if (input) input.value = ''; const pk = document.getElementById('peak-input'); if (pk) pk.value = ''; applyFilters(); });\n                const pk = document.getElementById('peak-input');\n                if (pk) pk.addEventListener('input', () => {\n                    const si = document.getElementById('search-input');\n                    if (si) { si.value = pk.value; applyFilters(); }\n                });\n                document.querySelectorAll('#intensity-filter input[type=checkbox]').forEach(cb => {\n                    cb.addEventListener('change', () => {\n                        activeIntensities = new Set();\n                        document.querySelectorAll('#intensity-filter input[type=checkbox]').forEach(x => { if (x.checked) activeIntensities.add(x.dataset.int); });\n                        applyFilters();\n                    });\n                });\n                document.querySelectorAll('.legend-item.toggle').forEach(el => {\n                    el.addEventListener('click', () => {\n                        const g = el.dataset.group;\n                        if (shownGroups.has(g)) { shownGroups.delete(g); el.classList.remove('selected'); }\n                        else { shownGroups.add(g); el.classList.add('selected'); }\n                        applyFilters();\n                    });\n                });\n                const all = document.getElementById('grp-all');\n                if (all) all.addEventListener('click', () => {\n                    shownGroups = new Set(allGroups());\n                    document.querySelectorAll('.legend-item.toggle').forEach(e => e.classList.add('selected'));\n                    applyFilters();\n                });\n                const none = document.getElementById('grp-none');\n                if (none) none.addEventListener('click', () => {\n                    shownGroups.clear();\n                    document.querySelectorAll('.legend-item.toggle').forEach(e => e.classList.remove('selected'));\n                    applyFilters();\n                });\n                const reset = document.getElementById('reset-all');\n                if (reset) reset.addEventListener('click', () => {\n                    shownGroups.clear();\n                    document.querySelectorAll('.legend-item.toggle').forEach(e => e.classList.remove('selected'));\n                    document.querySelectorAll('#intensity-filter input[type=checkbox]').forEach(x => { x.checked = true; });\n                    activeIntensities = new Set(['strong', 'medium', 'weak']);\n                    if (input) input.value = '';\n                    const pk = document.getElementById('peak-input'); if (pk) pk.value = '';\n                    setView(0, 4000, 'zoom-full');\n                });\n                const zf = document.getElementById('zoom-full');\n                if (zf) zf.addEventListener('click', () => setView(0, 4000, 'zoom-full'));\n                const zp = document.getElementById('zoom-fp');\n                if (zp) zp.addEventListener('click', () => setView(400, 1800, 'zoom-fp'));\n            }\n\n            function buildXAxis() {\n                xAxis.innerHTML = '';\n                topXAxis.innerHTML = '';\n                gridLines.innerHTML = '';\n\n                const axisStep = 100;\n                const majorStep = 200;\n                const W = chart.offsetWidth || 1200;\n                const H = 12;\n                let bottomRects = '';\n                let topRects = '';\n\n                for (let i = minWavenumber; i <= maxWavenumber; i += axisStep) {\n                    const perc = ((i - minWavenumber) \/ (maxWavenumber - minWavenumber)) * 100;\n                    const x = Math.round((perc \/ 100) * W);\n                    const isMajor = (i % majorStep === 0 || i === minWavenumber || i === maxWavenumber);\n                    const w = isMajor ? 2 : 1;\n                    const h = isMajor ? 12 : 6;\n                    const color = isMajor ? '#343a40' : '#adb5bd';\n                    const rx = x - Math.floor(w \/ 2);\n                    \/\/ Kreski jako prostok\u0105ty SVG (crispEdges) => jednolita, ostra grubo\u015b\u0107 przy ka\u017cdym DPR\n                    bottomRects += `<rect x=\"${rx}\" y=\"0\" width=\"${w}\" height=\"${h}\" fill=\"${color}\"\/>`;\n                    topRects += `<rect x=\"${rx}\" y=\"${H - h}\" width=\"${w}\" height=\"${h}\" fill=\"${color}\"\/>`;\n\n                    if (isMajor) {\n                        const lb = document.createElement('span');\n                        lb.className = 'tick-label';\n                        lb.style.left = `${x}px`;\n                        lb.textContent = i;\n                        xAxis.appendChild(lb);\n\n                        const lt = document.createElement('span');\n                        lt.className = 'top-tick-label';\n                        lt.style.left = `${x}px`;\n                        lt.textContent = i;\n                        topXAxis.appendChild(lt);\n                    }\n\n                    const gl = document.createElement('div');\n                    gl.className = 'grid-line-vertical';\n                    gl.style.left = `${x}px`;\n                    gridLines.appendChild(gl);\n                }\n\n                const svgBottom = `<svg class=\"tick-svg\" width=\"${W}\" height=\"${H}\" viewBox=\"0 0 ${W} ${H}\" shape-rendering=\"crispEdges\" preserveAspectRatio=\"none\" style=\"position:absolute;left:0;top:0;pointer-events:none;\">${bottomRects}<\/svg>`;\n                const svgTop = `<svg class=\"tick-svg\" width=\"${W}\" height=\"${H}\" viewBox=\"0 0 ${W} ${H}\" shape-rendering=\"crispEdges\" preserveAspectRatio=\"none\" style=\"position:absolute;left:0;bottom:0;pointer-events:none;\">${topRects}<\/svg>`;\n                xAxis.insertAdjacentHTML('afterbegin', svgBottom);\n                topXAxis.insertAdjacentHTML('afterbegin', svgTop);\n            }\n\nwindow.addEventListener('resize', buildXAxis);\n\n            \/\/ Szkic fragmentu molekularnego w dymku. Dla przypadk\u00f3w specyficznych\n            \/\/ rysunek odwzorowuje struktur\u0119 (np. furfuryl = pier\u015bcie\u0144 furanowy z -CH\u2082,\n            \/\/ aromatyczne C-H = pier\u015bcie\u0144 z wi\u0105zaniem C-H, para-podstawiony benzen = 2 podstawniki).\n            function fragmentFor(item) {\n                const md = item.mode;\n                const m = (item.mode + ' ' + item.group).toLowerCase();\n                const S = '<style>.a{font:600 13px Manrope,Arial,sans-serif;fill:#192A3D}.b{stroke:#192A3D;stroke-width:2;stroke-linecap:round}<\/style>';\n                const wrap = (inner) => '<svg class=\"frag\" viewBox=\"0 0 190 88\" width=\"190\" height=\"88\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">' + S + inner + '<\/svg>';\n                const at = (x, txt, y) => '<text class=\"a\" x=\"' + x + '\" y=\"' + (y || 47) + '\" text-anchor=\"middle\">' + txt + '<\/text>';\n                const seg = (x1, y1, x2, y2) => '<line class=\"b\" x1=\"' + x1 + '\" y1=\"' + y1 + '\" x2=\"' + x2 + '\" y2=\"' + y2 + '\"\/>';\n                const b1 = (a, z) => seg(a, 42, z, 42);\n                const b2 = (a, z) => seg(a, 38, z, 38) + seg(a, 46, z, 46);\n                const b3 = (a, z) => seg(a, 34, z, 34) + seg(a, 42, z, 42) + seg(a, 50, z, 50);\n                const link2 = (A, B, o) => wrap(at(58, A) + (o === 3 ? b3 : o === 2 ? b2 : b1)(74, 122) + at(140, B));\n                const link3 = (A, B, C, o1, o2) => wrap(at(24, A) + (o1 === 3 ? b3 : o1 === 2 ? b2 : b1)(38, 66) + at(90, B) + (o2 === 3 ? b3 : o2 === 2 ? b2 : b1)(114, 142) + at(156, C));\n                \/\/ domy\u015blny szkielet: R\u2013A(wi\u0105zanie)B, aby by\u0142 widoczny fragment wp\u0142ywaj\u0105cy na drganie\n                const link = (A, B, o) => link3('R', A, B, 1, o);\n                const carbonyl = () => wrap(at(18, 'R') + b1(32, 48) + at(62, 'C') + seg(58, 38, 58, 20) + seg(66, 38, 66, 20) + at(62, 'O', 15) + b1(76, 104) + at(120, 'R'));\n                const aldehyde = () => wrap(at(18, 'R') + b1(32, 48) + at(62, 'C') + seg(58, 38, 58, 20) + seg(66, 38, 66, 20) + at(62, 'O', 15) + b1(76, 104) + at(120, 'H'));\n                const linkRR = (A, B, o) => wrap(at(20, 'R') + b1(32, 44) + at(58, A) + (o === 3 ? b3 : o === 2 ? b2 : b1)(72, 108) + at(122, B) + b1(136, 148) + at(164, 'R'));\n                const sulfoxide = () => wrap(at(18, 'R') + b1(32, 48) + at(62, 'S') + seg(58, 38, 58, 20) + seg(66, 38, 66, 20) + at(62, 'O', 15) + b1(76, 104) + at(120, 'R'));\n                const label = (txt) => wrap('<rect class=\"b\" x=\"20\" y=\"24\" width=\"150\" height=\"36\" rx=\"9\" fill=\"none\"\/><text class=\"a\" x=\"95\" y=\"47\" text-anchor=\"middle\" style=\"font-size:16px\">' + txt + '<\/text>');\n                const methyl = () => wrap(at(56, 'H\u2083C') + b1(88, 122) + at(140, 'R'));\n                const methylene = () => wrap(at(28, 'R') + b1(44, 66) + at(96, 'CH\u2082') + b1(126, 148) + at(164, 'R'));\n                \/\/ grupa izopropylowa (CH\u2083)\u2082CH\u2013R oraz tert-butylowa C(CH\u2083)\u2083\u2013R \u2014 rozga\u0142\u0119ziony w\u0119giel\n                const isopropyl = () => wrap(at(92, 'C') + seg(82, 40, 56, 24) + at(42, 'CH\u2083', 26) + seg(82, 48, 56, 64) + at(42, 'CH\u2083', 68) + seg(92, 38, 92, 20) + at(92, 'H', 16) + b1(104, 136) + at(152, 'R'));\n                const tertbutyl = () => wrap(at(90, 'C') + seg(80, 40, 54, 24) + at(40, 'CH\u2083', 26) + seg(80, 48, 54, 64) + at(40, 'CH\u2083', 68) + seg(90, 38, 90, 20) + at(90, 'CH\u2083', 15) + b1(104, 136) + at(152, 'R'));\n                \/\/ --- budowniki szczeg\u00f3\u0142owe (konkretne mody) ---\n                const dash = (x1, y1, x2, y2) => '<line class=\"b\" stroke-dasharray=\"3,3\" x1=\"' + x1 + '\" y1=\"' + y1 + '\" x2=\"' + x2 + '\" y2=\"' + y2 + '\"\/>';\n                const co = (L, Rr) => at(16, L) + b1(30, 46) + at(60, 'C') + seg(56, 38, 56, 22) + seg(64, 38, 64, 22) + at(60, 'O', 17) + b1(74, 100) + at(116, Rr);\n                const carbonylR = () => wrap(co('R', 'R'));\n                const aldehydeCO = () => wrap(co('R', 'H'));\n                const acidMono = () => wrap(co('R', 'O') + b1(130, 150) + at(166, 'H'));\n                const acidAryl = () => wrap(co('Ar', 'O') + b1(130, 150) + at(166, 'H'));\n                const acylHal = () => wrap(co('R', 'Cl'));\n                const haloAld = () => wrap(co('X', 'H'));\n                const carboxylate = () => wrap(at(22, 'R') + b1(34, 50) + at(66, 'C') + seg(62, 40, 62, 22) + seg(70, 40, 70, 22) + at(66, 'O', 18) + seg(66, 52, 66, 68) + at(66, 'O\u207b', 82));\n                const anhydride = () => wrap(at(10, 'R') + b1(20, 34) + at(46, 'C') + seg(42, 38, 42, 22) + seg(50, 38, 50, 22) + at(46, 'O', 18) + b1(58, 74) + at(92, 'O') + b1(106, 122) + at(140, 'C') + seg(136, 38, 136, 22) + seg(144, 38, 144, 22) + at(140, 'O', 18) + b1(152, 166) + at(182, 'R'));\n                const acidDimer = () => wrap(at(84, 'R', 12) + seg(84, 16, 84, 24) + at(84, 'C', 30) + seg(78, 26, 62, 26) + seg(78, 34, 62, 34) + at(54, 'O', 30) + b1(96, 112) + at(124, 'O', 30) + b1(134, 148) + at(160, 'H', 30) + at(106, 'R', 80) + seg(106, 76, 106, 68) + at(106, 'C', 62) + seg(112, 58, 128, 58) + seg(112, 66, 128, 66) + at(136, 'O', 62) + b1(94, 78) + at(66, 'O', 62) + b1(56, 42) + at(30, 'H', 62) + dash(54, 36, 40, 56) + dash(160, 36, 138, 56));\n                const oxime = (top) => wrap(at(18, 'R') + b1(30, 44) + at(58, 'C') + seg(58, 38, 58, 20) + at(58, top, 16) + b2(72, 96) + at(116, 'N') + b1(130, 150) + at(172, 'OH'));\n                const hydrazone = () => wrap(at(16, 'R') + b1(28, 42) + at(56, 'C') + b2(70, 92) + at(112, 'N') + b1(126, 146) + at(172, 'NH\u2082'));\n                const thioamide = () => wrap(at(16, 'R') + b1(30, 46) + at(60, 'C') + seg(56, 38, 56, 20) + seg(64, 38, 64, 20) + at(60, 'S', 15) + b1(74, 100) + at(116, 'N') + seg(126, 42, 148, 28) + at(158, 'H', 30) + seg(126, 50, 148, 66) + at(158, 'H', 68));\n                const alkyne = (rr) => wrap(at(20, 'R') + b1(32, 44) + at(58, 'C') + b3(72, 108) + at(122, 'C') + b1(136, 148) + at(164, rr));\n                const ester = () => wrap(co('R', 'O') + b1(130, 150) + at(166, 'R'));\n                const thioester = () => wrap(co('R', 'S') + b1(130, 150) + at(166, 'R'));\n                const azine = () => wrap(at(12, 'C') + b2(26, 46) + at(64, 'N') + b1(78, 100) + at(120, 'N') + b2(134, 154) + at(172, 'C'));\n                const isonitrile = () => link3('R', 'N', 'C', 1, 3);\n                const thiocyanateCov = () => wrap(at(14, 'R') + b1(26, 42) + at(56, 'S') + b1(70, 86) + at(104, 'C') + b3(118, 138) + at(158, 'N'));\n                const nitrateEster = () => wrap(at(12, 'R') + b1(24, 40) + at(54, 'O') + b1(68, 84) + at(104, 'N') + seg(114, 40, 136, 24) + seg(120, 40, 142, 24) + at(150, 'O', 22) + seg(110, 50, 132, 66) + at(146, 'O', 70));\n                const nitriteEster = () => wrap(at(16, 'R') + b1(28, 44) + at(60, 'O') + b1(74, 92) + at(112, 'N') + b2(126, 146) + at(166, 'O'));\n                const sulf = (L, Rr) => wrap(at(95, 'S') + seg(88, 40, 66, 24) + seg(94, 40, 72, 24) + at(58, 'O', 22) + seg(102, 40, 124, 24) + seg(96, 40, 118, 24) + at(132, 'O', 22) + seg(88, 52, 62, 68) + at(50, L, 72) + seg(102, 52, 128, 68) + at(140, Rr, 72));\n                const oxalate = () => wrap(at(12, '\u207bO') + b1(28, 42) + at(58, 'C') + seg(54, 38, 54, 22) + seg(62, 38, 62, 22) + at(58, 'O', 18) + b1(72, 100) + at(116, 'C') + seg(112, 38, 112, 22) + seg(120, 38, 120, 22) + at(116, 'O', 18) + b1(130, 146) + at(168, 'O\u207b'));\n                const center4 = (mid, subs) => { const cx = 95, cy = 46, P = [[52, 22], [138, 22], [52, 72], [138, 72]]; let s = at(cx, mid); for (let i = 0; i < 4; i++) { const q = P[i], dx = q[0] - cx, dy = q[1] - cy, L = Math.hypot(dx, dy), ux = dx \/ L, uy = dy \/ L; s += seg(cx + ux * 13, cy + uy * 13, q[0] - ux * 12, q[1] - uy * 12) + at(q[0], subs[i], q[1] + 4); } return s; };\n                const ammonium = () => wrap(center4('N', ['H', 'H', 'H', 'H']) + at(118, '\u207a', 28));\n                const silica = () => wrap(center4('Si', ['O', 'O', 'O', 'O']));\n                const bicarbonate = () => wrap(at(12, 'H') + b1(22, 36) + at(52, 'O') + b1(66, 82) + at(104, 'C') + seg(100, 38, 100, 22) + seg(108, 38, 108, 22) + at(104, 'O', 18) + b1(118, 140) + at(166, 'O\u207b'));\n                const benzeneAlkil = () => { const V = poly(CX, CY, 22, 6, 90); return wrap(ppath(V) + '<circle class=\"b\" fill=\"none\" cx=\"' + CX + '\" cy=\"' + CY + '\" r=\"12\"\/>' + substAt(CX, CY, V[5], 'H') + substAt(CX, CY, V[2], 'R')); };\n\n                const vinyl = () => wrap(at(52, 'H\u2082C') + b2(78, 116) + at(142, 'CHR'));\n                \/\/ budowniki pier\u015bcieni\n                const CX = 58, CY = 44;\n                const poly = (cx, cy, r, n, rot) => { const p = []; for (let k = 0; k < n; k++) { const a = (rot + k * (360 \/ n)) * Math.PI \/ 180; p.push([cx + r * Math.cos(a), cy - r * Math.sin(a)]); } return p; };\n                const ppath = (pts) => '<polygon class=\"b\" fill=\"none\" points=\"' + pts.map(p => p[0].toFixed(1) + ',' + p[1].toFixed(1)).join(' ') + '\"\/>';\n                const dotAt = (p) => '<circle cx=\"' + p[0].toFixed(1) + '\" cy=\"' + p[1].toFixed(1) + '\" r=\"3.4\" fill=\"#192A3D\"\/>';\n                const heteroAt = (p, txt) => '<circle cx=\"' + p[0].toFixed(1) + '\" cy=\"' + p[1].toFixed(1) + '\" r=\"9\" fill=\"#fff\"\/><text class=\"a\" x=\"' + p[0].toFixed(1) + '\" y=\"' + (p[1] + 5).toFixed(1) + '\" text-anchor=\"middle\">' + txt + '<\/text>';\n                const substAt = (cx, cy, p, txt) => { const dx = p[0] - cx, dy = p[1] - cy, L = Math.hypot(dx, dy) || 1, ux = dx \/ L, uy = dy \/ L; return seg(p[0], p[1], p[0] + ux * 15, p[1] + uy * 15) + '<text class=\"a\" x=\"' + (p[0] + ux * 30).toFixed(1) + '\" y=\"' + (p[1] + uy * 30 + 4).toFixed(1) + '\" text-anchor=\"middle\">' + txt + '<\/text>'; };\n                const benzene = (dots) => { const V = poly(CX, CY, 22, 6, 90); return wrap(ppath(V) + '<circle class=\"b\" fill=\"none\" cx=\"' + CX + '\" cy=\"' + CY + '\" r=\"12\"\/>' + (dots || []).map(i => dotAt(V[i])).join('')); };\n                const benzeneCH = () => { const V = poly(CX, CY, 22, 6, 90); return wrap(ppath(V) + '<circle class=\"b\" fill=\"none\" cx=\"' + CX + '\" cy=\"' + CY + '\" r=\"12\"\/>' + substAt(CX, CY, V[5], 'H')); };\n                const six = (het) => { const V = poly(CX, CY, 22, 6, 90); return wrap(ppath(V) + '<circle class=\"b\" fill=\"none\" cx=\"' + CX + '\" cy=\"' + CY + '\" r=\"12\"\/>' + heteroAt(V[3], het)); };\n                const five = (het, sub) => { const V = poly(CX, CY, 22, 5, -90); return wrap(ppath(V) + heteroAt(V[0], het) + (sub ? substAt(CX, CY, V[1], 'CH\u2082') : '')); };\n                const pyrazole = () => { const V = poly(CX, CY, 22, 5, -90); return wrap(ppath(V) + heteroAt(V[0], 'N') + heteroAt(V[1], 'N')); };\n                const cyclo = (n) => { const V = poly(CX, CY, (n <= 3 ? 20 : 22), n, (n === 4 ? 45 : 90)); return wrap(ppath(V)); };\n                const epoxide = () => { const V = poly(CX, CY, 20, 3, 90); return wrap(ppath(V) + heteroAt(V[0], 'O')); };\n                const fused = (k) => { let s = ''; const r = 19; for (let i = 0; i < k; i++) { const cx = 42 + i * 33; const V = poly(cx, CY, r, 6, 90); s += ppath(V) + '<circle class=\"b\" fill=\"none\" cx=\"' + cx + '\" cy=\"' + CY + '\" r=\"10\"\/>'; } return wrap(s); };\n                const biphenyl = () => { const A = poly(38, CY, 17, 6, 90), B = poly(122, CY, 17, 6, 90); return wrap(ppath(A) + '<circle class=\"b\" fill=\"none\" cx=\"38\" cy=\"' + CY + '\" r=\"9\"\/>' + seg(55, CY, 105, CY) + ppath(B) + '<circle class=\"b\" fill=\"none\" cx=\"122\" cy=\"' + CY + '\" r=\"9\"\/>'); };\n                \/\/ rz\u0119dowo\u015b\u0107 (pierwszo-\/drugo-\/trzeciorz\u0119dowe) i budowniki uwzgl\u0119dniaj\u0105ce specyfik\u0119\n                const deg = \/trzeciorz\/.test(m) ? 'tert' : \/drugorz\/.test(m) ? 'sec' : \/pierwszorz\/.test(m) ? 'prim' : '';\n                const subsFor = (X, d) => d === 'tert' ? [X, 'R', 'R', 'R'] : d === 'sec' ? [X, 'R', 'R', 'H'] : [X, 'R', 'H', 'H'];\n                const cC = (subs) => { const cx = 95, cy = 45, P = [[52, 20], [138, 20], [52, 70], [138, 70]]; let s = at(cx, 'C'); for (let i = 0; i < 4; i++) { const q = P[i], dx = q[0] - cx, dy = q[1] - cy, L = Math.hypot(dx, dy), ux = dx \/ L, uy = dy \/ L; s += seg(cx + ux * 13, cy + uy * 13, q[0] - ux * 12, q[1] - uy * 12) + at(q[0], subs[i], q[1] + 4); } return wrap(s); };\n                const amide = (d) => { const up = d === 'tert' ? 'R' : 'H'; const dn = d === 'prim' ? 'H' : 'R'; return wrap(at(16, 'R') + b1(28, 44) + at(56, 'C') + seg(52, 38, 52, 22) + seg(60, 38, 60, 22) + at(56, 'O', 17) + b1(70, 98) + at(112, 'N') + seg(122, 42, 146, 28) + at(156, up, 30) + seg(122, 50, 146, 66) + at(156, dn, 68)); };\n                const amine = (d, X) => { X = X || 'R'; if (d === 'sec') { return wrap(at(18, X) + b1(30, 56) + at(78, 'N') + b1(98, 124) + at(140, X) + seg(78, 38, 78, 22) + at(78, 'H', 17)); } return wrap(at(28, X) + b1(42, 70) + at(92, 'N') + seg(104, 42, 140, 28) + at(150, 'H', 30) + seg(104, 50, 140, 66) + at(150, 'H', 68)); };\n                const alkene = (Ls, Rs) => { let s = at(72, 'C') + b2(86, 108) + at(122, 'C'); if (Ls[0]) s += seg(62, 40, 48, 28) + at(40, Ls[0], 26); if (Ls[1]) s += seg(62, 48, 48, 62) + at(40, Ls[1], 66); if (Rs[0]) s += seg(132, 40, 146, 28) + at(154, Rs[0], 26); if (Rs[1]) s += seg(132, 48, 146, 62) + at(154, Rs[1], 66); return wrap(s); };\n                const polyene = () => wrap(at(24, 'C') + b2(36, 56) + at(70, 'C') + b1(84, 100) + at(114, 'C') + b2(128, 148) + at(162, 'C'));\n\n                \/\/ --- kompleksy koordynacyjne: schematy M\u2013L ---\n                if (item.group === 'Coordination Complexes') {\n                    \/\/ wi\u0105zanie koordynacyjne rysowane OD atomu-donora do metalu M\n                    if (\/mostkuj\/.test(m)) return wrap(at(24, 'M') + b1(38, 66) + at(90, 'C') + b1(114, 142) + at(156, 'M') + seg(86, 38, 86, 22) + seg(94, 38, 94, 22) + at(90, 'O', 17)); \/\/ M\u2013C(=O)\u2013M, donor C\n                    if (md.includes('C\u2261O')) return link3('M', 'C', 'O', 1, 3);          \/\/ donor C: M\u2013C\u2261O\n                    if (\/karbonylki\/.test(m)) return link3('O', 'C', 'M', 3, 1);         \/\/ \u03bd(M\u2013C): O\u2261C\u2013M\n                    if (md.includes('C\u2261N')) return link3('M', 'C', 'N', 1, 3);          \/\/ donor C: M\u2013C\u2261N\n                    if (\/ncs|scn\/.test(m)) return wrap(at(16, 'M') + b1(28, 50) + at(68, 'N') + b2(82, 102) + at(118, 'C') + b2(130, 150) + at(166, 'S')); \/\/ donor N: M\u2013N=C=S\n                    if (\/nh\u2083\/.test(m)) return wrap(at(26, 'M') + b1(40, 66) + at(90, 'N') + seg(100, 38, 122, 24) + at(132, 'H', 26) + seg(102, 44, 126, 44) + at(138, 'H', 48) + seg(100, 50, 122, 64) + at(132, 'H', 68)); \/\/ donor N: M\u2013NH\u2083\n                    if (\/bidentny\/.test(m)) return wrap(at(90, 'R', 14) + seg(90, 20, 90, 30) + at(90, 'C', 40) + seg(82, 44, 52, 56) + at(44, 'O', 60) + seg(98, 44, 128, 56) + at(136, 'O', 60) + seg(50, 66, 84, 80) + seg(130, 66, 96, 80) + at(90, 'M', 84)); \/\/ chelat O\u2013M\u2013O, donor O\n                    if (\/unidentny\/.test(m)) return wrap(at(20, 'R') + b1(32, 48) + at(62, 'C') + seg(58, 38, 58, 20) + seg(66, 38, 66, 20) + at(62, 'O', 15) + b1(76, 100) + at(116, 'O') + b1(130, 152) + at(170, 'M')); \/\/ R\u2013C(=O)\u2013O\u2013M, donor O\n                    if (\/no\u2082\/.test(m)) return wrap(at(24, 'M') + b1(38, 64) + at(90, 'N') + seg(102, 38, 128, 24) + at(140, 'O', 26) + seg(102, 48, 128, 62) + at(140, 'O', 66)); \/\/ donor N: M\u2013NO\u2082\n                    if (\/ono|nitrito\/.test(m)) return wrap(at(16, 'M') + b1(28, 48) + at(66, 'O') + b1(80, 104) + at(122, 'N') + seg(134, 38, 158, 24) + at(168, 'O', 26)); \/\/ donor O: M\u2013O\u2013N=O\n                    if (\/m\u2013o\/.test(m)) return link3('C', 'O', 'M', 1, 1);               \/\/ donor O: C\u2013O\u2013M\n                    if (\/m\u2013n\/.test(m)) return link3('C', 'N', 'M', 1, 1);               \/\/ donor N: C\u2013N\u2013M\n                    if (\/m\u2013c\/.test(m)) return link3('O', 'C', 'M', 3, 1);               \/\/ donor C: O\u2261C\u2013M\n                    return label('M\u2013L');\n                }\n                \/\/ --- specyficzne struktury organiczne \/ jonowe ---\n                if (\/benzen\u00f3w alkilowych\/.test(m)) return benzeneAlkil();\n                if (\/dimer\/.test(m)) return acidDimer();\n                if (\/monomer\/.test(m)) return acidMono();\n                if (\/arylowe\\\/\u03b1,\u03b2\/.test(m) && item.group === 'Carboxylic Acids') return acidAryl();\n                if (\/aminokwas\/.test(m) && md.includes('C=O')) return acidMono();\n                if (md.includes('COO\u207b') || \/karboksylan\/.test(m)) return carboxylate();\n                if (\/bezwodnik\/.test(m)) return anhydride();\n                if (\/halogenki kwasowe\/.test(m)) return acylHal();\n                if (\/aldehydy halogenowane\/.test(m)) return haloAld();\n                if (\/aldehydy alifatyczne\/.test(m) && md.includes('C=O')) return aldehydeCO();\n                if (md.includes('C=S')) return thioamide();\n                if (\/tioestry\/.test(m)) return thioester();\n                if (\/estry\/.test(m) && md.includes('C=O')) return ester();\n                if (\/ketoksym\/.test(m)) return oxime('R');\n                if (\/aldoksym\/.test(m)) return oxime('H');\n                if (\/hydrazon\/.test(m)) return hydrazone();\n                if (\/azyn\/.test(m)) return azine();\n                if (\/tiosemikarbazon\/.test(m)) return label('C=N\u2013NH\u2013C(=S)NH\u2082');\n                if (\/semikarbazon\/.test(m)) return label('C=N\u2013NH\u2013C(=O)NH\u2082');\n                if (\/izonitryl\/.test(m)) return isonitrile();\n                if (\/tiocyjanian\/.test(m) && item.group === 'C-N Vibrations') return thiocyanateCov();\n                if (\/azotany alkilowe\/.test(m)) return nitrateEster();\n                if (\/azotyny alkilowe\/.test(m)) return nitriteEster();\n                if (\/siarczany alkilowe\/.test(m)) return sulf('OR', 'OR');\n                if (\/sulfoniany\/.test(m)) return sulf('R', 'OR');\n                if (\/sulfony\/.test(m)) return sulf('R', 'R');\n                if (\/szczawian\/.test(m)) return oxalate();\n                if (\/amonow\/.test(m)) return ammonium();\n                if (\/wodorow\u0119glan\/.test(m)) return bicarbonate();\n                if (\/krzemionka\/.test(m)) return silica();\n                \/\/ terminalny alkin H-C\u2261C\n                if (md.includes('\u2261C-H')) return link3('H', 'C', 'C', 1, 3);\n                \/\/ winylowe \/ =CH2\n                if (md.includes('=CH') && item.group.includes('C-H')) return vinyl();\n                \/\/ aromatyczne C-H: pier\u015bcie\u0144 z wi\u0105zaniem C-H\n                if (\/aromatic|benzen|aromatyczn\/.test(m) && (md.includes('CH') || md.includes('C-H'))) return benzeneCH();\n                \/\/ wi\u0105zania skumulowane\n                if (md.includes('N=C=O')) return link3('N', 'C', 'O', 2, 2);\n                if (md.includes('N=C=S')) return link3('N', 'C', 'S', 2, 2);\n                if (md.includes('C=C=C')) return link3('C', 'C', 'C', 2, 2);\n                if (\/amid\/.test(m)) return amide(deg || 'sec');\n                if (\/amin\/.test(m)) return amine(deg, \/cnc\/.test(m) ? 'C' : 'R');\n                \/\/ potr\u00f3jne \/ podw\u00f3jne\n                if (md.includes('C\u2261C')) {\n                    if (md.includes('CH\u2083')) return alkyne('CH\u2083');\n                    if (md.includes(\"R'\")) return alkyne(\"R'\");\n                    if (\/alkiny alkilowe\/.test(m)) return alkyne('H');\n                    return alkyne(\"R'\");\n                }\n                if (md.includes('C\u2261N')) return link('C', 'N', 3);\n                if (md.includes('C\u2261O')) return link2('C', 'O', 3);\n                if (md.includes('C=O')) return carbonyl();\n                if (md.includes('C=C')) {\n                    if (\/tetraalkil\/.test(m)) return alkene(['R', 'R'], ['R', 'R']);\n                    if (\/trialkil\/.test(m)) return alkene(['R', 'R'], ['R', 'H']);\n                    if (\/cis-dialkil\/.test(m)) return alkene(['R', 'H'], ['R', 'H']);\n                    if (\/trans-dialkil\/.test(m)) return alkene(['R', 'H'], ['H', 'R']);\n                    if (\/dialkil\/.test(m)) return alkene(['R', 'H'], ['H', 'R']);\n                    if (md.includes('R\u2082C=CH\u2082')) return alkene(['R', 'R'], ['H', 'H']);\n                    if (md.includes('H\u2082C=CHR')) return alkene(['H', 'H'], ['R', 'H']);\n                    if (\/monofluoro\/.test(m)) return alkene(['F', 'H'], ['H', 'H']);\n                    if (\/chloroalk\/.test(m)) return alkene(['Cl', 'H'], ['H', 'H']);\n                    if (\/bromoalk\/.test(m)) return alkene(['Br', 'H'], ['H', 'H']);\n                    if (\/jodoalk|iodoalk\/.test(m)) return alkene(['I', 'H'], ['H', 'H']);\n                    if (\/polien\/.test(m)) return polyene();\n                    return alkene(['R', 'H'], ['H', 'H']);\n                }\n                if (md.includes('C=N')) return link('C', 'N', 2);\n                if (md.includes('C=S')) return link('C', 'S', 2);\n                if (md.includes('S=O')) return sulfoxide();\n                if (md.includes('N=O')) return link('N', 'O', 2);\n                if (md.includes('M=O')) return link2('M', 'O', 2);\n                if (md.includes('NO\u2082') && deg) return cC(subsFor('NO\u2082', deg));\n                \/\/ pojedyncze \/ dwuatomowe\n                if (md.includes('S-S')) return linkRR('S', 'S', 1);\n                if (md.includes('Si-H') || md.includes('H-Si')) return link('Si', 'H', 1);\n                if (md.includes('Se-H')) return link('Se', 'H', 1);\n                if (md.includes('Si-O-Si')) return link3('Si', 'O', 'Si', 1, 1);\n                if (md.includes('Si-O')) return link('Si', 'O', 1);\n                if (md.includes('H-Cl')) return link('H', 'Cl', 1);\n                if (md.includes('H-Br')) return link('H', 'Br', 1);\n                if (md.includes('H-I')) return link('H', 'I', 1);\n                if (md.includes('H-F')) return link('H', 'F', 1);\n                if (md.includes('C-Cl')) return deg ? cC(subsFor('Cl', deg)) : link('C', 'Cl', 1);\n                if (md.includes('C-Br')) return deg ? cC(subsFor('Br', deg)) : link('C', 'Br', 1);\n                if (md.includes('C-I')) return deg ? cC(subsFor('I', deg)) : link('C', 'I', 1);\n                if (md.includes('C-S')) return linkRR('C', 'S', 1);\n                if (md.startsWith('SH') || md.includes('S-H')) return link('S', 'H', 1);\n                if (md.includes('O-H') || md.startsWith('OH')) return link('O', 'H', 1);\n                if (md.includes('CNC') || md.includes('C-N')) return link('C', 'N', 1);\n                if (md.startsWith('NH') || md.includes('N-H')) return link('N', 'H', 1);\n                if (\/alkohol\/.test(m) && deg) return cC(subsFor('OH', deg));\n                if (md.includes('C-O-C')) return link3('C', 'O', 'C', 1, 1);\n                if (md.includes('C-O')) return link('C', 'O', 1);\n                \/\/ --- pier\u015bcienie: dok\u0142adne struktury ---\n                if (\/furfuryl|furan\/.test(m)) return five('O', true);\n                if (\/tiofen|thiophen\/.test(m)) return five('S', false);\n                if (\/pirydyn|pyridine\/.test(m)) return six('N');\n                if (\/pirazol|pyrazol\/.test(m)) return pyrazole();\n                if (\/naftalen|naphthalen\/.test(m)) return fused(2);\n                if (\/antracen|anthracen\/.test(m)) return fused(3);\n                if (\/bifenyl|biphenyl\/.test(m)) return biphenyl();\n                if (\/epoksyd|epoxy\/.test(m)) return epoxide();\n                if (\/cyklopropan\/.test(m)) return cyclo(3);\n                if (\/cyklobutan\/.test(m)) return cyclo(4);\n                if (\/cyklopentan\/.test(m)) return cyclo(5);\n                if (\/cykloheksan|cyclohex\/.test(m)) return cyclo(6);\n                if (\/aromat|benzen|pier\u015bcien\/.test(m)) {\n                    if (\/1,3,5\/.test(m)) return benzene([0, 2, 4]);\n                    if (\/para\/.test(m)) return benzene([0, 3]);\n                    if (\/orto|ortho\/.test(m)) return benzene([0, 5]);\n                    if (\/meta\/.test(m)) return benzene([0, 4]);\n                    if (\/monopod|monosub|\\bmono\\b|monosubst\/.test(m)) return benzene([0]);\n                    return benzene([]);\n                }\n                \/\/ grupy alifatyczne \/ rozga\u0142\u0119zione\n                if (\/izopropyl\/.test(m)) return isopropyl();\n                if (\/tert-butyl\/.test(m)) return tertbutyl();\n                if (md.includes('CH\u2082')) return methylene();\n                if (md.includes('CH\u2083')) return methyl();\n                if (md.includes('CHO')) return aldehyde();\n                if (md.includes('C-H') || md.startsWith('CH')) return link('C', 'H', 1);\n                if (\/alkin\/.test(m)) return link('C', 'C', 3);\n                if (md.includes('CC') || md.includes('C-C') || \/szkielet\/.test(m)) return link('C', 'C', 1);\n                \/\/ jony \/ grupy wieloatomowe -> etykieta wzoru\n                const tok = md.split(' ')[0];\n                if (\/[0-9\u2080-\u2089\u207b\u207a\u00b2\u00b3\u2074]\/.test(tok) || \/^(NO|SO|CO|PO|CN|SCN|OCN|Al|As|Cr|Mn|Mo|Te|Se|W|V|Ti|Zn|Cu|Fe|Si|BF|BH|BO|PF|HF|HS|HC|HP|H2)\/.test(tok)) return label(tok);\n                return '';\n            }\n\n            function handleTooltipShow(e, item) {\n                const barRect = e.target.getBoundingClientRect();\n                tooltip.innerHTML = `\n                    ${fragmentFor(item)}\n                    <strong>${item.mode} (${item.type})<\/strong><br>\n                    Zakres: ${item.range[0]}-${item.range[1]} cm\u207b\u00b9<br>\n                    Intensywno\u015b\u0107: ${({strong:'Silna', medium:'\u015arednia', weak:'S\u0142aba'})[item.intensity] || item.intensity}\n                `;\n                tooltip.style.visibility = 'visible';\n                tooltip.style.opacity = '1';\n                const tooltipRect = tooltip.getBoundingClientRect();\n                const offset = 8;\n                let top = barRect.top - tooltipRect.height - offset;\n                let left = barRect.left + (barRect.width \/ 2) - (tooltipRect.width \/ 2);\n\n                if (top < 0) top = barRect.bottom + offset;\n                if (left < 0) left = offset;\n                if (left + tooltipRect.width > window.innerWidth) left = window.innerWidth - tooltipRect.width - offset;\n\n                tooltip.style.top = `${top + window.scrollY}px`;\n                tooltip.style.left = `${left + window.scrollX}px`;\n            }\n\n            function handleTooltipHide() {\n                tooltip.style.opacity = '0';\n                tooltip.style.visibility = 'hidden';\n            }\n\n            function escapeHtml(s) {\n                return s.replace(\/&\/g, '&amp;').replace(\/<\/g, '&lt;').replace(\/>\/g, '&gt;');\n            }\n\n            \/\/ Prosty renderer Markdown -> HTML (nag\u0142\u00f3wki, pogrubienie, kursywa, listy, kod).\n            \/\/ Dzi\u0119ki temu okno odpowiedzi poprawnie wy\u015bwietla formatowanie oraz znaki Unicode\n            \/\/ (greckie litery \u03bd, \u03b4, \u03c4 i indeksy dolne\/g\u00f3rne, np. CH\u2083, SO\u2084\u00b2\u207b, cm\u207b\u00b9).\n            function renderMarkdown(md) {\n                if (!md) return '<p class=\"placeholder\">Brak odpowiedzi.<\/p>';\n                let src = md.replace(\/\\r\\n\/g, '\\n');\n                \/\/ Modele bywaj\u0105 niekonsekwentne i czasem zwracaj\u0105 HTML zamiast Markdown.\n                \/\/ Sprowadzamy taki HTML do Markdown\/tekstu, a reszt\u0119 tag\u00f3w usuwamy.\n                src = src\n                    .replace(\/<br\\s*\\\/?>\/gi, '\\n')\n                    .replace(\/<\\\/(p|div)>\/gi, '\\n\\n')\n                    .replace(\/<(p|div)[^>]*>\/gi, '')\n                    .replace(\/<\\\/?(ul|ol)[^>]*>\/gi, '\\n')\n                    .replace(\/<h[1-6][^>]*>\/gi, '\\n## ')\n                    .replace(\/<\\\/h[1-6]>\/gi, '\\n')\n                    .replace(\/<li[^>]*>\/gi, '\\n- ')\n                    .replace(\/<\\\/li>\/gi, '')\n                    .replace(\/<(strong|b)>\/gi, '**').replace(\/<\\\/(strong|b)>\/gi, '**')\n                    .replace(\/<(em|i)>\/gi, '*').replace(\/<\\\/(em|i)>\/gi, '*')\n                    .replace(\/<[^>]+>\/g, '');\n                src = escapeHtml(src);\n                src = src.replace(\/`([^`]+)`\/g, '<code>$1<\/code>');\n                src = src.replace(\/\\*\\*([^*]+?)\\*\\*\/g, '<strong>$1<\/strong>');\n                src = src.replace(\/(^|[^*])\\*([^*\\n]+?)\\*\/g, '$1<em>$2<\/em>');\n                const lines = src.split('\\n');\n                let html = '', listType = null;\n                const closeList = () => { if (listType) { html += (listType === 'ul' ? '<\/ul>' : '<\/ol>'); listType = null; } };\n                for (const raw of lines) {\n                    const line = raw.trim();\n                    if (line === '') { closeList(); continue; }\n                    const h = line.match(\/^(#{1,6})\\s+(.*)$\/);\n                    if (h) { closeList(); const lvl = Math.min(h[1].length, 4); html += '<h' + lvl + '>' + h[2] + '<\/h' + lvl + '>'; continue; }\n                    if (\/^(-{3,}|\\*{3,}|_{3,})$\/.test(line)) { closeList(); html += '<hr>'; continue; }\n                    const ol = line.match(\/^\\d+[.)]\\s+(.*)$\/);\n                    if (ol) { if (listType !== 'ol') { closeList(); html += '<ol>'; listType = 'ol'; } html += '<li>' + ol[1] + '<\/li>'; continue; }\n                    const ul = line.match(\/^[-*+\u2022]\\s+(.*)$\/);\n                    if (ul) { if (listType !== 'ul') { closeList(); html += '<ul>'; listType = 'ul'; } html += '<li>' + ul[1] + '<\/li>'; continue; }\n                    closeList(); html += '<p>' + line + '<\/p>';\n                }\n                closeList();\n                return '<div class=\"md\">' + html + '<\/div>';\n            }\n\n            async function callApi(systemPrompt, userQuery, resultElement) {\n                if (!apiKey) {\n                    resultElement.classList.remove('has-answer');\n                    resultElement.innerHTML = `<p><strong>B\u0142\u0105d:<\/strong> Brak klucza API. Wklej sw\u00f3j klucz API w skrypcie.<\/p>`;\n                    return;\n                }\n                resultElement.classList.remove('has-answer');\n                resultElement.innerHTML = '<div class=\"loader\"><\/div>';\n                const modelName = 'gemini-3.5-flash';\n                const apiUrl = `about:blank\/v1beta\/models\/${modelName}:generateContent?key=${apiKey}`;\n                const payload = {\n                    contents: [{ parts: [{ text: userQuery }] }],\n                    systemInstruction: { parts: [{ text: systemPrompt }] },\n                    generationConfig: { temperature: 0.5 }\n                };\n                try {\n                    const response = await fetch(apiUrl, {\n                        method: 'POST',\n                        headers: { 'Content-Type': 'application\/json' },\n                        body: JSON.stringify(payload)\n                    });\n                    if (!response.ok) {\n                        const error = await response.json();\n                        throw new Error(`API request failed: ${error.error.message}`);\n                    }\n                    const result = await response.json();\n                    const candidate = result.candidates?.[0];\n                    if (candidate && candidate.content?.parts?.[0]?.text) {\n                        resultElement.classList.add('has-answer');\n                        resultElement.innerHTML = renderMarkdown(candidate.content.parts[0].text);\n                    } else {\n                        throw new Error('Invalid response structure from API.');\n                    }\n                } catch (error) {\n                    console.error(\"Error calling API:\", error);\n                    resultElement.classList.remove('has-answer');\n                    resultElement.innerHTML = '<p>' + escapeHtml('Wyst\u0105pi\u0142 b\u0142\u0105d: ' + error.message) + '<\/p>';\n                }\n            }\n\n            function setupEventListeners(){}\n            function init() {\n                buildLegend();\n                buildChart();\n                buildXAxis();\n                setupControls();\n                setupEventListeners();\n                applyFilters();\n            }\n            init();\n        });\n    \n<\/script>\n<div class=\"gk-tool\" style=\"padding-top:0\"><!-- SOFT CTA \/ lead capture \u2014 wsp\u00f3lny dla narz\u0119dzi. Publish.py wstawia przed stopk\u0105. Marker: gk-cta-main -->\n<section class=\"cta gk-cta-main\">\n  <h2>Masz pr\u00f3bk\u0119, kt\u00f3rej nie chcesz zgadywa\u0107?<\/h2>\n  <p>Robimy badania feasibility nawet na najtrudniejszych pr\u00f3bkach \u2014 na Twoim materiale, u Ciebie. Zostaw kontakt, a przejdziemy przez Tw\u00f3j przypadek i powiemy wprost, czy Spectrally to zmierzy.<\/p>\n  <form class=\"gk-lf\" data-region=\"Narz\u0119dzia Ramana \u2014 feasibility\" data-ok=\"Dzi\u0119ki \u2014 odezwiemy si\u0119 w sprawie feasibility.\" novalidate>\n    <div class=\"gk-lf-grid\">\n      <input type=\"text\" name=\"name_full\" placeholder=\"Imi\u0119 i nazwisko\" autocomplete=\"name\">\n      <input type=\"text\" name=\"company\" placeholder=\"Nazwa firmy\" autocomplete=\"organization\">\n      <input type=\"email\" name=\"email\" placeholder=\"E-mail s\u0142u\u017cbowy\" required autocomplete=\"email\">\n    <\/div>\n    <input type=\"text\" name=\"company_hp\" class=\"hp\" tabindex=\"-1\" autocomplete=\"off\" aria-hidden=\"true\">\n    <label class=\"consent\"><input type=\"checkbox\" name=\"consent\" required>\n      <span>Zgadzam si\u0119 na kontakt e-mail w sprawie mojego zapytania. Dane przetwarza Gekko Photonics \u2014 <a href=\"\/polityka-prywatnosci\/\">polityka prywatno\u015bci<\/a>.<\/span><\/label>\n    <div class=\"row\"><button type=\"submit\" class=\"btn btn-primary\">Popro\u015b o feasibility<\/button><\/div>\n    <div class=\"msg\" role=\"status\" aria-live=\"polite\"><\/div>\n  <\/form>\n<\/section>\n<\/div>\n<script>\n\/* Gekko Photonics \u2014 narz\u0119dzia Ramana: wsp\u00f3lna obs\u0142uga lead\u00f3w.\n   Wstrzykiwany RAZ na stron\u0119 przez publish.py. Dzia\u0142a przez delegacj\u0119,\n   wi\u0119c \u0142apie te\u017c formularze dobudowane dynamicznie (odpowiedzi asystenta).\n   - przycisk [data-gk-toggle=\"ID\"] wysuwa\/chowa formularz o danym ID\n   - dowolny <form class=\"gk-lf\"> wysy\u0142a si\u0119 do endpointu RSVP           *\/\n(function(){\n  var EP='\/wp-json\/gekko\/v1\/rsvp';\n\n  \/\/ wysuwane formularze\n  document.addEventListener('click',function(e){\n    var t=e.target.closest('[data-gk-toggle]'); if(!t) return;\n    e.preventDefault();\n    var f=document.getElementById(t.getAttribute('data-gk-toggle')); if(!f) return;\n    var open=f.classList.toggle('open');\n    t.setAttribute('aria-expanded',open?'true':'false');\n    if(open){var i=f.querySelector('input:not([type=hidden]):not(.hp)'); if(i) setTimeout(function(){i.focus();},260);}\n  });\n\n  \/\/ wysy\u0142ka\n  document.addEventListener('submit',function(e){\n    var f=e.target.closest('form.gk-lf'); if(!f) return;\n    e.preventDefault();\n    if(f.company_hp && f.company_hp.value) return;                 \/\/ honeypot\n    var msg=f.querySelector('.msg');\n    var email=((f.email&&f.email.value)||'').trim();\n    var consent=f.consent?f.consent.checked:true;\n    function say(c,txt){ if(msg){msg.style.color=c; msg.textContent=txt;} }\n    if(!email||!consent){ say('#F3C969','Podaj e-mail i zaznacz zgod\u0119.'); return; }\n    var btn=f.querySelector('button[type=submit]')||f.querySelector('button');\n    if(btn) btn.disabled=true;\n    say('#667085','Wysy\u0142am\u2026');\n    var body={\n      email:email, response:'yes', consent:true, company_hp:'',\n      name:((f.name_full&&f.name_full.value)||'').trim(),\n      company:((f.company&&f.company.value)||'').trim(),\n      context:((f.context&&f.context.value)||''),\n      region:'tools',\n      region_label:f.getAttribute('data-region')||'Narz\u0119dzia Ramana',\n      page_url:location.href, page_title:document.title\n    };\n    fetch(EP,{method:'POST',headers:{'Content-Type':'application\/json'},body:JSON.stringify(body)})\n      .then(function(r){return r.json().catch(function(){return {};});})\n      .then(function(){\n        say('#12B76A', f.getAttribute('data-ok')||'Dzi\u0119ki \u2014 odezwiemy si\u0119 do Ciebie.');\n        var keep=f.querySelector('.msg');\n        [].forEach.call(f.querySelectorAll('input:not(.hp), textarea'),function(el){\n          if(el.type==='checkbox'){el.checked=false;} else {el.value='';}\n        });\n      })\n      .catch(function(){ say('#F3C969','Co\u015b posz\u0142o nie tak. Napisz na kontakt@gekkophotonics.com'); })\n      .finally(function(){ if(btn) btn.disabled=false; });\n  });\n})();\n\n<\/script>\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>\n\t\t","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":2965,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"footnotes":""},"class_list":["post-2975","page","type-page","status-publish","hentry"],"blocksy_meta":[],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/gekkophotonics.com\/en\/wp-json\/wp\/v2\/pages\/2975","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gekkophotonics.com\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/gekkophotonics.com\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/gekkophotonics.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/gekkophotonics.com\/en\/wp-json\/wp\/v2\/comments?post=2975"}],"version-history":[{"count":1,"href":"https:\/\/gekkophotonics.com\/en\/wp-json\/wp\/v2\/pages\/2975\/revisions"}],"predecessor-version":[{"id":2981,"href":"https:\/\/gekkophotonics.com\/en\/wp-json\/wp\/v2\/pages\/2975\/revisions\/2981"}],"up":[{"embeddable":true,"href":"https:\/\/gekkophotonics.com\/en\/wp-json\/wp\/v2\/pages\/2965"}],"wp:attachment":[{"href":"https:\/\/gekkophotonics.com\/en\/wp-json\/wp\/v2\/media?parent=2975"}],"curies":[{"name":"entry","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}