{"title":"👉 nur noch wenige 🔥","description":"","products":[{"product_id":"mampfi-das-verfressene-monster-meine-kleinen-monster","title":"Mampfi, das verfressene Monster","description":"\u003ch2\u003e\n\u003ci\u003e\u003cb\u003e\u003c\/b\u003e\u003c\/i\u003e\u003cbr\u003e\n\u003c\/h2\u003e\n\u003cp\u003e\u003cstrong\u003e\u003cem\u003e\u003cimg alt=\"Mampfi Monster Logo\" src=\"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/mampf_animiert.gif?v=1571147371\" style=\"float: none;\"\u003e\u003c\/em\u003e\u003c\/strong\u003e\u003cbr\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003eÜberarbeitete Neuauflage von \"Mampfi, das verfressene Monster\" aus der Bücherreihe \"Meine kleinen Monster\".\u003c\/em\u003e\u003c\/p\u003e\n\u003cp style=\"text-align: left;\"\u003eIch bin Mampfi, das verfressene Monster.\u003c\/p\u003e\n\u003cp style=\"text-align: left;\"\u003e\u003cbr\u003eIch habe immer Hunger und verputze alles, was mir in die Quere kommt.\u003cbr\u003eHeute habe ich so einen Riesenhunger, dass ich nicht nur Radieschen, Äpfel, Gras und sogar Professor Hirnis ungenießbare Teigmischung esse, ich mampfe auch noch alle Süßigkeiten aus Glucksis Schublade. Oje, das war doch ein bisschen zu viel.\u003cbr\u003eHoffentlich platze ich nicht ...\u003cbr\u003eEine lustige Geschichte mit dem verfressenen Mampf i, der einfach\u003cbr\u003enie genug bekommen kann.\u003c\/p\u003e\n\u003cp style=\"text-align: left;\"\u003e\u003cstrong\u003e- Überarbeitete Neuauflage\u003c\/strong\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cb\u003e- Gebundenes Buch\u003c\/b\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cstrong\u003e- ISBN:  9783955350123\u003c\/strong\u003e\u003c\/p\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Default Title","offer_id":31016279343217,"sku":"550001593","price":8.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/products\/Mampfi_384e7d97-cff6-4c5d-bd89-3165e084034a.jpg?v=1572125018"},{"product_id":"klarlack-lebensmittelecht","title":"Klarlack lebensmittelecht, 180 ml","description":"\u003cp\u003eDécopatch - Klarlack lebensmittelecht - vaal180ac\u003c\/p\u003e\n\u003cp\u003e\u003cstrong\u003eSehr ergiebiger Klarlack, der speziell für Objekte geeignet ist, die direkt mit Lebensmitteln in Berührung kommen.\u003c\/strong\u003e\u003cbr data-mce-fragment=\"1\"\u003e\u003cbr data-mce-fragment=\"1\"\u003eSelbst dekorierte Obstschalen, Holztabletts, Porzellantassen und vieles mehr können mit diesem Klarlack lebensmittelecht versiegelt werden.\u003cbr data-mce-fragment=\"1\"\u003e\u003cbr data-mce-fragment=\"1\"\u003eBitte beachte, dass die versiegelten Gegenstände nicht mikrowellengeeignet und nicht spülmaschinenfest sind, also von Hand gespült werden sollten.\u003cbr data-mce-fragment=\"1\"\u003e\u003cbr data-mce-fragment=\"1\"\u003e\u003cstrong\u003e• für fast alle Oberflächen\u003c\/strong\u003e\u003cbr data-mce-fragment=\"1\"\u003e\u003cstrong\u003e\u003cmeta charset=\"UTF-8\"\u003e• auf Wasserbasis\u003c\/strong\u003e\u003cbr data-mce-fragment=\"1\"\u003e\u003cstrong\u003e\u003cmeta charset=\"UTF-8\"\u003e• nach dem Trocknen wasserfest\u003c\/strong\u003e\u003cbr data-mce-fragment=\"1\"\u003e\u003cstrong\u003e\u003cmeta charset=\"UTF-8\"\u003e• erzeugt eine seidenmatte Oberfläche\u003c\/strong\u003e\u003cbr data-mce-fragment=\"1\"\u003e\u003cstrong\u003e\u003cmeta charset=\"UTF-8\"\u003e• Trocknungszeit ca. 30 Min.\u003c\/strong\u003e\u003cbr data-mce-fragment=\"1\"\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003eAnwendung:\u003c\/em\u003e\u003cbr data-mce-fragment=\"1\"\u003eDer Untergrund sollte trocken, sauber, fett-und staubfrei sein.\u003cbr data-mce-fragment=\"1\"\u003eWir empfehlen einen zweimaligen Auftrag mit einem weichen Pinsel, dazwischen gut trocknen lassen.\u003cbr data-mce-fragment=\"1\"\u003e\u003cbr data-mce-fragment=\"1\"\u003eDen Pinsel anschließend mit Wasser reinigen.\u003cbr data-mce-fragment=\"1\"\u003e\u003cbr data-mce-fragment=\"1\"\u003e\u003cbr data-mce-fragment=\"1\"\u003e\u003cstrong\u003eKann allergische Hautreaktionen verursachen.\u003c\/strong\u003e\u003cbr data-mce-fragment=\"1\"\u003e\u003cbr data-mce-fragment=\"1\"\u003e\u003cstrong\u003eDarf nicht in die Hände von Kindern gelangen.\u003c\/strong\u003e\u003c\/p\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Default Title","offer_id":37643019714729,"sku":"550001579","price":17.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Klarlack_VAAL180AC_1.jpg?v=1702210345"},{"product_id":"dreckwasser-tasse","title":"Dreckwasser Tasse aus Emaille","description":"\u003cp\u003e\u003cstrong\u003eOriginelle Sammeltasse aus Emaille mit dem \"Dreckwasser\" Motiv.\u003c\/strong\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cstrong\u003eIllustriert von Bine Brändle\u003c\/strong\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan\u003eFür ca. 300ml Kaffee, Tee, Kakao oder Dreckwasser vom Pinsel.\u003c\/span\u003e\u003cbr\u003e\u003cbr\u003e\u003cspan\u003eDie brillanten Farben sind spülmaschinenfest, lebensmittelecht.\u003c\/span\u003e\u003c\/p\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Default Title","offer_id":42149454086371,"sku":"550001501","price":14.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/products\/tasse_dreckwasser_1600.jpg?v=1638435253"},{"product_id":"xxl-vorlagen-fur-fensterbilder-die-allerschonsten-winterkranze","title":"XXL-VORLAGEN FÜR FENSTERBILDER Die allerschönsten Winterkränze","description":"\u003cp\u003e\u003cstrong\u003e\u003cem\u003eRestposten \/ Überproduktion\u003c\/em\u003e\u003c\/strong\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cmeta charset=\"UTF-8\"\u003e\u003ci\u003eNur erhältlich solange der Vorrat noch reicht\u003c\/i\u003e\u003c\/p\u003e\n\u003ch4\u003eXXL-VORLAGEN FÜR FENSTERBILDER Die allerschönsten Winterkränze\u003c\/h4\u003e\n\u003ch4\u003eVielseitige Motive für abwaschbare Kreidemarker \u003c\/h4\u003e\n\u003ch4\u003eMach dir und anderen eine Freude und verschönere deine Fenster mit dem Kreidestift!\u003c\/h4\u003e\n\u003cp\u003eMit Hilfe der liebevoll entworfenen XXL-Vorlagen der Künstlerin Bine Brändle ist das kinderleicht.\u003c\/p\u003e\n\u003cp\u003eDie Motive werden einfach hinter der Scheibe befestigt und die Linien mit dem Kreidestift nachgezeichnet. Hast du einmal damit angefangen, ist bestimmt bald kein Fenster mehr vor deinem Malkasten sicher. Übrigens lässt sich der Kreidestift jederzeit wieder von der Scheibe abwischen.\u003c\/p\u003e\n\u003cp\u003eDie Mappe enthält 4 Vorlagebögen, Format je 62 x 86 cm,\u003c\/p\u003e\n\u003ch4\u003eMotive frei miteinander kombinierbar.\u003c\/h4\u003e\n\u003cp\u003e \u003c\/p\u003e\n\u003cp\u003e \u003c\/p\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Default Title","offer_id":43526798934283,"sku":"550001473","price":6.99,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/products\/Produkthotos_Halbweihnachten_0008_DieallerschonstenWinterkranzeVS.jpg?v=1654799402"},{"product_id":"mini-multimarker-ersatzspitzen","title":"Mini MULTIMARKER Spitzen","description":"\u003ch4 style=\"text-align: center;\"\u003eDie Ersatzspitzen Sets enthalten je 5 Markerspitzen.\u003c\/h4\u003e\n\u003cdiv style=\"text-align: center;\"\u003e\u003c\/div\u003e\n\u003cdiv style=\"text-align: center;\"\u003e\n\u003cp\u003e\"Mini-MULTIMARKER Ersatzspitzen\" sind für unsere Mini-MULTIMARKER Sets geeignet.\u003c\/p\u003e\n\u003ch5\u003e\u003cb data-mce-fragment=\"1\"\u003e\u003cimg data-mce-fragment=\"1\" src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Made_in_Germany_DESDA.png?v=1630349925\" alt=\"Made in Germany\" style=\"display: block; margin-left: auto; margin-right: auto;\" data-mce-src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Made_in_Germany_DESDA.png?v=1630349925\" data-mce-selected=\"1\"\u003e\u003c\/b\u003e\u003c\/h5\u003e\n\u003c\/div\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Mini-MULTIMARKER Ersatzspitzen 1 mm","offer_id":43611027964171,"sku":"550001449","price":5.95,"currency_code":"EUR","in_stock":true},{"title":"Mini-MULTIMARKER Ersatzspitzen 1-1,5 mm","offer_id":43650166063371,"sku":"550001450","price":4.95,"currency_code":"EUR","in_stock":true},{"title":"Mini-MULTIMARKER Ersatzspitzen 1-2 mm","offer_id":43611027931403,"sku":"550001451","price":4.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/products\/Spitzen_5x_mini_fineliner_673c9dcf-9f47-43e8-bba6-47874f2509fd.jpg?v=1759103574"},{"product_id":"multimarker-gruntone-einzeln","title":"MULTIMARKER Grüntöne Einzeln","description":"\u003cp\u003e\u003cem style=\"font-size: 1.4em;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Marker-B_480x480.jpg?v=1572784444\" alt=\"Bine Brändle MULTIMARKER\"\u003e\u003c\/em\u003e\u003cstrong\u003eStrichbreite 2-3 mm • von Glas nass entfernbar • auf rauen Flächen permanent\u003c\/strong\u003e\u003c\/p\u003e\n\u003cdiv style=\"text-align: left;\" data-mce-style=\"text-align: left;\"\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv style=\"text-align: left;\" data-mce-style=\"text-align: left;\"\u003e\n\u003cstrong\u003e\u003c\/strong\u003e\u003cbr\u003e\n\u003c\/div\u003e\n\u003cdiv style=\"text-align: left;\" data-mce-style=\"text-align: left;\"\u003e\n\u003cstrong\u003e\u003c\/strong\u003e\u003cbr\u003e\n\u003c\/div\u003e\n\u003cdiv style=\"text-align: left;\" data-mce-style=\"text-align: left;\"\u003e\u003cimg style=\"margin-top: 9px; margin-bottom: 9px; float: none;\" alt=\"von Glass Nass, auf Stein Holz permanent Made in Germany\" src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Kreise_Glass_Perma_G_600x200.png?v=1629532491\u0026gt;\u0026lt;\/div\u0026gt;%0A\u0026lt;div%20style=\" text-align:=\"\" data-mce-src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Kreise_Glass_Perma_G_600x200.png?v=1629532491\u0026gt;\u0026lt;\/div\u0026gt;%0A\u0026lt;div%20style=\"\u003e\u003c\/div\u003e\n\u003cdiv data-mce-style=\"text-align: left;\"\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv data-mce-style=\"text-align: left;\"\u003eUm die Farbe nach längerer Zeit von Glas zu entfernen, das Motiv mit Wasser oder Fensterputzmittel anfeuchten und die Flüssigkeit kurz einwirken lassen. Nun die Scheibe mit der rauen Seite eines für Glas geeigneten Spülschwamms putzen. Wenn nötig den Vorgang wiederholen, bis alle Rückstände entfernt sind. Achtung! Keine Putzutensilien verwenden, die das Fenster zerkratzen könnten! (wie z.B. Topfreiniger)\u003c\/div\u003e\n\u003cdiv data-mce-style=\"text-align: left;\"\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv style=\"text-align: start;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/anMULTI.png?v=1629539920\" alt=\"Start Anweisungen\" style=\"margin-top: 9px; margin-bottom: 9px; float: none;\"\u003e\u003c\/div\u003e\n\u003cp\u003eNicht die Rahmen der Fenster bemalen! Die Farben sind nach längerer Trocknungszeit auf vielen Oberflächen wie Holz und Stein permanent haftend.\u003c\/p\u003e\n\u003cul\u003e\n\u003cli\u003eAcryltinte auf Wasserbasis in Künstlerqualität | Füllmenge 6 ml\u003cbr\u003e\n\u003c\/li\u003e\n\u003cli\u003eDer Stift entspricht ISO 11540\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003cul\u003e\u003c\/ul\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Frühlingsgrün","offer_id":44743268204811,"sku":"550001296","price":3.95,"currency_code":"EUR","in_stock":false},{"title":"Türkis","offer_id":44743273054475,"sku":"550001297","price":3.95,"currency_code":"EUR","in_stock":false},{"title":"Schattengrün","offer_id":44743273545995,"sku":"550001298","price":3.95,"currency_code":"EUR","in_stock":false},{"title":"Seegrün","offer_id":44743274725643,"sku":"550001299","price":3.95,"currency_code":"EUR","in_stock":false},{"title":"Grasgrün","offer_id":44743276298507,"sku":"550001300","price":3.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/fruhlingsgrunmarker.jpg?v=1709758119"},{"product_id":"multimarker-blautone-einzeln","title":"MULTIMARKER Blautöne Einzeln","description":"\u003cp\u003e\u003cem style=\"font-size: 1.4em;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Marker-B_480x480.jpg?v=1572784444\" alt=\"Bine Brändle MULTIMARKER\"\u003e\u003c\/em\u003e\u003cstrong\u003eStrichbreite 2-3 mm • von Glas nass entfernbar • auf rauen Flächen permanent\u003c\/strong\u003e\u003c\/p\u003e\n\u003cdiv style=\"text-align: left;\" data-mce-style=\"text-align: left;\"\u003e\u003cimg style=\"margin-top: 9px; margin-bottom: 9px; float: none;\" alt=\"von Glass Nass, auf Stein Holz permanent Made in Germany\" src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Kreise_Glass_Perma_G_600x200.png?v=1629532491\u0026gt;\u0026lt;\/div\u0026gt;%0A\u0026lt;div%20style=\" text-align:=\"\" data-mce-src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Kreise_Glass_Perma_G_600x200.png?v=1629532491\u0026gt;\u0026lt;\/div\u0026gt;%0A\u0026lt;div%20style=\"\u003e\u003c\/div\u003e\n\u003cdiv data-mce-style=\"text-align: left;\"\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv data-mce-style=\"text-align: left;\"\u003eUm die Farbe nach längerer Zeit von Glas zu entfernen, das Motiv mit Wasser oder Fensterputzmittel anfeuchten und die Flüssigkeit kurz einwirken lassen. Nun die Scheibe mit der rauen Seite eines für Glas geeigneten Spülschwamms putzen. Wenn nötig den Vorgang wiederholen, bis alle Rückstände entfernt sind. Achtung! Keine Putzutensilien verwenden, die das Fenster zerkratzen könnten! (wie z.B. Topfreiniger)\u003c\/div\u003e\n\u003cdiv data-mce-style=\"text-align: left;\"\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv style=\"text-align: start;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/anMULTI.png?v=1629539920\" alt=\"Start Anweisungen\" style=\"margin-top: 9px; margin-bottom: 9px; float: none;\"\u003e\u003c\/div\u003e\n\u003cp\u003eNicht die Rahmen der Fenster bemalen! Die Farben sind nach längerer Trocknungszeit auf vielen Oberflächen wie Holz und Stein permanent haftend.\u003c\/p\u003e\n\u003cul\u003e\n\u003cli\u003eAcryltinte auf Wasserbasis in Künstlerqualität | Füllmenge 6 ml\u003cbr\u003e\n\u003c\/li\u003e\n\u003cli\u003eDer Stift entspricht ISO 11540\u003c\/li\u003e\n\u003c\/ul\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Kornblume","offer_id":44743279247627,"sku":"550001291","price":3.95,"currency_code":"EUR","in_stock":false},{"title":"Cyan","offer_id":44743282622731,"sku":"550001292","price":3.95,"currency_code":"EUR","in_stock":true},{"title":"Mitternachtsblau","offer_id":44743286948107,"sku":"550001293","price":3.95,"currency_code":"EUR","in_stock":false},{"title":"Babyblau","offer_id":44743287505163,"sku":"550001294","price":3.95,"currency_code":"EUR","in_stock":false},{"title":"Lavendel","offer_id":44743288160523,"sku":"550001295","price":3.95,"currency_code":"EUR","in_stock":false}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/kornblumemarker.jpg?v=1709759519"},{"product_id":"osterkonfetti-gelb","title":"Osterkonfetti-Gelb - Tasse","description":"\u003cp\u003e\u003cstrong\u003eOriginelle Sammeltasse aus Keramik mit dem \"Osterkonfetti\" Motiv auf Gelber Tasse. \u003c\/strong\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cstrong\u003eIllustriert von Bine Brändle \u003c\/strong\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan\u003eFür ca. 300ml Kaffee, Tee, Kakao oder Dreckwasser vom Pinsel.\u003c\/span\u003e\u003cbr\u003e\u003cbr\u003e\u003cspan\u003eDie brillanten Farben sind spülmaschinenfest, lebensmittelecht und mikrowellengeeignet.\u003c\/span\u003e\u003c\/p\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Default Title","offer_id":45071498477835,"sku":"550001241","price":14.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/tasse_osterkonfetti_F_gelb_d4bdcdea-f92d-48cc-90f3-2046dd99a1ed.jpg?v=1709854116"},{"product_id":"holzkiste-fur-desda-flussigwasserfarben","title":"Holzkiste für DESDA Flüssigwasserfarben","description":"\u003ch3\u003eHolzkiste mit genug Platz für 16 DESDA Flüssigwasserfarben.\u003c\/h3\u003e\n\u003cem\u003eNUR DIE HOLZKISTE, KEINE FLÜSSIGWASSERFARBEN ENTHALTEN.\u003c\/em\u003e\u003cbr\u003e\n\u003cp\u003e\u003cbr\u003e\u003c\/p\u003e\n\u003cp\u003e \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Kreis_Made_EU.png?v=1633629828\" alt=\"Made in EU\" width=\"121\" height=\"121\" style=\"font-size: 0.875rem;\" data-mce-fragment=\"1\" data-mce-src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Kreis_Made_EU.png?v=1633629828\"\u003e\u003c\/p\u003e\n\u003cp\u003e \u003c\/p\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Default Title","offer_id":45130100211979,"sku":"550001214","price":19.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Wasserfarben_inHolz_3_a47062f4-8ed9-4711-835d-4855cad6c41a.jpg?v=1710876759"},{"product_id":"dekokissen-bordurenliebe-gelb-80-x-40-cm","title":"Dekokissen - Bordürenliebe Gelb - 80 x 40 cm","description":"\u003cul\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003e80 x 40 cm\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eZwei Designs kombiniert in einem Kissen\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eAbziehbar und waschbar\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eBezug aus 100% Baumwolle\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eKomplett mit Innenkissen\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eDesigned by Bine Brändle\u003c\/span\u003e\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003cp\u003e\u003cspan style=\"font-size: 18px;\"\u003eMit viel Liebe entworfen von der bekannten Künstlerin Bine Brändle\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eDer Bordürenstoff in Gelbtönen wurde mit „Fetten Mintfarbenen Punkten“ auf der Rückseite kombiniert und mit einer hochwertigen gelben Biese abgerundet. Achtung: Aufgrund des Musterverlaufs kann jedes Kissen anders sein!\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan style=\"font-size: 18px;\"\u003eKeine Lust und Zeit zum Nähen?\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eBeschenken Sie sich selbst und andere mit den einmaligen Dekokissen aus der bunten Bine Brändle Stoffkollektion. Der besondere Hingucker als Bodenkissen oder als Dekoration auf dem Sofa, auf Stühlen oder Sesseln und natürlich auch im Garten. Für gute Laune und Gemütlichkeit!\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan style=\"font-size: 18px;\"\u003eDie Qual der Wahl\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eDie Dekokissen sind in unterschiedlichen Größen und Motiven erhältlich, kombiniert mit Biesen, Bommeln\/ Pompoms und Quasten im Boho-Style. Dank der unterschiedlichen Rückseiten kann die Ansicht ständig gewechselt werden. Mixen Sie wild durcheinander! Je mehr Farben und Designs, umso besser, denn alles passt zusammen.\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan style=\"font-size: 18px;\"\u003eQualität\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eDie Materialien wurden sorgfältig ausgewählt und sind qualitativ sehr hochwertig. Der Bezug der Dekokissen besteht aus 100% Baumwolle und die weiche Füllung aus Faserbällchen (OEKO-TEX® Standard 100). Es verfügt zudem über einen seitlich unauffällig integrierten Reißverschluss, wodurch der Bezug pflegeleicht bei 40°C in der Waschmaschine gereinigt werden kann. Die Produktion erfolgt in der EU zu fairen Bedingungen.\u003c\/p\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Default Title","offer_id":51327297683723,"sku":"550001012","price":35.99,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/G4-1.jpg?v=1745853707"},{"product_id":"dekokissen-bordurenliebe-grun-80-x-40-cm","title":"Dekokissen - Bordürenliebe Grün - 80 x 40 cm","description":"\u003cul\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003e80 x 40 cm\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eZwei Designs kombiniert in einem Kissen\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eAbziehbar und waschbar\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eBezug aus 100% Baumwolle\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eKomplett mit Innenkissen\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eDesigned by Bine Brändle\u003c\/span\u003e\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003cp\u003e\u003cspan style=\"font-size: 18px;\"\u003eMit viel Liebe entworfen von der bekannten Künstlerin Bine Brändle\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eDer Bordürenstoff in Gelbtönen wurde mit „Fetten Mintfarbenen Punkten“ auf der Rückseite kombiniert und mit einer hochwertigen gelben Biese abgerundet. Achtung: Aufgrund des Musterverlaufs kann jedes Kissen anders sein!\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan style=\"font-size: 18px;\"\u003eKeine Lust und Zeit zum Nähen?\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eBeschenken Sie sich selbst und andere mit den einmaligen Dekokissen aus der bunten Bine Brändle Stoffkollektion. Der besondere Hingucker als Bodenkissen oder als Dekoration auf dem Sofa, auf Stühlen oder Sesseln und natürlich auch im Garten. Für gute Laune und Gemütlichkeit!\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan style=\"font-size: 18px;\"\u003eDie Qual der Wahl\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eDie Dekokissen sind in unterschiedlichen Größen und Motiven erhältlich, kombiniert mit Biesen, Bommeln\/ Pompoms und Quasten im Boho-Style. Dank der unterschiedlichen Rückseiten kann die Ansicht ständig gewechselt werden. Mixen Sie wild durcheinander! Je mehr Farben und Designs, umso besser, denn alles passt zusammen.\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan style=\"font-size: 18px;\"\u003eQualität\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eDie Materialien wurden sorgfältig ausgewählt und sind qualitativ sehr hochwertig. Der Bezug der Dekokissen besteht aus 100% Baumwolle und die weiche Füllung aus Faserbällchen (OEKO-TEX® Standard 100). Es verfügt zudem über einen seitlich unauffällig integrierten Reißverschluss, wodurch der Bezug pflegeleicht bei 40°C in der Waschmaschine gereinigt werden kann. Die Produktion erfolgt in der EU zu fairen Bedingungen.\u003c\/p\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Default Title","offer_id":51327349424395,"sku":"550001010","price":35.99,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/GR4-1.jpg?v=1745854918"},{"product_id":"dekokissen-bordurenliebe-lila-60-x-30-cm","title":"Dekokissen - Bordürenliebe Lila - 60 x 30 cm","description":"\u003cul\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003e60 x 30 cm\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eZwei Designs kombiniert in einem Kissen\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eAbziehbar und waschbar\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eBezug aus 100% Baumwolle\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eKomplett mit Innenkissen\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eDesigned by Bine Brändle\u003c\/span\u003e\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003cp\u003e\u003cspan style=\"font-size: 18px;\"\u003eMit viel Liebe entworfen von der bekannten Künstlerin Bine Brändle\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eDer Bordürenstoff in Lilatönen wurde mit „Fetten Blauen Punkten“ auf der Rückseite kombiniert und erhält durch eine 2-farbige Boho-Quastenborte noch einen Upgrade. Achtung: Aufgrund des Musterverlaufs kann jedes Kissen anders sein!\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan style=\"font-size: 18px;\"\u003eKeine Lust und Zeit zum Nähen?\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eBeschenken Sie sich selbst und andere mit den einmaligen Dekokissen aus der bunten Bine Brändle Stoffkollektion. Der besondere Hingucker als Bodenkissen oder als Dekoration auf dem Sofa, auf Stühlen oder Sesseln und natürlich auch im Garten. Für gute Laune und Gemütlichkeit!\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan style=\"font-size: 18px;\"\u003eDie Qual der Wahl\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eDie Dekokissen sind in unterschiedlichen Größen und Motiven erhältlich, kombiniert mit Biesen, Bommeln\/ Pompoms und Quasten im Boho-Style. Dank der unterschiedlichen Rückseiten kann die Ansicht ständig gewechselt werden. Mixen Sie wild durcheinander! Je mehr Farben und Designs, umso besser, denn alles passt zusammen.\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan style=\"font-size: 18px;\"\u003eQualität\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eDie Materialien wurden sorgfältig ausgewählt und sind qualitativ sehr hochwertig. Der Bezug der Dekokissen besteht aus 100% Baumwolle und die weiche Füllung aus Faserbällchen (OEKO-TEX® Standard 100). Es verfügt zudem über einen seitlich unauffällig integrierten Reißverschluss, wodurch der Bezug pflegeleicht bei 40°C in der Waschmaschine gereinigt werden kann. Die Produktion erfolgt in der EU zu fairen Bedingungen.\u003c\/p\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Default Title","offer_id":51328743342347,"sku":"550001006","price":27.99,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/L3-1.jpg?v=1745867601"},{"product_id":"dekokissen-mandala-sonnentag-50-cm","title":"Dekokissen - Mandala Sonnentag - ⌀ 50 cm","description":"\u003cul\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eDurchmesser 50 cm\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eAbziehbar und waschbar\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eBezug aus 100% Baumwolle\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eKomplett mit Innenkissen\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eDesigned by Bine Brändle\u003c\/span\u003e\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003cp\u003e\u003cspan style=\"font-size: 18px;\"\u003eMit viel Liebe entworfen von der bekannten Künstlerin Bine Brändle\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eDas wunderschöne Mandalamotiv auf Vorder- und Rückseite wurde mit einer Pompom-Borte kombiniert. Auch als Sitzkissen mit Schaumstoffkern erhältlich!\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan style=\"font-size: 18px;\"\u003eKeine Lust und Zeit zum Nähen?\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eBeschenken Sie sich selbst und andere mit den einmaligen Dekokissen aus der bunten Bine Brändle Stoffkollektion. Der besondere Hingucker als Bodenkissen oder als Dekoration auf dem Sofa, auf Stühlen oder Sesseln und natürlich auch im Garten. Für gute Laune und Gemütlichkeit!\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan style=\"font-size: 18px;\"\u003eDie Qual der Wahl\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eDie Dekokissen sind in unterschiedlichen Größen und Motiven erhältlich, kombiniert mit Biesen, Bommeln\/ Pompoms und Quasten im Boho-Style. Dank der unterschiedlichen Rückseiten kann die Ansicht ständig gewechselt werden. Mixen Sie wild durcheinander! Je mehr Farben und Designs, umso besser, denn alles passt zusammen.\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan style=\"font-size: 18px;\"\u003eQualität\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eDie Materialien wurden sorgfältig ausgewählt und sind qualitativ sehr hochwertig. Der Bezug der Dekokissen besteht aus 100% Baumwolle (OEKO-TEX® Standard 100). Es verfügt zudem über einen seitlich unauffällig integrierten Reißverschluss, wodurch der Bezug pflegeleicht bei 40°C in der Waschmaschine gereinigt werden kann. Die Produktion erfolgt in der EU zu fairen Bedingungen.\u003c\/p\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Default Title","offer_id":51328788496651,"sku":"550001002","price":27.99,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/M4-50-1.jpg?v=1745868427"},{"product_id":"dekokissen-seerose-lila-42-cm","title":"Dekokissen - Seerose Lila - ⌀ 42 cm","description":"\u003cul\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eDurchmesser 42 cm\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eAbziehbar und waschbar\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eBezug aus 100% Baumwolle\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eKomplett mit Innenkissen\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eDesigned by Bine Brändle\u003c\/span\u003e\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003cp\u003e\u003cspan style=\"font-size: 18px;\"\u003eMit viel Liebe entworfen von der bekannten Künstlerin Bine Brändle\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eDas wunderschöne Blütenmotiv auf Vorder- und Rückseite wurde mit einer pinken Biese kombiniert. Auch als Sitzkissen mit Schaumstoffkern erhältlich!\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan style=\"font-size: 18px;\"\u003eKeine Lust und Zeit zum Nähen?\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eBeschenken Sie sich selbst und andere mit den einmaligen Dekokissen aus der bunten Bine Brändle Stoffkollektion. Der besondere Hingucker als Bodenkissen oder als Dekoration auf dem Sofa, auf Stühlen oder Sesseln und natürlich auch im Garten. Für gute Laune und Gemütlichkeit!\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan style=\"font-size: 18px;\"\u003eDie Qual der Wahl\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eDie Dekokissen sind in unterschiedlichen Größen und Motiven erhältlich, kombiniert mit Biesen, Bommeln\/ Pompoms und Quasten im Boho-Style. Dank der unterschiedlichen Rückseiten kann die Ansicht ständig gewechselt werden. Mixen Sie wild durcheinander! Je mehr Farben und Designs, umso besser, denn alles passt zusammen.\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan style=\"font-size: 18px;\"\u003eQualität\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eDie Materialien wurden sorgfältig ausgewählt und sind qualitativ sehr hochwertig. Der Bezug der Dekokissen besteht aus 100% Baumwolle und die weiche Füllung aus Faserbällchen (OEKO-TEX® Standard 100). Es verfügt zudem über einen seitlich unauffällig integrierten Reißverschluss, wodurch der Bezug pflegeleicht bei 40°C in der Waschmaschine gereinigt werden kann. Die Produktion erfolgt in der EU zu fairen Bedingungen.\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan style=\"font-size: 18px;\"\u003eZufriedenheitsgarantie\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eWir freuen uns sehr, diese tollen Produkte von Bine Brändle anbieten zu können und unser Anspruch ist es, dass unsere kleinen und großen Kunden zufrieden sind. Wir verwenden daher nur hochwertige Materialien und unsere Kissen unterliegen einer ständigen Qualitätskontrolle. Bei Fragen und Anregungen sind wir jederzeit für Sie da!\u003cbr\u003e \u003c\/p\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Default Title","offer_id":51329280246027,"sku":"550000987","price":23.99,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/S4-2.jpg?v=1745873244"},{"product_id":"dekokissen-mandalablute-blau-42-cm","title":"Dekokissen - Mandalablüte Blau - ⌀ 42 cm","description":"\u003cul\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eDurchmesser 42 cm\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eAbziehbar und waschbar\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eBezug aus 100% Baumwolle\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eKomplett mit Innenkissen\u003c\/span\u003e\u003c\/li\u003e\n\u003cli\u003e\u003cspan style=\"color: rgb(136, 136, 136);\"\u003eDesigned by Bine Brändle\u003c\/span\u003e\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003cp\u003e\u003cspan style=\"font-size: 18px;\"\u003eMit viel Liebe entworfen von der bekannten Künstlerin Bine Brändle\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eDas wunderschöne Mandalamotiv auf Vorder- und Rückseite wurde mit einer Pompom-Borte kombiniert. Auch als Sitzkissen mit Schaumstoffkern erhältlich!\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan style=\"font-size: 18px;\"\u003eKeine Lust und Zeit zum Nähen?\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eBeschenken Sie sich selbst und andere mit den einmaligen Dekokissen aus der bunten Bine Brändle Stoffkollektion. Der besondere Hingucker als Bodenkissen oder als Dekoration auf dem Sofa, auf Stühlen oder Sesseln und natürlich auch im Garten. Für gute Laune und Gemütlichkeit!\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan style=\"font-size: 18px;\"\u003eDie Qual der Wahl\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eDie Dekokissen sind in unterschiedlichen Größen und Motiven erhältlich, kombiniert mit Biesen, Bommeln\/ Pompoms und Quasten im Boho-Style. Dank der unterschiedlichen Rückseiten kann die Ansicht ständig gewechselt werden. Mixen Sie wild durcheinander! Je mehr Farben und Designs, umso besser, denn alles passt zusammen.\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003e\u003cspan style=\"font-size: 18px;\"\u003eQualität\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003eDie Materialien wurden sorgfältig ausgewählt und sind qualitativ sehr hochwertig. Der Bezug der Dekokissen besteht aus 100% Baumwolle (OEKO-TEX® Standard 100). Es verfügt zudem über einen seitlich unauffällig integrierten Reißverschluss, wodurch der Bezug pflegeleicht bei 40°C in der Waschmaschine gereinigt werden kann. Die Produktion erfolgt in der EU zu fairen Bedingungen.\u003c\/p\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Default Title","offer_id":51329406828811,"sku":"550000985","price":23.99,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/MB1-1.jpg?v=1745873718"},{"product_id":"dreiecke-bunt-stoff","title":"Dreiecke bunt - Stoff","description":"\u003cdiv style=\"text-align: left;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo_Stoffe_by_Bine_Brandle_480x480.png?v=1750456378\" alt=\"Bines Stoffe Logo\" style=\"margin-bottom: 16px; float: none; display: block; margin-left: auto; margin-right: auto;\"\u003e\u003c\/div\u003e\n\u003cdiv style=\"text-align: left;\"\u003e\n\u003cp\u003e\u003cem\u003eBei der Auswahl der Menge entspricht jede Einheit 1 Meter Stoff – z. B. Menge 3 = 3 Meter Stoff.\u003c\/em\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003eRapport-Muster können variieren.\u003c\/em\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003eBei Bestellungen über einer länge von 5 Metern kann eine Lieferung aus einem zusammenhängendem Stoffstück nicht garantiert werden.\u003c\/em\u003e\u003c\/p\u003e\n\u003c\/div\u003e\n\u003cp\u003e\u003cem\u003eAlle Stoffe verfügen über den \"Öko-Tex® Standard 100\"\u003c\/em\u003e\u003c\/p\u003e\n\u003cdiv style=\"text-align: left;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"\" style=\"float: none;\"\u003e\u003c\/div\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n      Jersey\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n      Baumwolle\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Musselin\" role=\"tab\" aria-selected=\"false\"\u003e\n      Musselin\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Sweat\" role=\"tab\" aria-selected=\"false\"\u003e\n      Sweat\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eJersey\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Weicher und elastischer Stoff, ideal für bequeme Kleidung.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Jersey\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Shirts, Kleider, Mützen und Kinderbekleidung\u003c\/li\u003e\n      \u003cli\u003eBreite: ca. 160 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht: cs. 220 g\/qm2\u003c\/li\u003e\n      \u003cli\u003e95% Baumwolle 5% Elasthan\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eBaumwolle\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Vielseitig verwendbarer Baumwollstoff.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Baumwolle\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Taschen, Kissen, Dekoration und vieles mehr\u003c\/li\u003e\n      \u003cli\u003eBreite: ca. 160 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht: ca. 150 g\/qm²\u003c\/li\u003e\n      \u003cli\u003e100% Baumwolle\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- MUSSELIN --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Musselin\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eMusselin\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Luftiger, weicher Stoff mit charakteristischer Struktur.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Musselin\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Tücher, leichte Kleidung\u003c\/li\u003e\n      \u003cli\u003eBreite ca. 134 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht ca. 120 g\/m²\u003c\/li\u003e\n      \u003cli\u003eDouble Layer\u003c\/li\u003e\n      \u003cli\u003e100 % Baumwolle\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C mit Feinwaschmittel\u003c\/li\u003e\n      \u003cli\u003eDas Material kann bis zu 8% einlaufen\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- SWEAT --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Sweat\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eSweat\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Weicher, etwas kräftigerer Stoff für gemütliche Kleidungsstücke.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Sweat\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Pullover, Hosen, Jacken und Hoodies\u003c\/li\u003e\n     \u003cli\u003e Breite ca. 160 cm\u003c\/li\u003e\n     \u003cli\u003eGewicht ca. 310 g\/m²\u003c\/li\u003e\n     \u003cli\u003e96% Baumwolle 4% Elasthan mit Feinfutter\u003c\/li\u003e\n     \u003cli\u003eToller, kuschelig weicher Sommersweat, innen flauschig-weich angerauht\u003c\/li\u003e\n     \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n     \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    padding: 12px 18px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tabs__buttons {\n      display: grid;\n      grid-template-columns: repeat(2, 1fr);\n    }\n\n    .stoff-tab-button {\n      width: 100%;\n      padding: 11px 7px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\n    \"Jersey\",\n    \"Baumwolle\",\n    \"Musselin\",\n    \"Sweat\"\n  ];\n\n  function normalizeText(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isKnownStoffart(value) {\n    const normalizedValue = normalizeText(value);\n\n    return STOFFARTEN.some(function (stoffart) {\n      return normalizeText(stoffart) === normalizedValue;\n    });\n  }\n\n  function getCorrectName(value) {\n    const normalizedValue = normalizeText(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalizeText(stoffart) === normalizedValue;\n    }) || null;\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalizeText(element.dataset.stoffButton) ===\n        normalizeText(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalizeText(correctName);\n\n    container.querySelectorAll(\"[data-stoff-button]\").forEach(function (button) {\n      const active =\n        !button.classList.contains(\"is-unavailable\") \u0026\u0026\n        normalizeText(button.dataset.stoffButton) === wanted;\n\n      button.classList.toggle(\"is-active\", active);\n      button.setAttribute(\"aria-selected\", active ? \"true\" : \"false\");\n    });\n\n    container.querySelectorAll(\"[data-stoff-content]\").forEach(function (content) {\n      const active =\n        !content.classList.contains(\"is-unavailable\") \u0026\u0026\n        normalizeText(content.dataset.stoffContent) === wanted;\n\n      content.classList.toggle(\"is-active\", active);\n      content.hidden = !active;\n    });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(container, firstButton.dataset.stoffButton);\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section = container.closest(\".shopify-section\") || document;\n\n    const checkedInputs = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const input of checkedInputs) {\n      const value = input.value;\n\n      if (isKnownStoffart(value)) {\n        return getCorrectName(value);\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabsWithSelection() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart = getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function getProductJsonUrl() {\n    let productPath = window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return null;\n    }\n\n    return productPath + \".js\";\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabsWithSelection();\n      return;\n    }\n\n    const availability = {};\n\n    STOFFARTEN.forEach(function (stoffart) {\n      availability[normalizeText(stoffart)] = false;\n    });\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const optionValues = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      optionValues.forEach(function (optionValue) {\n        const correctName = getCorrectName(optionValue);\n\n        if (correctName) {\n          availability[normalizeText(correctName)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available = availability[normalizeText(stoffart)] === true;\n\n        const button = Array.from(\n          container.querySelectorAll(\"[data-stoff-button]\")\n        ).find(function (element) {\n          return normalizeText(element.dataset.stoffButton) ===\n            normalizeText(stoffart);\n        });\n\n        const content = Array.from(\n          container.querySelectorAll(\"[data-stoff-content]\")\n        ).find(function (element) {\n          return normalizeText(element.dataset.stoffContent) ===\n            normalizeText(stoffart);\n        });\n\n        if (button) {\n          button.classList.toggle(\"is-unavailable\", !available);\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\"is-unavailable\", !available);\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabsWithSelection();\n  }\n\n  function loadAvailability() {\n    const productJsonUrl = getProductJsonUrl();\n\n    if (!productJsonUrl) {\n      syncTabsWithSelection();\n      return;\n    }\n\n    fetch(productJsonUrl)\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\"Produktdaten konnten nicht geladen werden.\");\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        \/*\n         * Falls die Produktdaten nicht geladen werden können,\n         * bleiben vorsichtshalber alle Registerkarten sichtbar.\n         *\/\n        syncTabsWithSelection();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button = event.target.closest(\"[data-stoff-button]\");\n\n    if (!button || button.classList.contains(\"is-unavailable\")) {\n      return;\n    }\n\n    const container = button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(container, button.dataset.stoffButton);\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches('input[type=\"radio\"], select')\n    ) {\n      window.setTimeout(syncTabsWithSelection, 50);\n    }\n  });\n\n  document.addEventListener(\"DOMContentLoaded\", function () {\n    loadAvailability();\n    window.setTimeout(syncTabsWithSelection, 300);\n  });\n\n  if (document.readyState !== \"loading\") {\n    loadAvailability();\n    window.setTimeout(syncTabsWithSelection, 300);\n  }\n})();\n\u003c\/script\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Jersey","offer_id":51754043244811,"sku":"550000844","price":26.95,"currency_code":"EUR","in_stock":false},{"title":"Baumwolle","offer_id":51754043277579,"sku":"550000845","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Musselin","offer_id":51754043310347,"sku":"550000846","price":26.95,"currency_code":"EUR","in_stock":false},{"title":"Sweat","offer_id":51754043343115,"sku":"550000847","price":33.95,"currency_code":"EUR","in_stock":false}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-Dreieckebunt-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1750544162"},{"product_id":"patchwork-nahte-weiss-bunt-stoff","title":"Patchwork Nähte weiß bunt - Stoff","description":"\u003cdiv style=\"text-align: left;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo_Stoffe_by_Bine_Brandle_480x480.png?v=1750456378\" alt=\"Bines Stoffe Logo\" style=\"margin-bottom: 16px; float: none; display: block; margin-left: auto; margin-right: auto;\"\u003e\u003c\/div\u003e\n\u003cdiv style=\"text-align: left;\"\u003e\n\u003cp\u003e\u003cem\u003eBei der Auswahl der Menge entspricht jede Einheit 1 Meter Stoff – z. B. Menge 3 = 3 Meter Stoff.\u003c\/em\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003eRapport-Muster können variieren.\u003c\/em\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003eBei Bestellungen über einer länge von 5 Metern kann eine Lieferung aus einem zusammenhängendem Stoffstück nicht garantiert werden.\u003c\/em\u003e\u003c\/p\u003e\n\u003c\/div\u003e\n\u003cp\u003e\u003cem\u003eAlle Stoffe verfügen über den \"Öko-Tex® Standard 100\"\u003c\/em\u003e\u003c\/p\u003e\n\u003cdiv style=\"text-align: left;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"\" style=\"float: none;\"\u003e\u003c\/div\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n      Jersey\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n      Baumwolle\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Musselin\" role=\"tab\" aria-selected=\"false\"\u003e\n      Musselin\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Sweat\" role=\"tab\" aria-selected=\"false\"\u003e\n      Sweat\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eJersey\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Weicher und elastischer Stoff, ideal für bequeme Kleidung.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Jersey\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Shirts, Kleider, Mützen und Kinderbekleidung\u003c\/li\u003e\n      \u003cli\u003eBreite: ca. 160 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht: cs. 220 g\/qm2\u003c\/li\u003e\n      \u003cli\u003e95% Baumwolle 5% Elasthan\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eBaumwolle\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Vielseitig verwendbarer Baumwollstoff.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Baumwolle\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Taschen, Kissen, Dekoration und vieles mehr\u003c\/li\u003e\n      \u003cli\u003eBreite: ca. 160 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht: ca. 150 g\/qm²\u003c\/li\u003e\n      \u003cli\u003e100% Baumwolle\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- MUSSELIN --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Musselin\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eMusselin\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Luftiger, weicher Stoff mit charakteristischer Struktur.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Musselin\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Tücher, leichte Kleidung\u003c\/li\u003e\n      \u003cli\u003eBreite ca. 134 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht ca. 120 g\/m²\u003c\/li\u003e\n      \u003cli\u003eDouble Layer\u003c\/li\u003e\n      \u003cli\u003e100 % Baumwolle\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C mit Feinwaschmittel\u003c\/li\u003e\n      \u003cli\u003eDas Material kann bis zu 8% einlaufen\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- SWEAT --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Sweat\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eSweat\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Weicher, etwas kräftigerer Stoff für gemütliche Kleidungsstücke.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Sweat\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Pullover, Hosen, Jacken und Hoodies\u003c\/li\u003e\n     \u003cli\u003e Breite ca. 160 cm\u003c\/li\u003e\n     \u003cli\u003eGewicht ca. 310 g\/m²\u003c\/li\u003e\n     \u003cli\u003e96% Baumwolle 4% Elasthan mit Feinfutter\u003c\/li\u003e\n     \u003cli\u003eToller, kuschelig weicher Sommersweat, innen flauschig-weich angerauht\u003c\/li\u003e\n     \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n     \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    padding: 12px 18px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tabs__buttons {\n      display: grid;\n      grid-template-columns: repeat(2, 1fr);\n    }\n\n    .stoff-tab-button {\n      width: 100%;\n      padding: 11px 7px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\n    \"Jersey\",\n    \"Baumwolle\",\n    \"Musselin\",\n    \"Sweat\"\n  ];\n\n  function normalizeText(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isKnownStoffart(value) {\n    const normalizedValue = normalizeText(value);\n\n    return STOFFARTEN.some(function (stoffart) {\n      return normalizeText(stoffart) === normalizedValue;\n    });\n  }\n\n  function getCorrectName(value) {\n    const normalizedValue = normalizeText(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalizeText(stoffart) === normalizedValue;\n    }) || null;\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalizeText(element.dataset.stoffButton) ===\n        normalizeText(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalizeText(correctName);\n\n    container.querySelectorAll(\"[data-stoff-button]\").forEach(function (button) {\n      const active =\n        !button.classList.contains(\"is-unavailable\") \u0026\u0026\n        normalizeText(button.dataset.stoffButton) === wanted;\n\n      button.classList.toggle(\"is-active\", active);\n      button.setAttribute(\"aria-selected\", active ? \"true\" : \"false\");\n    });\n\n    container.querySelectorAll(\"[data-stoff-content]\").forEach(function (content) {\n      const active =\n        !content.classList.contains(\"is-unavailable\") \u0026\u0026\n        normalizeText(content.dataset.stoffContent) === wanted;\n\n      content.classList.toggle(\"is-active\", active);\n      content.hidden = !active;\n    });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(container, firstButton.dataset.stoffButton);\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section = container.closest(\".shopify-section\") || document;\n\n    const checkedInputs = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const input of checkedInputs) {\n      const value = input.value;\n\n      if (isKnownStoffart(value)) {\n        return getCorrectName(value);\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabsWithSelection() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart = getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function getProductJsonUrl() {\n    let productPath = window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return null;\n    }\n\n    return productPath + \".js\";\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabsWithSelection();\n      return;\n    }\n\n    const availability = {};\n\n    STOFFARTEN.forEach(function (stoffart) {\n      availability[normalizeText(stoffart)] = false;\n    });\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const optionValues = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      optionValues.forEach(function (optionValue) {\n        const correctName = getCorrectName(optionValue);\n\n        if (correctName) {\n          availability[normalizeText(correctName)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available = availability[normalizeText(stoffart)] === true;\n\n        const button = Array.from(\n          container.querySelectorAll(\"[data-stoff-button]\")\n        ).find(function (element) {\n          return normalizeText(element.dataset.stoffButton) ===\n            normalizeText(stoffart);\n        });\n\n        const content = Array.from(\n          container.querySelectorAll(\"[data-stoff-content]\")\n        ).find(function (element) {\n          return normalizeText(element.dataset.stoffContent) ===\n            normalizeText(stoffart);\n        });\n\n        if (button) {\n          button.classList.toggle(\"is-unavailable\", !available);\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\"is-unavailable\", !available);\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabsWithSelection();\n  }\n\n  function loadAvailability() {\n    const productJsonUrl = getProductJsonUrl();\n\n    if (!productJsonUrl) {\n      syncTabsWithSelection();\n      return;\n    }\n\n    fetch(productJsonUrl)\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\"Produktdaten konnten nicht geladen werden.\");\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        \/*\n         * Falls die Produktdaten nicht geladen werden können,\n         * bleiben vorsichtshalber alle Registerkarten sichtbar.\n         *\/\n        syncTabsWithSelection();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button = event.target.closest(\"[data-stoff-button]\");\n\n    if (!button || button.classList.contains(\"is-unavailable\")) {\n      return;\n    }\n\n    const container = button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(container, button.dataset.stoffButton);\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches('input[type=\"radio\"], select')\n    ) {\n      window.setTimeout(syncTabsWithSelection, 50);\n    }\n  });\n\n  document.addEventListener(\"DOMContentLoaded\", function () {\n    loadAvailability();\n    window.setTimeout(syncTabsWithSelection, 300);\n  });\n\n  if (document.readyState !== \"loading\") {\n    loadAvailability();\n    window.setTimeout(syncTabsWithSelection, 300);\n  }\n})();\n\u003c\/script\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Jersey","offer_id":51754075390219,"sku":"550000832","price":26.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle","offer_id":51754075422987,"sku":"550000833","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Musselin","offer_id":51754075455755,"sku":"550000834","price":26.95,"currency_code":"EUR","in_stock":false},{"title":"Sweat","offer_id":51754075488523,"sku":"550000835","price":33.95,"currency_code":"EUR","in_stock":false}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-PatchworkNahteweissbunt-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1750545758"},{"product_id":"schrage-streifen-bunt-stoff","title":"Schräge Streifen Bunt - Stoff","description":"\u003cdiv style=\"text-align: left;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo_Stoffe_by_Bine_Brandle_480x480.png?v=1750456378\" alt=\"Bines Stoffe Logo\" style=\"margin-bottom: 16px; float: none; display: block; margin-left: auto; margin-right: auto;\"\u003e\u003c\/div\u003e\n\u003cdiv style=\"text-align: left;\"\u003e\n\u003cp\u003e\u003cem\u003eBei der Auswahl der Menge entspricht jede Einheit 1 Meter Stoff – z. B. Menge 3 = 3 Meter Stoff.\u003c\/em\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003eRapport-Muster können variieren.\u003c\/em\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003eBei Bestellungen über einer länge von 5 Metern kann eine Lieferung aus einem zusammenhängendem Stoffstück nicht garantiert werden.\u003c\/em\u003e\u003c\/p\u003e\n\u003c\/div\u003e\n\u003cp\u003e\u003cem\u003eAlle Stoffe verfügen über den \"Öko-Tex® Standard 100\"\u003c\/em\u003e\u003c\/p\u003e\n\u003cdiv style=\"text-align: left;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"\" style=\"float: none;\"\u003e\u003c\/div\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n      Jersey\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n      Baumwolle\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Musselin\" role=\"tab\" aria-selected=\"false\"\u003e\n      Musselin\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Sweat\" role=\"tab\" aria-selected=\"false\"\u003e\n      Sweat\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eJersey\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Weicher und elastischer Stoff, ideal für bequeme Kleidung.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Jersey\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Shirts, Kleider, Mützen und Kinderbekleidung\u003c\/li\u003e\n      \u003cli\u003eBreite: ca. 160 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht: cs. 220 g\/qm2\u003c\/li\u003e\n      \u003cli\u003e95% Baumwolle 5% Elasthan\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eBaumwolle\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Vielseitig verwendbarer Baumwollstoff.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Baumwolle\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Taschen, Kissen, Dekoration und vieles mehr\u003c\/li\u003e\n      \u003cli\u003eBreite: ca. 160 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht: ca. 150 g\/qm²\u003c\/li\u003e\n      \u003cli\u003e100% Baumwolle\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- MUSSELIN --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Musselin\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eMusselin\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Luftiger, weicher Stoff mit charakteristischer Struktur.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Musselin\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Tücher, leichte Kleidung\u003c\/li\u003e\n      \u003cli\u003eBreite ca. 134 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht ca. 120 g\/m²\u003c\/li\u003e\n      \u003cli\u003eDouble Layer\u003c\/li\u003e\n      \u003cli\u003e100 % Baumwolle\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C mit Feinwaschmittel\u003c\/li\u003e\n      \u003cli\u003eDas Material kann bis zu 8% einlaufen\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- SWEAT --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Sweat\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eSweat\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Weicher, etwas kräftigerer Stoff für gemütliche Kleidungsstücke.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Sweat\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Pullover, Hosen, Jacken und Hoodies\u003c\/li\u003e\n     \u003cli\u003e Breite ca. 160 cm\u003c\/li\u003e\n     \u003cli\u003eGewicht ca. 310 g\/m²\u003c\/li\u003e\n     \u003cli\u003e96% Baumwolle 4% Elasthan mit Feinfutter\u003c\/li\u003e\n     \u003cli\u003eToller, kuschelig weicher Sommersweat, innen flauschig-weich angerauht\u003c\/li\u003e\n     \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n     \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    padding: 12px 18px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tabs__buttons {\n      display: grid;\n      grid-template-columns: repeat(2, 1fr);\n    }\n\n    .stoff-tab-button {\n      width: 100%;\n      padding: 11px 7px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\n    \"Jersey\",\n    \"Baumwolle\",\n    \"Musselin\",\n    \"Sweat\"\n  ];\n\n  function normalizeText(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isKnownStoffart(value) {\n    const normalizedValue = normalizeText(value);\n\n    return STOFFARTEN.some(function (stoffart) {\n      return normalizeText(stoffart) === normalizedValue;\n    });\n  }\n\n  function getCorrectName(value) {\n    const normalizedValue = normalizeText(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalizeText(stoffart) === normalizedValue;\n    }) || null;\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalizeText(element.dataset.stoffButton) ===\n        normalizeText(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalizeText(correctName);\n\n    container.querySelectorAll(\"[data-stoff-button]\").forEach(function (button) {\n      const active =\n        !button.classList.contains(\"is-unavailable\") \u0026\u0026\n        normalizeText(button.dataset.stoffButton) === wanted;\n\n      button.classList.toggle(\"is-active\", active);\n      button.setAttribute(\"aria-selected\", active ? \"true\" : \"false\");\n    });\n\n    container.querySelectorAll(\"[data-stoff-content]\").forEach(function (content) {\n      const active =\n        !content.classList.contains(\"is-unavailable\") \u0026\u0026\n        normalizeText(content.dataset.stoffContent) === wanted;\n\n      content.classList.toggle(\"is-active\", active);\n      content.hidden = !active;\n    });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(container, firstButton.dataset.stoffButton);\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section = container.closest(\".shopify-section\") || document;\n\n    const checkedInputs = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const input of checkedInputs) {\n      const value = input.value;\n\n      if (isKnownStoffart(value)) {\n        return getCorrectName(value);\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabsWithSelection() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart = getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function getProductJsonUrl() {\n    let productPath = window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return null;\n    }\n\n    return productPath + \".js\";\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabsWithSelection();\n      return;\n    }\n\n    const availability = {};\n\n    STOFFARTEN.forEach(function (stoffart) {\n      availability[normalizeText(stoffart)] = false;\n    });\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const optionValues = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      optionValues.forEach(function (optionValue) {\n        const correctName = getCorrectName(optionValue);\n\n        if (correctName) {\n          availability[normalizeText(correctName)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available = availability[normalizeText(stoffart)] === true;\n\n        const button = Array.from(\n          container.querySelectorAll(\"[data-stoff-button]\")\n        ).find(function (element) {\n          return normalizeText(element.dataset.stoffButton) ===\n            normalizeText(stoffart);\n        });\n\n        const content = Array.from(\n          container.querySelectorAll(\"[data-stoff-content]\")\n        ).find(function (element) {\n          return normalizeText(element.dataset.stoffContent) ===\n            normalizeText(stoffart);\n        });\n\n        if (button) {\n          button.classList.toggle(\"is-unavailable\", !available);\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\"is-unavailable\", !available);\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabsWithSelection();\n  }\n\n  function loadAvailability() {\n    const productJsonUrl = getProductJsonUrl();\n\n    if (!productJsonUrl) {\n      syncTabsWithSelection();\n      return;\n    }\n\n    fetch(productJsonUrl)\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\"Produktdaten konnten nicht geladen werden.\");\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        \/*\n         * Falls die Produktdaten nicht geladen werden können,\n         * bleiben vorsichtshalber alle Registerkarten sichtbar.\n         *\/\n        syncTabsWithSelection();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button = event.target.closest(\"[data-stoff-button]\");\n\n    if (!button || button.classList.contains(\"is-unavailable\")) {\n      return;\n    }\n\n    const container = button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(container, button.dataset.stoffButton);\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches('input[type=\"radio\"], select')\n    ) {\n      window.setTimeout(syncTabsWithSelection, 50);\n    }\n  });\n\n  document.addEventListener(\"DOMContentLoaded\", function () {\n    loadAvailability();\n    window.setTimeout(syncTabsWithSelection, 300);\n  });\n\n  if (document.readyState !== \"loading\") {\n    loadAvailability();\n    window.setTimeout(syncTabsWithSelection, 300);\n  }\n})();\n\u003c\/script\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Jersey","offer_id":51754116546827,"sku":"550000812","price":26.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle","offer_id":51754116579595,"sku":"550000813","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Musselin","offer_id":51754116612363,"sku":"550000814","price":26.95,"currency_code":"EUR","in_stock":false},{"title":"Sweat","offer_id":51754116645131,"sku":"550000815","price":33.95,"currency_code":"EUR","in_stock":false}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-SchrageStreifenBunt-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1750548122"},{"product_id":"bastelpaket-fensterfolien","title":"Bastelpaket Fensterfolien","description":"\u003cp\u003e\u003cmeta charset=\"UTF-8\"\u003e\u003cspan\u003eDie Folie kann beliebig zugeschnitten oder ausgestanzt werden – ideal für kreative Fensterdesigns mit tollen Farbeffekten durch Überlappung.\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003e- 90 Fensterfolien der Größe 12cm x 12cm (gesamt ca. 1,3qm)\u003c\/p\u003e\n\u003cp\u003e- farbig sortiert in den Farben Dunkelrot, Hellrot, Dunkelgrün, \u003c\/p\u003e\n\u003cp\u003e  Hellgrün, Dunkelblau, Hellblau, Pink, Orange und Gelb\u003c\/p\u003e\n\u003cp\u003e-phthalatfrei\u003c\/p\u003e\n\u003cp\u003e-gut UV-beständig\u003c\/p\u003e\n\u003cp\u003e-ohne Klebstoff statisch auf der Fensterscheibe haftend\u003c\/p\u003e\n\u003cp\u003e-rückstandsfrei ablösbar\u003c\/p\u003e\n\u003cp\u003e-wiederverwendbar\u003c\/p\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Default Title","offer_id":51908133355787,"sku":"550000806","price":12.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Fensterfolie_Quadrate4.jpg?v=1752176270"},{"product_id":"patchwork-dreiecke-dunkelblau","title":"Patchwork Dreiecke dunkelblau - Stoff","description":"\u003cdiv style=\"text-align: left;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo_Stoffe_by_Bine_Brandle_480x480.png?v=1750456378\" alt=\"Bines Stoffe Logo\" style=\"margin-bottom: 16px; float: none; display: block; margin-left: auto; margin-right: auto;\"\u003e\u003c\/div\u003e\n\u003cdiv style=\"text-align: left;\"\u003e\n\u003cp\u003e\u003cem\u003eBei der Auswahl der Menge entspricht jede Einheit 1 Meter Stoff – z. B. Menge 3 = 3 Meter Stoff.\u003c\/em\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003eRapport-Muster können variieren.\u003c\/em\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003eBei Bestellungen über einer länge von 5 Metern kann eine Lieferung aus einem zusammenhängendem Stoffstück nicht garantiert werden.\u003c\/em\u003e\u003c\/p\u003e\n\u003c\/div\u003e\n\u003cp\u003e\u003cem\u003eAlle Stoffe verfügen über den \"Öko-Tex® Standard 100\"\u003c\/em\u003e\u003c\/p\u003e\n\u003cdiv style=\"text-align: left;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"\" style=\"float: none;\"\u003e\u003c\/div\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n      Jersey\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n      Baumwolle\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Musselin\" role=\"tab\" aria-selected=\"false\"\u003e\n      Musselin\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Sweat\" role=\"tab\" aria-selected=\"false\"\u003e\n      Sweat\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eJersey\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Weicher und elastischer Stoff, ideal für bequeme Kleidung.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Jersey\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Shirts, Kleider, Mützen und Kinderbekleidung\u003c\/li\u003e\n      \u003cli\u003eBreite: ca. 160 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht: cs. 220 g\/qm2\u003c\/li\u003e\n      \u003cli\u003e95% Baumwolle 5% Elasthan\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eBaumwolle\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Vielseitig verwendbarer Baumwollstoff.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Baumwolle\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Taschen, Kissen, Dekoration und vieles mehr\u003c\/li\u003e\n      \u003cli\u003eBreite: ca. 160 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht: ca. 150 g\/qm²\u003c\/li\u003e\n      \u003cli\u003e100% Baumwolle\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- MUSSELIN --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Musselin\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eMusselin\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Luftiger, weicher Stoff mit charakteristischer Struktur.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Musselin\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Tücher, leichte Kleidung\u003c\/li\u003e\n      \u003cli\u003eBreite ca. 134 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht ca. 120 g\/m²\u003c\/li\u003e\n      \u003cli\u003eDouble Layer\u003c\/li\u003e\n      \u003cli\u003e100 % Baumwolle\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C mit Feinwaschmittel\u003c\/li\u003e\n      \u003cli\u003eDas Material kann bis zu 8% einlaufen\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- SWEAT --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Sweat\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eSweat\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Weicher, etwas kräftigerer Stoff für gemütliche Kleidungsstücke.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Sweat\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Pullover, Hosen, Jacken und Hoodies\u003c\/li\u003e\n     \u003cli\u003e Breite ca. 160 cm\u003c\/li\u003e\n     \u003cli\u003eGewicht ca. 310 g\/m²\u003c\/li\u003e\n     \u003cli\u003e96% Baumwolle 4% Elasthan mit Feinfutter\u003c\/li\u003e\n     \u003cli\u003eToller, kuschelig weicher Sommersweat, innen flauschig-weich angerauht\u003c\/li\u003e\n     \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n     \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    padding: 12px 18px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tabs__buttons {\n      display: grid;\n      grid-template-columns: repeat(2, 1fr);\n    }\n\n    .stoff-tab-button {\n      width: 100%;\n      padding: 11px 7px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\n    \"Jersey\",\n    \"Baumwolle\",\n    \"Musselin\",\n    \"Sweat\"\n  ];\n\n  function normalizeText(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isKnownStoffart(value) {\n    const normalizedValue = normalizeText(value);\n\n    return STOFFARTEN.some(function (stoffart) {\n      return normalizeText(stoffart) === normalizedValue;\n    });\n  }\n\n  function getCorrectName(value) {\n    const normalizedValue = normalizeText(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalizeText(stoffart) === normalizedValue;\n    }) || null;\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalizeText(element.dataset.stoffButton) ===\n        normalizeText(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalizeText(correctName);\n\n    container.querySelectorAll(\"[data-stoff-button]\").forEach(function (button) {\n      const active =\n        !button.classList.contains(\"is-unavailable\") \u0026\u0026\n        normalizeText(button.dataset.stoffButton) === wanted;\n\n      button.classList.toggle(\"is-active\", active);\n      button.setAttribute(\"aria-selected\", active ? \"true\" : \"false\");\n    });\n\n    container.querySelectorAll(\"[data-stoff-content]\").forEach(function (content) {\n      const active =\n        !content.classList.contains(\"is-unavailable\") \u0026\u0026\n        normalizeText(content.dataset.stoffContent) === wanted;\n\n      content.classList.toggle(\"is-active\", active);\n      content.hidden = !active;\n    });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(container, firstButton.dataset.stoffButton);\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section = container.closest(\".shopify-section\") || document;\n\n    const checkedInputs = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const input of checkedInputs) {\n      const value = input.value;\n\n      if (isKnownStoffart(value)) {\n        return getCorrectName(value);\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabsWithSelection() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart = getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function getProductJsonUrl() {\n    let productPath = window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return null;\n    }\n\n    return productPath + \".js\";\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabsWithSelection();\n      return;\n    }\n\n    const availability = {};\n\n    STOFFARTEN.forEach(function (stoffart) {\n      availability[normalizeText(stoffart)] = false;\n    });\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const optionValues = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      optionValues.forEach(function (optionValue) {\n        const correctName = getCorrectName(optionValue);\n\n        if (correctName) {\n          availability[normalizeText(correctName)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available = availability[normalizeText(stoffart)] === true;\n\n        const button = Array.from(\n          container.querySelectorAll(\"[data-stoff-button]\")\n        ).find(function (element) {\n          return normalizeText(element.dataset.stoffButton) ===\n            normalizeText(stoffart);\n        });\n\n        const content = Array.from(\n          container.querySelectorAll(\"[data-stoff-content]\")\n        ).find(function (element) {\n          return normalizeText(element.dataset.stoffContent) ===\n            normalizeText(stoffart);\n        });\n\n        if (button) {\n          button.classList.toggle(\"is-unavailable\", !available);\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\"is-unavailable\", !available);\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabsWithSelection();\n  }\n\n  function loadAvailability() {\n    const productJsonUrl = getProductJsonUrl();\n\n    if (!productJsonUrl) {\n      syncTabsWithSelection();\n      return;\n    }\n\n    fetch(productJsonUrl)\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\"Produktdaten konnten nicht geladen werden.\");\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        \/*\n         * Falls die Produktdaten nicht geladen werden können,\n         * bleiben vorsichtshalber alle Registerkarten sichtbar.\n         *\/\n        syncTabsWithSelection();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button = event.target.closest(\"[data-stoff-button]\");\n\n    if (!button || button.classList.contains(\"is-unavailable\")) {\n      return;\n    }\n\n    const container = button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(container, button.dataset.stoffButton);\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches('input[type=\"radio\"], select')\n    ) {\n      window.setTimeout(syncTabsWithSelection, 50);\n    }\n  });\n\n  document.addEventListener(\"DOMContentLoaded\", function () {\n    loadAvailability();\n    window.setTimeout(syncTabsWithSelection, 300);\n  });\n\n  if (document.readyState !== \"loading\") {\n    loadAvailability();\n    window.setTimeout(syncTabsWithSelection, 300);\n  }\n})();\n\u003c\/script\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle","offer_id":52019523813643,"sku":"550000804","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey","offer_id":52019523780875,"sku":"550000805","price":26.95,"currency_code":"EUR","in_stock":false}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-Dreieckedunkelblau-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1754046267"},{"product_id":"patchwork-nahte-blau","title":"Patchwork Nähte blau - Stoff","description":"\u003cdiv style=\"text-align: left;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo_Stoffe_by_Bine_Brandle_480x480.png?v=1750456378\" alt=\"Bines Stoffe Logo\" style=\"margin-bottom: 16px; float: none; display: block; margin-left: auto; margin-right: auto;\"\u003e\u003c\/div\u003e\n\u003cdiv style=\"text-align: left;\"\u003e\n\u003cp\u003e\u003cem\u003eBei der Auswahl der Menge entspricht jede Einheit 1 Meter Stoff – z. B. Menge 3 = 3 Meter Stoff.\u003c\/em\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003eRapport-Muster können variieren.\u003c\/em\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003eBei Bestellungen über einer länge von 5 Metern kann eine Lieferung aus einem zusammenhängendem Stoffstück nicht garantiert werden.\u003c\/em\u003e\u003c\/p\u003e\n\u003c\/div\u003e\n\u003cp\u003e\u003cem\u003eAlle Stoffe verfügen über den \"Öko-Tex® Standard 100\"\u003c\/em\u003e\u003c\/p\u003e\n\u003cdiv style=\"text-align: left;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"\" style=\"float: none;\"\u003e\u003c\/div\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n      Jersey\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n      Baumwolle\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Musselin\" role=\"tab\" aria-selected=\"false\"\u003e\n      Musselin\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Sweat\" role=\"tab\" aria-selected=\"false\"\u003e\n      Sweat\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eJersey\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Weicher und elastischer Stoff, ideal für bequeme Kleidung.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Jersey\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Shirts, Kleider, Mützen und Kinderbekleidung\u003c\/li\u003e\n      \u003cli\u003eBreite: ca. 160 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht: cs. 220 g\/qm2\u003c\/li\u003e\n      \u003cli\u003e95% Baumwolle 5% Elasthan\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eBaumwolle\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Vielseitig verwendbarer Baumwollstoff.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Baumwolle\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Taschen, Kissen, Dekoration und vieles mehr\u003c\/li\u003e\n      \u003cli\u003eBreite: ca. 160 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht: ca. 150 g\/qm²\u003c\/li\u003e\n      \u003cli\u003e100% Baumwolle\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- MUSSELIN --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Musselin\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eMusselin\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Luftiger, weicher Stoff mit charakteristischer Struktur.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Musselin\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Tücher, leichte Kleidung\u003c\/li\u003e\n      \u003cli\u003eBreite ca. 134 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht ca. 120 g\/m²\u003c\/li\u003e\n      \u003cli\u003eDouble Layer\u003c\/li\u003e\n      \u003cli\u003e100 % Baumwolle\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C mit Feinwaschmittel\u003c\/li\u003e\n      \u003cli\u003eDas Material kann bis zu 8% einlaufen\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- SWEAT --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Sweat\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eSweat\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Weicher, etwas kräftigerer Stoff für gemütliche Kleidungsstücke.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Sweat\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Pullover, Hosen, Jacken und Hoodies\u003c\/li\u003e\n     \u003cli\u003e Breite ca. 160 cm\u003c\/li\u003e\n     \u003cli\u003eGewicht ca. 310 g\/m²\u003c\/li\u003e\n     \u003cli\u003e96% Baumwolle 4% Elasthan mit Feinfutter\u003c\/li\u003e\n     \u003cli\u003eToller, kuschelig weicher Sommersweat, innen flauschig-weich angerauht\u003c\/li\u003e\n     \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n     \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    padding: 12px 18px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tabs__buttons {\n      display: grid;\n      grid-template-columns: repeat(2, 1fr);\n    }\n\n    .stoff-tab-button {\n      width: 100%;\n      padding: 11px 7px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\n    \"Jersey\",\n    \"Baumwolle\",\n    \"Musselin\",\n    \"Sweat\"\n  ];\n\n  function normalizeText(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isKnownStoffart(value) {\n    const normalizedValue = normalizeText(value);\n\n    return STOFFARTEN.some(function (stoffart) {\n      return normalizeText(stoffart) === normalizedValue;\n    });\n  }\n\n  function getCorrectName(value) {\n    const normalizedValue = normalizeText(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalizeText(stoffart) === normalizedValue;\n    }) || null;\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalizeText(element.dataset.stoffButton) ===\n        normalizeText(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalizeText(correctName);\n\n    container.querySelectorAll(\"[data-stoff-button]\").forEach(function (button) {\n      const active =\n        !button.classList.contains(\"is-unavailable\") \u0026\u0026\n        normalizeText(button.dataset.stoffButton) === wanted;\n\n      button.classList.toggle(\"is-active\", active);\n      button.setAttribute(\"aria-selected\", active ? \"true\" : \"false\");\n    });\n\n    container.querySelectorAll(\"[data-stoff-content]\").forEach(function (content) {\n      const active =\n        !content.classList.contains(\"is-unavailable\") \u0026\u0026\n        normalizeText(content.dataset.stoffContent) === wanted;\n\n      content.classList.toggle(\"is-active\", active);\n      content.hidden = !active;\n    });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(container, firstButton.dataset.stoffButton);\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section = container.closest(\".shopify-section\") || document;\n\n    const checkedInputs = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const input of checkedInputs) {\n      const value = input.value;\n\n      if (isKnownStoffart(value)) {\n        return getCorrectName(value);\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabsWithSelection() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart = getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function getProductJsonUrl() {\n    let productPath = window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return null;\n    }\n\n    return productPath + \".js\";\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabsWithSelection();\n      return;\n    }\n\n    const availability = {};\n\n    STOFFARTEN.forEach(function (stoffart) {\n      availability[normalizeText(stoffart)] = false;\n    });\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const optionValues = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      optionValues.forEach(function (optionValue) {\n        const correctName = getCorrectName(optionValue);\n\n        if (correctName) {\n          availability[normalizeText(correctName)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available = availability[normalizeText(stoffart)] === true;\n\n        const button = Array.from(\n          container.querySelectorAll(\"[data-stoff-button]\")\n        ).find(function (element) {\n          return normalizeText(element.dataset.stoffButton) ===\n            normalizeText(stoffart);\n        });\n\n        const content = Array.from(\n          container.querySelectorAll(\"[data-stoff-content]\")\n        ).find(function (element) {\n          return normalizeText(element.dataset.stoffContent) ===\n            normalizeText(stoffart);\n        });\n\n        if (button) {\n          button.classList.toggle(\"is-unavailable\", !available);\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\"is-unavailable\", !available);\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabsWithSelection();\n  }\n\n  function loadAvailability() {\n    const productJsonUrl = getProductJsonUrl();\n\n    if (!productJsonUrl) {\n      syncTabsWithSelection();\n      return;\n    }\n\n    fetch(productJsonUrl)\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\"Produktdaten konnten nicht geladen werden.\");\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        \/*\n         * Falls die Produktdaten nicht geladen werden können,\n         * bleiben vorsichtshalber alle Registerkarten sichtbar.\n         *\/\n        syncTabsWithSelection();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button = event.target.closest(\"[data-stoff-button]\");\n\n    if (!button || button.classList.contains(\"is-unavailable\")) {\n      return;\n    }\n\n    const container = button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(container, button.dataset.stoffButton);\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches('input[type=\"radio\"], select')\n    ) {\n      window.setTimeout(syncTabsWithSelection, 50);\n    }\n  });\n\n  document.addEventListener(\"DOMContentLoaded\", function () {\n    loadAvailability();\n    window.setTimeout(syncTabsWithSelection, 300);\n  });\n\n  if (document.readyState !== \"loading\") {\n    loadAvailability();\n    window.setTimeout(syncTabsWithSelection, 300);\n  }\n})();\n\u003c\/script\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle","offer_id":52019645776139,"sku":"550000802","price":24.95,"currency_code":"EUR","in_stock":false},{"title":"Jersey","offer_id":52019645808907,"sku":"550000803","price":26.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-PatchworkNahteblau-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1754047679"},{"product_id":"kleine-punkte-dunkelblau-rot-stoff-klassiker","title":"Kleine Punkte dunkelblau rot - Stoff Klassiker","description":"\u003cdiv style=\"text-align: left;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo_Stoffe_by_Bine_Brandle_480x480.png?v=1750456378\" alt=\"Bines Stoffe Logo\" style=\"margin-bottom: 16px; float: none; display: block; margin-left: auto; margin-right: auto;\"\u003e\u003c\/div\u003e\n\u003cdiv style=\"text-align: left;\"\u003e\n\u003cp\u003e\u003cem\u003eBei der Auswahl der Menge entspricht jede Einheit 1 Meter Stoff – z. B. Menge 3 = 3 Meter Stoff.\u003c\/em\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003eRapport-Muster können variieren.\u003c\/em\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003eBei Bestellungen über einer länge von 5 Metern kann eine Lieferung aus einem zusammenhängendem Stoffstück nicht garantiert werden.\u003c\/em\u003e\u003c\/p\u003e\n\u003c\/div\u003e\n\u003cp\u003e\u003cem\u003eAlle Stoffe verfügen über den \"Öko-Tex® Standard 100\"\u003c\/em\u003e\u003c\/p\u003e\n\u003cdiv style=\"text-align: left;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"\" style=\"float: none;\"\u003e\u003c\/div\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n      Jersey\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n      Baumwolle\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Musselin\" role=\"tab\" aria-selected=\"false\"\u003e\n      Musselin\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Sweat\" role=\"tab\" aria-selected=\"false\"\u003e\n      Sweat\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eJersey\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Weicher und elastischer Stoff, ideal für bequeme Kleidung.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Jersey\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Shirts, Kleider, Mützen und Kinderbekleidung\u003c\/li\u003e\n      \u003cli\u003eBreite: ca. 160 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht: cs. 220 g\/qm2\u003c\/li\u003e\n      \u003cli\u003e95% Baumwolle 5% Elasthan\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eBaumwolle\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Vielseitig verwendbarer Baumwollstoff.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Baumwolle\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Taschen, Kissen, Dekoration und vieles mehr\u003c\/li\u003e\n      \u003cli\u003eBreite: ca. 160 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht: ca. 150 g\/qm²\u003c\/li\u003e\n      \u003cli\u003e100% Baumwolle\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- MUSSELIN --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Musselin\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eMusselin\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Luftiger, weicher Stoff mit charakteristischer Struktur.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Musselin\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Tücher, leichte Kleidung\u003c\/li\u003e\n      \u003cli\u003eBreite ca. 134 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht ca. 120 g\/m²\u003c\/li\u003e\n      \u003cli\u003eDouble Layer\u003c\/li\u003e\n      \u003cli\u003e100 % Baumwolle\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C mit Feinwaschmittel\u003c\/li\u003e\n      \u003cli\u003eDas Material kann bis zu 8% einlaufen\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- SWEAT --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Sweat\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eSweat\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Weicher, etwas kräftigerer Stoff für gemütliche Kleidungsstücke.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Sweat\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Pullover, Hosen, Jacken und Hoodies\u003c\/li\u003e\n     \u003cli\u003e Breite ca. 160 cm\u003c\/li\u003e\n     \u003cli\u003eGewicht ca. 310 g\/m²\u003c\/li\u003e\n     \u003cli\u003e96% Baumwolle 4% Elasthan mit Feinfutter\u003c\/li\u003e\n     \u003cli\u003eToller, kuschelig weicher Sommersweat, innen flauschig-weich angerauht\u003c\/li\u003e\n     \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n     \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    padding: 12px 18px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tabs__buttons {\n      display: grid;\n      grid-template-columns: repeat(2, 1fr);\n    }\n\n    .stoff-tab-button {\n      width: 100%;\n      padding: 11px 7px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\n    \"Jersey\",\n    \"Baumwolle\",\n    \"Musselin\",\n    \"Sweat\"\n  ];\n\n  function normalizeText(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isKnownStoffart(value) {\n    const normalizedValue = normalizeText(value);\n\n    return STOFFARTEN.some(function (stoffart) {\n      return normalizeText(stoffart) === normalizedValue;\n    });\n  }\n\n  function getCorrectName(value) {\n    const normalizedValue = normalizeText(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalizeText(stoffart) === normalizedValue;\n    }) || null;\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalizeText(element.dataset.stoffButton) ===\n        normalizeText(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalizeText(correctName);\n\n    container.querySelectorAll(\"[data-stoff-button]\").forEach(function (button) {\n      const active =\n        !button.classList.contains(\"is-unavailable\") \u0026\u0026\n        normalizeText(button.dataset.stoffButton) === wanted;\n\n      button.classList.toggle(\"is-active\", active);\n      button.setAttribute(\"aria-selected\", active ? \"true\" : \"false\");\n    });\n\n    container.querySelectorAll(\"[data-stoff-content]\").forEach(function (content) {\n      const active =\n        !content.classList.contains(\"is-unavailable\") \u0026\u0026\n        normalizeText(content.dataset.stoffContent) === wanted;\n\n      content.classList.toggle(\"is-active\", active);\n      content.hidden = !active;\n    });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(container, firstButton.dataset.stoffButton);\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section = container.closest(\".shopify-section\") || document;\n\n    const checkedInputs = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const input of checkedInputs) {\n      const value = input.value;\n\n      if (isKnownStoffart(value)) {\n        return getCorrectName(value);\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabsWithSelection() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart = getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function getProductJsonUrl() {\n    let productPath = window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return null;\n    }\n\n    return productPath + \".js\";\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabsWithSelection();\n      return;\n    }\n\n    const availability = {};\n\n    STOFFARTEN.forEach(function (stoffart) {\n      availability[normalizeText(stoffart)] = false;\n    });\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const optionValues = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      optionValues.forEach(function (optionValue) {\n        const correctName = getCorrectName(optionValue);\n\n        if (correctName) {\n          availability[normalizeText(correctName)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available = availability[normalizeText(stoffart)] === true;\n\n        const button = Array.from(\n          container.querySelectorAll(\"[data-stoff-button]\")\n        ).find(function (element) {\n          return normalizeText(element.dataset.stoffButton) ===\n            normalizeText(stoffart);\n        });\n\n        const content = Array.from(\n          container.querySelectorAll(\"[data-stoff-content]\")\n        ).find(function (element) {\n          return normalizeText(element.dataset.stoffContent) ===\n            normalizeText(stoffart);\n        });\n\n        if (button) {\n          button.classList.toggle(\"is-unavailable\", !available);\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\"is-unavailable\", !available);\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabsWithSelection();\n  }\n\n  function loadAvailability() {\n    const productJsonUrl = getProductJsonUrl();\n\n    if (!productJsonUrl) {\n      syncTabsWithSelection();\n      return;\n    }\n\n    fetch(productJsonUrl)\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\"Produktdaten konnten nicht geladen werden.\");\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        \/*\n         * Falls die Produktdaten nicht geladen werden können,\n         * bleiben vorsichtshalber alle Registerkarten sichtbar.\n         *\/\n        syncTabsWithSelection();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button = event.target.closest(\"[data-stoff-button]\");\n\n    if (!button || button.classList.contains(\"is-unavailable\")) {\n      return;\n    }\n\n    const container = button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(container, button.dataset.stoffButton);\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches('input[type=\"radio\"], select')\n    ) {\n      window.setTimeout(syncTabsWithSelection, 50);\n    }\n  });\n\n  document.addEventListener(\"DOMContentLoaded\", function () {\n    loadAvailability();\n    window.setTimeout(syncTabsWithSelection, 300);\n  });\n\n  if (document.readyState !== \"loading\") {\n    loadAvailability();\n    window.setTimeout(syncTabsWithSelection, 300);\n  }\n})();\n\u003c\/script\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle","offer_id":52020999094539,"sku":"550000787","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey","offer_id":52020999127307,"sku":"550000788","price":26.95,"currency_code":"EUR","in_stock":false}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-kleinePunktedunkelblaurot-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1759098379"},{"product_id":"patchwork-quadrate-fledermause-stoff","title":"Patchwork Quadrate Fledermäuse - Stoff","description":"\u003cdiv style=\"text-align: left;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo_Stoffe_by_Bine_Brandle_480x480.png?v=1750456378\" alt=\"Bines Stoffe Logo\" style=\"margin-bottom: 16px; float: none; display: block; margin-left: auto; margin-right: auto;\"\u003e\u003c\/div\u003e\n\u003cdiv style=\"text-align: left;\"\u003e\n\u003cp\u003e\u003cem\u003eBei der Auswahl der Menge entspricht jede Einheit 1 Meter Stoff – z. B. Menge 3 = 3 Meter Stoff.\u003c\/em\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003eRapport-Muster können variieren.\u003c\/em\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003eBei Bestellungen über einer länge von 5 Metern kann eine Lieferung aus einem zusammenhängendem Stoffstück nicht garantiert werden.\u003c\/em\u003e\u003c\/p\u003e\n\u003c\/div\u003e\n\u003cp\u003e\u003cem\u003eAlle Stoffe verfügen über den \"Öko-Tex® Standard 100\"\u003c\/em\u003e\u003c\/p\u003e\n\u003cdiv style=\"text-align: left;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"\" style=\"float: none;\"\u003e\u003c\/div\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n      Jersey\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n      Baumwolle\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Musselin\" role=\"tab\" aria-selected=\"false\"\u003e\n      Musselin\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Sweat\" role=\"tab\" aria-selected=\"false\"\u003e\n      Sweat\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eJersey\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Weicher und elastischer Stoff, ideal für bequeme Kleidung.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Jersey\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Shirts, Kleider, Mützen und Kinderbekleidung\u003c\/li\u003e\n      \u003cli\u003eBreite: ca. 160 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht: cs. 220 g\/qm2\u003c\/li\u003e\n      \u003cli\u003e95% Baumwolle 5% Elasthan\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eBaumwolle\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Vielseitig verwendbarer Baumwollstoff.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Baumwolle\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Taschen, Kissen, Dekoration und vieles mehr\u003c\/li\u003e\n      \u003cli\u003eBreite: ca. 160 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht: ca. 150 g\/qm²\u003c\/li\u003e\n      \u003cli\u003e100% Baumwolle\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- MUSSELIN --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Musselin\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eMusselin\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Luftiger, weicher Stoff mit charakteristischer Struktur.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Musselin\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Tücher, leichte Kleidung\u003c\/li\u003e\n      \u003cli\u003eBreite ca. 134 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht ca. 120 g\/m²\u003c\/li\u003e\n      \u003cli\u003eDouble Layer\u003c\/li\u003e\n      \u003cli\u003e100 % Baumwolle\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C mit Feinwaschmittel\u003c\/li\u003e\n      \u003cli\u003eDas Material kann bis zu 8% einlaufen\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- SWEAT --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Sweat\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eSweat\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Weicher, etwas kräftigerer Stoff für gemütliche Kleidungsstücke.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Sweat\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Pullover, Hosen, Jacken und Hoodies\u003c\/li\u003e\n     \u003cli\u003e Breite ca. 160 cm\u003c\/li\u003e\n     \u003cli\u003eGewicht ca. 310 g\/m²\u003c\/li\u003e\n     \u003cli\u003e96% Baumwolle 4% Elasthan mit Feinfutter\u003c\/li\u003e\n     \u003cli\u003eToller, kuschelig weicher Sommersweat, innen flauschig-weich angerauht\u003c\/li\u003e\n     \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n     \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    padding: 12px 18px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tabs__buttons {\n      display: grid;\n      grid-template-columns: repeat(2, 1fr);\n    }\n\n    .stoff-tab-button {\n      width: 100%;\n      padding: 11px 7px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\n    \"Jersey\",\n    \"Baumwolle\",\n    \"Musselin\",\n    \"Sweat\"\n  ];\n\n  function normalizeText(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isKnownStoffart(value) {\n    const normalizedValue = normalizeText(value);\n\n    return STOFFARTEN.some(function (stoffart) {\n      return normalizeText(stoffart) === normalizedValue;\n    });\n  }\n\n  function getCorrectName(value) {\n    const normalizedValue = normalizeText(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalizeText(stoffart) === normalizedValue;\n    }) || null;\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalizeText(element.dataset.stoffButton) ===\n        normalizeText(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalizeText(correctName);\n\n    container.querySelectorAll(\"[data-stoff-button]\").forEach(function (button) {\n      const active =\n        !button.classList.contains(\"is-unavailable\") \u0026\u0026\n        normalizeText(button.dataset.stoffButton) === wanted;\n\n      button.classList.toggle(\"is-active\", active);\n      button.setAttribute(\"aria-selected\", active ? \"true\" : \"false\");\n    });\n\n    container.querySelectorAll(\"[data-stoff-content]\").forEach(function (content) {\n      const active =\n        !content.classList.contains(\"is-unavailable\") \u0026\u0026\n        normalizeText(content.dataset.stoffContent) === wanted;\n\n      content.classList.toggle(\"is-active\", active);\n      content.hidden = !active;\n    });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(container, firstButton.dataset.stoffButton);\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section = container.closest(\".shopify-section\") || document;\n\n    const checkedInputs = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const input of checkedInputs) {\n      const value = input.value;\n\n      if (isKnownStoffart(value)) {\n        return getCorrectName(value);\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabsWithSelection() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart = getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function getProductJsonUrl() {\n    let productPath = window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return null;\n    }\n\n    return productPath + \".js\";\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabsWithSelection();\n      return;\n    }\n\n    const availability = {};\n\n    STOFFARTEN.forEach(function (stoffart) {\n      availability[normalizeText(stoffart)] = false;\n    });\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const optionValues = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      optionValues.forEach(function (optionValue) {\n        const correctName = getCorrectName(optionValue);\n\n        if (correctName) {\n          availability[normalizeText(correctName)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available = availability[normalizeText(stoffart)] === true;\n\n        const button = Array.from(\n          container.querySelectorAll(\"[data-stoff-button]\")\n        ).find(function (element) {\n          return normalizeText(element.dataset.stoffButton) ===\n            normalizeText(stoffart);\n        });\n\n        const content = Array.from(\n          container.querySelectorAll(\"[data-stoff-content]\")\n        ).find(function (element) {\n          return normalizeText(element.dataset.stoffContent) ===\n            normalizeText(stoffart);\n        });\n\n        if (button) {\n          button.classList.toggle(\"is-unavailable\", !available);\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\"is-unavailable\", !available);\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabsWithSelection();\n  }\n\n  function loadAvailability() {\n    const productJsonUrl = getProductJsonUrl();\n\n    if (!productJsonUrl) {\n      syncTabsWithSelection();\n      return;\n    }\n\n    fetch(productJsonUrl)\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\"Produktdaten konnten nicht geladen werden.\");\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        \/*\n         * Falls die Produktdaten nicht geladen werden können,\n         * bleiben vorsichtshalber alle Registerkarten sichtbar.\n         *\/\n        syncTabsWithSelection();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button = event.target.closest(\"[data-stoff-button]\");\n\n    if (!button || button.classList.contains(\"is-unavailable\")) {\n      return;\n    }\n\n    const container = button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(container, button.dataset.stoffButton);\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches('input[type=\"radio\"], select')\n    ) {\n      window.setTimeout(syncTabsWithSelection, 50);\n    }\n  });\n\n  document.addEventListener(\"DOMContentLoaded\", function () {\n    loadAvailability();\n    window.setTimeout(syncTabsWithSelection, 300);\n  });\n\n  if (document.readyState !== \"loading\") {\n    loadAvailability();\n    window.setTimeout(syncTabsWithSelection, 300);\n  }\n})();\n\u003c\/script\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Jersey","offer_id":52414973608203,"sku":"550000685","price":26.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle","offer_id":52414973640971,"sku":"550000686","price":24.95,"currency_code":"EUR","in_stock":false}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-PatchworkQuadrateFledermause-Stoff-Baumwolle-Jersey-nahen-bunt-Muster_9c7a4a22-2b95-4ef7-ab0f-14baabef4d3e.jpg?v=1758888337"},{"product_id":"patchwork-quadrate-weihnachten-stoff","title":"Patchwork Quadrate Weihnachten - Stoff","description":"\u003cdiv style=\"text-align: left;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo_Stoffe_by_Bine_Brandle_480x480.png?v=1750456378\" alt=\"Bines Stoffe Logo\" style=\"margin-bottom: 16px; float: none; display: block; margin-left: auto; margin-right: auto;\"\u003e\u003c\/div\u003e\n\u003cdiv style=\"text-align: left;\"\u003e\n\u003cp\u003e\u003cem\u003eBei der Auswahl der Menge entspricht jede Einheit 1 Meter Stoff – z. B. Menge 3 = 3 Meter Stoff.\u003c\/em\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003eRapport-Muster können variieren.\u003c\/em\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003eBei Bestellungen über einer länge von 5 Metern kann eine Lieferung aus einem zusammenhängendem Stoffstück nicht garantiert werden.\u003c\/em\u003e\u003c\/p\u003e\n\u003c\/div\u003e\n\u003cp\u003e\u003cem\u003eAlle Stoffe verfügen über den \"Öko-Tex® Standard 100\"\u003c\/em\u003e\u003c\/p\u003e\n\u003cdiv style=\"text-align: left;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"\" style=\"float: none;\"\u003e\u003c\/div\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n      Jersey\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n      Baumwolle\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Musselin\" role=\"tab\" aria-selected=\"false\"\u003e\n      Musselin\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Sweat\" role=\"tab\" aria-selected=\"false\"\u003e\n      Sweat\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eJersey\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Weicher und elastischer Stoff, ideal für bequeme Kleidung.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Jersey\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Shirts, Kleider, Mützen und Kinderbekleidung\u003c\/li\u003e\n      \u003cli\u003eBreite: ca. 160 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht: cs. 220 g\/qm2\u003c\/li\u003e\n      \u003cli\u003e95% Baumwolle 5% Elasthan\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eBaumwolle\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Vielseitig verwendbarer Baumwollstoff.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Baumwolle\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Taschen, Kissen, Dekoration und vieles mehr\u003c\/li\u003e\n      \u003cli\u003eBreite: ca. 160 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht: ca. 150 g\/qm²\u003c\/li\u003e\n      \u003cli\u003e100% Baumwolle\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- MUSSELIN --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Musselin\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eMusselin\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Luftiger, weicher Stoff mit charakteristischer Struktur.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Musselin\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Tücher, leichte Kleidung\u003c\/li\u003e\n      \u003cli\u003eBreite ca. 134 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht ca. 120 g\/m²\u003c\/li\u003e\n      \u003cli\u003eDouble Layer\u003c\/li\u003e\n      \u003cli\u003e100 % Baumwolle\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C mit Feinwaschmittel\u003c\/li\u003e\n      \u003cli\u003eDas Material kann bis zu 8% einlaufen\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- SWEAT --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Sweat\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eSweat\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Weicher, etwas kräftigerer Stoff für gemütliche Kleidungsstücke.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Sweat\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Pullover, Hosen, Jacken und Hoodies\u003c\/li\u003e\n     \u003cli\u003e Breite ca. 160 cm\u003c\/li\u003e\n     \u003cli\u003eGewicht ca. 310 g\/m²\u003c\/li\u003e\n     \u003cli\u003e96% Baumwolle 4% Elasthan mit Feinfutter\u003c\/li\u003e\n     \u003cli\u003eToller, kuschelig weicher Sommersweat, innen flauschig-weich angerauht\u003c\/li\u003e\n     \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n     \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    padding: 12px 18px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tabs__buttons {\n      display: grid;\n      grid-template-columns: repeat(2, 1fr);\n    }\n\n    .stoff-tab-button {\n      width: 100%;\n      padding: 11px 7px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\n    \"Jersey\",\n    \"Baumwolle\",\n    \"Musselin\",\n    \"Sweat\"\n  ];\n\n  function normalizeText(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isKnownStoffart(value) {\n    const normalizedValue = normalizeText(value);\n\n    return STOFFARTEN.some(function (stoffart) {\n      return normalizeText(stoffart) === normalizedValue;\n    });\n  }\n\n  function getCorrectName(value) {\n    const normalizedValue = normalizeText(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalizeText(stoffart) === normalizedValue;\n    }) || null;\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalizeText(element.dataset.stoffButton) ===\n        normalizeText(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalizeText(correctName);\n\n    container.querySelectorAll(\"[data-stoff-button]\").forEach(function (button) {\n      const active =\n        !button.classList.contains(\"is-unavailable\") \u0026\u0026\n        normalizeText(button.dataset.stoffButton) === wanted;\n\n      button.classList.toggle(\"is-active\", active);\n      button.setAttribute(\"aria-selected\", active ? \"true\" : \"false\");\n    });\n\n    container.querySelectorAll(\"[data-stoff-content]\").forEach(function (content) {\n      const active =\n        !content.classList.contains(\"is-unavailable\") \u0026\u0026\n        normalizeText(content.dataset.stoffContent) === wanted;\n\n      content.classList.toggle(\"is-active\", active);\n      content.hidden = !active;\n    });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(container, firstButton.dataset.stoffButton);\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section = container.closest(\".shopify-section\") || document;\n\n    const checkedInputs = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const input of checkedInputs) {\n      const value = input.value;\n\n      if (isKnownStoffart(value)) {\n        return getCorrectName(value);\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabsWithSelection() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart = getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function getProductJsonUrl() {\n    let productPath = window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return null;\n    }\n\n    return productPath + \".js\";\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabsWithSelection();\n      return;\n    }\n\n    const availability = {};\n\n    STOFFARTEN.forEach(function (stoffart) {\n      availability[normalizeText(stoffart)] = false;\n    });\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const optionValues = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      optionValues.forEach(function (optionValue) {\n        const correctName = getCorrectName(optionValue);\n\n        if (correctName) {\n          availability[normalizeText(correctName)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available = availability[normalizeText(stoffart)] === true;\n\n        const button = Array.from(\n          container.querySelectorAll(\"[data-stoff-button]\")\n        ).find(function (element) {\n          return normalizeText(element.dataset.stoffButton) ===\n            normalizeText(stoffart);\n        });\n\n        const content = Array.from(\n          container.querySelectorAll(\"[data-stoff-content]\")\n        ).find(function (element) {\n          return normalizeText(element.dataset.stoffContent) ===\n            normalizeText(stoffart);\n        });\n\n        if (button) {\n          button.classList.toggle(\"is-unavailable\", !available);\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\"is-unavailable\", !available);\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabsWithSelection();\n  }\n\n  function loadAvailability() {\n    const productJsonUrl = getProductJsonUrl();\n\n    if (!productJsonUrl) {\n      syncTabsWithSelection();\n      return;\n    }\n\n    fetch(productJsonUrl)\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\"Produktdaten konnten nicht geladen werden.\");\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        \/*\n         * Falls die Produktdaten nicht geladen werden können,\n         * bleiben vorsichtshalber alle Registerkarten sichtbar.\n         *\/\n        syncTabsWithSelection();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button = event.target.closest(\"[data-stoff-button]\");\n\n    if (!button || button.classList.contains(\"is-unavailable\")) {\n      return;\n    }\n\n    const container = button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(container, button.dataset.stoffButton);\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches('input[type=\"radio\"], select')\n    ) {\n      window.setTimeout(syncTabsWithSelection, 50);\n    }\n  });\n\n  document.addEventListener(\"DOMContentLoaded\", function () {\n    loadAvailability();\n    window.setTimeout(syncTabsWithSelection, 300);\n  });\n\n  if (document.readyState !== \"loading\") {\n    loadAvailability();\n    window.setTimeout(syncTabsWithSelection, 300);\n  }\n})();\n\u003c\/script\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Jersey","offer_id":52414998315275,"sku":"550000683","price":26.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle","offer_id":52414998348043,"sku":"550000684","price":24.95,"currency_code":"EUR","in_stock":false}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-PatchworkQuadrateWeihnachten-Stoff-Baumwolle-Jersey-nahen-bunt-Muster_83d64b81-3a51-4fc0-98d8-04e7e7186301.jpg?v=1758888136"},{"product_id":"lebkuchenmannchen-gross-weiss-stoff-klassiker","title":"Lebkuchenmännchen groß weiß - Stoff Klassiker","description":"\u003cdiv style=\"text-align: left;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo_Stoffe_by_Bine_Brandle_480x480.png?v=1750456378\" alt=\"Bines Stoffe Logo\" style=\"margin-bottom: 16px; float: none; display: block; margin-left: auto; margin-right: auto;\"\u003e\u003c\/div\u003e\n\u003cdiv style=\"text-align: left;\"\u003e\n\u003cp\u003e\u003cem\u003eAls Set - 1 m Stück - ideal zum Nähen von Stofffiguren\u003c\/em\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003eBei Bestellungen über einer länge von 5 Metern kann eine Lieferung aus einem zusammenhängendem Stoffstück nicht garantiert werden.\u003c\/em\u003e\u003c\/p\u003e\n\u003c\/div\u003e\n\u003cp\u003e\u003cem\u003eAlle Stoffe verfügen über den \"Öko-Tex® Standard 100\"\u003c\/em\u003e\u003c\/p\u003e\n\u003cdiv style=\"text-align: left;\"\u003e\u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"\" style=\"float: none;\"\u003e\u003c\/div\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n      Jersey\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n      Baumwolle\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Musselin\" role=\"tab\" aria-selected=\"false\"\u003e\n      Musselin\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Sweat\" role=\"tab\" aria-selected=\"false\"\u003e\n      Sweat\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eJersey\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Weicher und elastischer Stoff, ideal für bequeme Kleidung.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Jersey\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Shirts, Kleider, Mützen und Kinderbekleidung\u003c\/li\u003e\n      \u003cli\u003eBreite: ca. 160 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht: cs. 220 g\/qm2\u003c\/li\u003e\n      \u003cli\u003e95% Baumwolle 5% Elasthan\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eBaumwolle\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Vielseitig verwendbarer Baumwollstoff.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Baumwolle\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Taschen, Kissen, Dekoration und vieles mehr\u003c\/li\u003e\n      \u003cli\u003eBreite: ca. 160 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht: ca. 150 g\/qm²\u003c\/li\u003e\n      \u003cli\u003e100% Baumwolle\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- MUSSELIN --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Musselin\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eMusselin\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Luftiger, weicher Stoff mit charakteristischer Struktur.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Musselin\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Tücher, leichte Kleidung\u003c\/li\u003e\n      \u003cli\u003eBreite ca. 134 cm\u003c\/li\u003e\n      \u003cli\u003eGewicht ca. 120 g\/m²\u003c\/li\u003e\n      \u003cli\u003eDouble Layer\u003c\/li\u003e\n      \u003cli\u003e100 % Baumwolle\u003c\/li\u003e\n      \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n      \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C mit Feinwaschmittel\u003c\/li\u003e\n      \u003cli\u003eDas Material kann bis zu 8% einlaufen\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- SWEAT --\u003e\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Sweat\" role=\"tabpanel\" hidden\u003e\n\n    \u003ch3\u003eSweat\u003c\/h3\u003e\n\n    \u003cp\u003e\n      Weicher, etwas kräftigerer Stoff für gemütliche Kleidungsstücke.\n    \u003c\/p\u003e\n\n    \u003cul\u003e\n      \u003cli\u003eStoffart: Sweat\u003c\/li\u003e\n      \u003cli\u003eGeeignet für: Pullover, Hosen, Jacken und Hoodies\u003c\/li\u003e\n     \u003cli\u003e Breite ca. 160 cm\u003c\/li\u003e\n     \u003cli\u003eGewicht ca. 310 g\/m²\u003c\/li\u003e\n     \u003cli\u003e96% Baumwolle 4% Elasthan mit Feinfutter\u003c\/li\u003e\n     \u003cli\u003eToller, kuschelig weicher Sommersweat, innen flauschig-weich angerauht\u003c\/li\u003e\n     \u003cli\u003eDesigned und gedruckt in Deutschland\u003c\/li\u003e\n     \u003cli\u003eÖko-Tex® Standard 100, waschbar bei 40°C\u003c\/li\u003e\n\n    \u003c\/ul\u003e\n  \u003c\/div\u003e\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    padding: 12px 18px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tabs__buttons {\n      display: grid;\n      grid-template-columns: repeat(2, 1fr);\n    }\n\n    .stoff-tab-button {\n      width: 100%;\n      padding: 11px 7px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\n    \"Jersey\",\n    \"Baumwolle\",\n    \"Musselin\",\n    \"Sweat\"\n  ];\n\n  function normalizeText(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isKnownStoffart(value) {\n    const normalizedValue = normalizeText(value);\n\n    return STOFFARTEN.some(function (stoffart) {\n      return normalizeText(stoffart) === normalizedValue;\n    });\n  }\n\n  function getCorrectName(value) {\n    const normalizedValue = normalizeText(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalizeText(stoffart) === normalizedValue;\n    }) || null;\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalizeText(element.dataset.stoffButton) ===\n        normalizeText(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalizeText(correctName);\n\n    container.querySelectorAll(\"[data-stoff-button]\").forEach(function (button) {\n      const active =\n        !button.classList.contains(\"is-unavailable\") \u0026\u0026\n        normalizeText(button.dataset.stoffButton) === wanted;\n\n      button.classList.toggle(\"is-active\", active);\n      button.setAttribute(\"aria-selected\", active ? \"true\" : \"false\");\n    });\n\n    container.querySelectorAll(\"[data-stoff-content]\").forEach(function (content) {\n      const active =\n        !content.classList.contains(\"is-unavailable\") \u0026\u0026\n        normalizeText(content.dataset.stoffContent) === wanted;\n\n      content.classList.toggle(\"is-active\", active);\n      content.hidden = !active;\n    });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(container, firstButton.dataset.stoffButton);\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section = container.closest(\".shopify-section\") || document;\n\n    const checkedInputs = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const input of checkedInputs) {\n      const value = input.value;\n\n      if (isKnownStoffart(value)) {\n        return getCorrectName(value);\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabsWithSelection() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart = getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function getProductJsonUrl() {\n    let productPath = window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return null;\n    }\n\n    return productPath + \".js\";\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabsWithSelection();\n      return;\n    }\n\n    const availability = {};\n\n    STOFFARTEN.forEach(function (stoffart) {\n      availability[normalizeText(stoffart)] = false;\n    });\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const optionValues = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      optionValues.forEach(function (optionValue) {\n        const correctName = getCorrectName(optionValue);\n\n        if (correctName) {\n          availability[normalizeText(correctName)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available = availability[normalizeText(stoffart)] === true;\n\n        const button = Array.from(\n          container.querySelectorAll(\"[data-stoff-button]\")\n        ).find(function (element) {\n          return normalizeText(element.dataset.stoffButton) ===\n            normalizeText(stoffart);\n        });\n\n        const content = Array.from(\n          container.querySelectorAll(\"[data-stoff-content]\")\n        ).find(function (element) {\n          return normalizeText(element.dataset.stoffContent) ===\n            normalizeText(stoffart);\n        });\n\n        if (button) {\n          button.classList.toggle(\"is-unavailable\", !available);\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\"is-unavailable\", !available);\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabsWithSelection();\n  }\n\n  function loadAvailability() {\n    const productJsonUrl = getProductJsonUrl();\n\n    if (!productJsonUrl) {\n      syncTabsWithSelection();\n      return;\n    }\n\n    fetch(productJsonUrl)\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\"Produktdaten konnten nicht geladen werden.\");\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        \/*\n         * Falls die Produktdaten nicht geladen werden können,\n         * bleiben vorsichtshalber alle Registerkarten sichtbar.\n         *\/\n        syncTabsWithSelection();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button = event.target.closest(\"[data-stoff-button]\");\n\n    if (!button || button.classList.contains(\"is-unavailable\")) {\n      return;\n    }\n\n    const container = button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(container, button.dataset.stoffButton);\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches('input[type=\"radio\"], select')\n    ) {\n      window.setTimeout(syncTabsWithSelection, 50);\n    }\n  });\n\n  document.addEventListener(\"DOMContentLoaded\", function () {\n    loadAvailability();\n    window.setTimeout(syncTabsWithSelection, 300);\n  });\n\n  if (document.readyState !== \"loading\") {\n    loadAvailability();\n    window.setTimeout(syncTabsWithSelection, 300);\n  }\n})();\n\u003c\/script\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle","offer_id":52416691405067,"sku":"550000674","price":24.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-LebkuchenmaennchengrossweissAusschneidebogen-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1758917485"},{"product_id":"tuch-blumenmeer-weiss","title":"Tuch Blumenmeer weiß","description":"\u003cp\u003e🌸 Bine Brändle Halstuch – luftig, weich \u0026amp; voller Farbenfreude\u003c\/p\u003e\n\u003cp\u003eUnser neues Bine-Brändle-Halstuch ist ein echtes Lieblingsaccessoire für alle Jahreszeiten!\u003cbr\u003eGefertigt aus 100 % hochwertiger Baumwolle, ist es luftig leicht, seidig weich und leuchtend bunt bedruckt – natürlich mit original handillustrierten Mustern von Bine Brändle.\u003c\/p\u003e\n\u003cp\u003eMit einer Größe von ca. 175 × 72 cm lässt sich das Tuch wunderbar vielseitig tragen:\u003cbr\u003e✨ locker und gemütlich um den Hals\u003cbr\u003e☀️ als farbenfrohes Strandtuch oder Pareo\u003cbr\u003e💫 oder als leichter Schulterschmeichler an kühleren Abenden\u003c\/p\u003e\n\u003cp\u003eDer einseitige Druck sorgt dafür, dass die Rückseite etwas pastelliger wirkt – so entsteht ein wunderschöner, sanfter Farbverlauf, der das Tuch noch lebendiger macht.\u003c\/p\u003e\n\u003cp\u003eErhältlich in zehn einzigartigen Designs, jedes davon ein kleines Kunstwerk – übersät mit unzähligen bunten Musterdetails, die typisch Bine sind: verspielt, fröhlich und einfach zum Verlieben.\u003c\/p\u003e\n\u003cp\u003e🧵 Ein echtes Herzensprodukt aus reiner Naturfaser:\u003cbr\u003eBei Baumwolle lässt sich – im Gegensatz zu Polyester – keine 100 %ige Durchfärbung erreichen. Trotzdem hat sich Bine ganz bewusst für reine Baumwolle entschieden, weil sie sich so angenehm und hautfreundlich trägt.\u003cbr\u003eDie zarte Rückseite ist also kein Mangel, sondern ein Zeichen echter Naturqualität!\u003c\/p\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Default Title","offer_id":52688040132875,"sku":"550000440","price":29.9,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Tuch-Blumenmeer-weiss.jpg?v=1763164474"},{"product_id":"tuch-wasserfarben-blume","title":"Tuch Wasserfarben Blume","description":"\u003cp\u003e🌸 Bine Brändle Halstuch – luftig, weich \u0026amp; voller Farbenfreude\u003c\/p\u003e\n\u003cp\u003eUnser neues Bine-Brändle-Halstuch ist ein echtes Lieblingsaccessoire für alle Jahreszeiten!\u003cbr\u003eGefertigt aus 100 % hochwertiger Baumwolle, ist es luftig leicht, seidig weich und leuchtend bunt bedruckt – natürlich mit original handillustrierten Mustern von Bine Brändle.\u003c\/p\u003e\n\u003cp\u003eMit einer Größe von ca. 175 × 72 cm lässt sich das Tuch wunderbar vielseitig tragen:\u003cbr\u003e✨ locker und gemütlich um den Hals\u003cbr\u003e☀️ als farbenfrohes Strandtuch oder Pareo\u003cbr\u003e💫 oder als leichter Schulterschmeichler an kühleren Abenden\u003c\/p\u003e\n\u003cp\u003eDer einseitige Druck sorgt dafür, dass die Rückseite etwas pastelliger wirkt – so entsteht ein wunderschöner, sanfter Farbverlauf, der das Tuch noch lebendiger macht.\u003c\/p\u003e\n\u003cp\u003eErhältlich in zehn einzigartigen Designs, jedes davon ein kleines Kunstwerk – übersät mit unzähligen bunten Musterdetails, die typisch Bine sind: verspielt, fröhlich und einfach zum Verlieben.\u003c\/p\u003e\n\u003cp\u003e🧵 Ein echtes Herzensprodukt aus reiner Naturfaser:\u003cbr\u003eBei Baumwolle lässt sich – im Gegensatz zu Polyester – keine 100 %ige Durchfärbung erreichen. Trotzdem hat sich Bine ganz bewusst für reine Baumwolle entschieden, weil sie sich so angenehm und hautfreundlich trägt.\u003cbr\u003eDie zarte Rückseite ist also kein Mangel, sondern ein Zeichen echter Naturqualität!\u003c\/p\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Default Title","offer_id":52688106291467,"sku":"550000432","price":29.9,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Tuch-Wasserfarben-Blume.jpg?v=1763165932"},{"product_id":"dosi-meine-kleinen-monster-tasse","title":"Dösi \"Meine kleinen Monster\" - Tasse","description":"\u003cp\u003e\u003cstrong\u003eOriginelle Sammeltasse aus Keramik mit dem Monster Dösi aus \"Meine kleinen Monster\u003cspan\u003e\"\u003c\/span\u003e als Motiv.\u003c\/strong\u003e\u003c\/p\u003e\n\u003cp\u003eOb für den ersten schluck Wasser am Morgen, eine gemütliche Tasse Tee am Nachmittag, heißen Kakao mit Sahne an kalten Tagen – oder einfach zum Auswaschen deines Pinsels nach dem Malen – diese Tasse ist für (fast) alles zu haben.\u003c\/p\u003e\n\u003cp data-start=\"678\" data-end=\"933\"\u003eGefertigt aus hochwertiger Keramik und bedruckt mit \u003cstrong data-start=\"730\" data-end=\"761\"\u003ebrillant leuchtenden Farben\u003c\/strong\u003e, ist sie selbstverständlich \u003cstrong data-start=\"790\" data-end=\"853\"\u003espülmaschinenfest, lebensmittelecht und mikrowellengeeignet\u003c\/strong\u003e.\u003cbr data-start=\"854\" data-end=\"857\"\u003eSo bleibt das fröhliche Design lange so strahlend schön wie am ersten Tag!\u003c\/p\u003e\n\u003cp data-start=\"935\" data-end=\"962\"\u003e\u003cstrong data-start=\"935\" data-end=\"949\"\u003eFüllmenge:\u003c\/strong\u003e ca. 300 ml\u003c\/p\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Default Title","offer_id":52849466900747,"sku":"550000426","price":14.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/monster-tasse-dosi-A.jpg?v=1769552937"},{"product_id":"leseknochen","title":"Leseknochen Monster – ergonomisches Lesekissen im Design von Bine Brändle","description":"\u003cdiv\u003eLeseknochen – ergonomisches Lesekissen im Design von Bine Brändle\u003c\/div\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eDieser farbenfrohe Leseknochen verbindet Komfort, Funktionalität und ein außergewöhnliches Design. Die lebendigen Muster stammen von der bekannten Künstlerin Bine Brändle und machen das Kissen zu einem echten Blickfang im Wohnraum.\u003c\/div\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eDer Leseknochen ist vielseitig einsetzbar: als Nackenstütze beim Lesen, Buchstütze, Tablet- oder Smartphonehalter, Seitenschläferkissen oder einfach als gemütliche Unterstützung für Rücken und Nacken auf Sofa, Bett oder Sessel. Durch seine spezielle Knochenform passt er sich optimal an und sorgt für eine entspannte Haltung.\u003c\/div\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eDer Bezug besteht aus 100 % Baumwolle und fühlt sich angenehm weich auf der Haut an. Die Füllung aus 100 % rPET (recyceltes Polyester) macht das Kissen formstabil, leicht und gleichzeitig nachhaltig.\u003c\/div\u003e\n\u003cdiv\u003e\u003c\/div\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eProduktdetails\u003c\/div\u003e\n\u003cul\u003e\n\u003cli\u003eProdukt: Leseknochen \/ Lesekissen\u003c\/li\u003e\n\u003cli\u003eDesign: Bine Brändle\u003c\/li\u003e\n\u003cli\u003eBreite: ca. 48 cm\u003c\/li\u003e\n\u003cli\u003eHöhe: ca. 25 cm an den Seiten, ca. 20 cm in der Mitte\u003c\/li\u003e\n\u003cli\u003eBezug: 100 % Baumwolle\u003c\/li\u003e\n\u003cli\u003eFüllung: 100 % rPET (recyceltes Polyester)\u003c\/li\u003e\n\u003cli\u003eForm: ergonomische Knochenform für vielseitige Nutzung\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eMaterialbeschreibung rPET\u003c\/div\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eDie Füllung besteht aus rPET, einem nachhaltigen Recyclingmaterial. rPET wird aus wiederverwerteten PET-Flaschen hergestellt. Dadurch werden Ressourcen geschont und Kunststoffabfälle reduziert.\u003c\/div\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eVorteile von rPET:\u003c\/div\u003e\n\u003cul\u003e\n\u003cli\u003eumweltfreundlich durch Recycling\u003c\/li\u003e\n\u003cli\u003eleicht und formstabil\u003c\/li\u003e\n\u003cli\u003elanglebig und hygienisch\u003c\/li\u003e\n\u003cli\u003ebehält dauerhaft seine stützende Form\u003c\/li\u003e\n\u003c\/ul\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Default Title","offer_id":52950085337355,"sku":"550000416","price":39.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/LeseknochenMonster_3.jpg?v=1772817288"},{"product_id":"leseknochen-blau-blumen-ergonomisches-lesekissen-im-design-von-bine-brandle-copy","title":"Leseknochen Blumenzauber Blau – ergonomisches Lesekissen im Design von Bine Brändle","description":"\u003cdiv\u003eLeseknochen – ergonomisches Lesekissen im Design von Bine Brändle\u003c\/div\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eDieser farbenfrohe Leseknochen verbindet Komfort, Funktionalität und ein außergewöhnliches Design. Die lebendigen Muster stammen von der bekannten Künstlerin Bine Brändle und machen das Kissen zu einem echten Blickfang im Wohnraum.\u003c\/div\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eDer Leseknochen ist vielseitig einsetzbar: als Nackenstütze beim Lesen, Buchstütze, Tablet- oder Smartphonehalter, Seitenschläferkissen oder einfach als gemütliche Unterstützung für Rücken und Nacken auf Sofa, Bett oder Sessel. Durch seine spezielle Knochenform passt er sich optimal an und sorgt für eine entspannte Haltung.\u003c\/div\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eDer Bezug besteht aus 100 % Baumwolle und fühlt sich angenehm weich auf der Haut an. Die Füllung aus 100 % rPET (recyceltes Polyester) macht das Kissen formstabil, leicht und gleichzeitig nachhaltig.\u003c\/div\u003e\n\u003cdiv\u003e\u003c\/div\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eProduktdetails\u003c\/div\u003e\n\u003cul\u003e\n\u003cli\u003eProdukt: Leseknochen \/ Lesekissen\u003c\/li\u003e\n\u003cli\u003eDesign: Bine Brändle\u003c\/li\u003e\n\u003cli\u003eBreite: ca. 48 cm\u003c\/li\u003e\n\u003cli\u003eHöhe: ca. 25 cm an den Seiten, ca. 20 cm in der Mitte\u003c\/li\u003e\n\u003cli\u003eBezug: 100 % Baumwolle\u003c\/li\u003e\n\u003cli\u003eFüllung: 100 % rPET (recyceltes Polyester)\u003c\/li\u003e\n\u003cli\u003eForm: ergonomische Knochenform für vielseitige Nutzung\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eMaterialbeschreibung rPET\u003c\/div\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eDie Füllung besteht aus rPET, einem nachhaltigen Recyclingmaterial. rPET wird aus wiederverwerteten PET-Flaschen hergestellt. Dadurch werden Ressourcen geschont und Kunststoffabfälle reduziert.\u003c\/div\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eVorteile von rPET:\u003c\/div\u003e\n\u003cul\u003e\n\u003cli\u003eumweltfreundlich durch Recycling\u003c\/li\u003e\n\u003cli\u003eleicht und formstabil\u003c\/li\u003e\n\u003cli\u003elanglebig und hygienisch\u003c\/li\u003e\n\u003cli\u003ebehält dauerhaft seine stützende Form\u003c\/li\u003e\n\u003c\/ul\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Default Title","offer_id":52950108242187,"sku":"550000415","price":39.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/LeseknochenBlauBlumen_2.jpg?v=1772817504"},{"product_id":"leseknochen-seerose-ergonomisches-lesekissen-im-design-von-bine-brandle","title":"Leseknochen Seerose – ergonomisches Lesekissen im Design von Bine Brändle","description":"\u003cdiv\u003eLeseknochen – ergonomisches Lesekissen im Design von Bine Brändle\u003c\/div\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eDieser farbenfrohe Leseknochen verbindet Komfort, Funktionalität und ein außergewöhnliches Design. Die lebendigen Muster stammen von der bekannten Künstlerin Bine Brändle und machen das Kissen zu einem echten Blickfang im Wohnraum.\u003c\/div\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eDer Leseknochen ist vielseitig einsetzbar: als Nackenstütze beim Lesen, Buchstütze, Tablet- oder Smartphonehalter, Seitenschläferkissen oder einfach als gemütliche Unterstützung für Rücken und Nacken auf Sofa, Bett oder Sessel. Durch seine spezielle Knochenform passt er sich optimal an und sorgt für eine entspannte Haltung.\u003c\/div\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eDer Bezug besteht aus 100 % Baumwolle und fühlt sich angenehm weich auf der Haut an. Die Füllung aus 100 % rPET (recyceltes Polyester) macht das Kissen formstabil, leicht und gleichzeitig nachhaltig.\u003c\/div\u003e\n\u003cdiv\u003e\u003c\/div\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eProduktdetails\u003c\/div\u003e\n\u003cul\u003e\n\u003cli\u003eProdukt: Leseknochen \/ Lesekissen\u003c\/li\u003e\n\u003cli\u003eDesign: Bine Brändle\u003c\/li\u003e\n\u003cli\u003eBreite: ca. 48 cm\u003c\/li\u003e\n\u003cli\u003eHöhe: ca. 25 cm an den Seiten, ca. 20 cm in der Mitte\u003c\/li\u003e\n\u003cli\u003eBezug: 100 % Baumwolle\u003c\/li\u003e\n\u003cli\u003eFüllung: 100 % rPET (recyceltes Polyester)\u003c\/li\u003e\n\u003cli\u003eForm: ergonomische Knochenform für vielseitige Nutzung\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eMaterialbeschreibung rPET\u003c\/div\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eDie Füllung besteht aus rPET, einem nachhaltigen Recyclingmaterial. rPET wird aus wiederverwerteten PET-Flaschen hergestellt. Dadurch werden Ressourcen geschont und Kunststoffabfälle reduziert.\u003c\/div\u003e\n\u003cdiv\u003e\u003cbr\u003e\u003c\/div\u003e\n\u003cdiv\u003eVorteile von rPET:\u003c\/div\u003e\n\u003cul\u003e\n\u003cli\u003eumweltfreundlich durch Recycling\u003c\/li\u003e\n\u003cli\u003eleicht und formstabil\u003c\/li\u003e\n\u003cli\u003elanglebig und hygienisch\u003c\/li\u003e\n\u003cli\u003ebehält dauerhaft seine stützende Form\u003c\/li\u003e\n\u003c\/ul\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Default Title","offer_id":52950110830859,"sku":"550000409","price":39.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/LeseknochenSeerose_1.jpg?v=1772817911"},{"product_id":"teppich-jasmin-rot","title":"Teppich - Jasmin Rot","description":"\u003cp\u003e\u003cmeta charset=\"UTF-8\"\u003e\u003cstrong\u003eSuperweicher Teppich im Design von Bine Brändle mit dem Motiv Jasmin in Rot, lebendig und dekorativ gestaltet.\u003c\/strong\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cstrong\u003eWOHLFÜHLTEPPICH SUPERWEICH\u003c\/strong\u003e\u003c\/p\u003e\n\u003cul\u003e\n\u003cli style=\"font-weight: bold;\"\u003e\u003cstrong\u003e\u003cspan style=\"color: #08868a;\"\u003eFlorhöhe: 11 mm\u003c\/span\u003e\u003c\/strong\u003e\u003c\/li\u003e\n\u003cli style=\"font-weight: bold;\"\u003e\u003cstrong\u003e\u003cspan style=\"color: #08868a;\"\u003eGewicht pro m²: 850 g\u003c\/span\u003e\u003c\/strong\u003e\u003c\/li\u003e\n\u003cli style=\"font-weight: bold;\"\u003e\u003cstrong\u003e\u003cspan style=\"color: #08868a;\"\u003eMaterial: Polyester \u003c\/span\u003e\u003c\/strong\u003e\u003c\/li\u003e\n\u003cli style=\"font-weight: bold;\"\u003e\u003cstrong\u003e\u003cspan style=\"color: #08868a;\"\u003eBoden: rutschfester Polyester \u003c\/span\u003e\u003c\/strong\u003e\u003c\/li\u003e\n\u003cli style=\"font-weight: bold;\"\u003e\u003cstrong\u003e\u003cspan style=\"color: #08868a;\"\u003eErhältlich in mehreren Größen.\u003c\/span\u003e\u003c\/strong\u003e\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003cp\u003eVerschönere dein Zuhause mit Bines bunten Teppichen!\u003cbr\u003eSie sind mit leuchtenden Mustern in strahlenden Farben hochwertig bedruckt, robust und pflegeleicht.\u003c\/p\u003e\n\u003cp\u003eEin toller Blickfang und superpraktisch z.B. im Wohnzimmer, Flur, in deiner Küche, im Bad oder vor der Balkontüre und sogar in der Maschine waschbar.\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003eIm Regelfall entsprechen Farbe und Größe den Angaben; produktionsbedingt sind geringfügige Abweichungen möglich. Die Farbdarstellung kann je nach Bildschirm variieren.\u003c\/em\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cstrong\u003ePflegehinweise:\u003c\/strong\u003e\u003cbr\u003e\u003cspan style=\"text-decoration: underline;\"\u003e\u003cem\u003e\u003c\/em\u003e\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cspan style=\"text-decoration: underline;\"\u003e\u003cem\u003eWaschen:\u003c\/em\u003e\u003c\/span\u003e\u003cem\u003e \u003c\/em\u003eBei 30°C im Schonwaschgang waschen oder von Hand waschen.\u003cbr\u003eKein Bleichmittel verwenden.\u003c\/p\u003e\n\u003cp\u003e\u003cspan style=\"text-decoration: underline;\"\u003e\u003cem\u003eTrocknen:\u003c\/em\u003e\u003c\/span\u003e\u003cem\u003e \u003c\/em\u003eNicht im Trockner trocknen.\u003cbr\u003e\u003cspan style=\"text-decoration: underline;\"\u003e\u003cem\u003e\u003c\/em\u003e\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cspan style=\"text-decoration: underline;\"\u003e\u003cem\u003eBügeln:\u003c\/em\u003e\u003c\/span\u003e Nicht bügeln.\u003cbr\u003e\u003cspan style=\"text-decoration: underline;\"\u003e\u003cem\u003e\u003c\/em\u003e\u003c\/span\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cspan style=\"text-decoration: underline;\"\u003e\u003cem\u003eChemische Reinigung:\u003c\/em\u003e\u003c\/span\u003e Keine chemische Reinigung durchführen.\u003cbr\u003e\u003cem\u003e\u003cspan style=\"text-decoration: underline;\"\u003e\u003c\/span\u003e\u003c\/em\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cem\u003e\u003cspan style=\"text-decoration: underline;\"\u003eWasser entfernen:\u003c\/span\u003e\u003c\/em\u003e Überschüssiges Wasser nicht auswringen.\u003cbr\u003eStattdessen flach hinlegen oder aufhängen, um das Wasser abtropfen zu lassen.\u003cbr\u003e\u003cbr\u003eEs ist wichtig, diese Pflegehinweise zu befolgen, um die Haltbarkeit und das Aussehen des Teppichs oder der Textilunterlage zu erhalten. Durch die Verwendung von Schonwaschgang und dem Verzicht auf den Trockner und chemische Reinigung wird die Materialqualität geschont. Zusätzlich wird durch das Vermeiden von Bügeln und Auswringen das Risiko von Beschädigungen minimiert.\u003c\/p\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"80cm x 115cm","offer_id":53060104519947,"sku":"550000353","price":89.95,"currency_code":"EUR","in_stock":false},{"title":"110cm x 145cm","offer_id":53060104552715,"sku":"550000354","price":149.95,"currency_code":"EUR","in_stock":false},{"title":"120cm x 170cm","offer_id":53060104585483,"sku":"550000355","price":189.95,"currency_code":"EUR","in_stock":false},{"title":"140cm x 198cm","offer_id":53060104618251,"sku":"550000356","price":269.95,"currency_code":"EUR","in_stock":false},{"title":"160cm x 225cm","offer_id":53060104651019,"sku":"550000357","price":349.95,"currency_code":"EUR","in_stock":true},{"title":"170cm x 240cm","offer_id":53060104683787,"sku":"550000358","price":389.95,"currency_code":"EUR","in_stock":false}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Jasmin_ROT_170x240cm.jpg?v=1776611666"},{"product_id":"blatter-dschungel-blau-stoff","title":"Blätter-Dschungel blau – Stoff","description":"\u003c!-- PRODUKTBESCHREIBUNG MIT PARALLAX-LOGO --\u003e\n\n\u003csection class=\"bine-stoff-intro\"\u003e\n\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo-Stoffe-bunt.png?v=1787871207\" alt=\"Bine Brändle Stoffe Logo\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"bine-stoff-text\"\u003e\n\n    \u003ch2\u003eFarbenfrohe Stoffe mit Design von Bine Brändle\u003c\/h2\u003e\n\n    \u003cp class=\"bine-stoff-einleitung\"\u003e\n      Mach dein nächstes Nähprojekt zu etwas ganz Besonderem! Unsere\n      wunderschön bedruckten Stoffe mit einem liebevoll gestalteten Design\n      von \u003cstrong\u003eBine Brändle\u003c\/strong\u003e bringen Farbe, Freude und gute Laune\n      an die Nähmaschine.\n    \u003c\/p\u003e\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"Bine Brändle\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n    \u003cp\u003e\n      Du kannst dein Lieblingsdesign passend zu deinem Projekt als\n      \u003cstrong\u003eBaumwollgewebe\u003c\/strong\u003e oder \u003cstrong\u003eJersey\u003c\/strong\u003e auswählen.\n    \u003c\/p\u003e\n\n    \u003cdiv class=\"bine-stoffarten\"\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Das angenehm glatte und formstabile Baumwollgewebe eignet sich\n          wunderbar für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eHemden und Hosen\u003c\/li\u003e\n          \u003cli\u003eTaschen und Beutel\u003c\/li\u003e\n          \u003cli\u003eKissen und Wohnaccessoires\u003c\/li\u003e\n          \u003cli\u003eDekorationen\u003c\/li\u003e\n          \u003cli\u003ePatchwork-Projekte\u003c\/li\u003e\n          \u003cli\u003eSchultüten und kleine Geschenkideen\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eJersey\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Der weiche und elastische Jersey-Stoff ist besonders angenehm\n          auf der Haut und ideal für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eShirts und Kleider\u003c\/li\u003e\n          \u003cli\u003eHosen und Leggings\u003c\/li\u003e\n          \u003cli\u003eMützen und Halstücher\u003c\/li\u003e\n          \u003cli\u003eKinder- und Babykleidung\u003c\/li\u003e\n          \u003cli\u003eBequeme Lieblingsstücke für Groß und Klein\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"bine-stofflaengen\"\u003e\n      \u003ch3\u003eErhältliche Stofflängen\u003c\/h3\u003e\n\n      \u003cp\u003e\n        Du erhältst den Stoff fertig zugeschnitten in folgenden Längen:\n      \u003c\/p\u003e\n\n      \u003cdiv class=\"bine-laengen-auswahl\"\u003e\n        \u003cspan\u003e1 Meter\u003c\/span\u003e\n        \u003cspan\u003e2 Meter\u003c\/span\u003e\n        \u003cspan\u003e3 Meter\u003c\/span\u003e\n      \u003c\/div\u003e\n\n      \u003cp\u003e\n        Wähle einfach deine gewünschte Stoffart und anschließend die\n        passende Länge aus.\n      \u003c\/p\u003e\n\n      \u003cp class=\"bine-stoff-hinweis\"\u003e\n        \u003cstrong\u003eBitte beachte:\u003c\/strong\u003e Die ausgewählte Länge wird als\n        zusammenhängendes Stoffstück geliefert. Bei der Auswahl von\n        2 Metern erhältst du ein 2 Meter langes Stück, bei 3 Metern ein\n        zusammenhängendes 3-Meter-Stück.\n      \u003c\/p\u003e\n    \u003c\/div\u003e\n\n    \u003cp\u003e\n      Ob als farbenfrohes Kleidungsstück, liebevoll genähtes Geschenk oder\n      besonderer Blickfang für dein Zuhause – mit den Designs von\n      \u003cstrong\u003eBine Brändle\u003c\/strong\u003e wird jedes Nähprojekt herrlich bunt und\n      unverwechselbar.\n    \u003c\/p\u003e\n\n  \u003c\/div\u003e\n\n\u003c\/section\u003e\n\n\u003cstyle\u003e\n  .bine-stoff-intro {\n    margin: 20px 0 35px;\n    overflow: hidden;\n  }\n\n  .bine-logo-parallax {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    min-height: 150px;\n    margin-bottom: 20px;\n    padding: 20px;\n    \n    border-radius: 16px;\n    overflow: hidden;\n  }\n\n  .bine-stoff-logo {\n    display: block;\n    width: auto;\n    max-width: min(320px, 80%);\n    max-height: 130px;\n    object-fit: contain;\n    will-change: transform;\n    transform: translate3d(0, 0, 0);\n  }\n\n  .bine-stoff-text h2 {\n    margin-top: 0;\n    margin-bottom: 15px;\n    color: #08868a;\n    font-size: clamp(24px, 4vw, 34px);\n    line-height: 1.2;\n  }\n\n  .bine-stoff-text h3 {\n    margin-top: 0;\n    margin-bottom: 10px;\n  }\n\n  .bine-stoff-einleitung {\n    font-size: 1.08em;\n    line-height: 1.65;\n  }\n\n  .bine-stoffarten {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 16px;\n    margin: 25px 0;\n  }\n\n  .bine-stoffart {\n    padding: 20px;\n    background: #f7f7f7;\n    border-top: 4px solid #08868a;\n    border-radius: 10px;\n  }\n\n  .bine-stoffart ul {\n    margin-bottom: 0;\n    padding-left: 20px;\n  }\n\n  .bine-stoffart li {\n    margin-bottom: 5px;\n  }\n\n  .bine-stofflaengen {\n    margin: 25px 0;\n    padding: 22px;\n    background: rgba(8, 134, 138, 0.08);\n    border-radius: 12px;\n  }\n\n  .bine-laengen-auswahl {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin: 16px 0;\n  }\n\n  .bine-laengen-auswahl span {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 90px;\n    padding: 9px 15px;\n    background: #08868a;\n    color: #ffffff;\n    border-radius: 20px;\n    font-weight: 700;\n  }\n\n  .bine-stoff-hinweis {\n    margin-bottom: 0;\n    padding: 14px;\n    background: #ffffff;\n    border-left: 4px solid #08868a;\n    border-radius: 5px;\n  }\n\n  @media screen and (max-width: 650px) {\n    .bine-stoffarten {\n      grid-template-columns: 1fr;\n    }\n\n    .bine-logo-parallax {\n      min-height: 120px;\n    }\n\n    .bine-stoff-logo {\n      max-width: 75%;\n      max-height: 100px;\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .bine-stoff-logo {\n      transform: none !important;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const parallaxElements = document.querySelectorAll(\n    \"[data-bine-parallax] .bine-stoff-logo\"\n  );\n\n  if (\n    !parallaxElements.length ||\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  ) {\n    return;\n  }\n\n  let animationFrameRequested = false;\n\n  function updateParallax() {\n    parallaxElements.forEach(function (logo) {\n      const container = logo.closest(\"[data-bine-parallax]\");\n      const position = container.getBoundingClientRect();\n      const windowHeight = window.innerHeight;\n\n      if (\n        position.bottom \u003e= 0 \u0026\u0026\n        position.top \u003c= windowHeight\n      ) {\n        const containerCenter =\n          position.top + position.height \/ 2;\n\n        const screenCenter = windowHeight \/ 2;\n\n        const offset =\n          (containerCenter - screenCenter) * -0.08;\n\n        const limitedOffset = Math.max(\n          -22,\n          Math.min(22, offset)\n        );\n\n        logo.style.transform =\n          \"translate3d(0,\" + limitedOffset + \"px,0)\";\n      }\n    });\n\n    animationFrameRequested = false;\n  }\n\n  function requestParallaxUpdate() {\n    if (!animationFrameRequested) {\n      window.requestAnimationFrame(updateParallax);\n      animationFrameRequested = true;\n    }\n  }\n\n  window.addEventListener(\n    \"scroll\",\n    requestParallaxUpdate,\n    { passive: true }\n  );\n\n  window.addEventListener(\n    \"resize\",\n    requestParallaxUpdate\n  );\n\n  requestParallaxUpdate();\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif02?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eBaumwolle\u003c\/span\u003e\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif01?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eJersey\u003c\/span\u003e\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n   \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e \u003cp\u003e 100 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Rohgewebe aus 100 % Baumwolle,\u003cbr\u003e hergestellt aus Materialien, die gemäß OEKO-TEX® STANDARD 100 zertifiziert sind. \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 2 cm\u003cbr\u003e\u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 150–153 g\/m² \u003c\/p\u003e \u003chr\u003e \n\u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n\n   \u003ch3\u003eJersey\u003c\/h3\u003e \u003cp\u003e \u003cstrong\u003eZusammensetzung:\u003c\/strong\u003e\u003cbr\u003e 92 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche\u003cbr\u003e 8 % Elasthan \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Maschenwaren aus Baumwollgarnen mit Zusatz von Elastangarnen (EL-Anteil bis zu 8 %).\u003cbr\u003e Ausschließlich hergestellt unter Verwendung von Komponenten und Veredelungsverfahren, die gemäß OEKO-TEX® STANDARD 100 vorzertifiziert sind. \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 3 cm\u003cbr\u003e \u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 200–210 g\/m² \u003c\/p\u003e\n\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    flex: 1;\n    padding: 12px 20px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-gif {\n    width: 32px;\n    height: 32px;\n    object-fit: contain;\n    flex-shrink: 0;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content h3 {\n    margin-top: 0;\n  }\n\n  .stoff-tab-content h4 {\n    margin-top: 20px;\n    margin-bottom: 8px;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tab-button {\n      padding: 11px 8px;\n      gap: 6px;\n    }\n\n    .stoff-tab-gif {\n      width: 28px;\n      height: 28px;\n    }\n\n    .stoff-tab-content {\n      padding: 16px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\"Baumwolle\", \"Jersey\"];\n\n  function normalize(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getCorrectName(value) {\n    const wanted = normalize(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalize(stoffart) === wanted;\n    }) || null;\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalize(element.dataset.stoffButton) === normalize(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalize(correctName);\n\n    container\n      .querySelectorAll(\"[data-stoff-button]\")\n      .forEach(function (button) {\n        const active =\n          !button.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(button.dataset.stoffButton) === wanted;\n\n        button.classList.toggle(\"is-active\", active);\n        button.setAttribute(\n          \"aria-selected\",\n          active ? \"true\" : \"false\"\n        );\n      });\n\n    container\n      .querySelectorAll(\"[data-stoff-content]\")\n      .forEach(function (content) {\n        const active =\n          !content.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(content.dataset.stoffContent) === wanted;\n\n        content.classList.toggle(\"is-active\", active);\n        content.hidden = !active;\n      });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(\n        container,\n        firstButton.dataset.stoffButton\n      );\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section =\n      container.closest(\".shopify-section\") || document;\n\n    const selectedFields = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const field of selectedFields) {\n      const name = getCorrectName(field.value);\n\n      if (name) {\n        return name;\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabs() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart =\n        getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabs();\n      return;\n    }\n\n    const availability = {\n      baumwolle: false,\n      jersey: false\n    };\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const options = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      options.forEach(function (optionValue) {\n        const stoffart =\n          getCorrectName(optionValue);\n\n        if (stoffart) {\n          availability[normalize(stoffart)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available =\n          availability[normalize(stoffart)] === true;\n\n        const button = container.querySelector(\n          '[data-stoff-button=\"' + stoffart + '\"]'\n        );\n\n        const content = container.querySelector(\n          '[data-stoff-content=\"' + stoffart + '\"]'\n        );\n\n        if (button) {\n          button.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabs();\n  }\n\n  function loadProductAvailability() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      syncTabs();\n      return;\n    }\n\n    fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        syncTabs();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button =\n      event.target.closest(\"[data-stoff-button]\");\n\n    if (\n      !button ||\n      button.classList.contains(\"is-unavailable\")\n    ) {\n      return;\n    }\n\n    const container =\n      button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(\n        container,\n        button.dataset.stoffButton\n      );\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches(\n        'input[type=\"radio\"], select'\n      )\n    ) {\n      window.setTimeout(syncTabs, 50);\n    }\n  });\n\n  function initializeStoffTabs() {\n    loadProductAvailability();\n    window.setTimeout(syncTabs, 300);\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeStoffTabs\n    );\n  } else {\n    initializeStoffTabs();\n  }\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"dynamisches-moodbild\" data-moodbild\u003e\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .dynamisches-moodbild {\n    display: none;\n    margin: 25px 0;\n    text-align: center;\n  }\n\n  .dynamisches-moodbild.is-visible {\n    display: block;\n  }\n\n  .moodbild-link {\n    display: block;\n    text-decoration: none;\n    border-radius: 14px;\n    overflow: hidden;\n  }\n\n  .dynamisches-moodbild img {\n    display: block;\n    width: 100%;\n    height: auto;\n    margin: 0 auto;\n    border-radius: 14px;\n    transition: transform 0.25s ease;\n  }\n\n  .moodbild-link:hover img {\n    transform: scale(1.015);\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .dynamisches-moodbild img {\n      transition: none;\n    }\n\n    .moodbild-link:hover img {\n      transform: none;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const MARKER = \"#moodbild\";\n\n  const containers = document.querySelectorAll(\n    \"[data-moodbild]\"\n  );\n\n  if (!containers.length) return;\n\n  function cleanAlt(text) {\n    return String(text || \"\")\n      .replace(\/\\s*#Moodbild\\s*\/gi, \"\")\n      .trim();\n  }\n\n  function getImageUrl(image) {\n    if (image.currentSrc) {\n      return image.currentSrc;\n    }\n\n    const srcset =\n      image.getAttribute(\"srcset\") ||\n      image.getAttribute(\"data-srcset\");\n\n    if (srcset) {\n      const sources = srcset\n        .split(\",\")\n        .map(function (entry) {\n          return entry.trim().split(\/\\s+\/)[0];\n        })\n        .filter(Boolean);\n\n      if (sources.length) {\n        return sources[sources.length - 1];\n      }\n    }\n\n    return (\n      image.getAttribute(\"data-src\") ||\n      image.getAttribute(\"src\") ||\n      \"\"\n    );\n  }\n\n  function insertMoodbild(imageUrl, altText) {\n    if (!imageUrl) return false;\n\n    containers.forEach(function (container) {\n      if (\n        container.dataset.moodbildLoaded === \"true\"\n      ) {\n        return;\n      }\n\n      const link = document.createElement(\"a\");\n\n      link.href = \"https:\/\/www.bines-shop.com\";\n      link.target = \"_blank\";\n      link.rel = \"noopener noreferrer\";\n      link.className = \"moodbild-link\";\n\n      link.setAttribute(\n        \"aria-label\",\n        \"Bines Shop in einem neuen Fenster öffnen\"\n      );\n\n      const image = document.createElement(\"img\");\n\n      image.src = imageUrl;\n\n      image.alt =\n        cleanAlt(altText) ||\n        \"Moodbild zum Produkt\";\n\n      image.loading = \"lazy\";\n      image.decoding = \"async\";\n\n      link.appendChild(image);\n      container.replaceChildren(link);\n\n      container.classList.add(\"is-visible\");\n\n      container.dataset.moodbildLoaded = \"true\";\n    });\n\n    return true;\n  }\n\n  function findMoodbildInGallery() {\n    const images = Array.from(\n      document.querySelectorAll(\"img\")\n    ).filter(function (image) {\n      if (image.closest(\"[data-moodbild]\")) {\n        return false;\n      }\n\n      const searchableText = [\n        image.getAttribute(\"alt\"),\n        image.getAttribute(\"data-alt\"),\n        image.getAttribute(\"aria-label\")\n      ]\n        .filter(Boolean)\n        .join(\" \")\n        .toLowerCase();\n\n      return searchableText.includes(MARKER);\n    });\n\n    if (!images.length) {\n      return false;\n    }\n\n    const moodbild = images.find(function (image) {\n      return getImageUrl(image);\n    });\n\n    if (!moodbild) {\n      return false;\n    }\n\n    const altText =\n      moodbild.getAttribute(\"alt\") ||\n      moodbild.getAttribute(\"data-alt\") ||\n      moodbild.getAttribute(\"aria-label\") ||\n      \"\";\n\n    return insertMoodbild(\n      getImageUrl(moodbild),\n      altText\n    );\n  }\n\n  function findMoodbildInProductData() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return Promise.resolve(false);\n    }\n\n    return fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(function (product) {\n        const media = Array.isArray(product.media)\n          ? product.media\n          : [];\n\n        const moodbild = media.find(function (item) {\n          return String(item.alt || \"\")\n            .toLowerCase()\n            .includes(MARKER);\n        });\n\n        if (!moodbild) {\n          return false;\n        }\n\n        const imageUrl =\n          moodbild.preview_image \u0026\u0026\n          moodbild.preview_image.src\n            ? moodbild.preview_image.src\n            : moodbild.src;\n\n        return insertMoodbild(\n          imageUrl,\n          moodbild.alt\n        );\n      })\n      .catch(function () {\n        return false;\n      });\n  }\n\n  function initializeMoodbild() {\n    if (findMoodbildInGallery()) {\n      return;\n    }\n\n    findMoodbildInProductData()\n      .then(function (found) {\n        if (found) return;\n\n        const observer = new MutationObserver(\n          function () {\n            if (findMoodbildInGallery()) {\n              observer.disconnect();\n            }\n          }\n        );\n\n        observer.observe(\n          document.documentElement,\n          {\n            childList: true,\n            subtree: true,\n            attributes: true,\n            attributeFilter: [\n              \"alt\",\n              \"data-alt\",\n              \"src\",\n              \"data-src\",\n              \"srcset\",\n              \"data-srcset\"\n            ]\n          }\n        );\n\n        window.setTimeout(function () {\n          observer.disconnect();\n        }, 15000);\n      });\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeMoodbild\n    );\n  } else {\n    initializeMoodbild();\n  }\n})();\n\u003c\/script\u003e\n\u003c\/div\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle \/ 1 Meter","offer_id":53327292629259,"sku":"550000251","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 2 Meter","offer_id":53327292662027,"sku":"550000252","price":47.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 3 Meter","offer_id":53327292694795,"sku":"550000253","price":69.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 1 Meter","offer_id":53327292727563,"sku":"550000254","price":27.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 2 Meter","offer_id":53327292760331,"sku":"550000255","price":53.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 3 Meter","offer_id":53327292793099,"sku":"550000256","price":77.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-Blatter-Dschungelblau-Stoff-Baumwolle-Jersey-nahen-bunt-Muster_a2043e40-93d4-46c4-81d5-30cb99260017.jpg?v=1787873552"},{"product_id":"blatter-dschungel-grun-stoff","title":"Blätter-Dschungel grün – Stoff","description":"\u003c!-- PRODUKTBESCHREIBUNG MIT PARALLAX-LOGO --\u003e\n\n\u003csection class=\"bine-stoff-intro\"\u003e\n\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo-Stoffe-bunt.png?v=1787871207\" alt=\"Bine Brändle Stoffe Logo\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"bine-stoff-text\"\u003e\n\n    \u003ch2\u003eFarbenfrohe Stoffe mit Design von Bine Brändle\u003c\/h2\u003e\n\n    \u003cp class=\"bine-stoff-einleitung\"\u003e\n      Mach dein nächstes Nähprojekt zu etwas ganz Besonderem! Unsere\n      wunderschön bedruckten Stoffe mit einem liebevoll gestalteten Design\n      von \u003cstrong\u003eBine Brändle\u003c\/strong\u003e bringen Farbe, Freude und gute Laune\n      an die Nähmaschine.\n    \u003c\/p\u003e\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"Bine Brändle\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n    \u003cp\u003e\n      Du kannst dein Lieblingsdesign passend zu deinem Projekt als\n      \u003cstrong\u003eBaumwollgewebe\u003c\/strong\u003e oder \u003cstrong\u003eJersey\u003c\/strong\u003e auswählen.\n    \u003c\/p\u003e\n\n    \u003cdiv class=\"bine-stoffarten\"\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Das angenehm glatte und formstabile Baumwollgewebe eignet sich\n          wunderbar für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eHemden und Hosen\u003c\/li\u003e\n          \u003cli\u003eTaschen und Beutel\u003c\/li\u003e\n          \u003cli\u003eKissen und Wohnaccessoires\u003c\/li\u003e\n          \u003cli\u003eDekorationen\u003c\/li\u003e\n          \u003cli\u003ePatchwork-Projekte\u003c\/li\u003e\n          \u003cli\u003eSchultüten und kleine Geschenkideen\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eJersey\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Der weiche und elastische Jersey-Stoff ist besonders angenehm\n          auf der Haut und ideal für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eShirts und Kleider\u003c\/li\u003e\n          \u003cli\u003eHosen und Leggings\u003c\/li\u003e\n          \u003cli\u003eMützen und Halstücher\u003c\/li\u003e\n          \u003cli\u003eKinder- und Babykleidung\u003c\/li\u003e\n          \u003cli\u003eBequeme Lieblingsstücke für Groß und Klein\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"bine-stofflaengen\"\u003e\n      \u003ch3\u003eErhältliche Stofflängen\u003c\/h3\u003e\n\n      \u003cp\u003e\n        Du erhältst den Stoff fertig zugeschnitten in folgenden Längen:\n      \u003c\/p\u003e\n\n      \u003cdiv class=\"bine-laengen-auswahl\"\u003e\n        \u003cspan\u003e1 Meter\u003c\/span\u003e\n        \u003cspan\u003e2 Meter\u003c\/span\u003e\n        \u003cspan\u003e3 Meter\u003c\/span\u003e\n      \u003c\/div\u003e\n\n      \u003cp\u003e\n        Wähle einfach deine gewünschte Stoffart und anschließend die\n        passende Länge aus.\n      \u003c\/p\u003e\n\n      \u003cp class=\"bine-stoff-hinweis\"\u003e\n        \u003cstrong\u003eBitte beachte:\u003c\/strong\u003e Die ausgewählte Länge wird als\n        zusammenhängendes Stoffstück geliefert. Bei der Auswahl von\n        2 Metern erhältst du ein 2 Meter langes Stück, bei 3 Metern ein\n        zusammenhängendes 3-Meter-Stück.\n      \u003c\/p\u003e\n    \u003c\/div\u003e\n\n    \u003cp\u003e\n      Ob als farbenfrohes Kleidungsstück, liebevoll genähtes Geschenk oder\n      besonderer Blickfang für dein Zuhause – mit den Designs von\n      \u003cstrong\u003eBine Brändle\u003c\/strong\u003e wird jedes Nähprojekt herrlich bunt und\n      unverwechselbar.\n    \u003c\/p\u003e\n\n  \u003c\/div\u003e\n\n\u003c\/section\u003e\n\n\u003cstyle\u003e\n  .bine-stoff-intro {\n    margin: 20px 0 35px;\n    overflow: hidden;\n  }\n\n  .bine-logo-parallax {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    min-height: 150px;\n    margin-bottom: 20px;\n    padding: 20px;\n    \n    border-radius: 16px;\n    overflow: hidden;\n  }\n\n  .bine-stoff-logo {\n    display: block;\n    width: auto;\n    max-width: min(320px, 80%);\n    max-height: 130px;\n    object-fit: contain;\n    will-change: transform;\n    transform: translate3d(0, 0, 0);\n  }\n\n  .bine-stoff-text h2 {\n    margin-top: 0;\n    margin-bottom: 15px;\n    color: #08868a;\n    font-size: clamp(24px, 4vw, 34px);\n    line-height: 1.2;\n  }\n\n  .bine-stoff-text h3 {\n    margin-top: 0;\n    margin-bottom: 10px;\n  }\n\n  .bine-stoff-einleitung {\n    font-size: 1.08em;\n    line-height: 1.65;\n  }\n\n  .bine-stoffarten {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 16px;\n    margin: 25px 0;\n  }\n\n  .bine-stoffart {\n    padding: 20px;\n    background: #f7f7f7;\n    border-top: 4px solid #08868a;\n    border-radius: 10px;\n  }\n\n  .bine-stoffart ul {\n    margin-bottom: 0;\n    padding-left: 20px;\n  }\n\n  .bine-stoffart li {\n    margin-bottom: 5px;\n  }\n\n  .bine-stofflaengen {\n    margin: 25px 0;\n    padding: 22px;\n    background: rgba(8, 134, 138, 0.08);\n    border-radius: 12px;\n  }\n\n  .bine-laengen-auswahl {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin: 16px 0;\n  }\n\n  .bine-laengen-auswahl span {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 90px;\n    padding: 9px 15px;\n    background: #08868a;\n    color: #ffffff;\n    border-radius: 20px;\n    font-weight: 700;\n  }\n\n  .bine-stoff-hinweis {\n    margin-bottom: 0;\n    padding: 14px;\n    background: #ffffff;\n    border-left: 4px solid #08868a;\n    border-radius: 5px;\n  }\n\n  @media screen and (max-width: 650px) {\n    .bine-stoffarten {\n      grid-template-columns: 1fr;\n    }\n\n    .bine-logo-parallax {\n      min-height: 120px;\n    }\n\n    .bine-stoff-logo {\n      max-width: 75%;\n      max-height: 100px;\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .bine-stoff-logo {\n      transform: none !important;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const parallaxElements = document.querySelectorAll(\n    \"[data-bine-parallax] .bine-stoff-logo\"\n  );\n\n  if (\n    !parallaxElements.length ||\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  ) {\n    return;\n  }\n\n  let animationFrameRequested = false;\n\n  function updateParallax() {\n    parallaxElements.forEach(function (logo) {\n      const container = logo.closest(\"[data-bine-parallax]\");\n      const position = container.getBoundingClientRect();\n      const windowHeight = window.innerHeight;\n\n      if (\n        position.bottom \u003e= 0 \u0026\u0026\n        position.top \u003c= windowHeight\n      ) {\n        const containerCenter =\n          position.top + position.height \/ 2;\n\n        const screenCenter = windowHeight \/ 2;\n\n        const offset =\n          (containerCenter - screenCenter) * -0.08;\n\n        const limitedOffset = Math.max(\n          -22,\n          Math.min(22, offset)\n        );\n\n        logo.style.transform =\n          \"translate3d(0,\" + limitedOffset + \"px,0)\";\n      }\n    });\n\n    animationFrameRequested = false;\n  }\n\n  function requestParallaxUpdate() {\n    if (!animationFrameRequested) {\n      window.requestAnimationFrame(updateParallax);\n      animationFrameRequested = true;\n    }\n  }\n\n  window.addEventListener(\n    \"scroll\",\n    requestParallaxUpdate,\n    { passive: true }\n  );\n\n  window.addEventListener(\n    \"resize\",\n    requestParallaxUpdate\n  );\n\n  requestParallaxUpdate();\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif02?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eBaumwolle\u003c\/span\u003e\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif01?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eJersey\u003c\/span\u003e\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n   \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e \u003cp\u003e 100 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Rohgewebe aus 100 % Baumwolle,\u003cbr\u003e hergestellt aus Materialien, die gemäß OEKO-TEX® STANDARD 100 zertifiziert sind. \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 2 cm\u003cbr\u003e\u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 150–153 g\/m² \u003c\/p\u003e \u003chr\u003e \n\u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n\n   \u003ch3\u003eJersey\u003c\/h3\u003e \u003cp\u003e \u003cstrong\u003eZusammensetzung:\u003c\/strong\u003e\u003cbr\u003e 92 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche\u003cbr\u003e 8 % Elasthan \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Maschenwaren aus Baumwollgarnen mit Zusatz von Elastangarnen (EL-Anteil bis zu 8 %).\u003cbr\u003e Ausschließlich hergestellt unter Verwendung von Komponenten und Veredelungsverfahren, die gemäß OEKO-TEX® STANDARD 100 vorzertifiziert sind. \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 3 cm\u003cbr\u003e \u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 200–210 g\/m² \u003c\/p\u003e\n\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    flex: 1;\n    padding: 12px 20px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-gif {\n    width: 32px;\n    height: 32px;\n    object-fit: contain;\n    flex-shrink: 0;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content h3 {\n    margin-top: 0;\n  }\n\n  .stoff-tab-content h4 {\n    margin-top: 20px;\n    margin-bottom: 8px;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tab-button {\n      padding: 11px 8px;\n      gap: 6px;\n    }\n\n    .stoff-tab-gif {\n      width: 28px;\n      height: 28px;\n    }\n\n    .stoff-tab-content {\n      padding: 16px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\"Baumwolle\", \"Jersey\"];\n\n  function normalize(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getCorrectName(value) {\n    const wanted = normalize(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalize(stoffart) === wanted;\n    }) || null;\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalize(element.dataset.stoffButton) === normalize(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalize(correctName);\n\n    container\n      .querySelectorAll(\"[data-stoff-button]\")\n      .forEach(function (button) {\n        const active =\n          !button.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(button.dataset.stoffButton) === wanted;\n\n        button.classList.toggle(\"is-active\", active);\n        button.setAttribute(\n          \"aria-selected\",\n          active ? \"true\" : \"false\"\n        );\n      });\n\n    container\n      .querySelectorAll(\"[data-stoff-content]\")\n      .forEach(function (content) {\n        const active =\n          !content.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(content.dataset.stoffContent) === wanted;\n\n        content.classList.toggle(\"is-active\", active);\n        content.hidden = !active;\n      });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(\n        container,\n        firstButton.dataset.stoffButton\n      );\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section =\n      container.closest(\".shopify-section\") || document;\n\n    const selectedFields = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const field of selectedFields) {\n      const name = getCorrectName(field.value);\n\n      if (name) {\n        return name;\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabs() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart =\n        getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabs();\n      return;\n    }\n\n    const availability = {\n      baumwolle: false,\n      jersey: false\n    };\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const options = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      options.forEach(function (optionValue) {\n        const stoffart =\n          getCorrectName(optionValue);\n\n        if (stoffart) {\n          availability[normalize(stoffart)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available =\n          availability[normalize(stoffart)] === true;\n\n        const button = container.querySelector(\n          '[data-stoff-button=\"' + stoffart + '\"]'\n        );\n\n        const content = container.querySelector(\n          '[data-stoff-content=\"' + stoffart + '\"]'\n        );\n\n        if (button) {\n          button.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabs();\n  }\n\n  function loadProductAvailability() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      syncTabs();\n      return;\n    }\n\n    fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        syncTabs();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button =\n      event.target.closest(\"[data-stoff-button]\");\n\n    if (\n      !button ||\n      button.classList.contains(\"is-unavailable\")\n    ) {\n      return;\n    }\n\n    const container =\n      button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(\n        container,\n        button.dataset.stoffButton\n      );\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches(\n        'input[type=\"radio\"], select'\n      )\n    ) {\n      window.setTimeout(syncTabs, 50);\n    }\n  });\n\n  function initializeStoffTabs() {\n    loadProductAvailability();\n    window.setTimeout(syncTabs, 300);\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeStoffTabs\n    );\n  } else {\n    initializeStoffTabs();\n  }\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"dynamisches-moodbild\" data-moodbild\u003e\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .dynamisches-moodbild {\n    display: none;\n    margin: 25px 0;\n    text-align: center;\n  }\n\n  .dynamisches-moodbild.is-visible {\n    display: block;\n  }\n\n  .moodbild-link {\n    display: block;\n    text-decoration: none;\n    border-radius: 14px;\n    overflow: hidden;\n  }\n\n  .dynamisches-moodbild img {\n    display: block;\n    width: 100%;\n    height: auto;\n    margin: 0 auto;\n    border-radius: 14px;\n    transition: transform 0.25s ease;\n  }\n\n  .moodbild-link:hover img {\n    transform: scale(1.015);\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .dynamisches-moodbild img {\n      transition: none;\n    }\n\n    .moodbild-link:hover img {\n      transform: none;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const MARKER = \"#moodbild\";\n\n  const containers = document.querySelectorAll(\n    \"[data-moodbild]\"\n  );\n\n  if (!containers.length) return;\n\n  function cleanAlt(text) {\n    return String(text || \"\")\n      .replace(\/\\s*#Moodbild\\s*\/gi, \"\")\n      .trim();\n  }\n\n  function getImageUrl(image) {\n    if (image.currentSrc) {\n      return image.currentSrc;\n    }\n\n    const srcset =\n      image.getAttribute(\"srcset\") ||\n      image.getAttribute(\"data-srcset\");\n\n    if (srcset) {\n      const sources = srcset\n        .split(\",\")\n        .map(function (entry) {\n          return entry.trim().split(\/\\s+\/)[0];\n        })\n        .filter(Boolean);\n\n      if (sources.length) {\n        return sources[sources.length - 1];\n      }\n    }\n\n    return (\n      image.getAttribute(\"data-src\") ||\n      image.getAttribute(\"src\") ||\n      \"\"\n    );\n  }\n\n  function insertMoodbild(imageUrl, altText) {\n    if (!imageUrl) return false;\n\n    containers.forEach(function (container) {\n      if (\n        container.dataset.moodbildLoaded === \"true\"\n      ) {\n        return;\n      }\n\n      const link = document.createElement(\"a\");\n\n      link.href = \"https:\/\/www.bines-shop.com\";\n      link.target = \"_blank\";\n      link.rel = \"noopener noreferrer\";\n      link.className = \"moodbild-link\";\n\n      link.setAttribute(\n        \"aria-label\",\n        \"Bines Shop in einem neuen Fenster öffnen\"\n      );\n\n      const image = document.createElement(\"img\");\n\n      image.src = imageUrl;\n\n      image.alt =\n        cleanAlt(altText) ||\n        \"Moodbild zum Produkt\";\n\n      image.loading = \"lazy\";\n      image.decoding = \"async\";\n\n      link.appendChild(image);\n      container.replaceChildren(link);\n\n      container.classList.add(\"is-visible\");\n\n      container.dataset.moodbildLoaded = \"true\";\n    });\n\n    return true;\n  }\n\n  function findMoodbildInGallery() {\n    const images = Array.from(\n      document.querySelectorAll(\"img\")\n    ).filter(function (image) {\n      if (image.closest(\"[data-moodbild]\")) {\n        return false;\n      }\n\n      const searchableText = [\n        image.getAttribute(\"alt\"),\n        image.getAttribute(\"data-alt\"),\n        image.getAttribute(\"aria-label\")\n      ]\n        .filter(Boolean)\n        .join(\" \")\n        .toLowerCase();\n\n      return searchableText.includes(MARKER);\n    });\n\n    if (!images.length) {\n      return false;\n    }\n\n    const moodbild = images.find(function (image) {\n      return getImageUrl(image);\n    });\n\n    if (!moodbild) {\n      return false;\n    }\n\n    const altText =\n      moodbild.getAttribute(\"alt\") ||\n      moodbild.getAttribute(\"data-alt\") ||\n      moodbild.getAttribute(\"aria-label\") ||\n      \"\";\n\n    return insertMoodbild(\n      getImageUrl(moodbild),\n      altText\n    );\n  }\n\n  function findMoodbildInProductData() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return Promise.resolve(false);\n    }\n\n    return fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(function (product) {\n        const media = Array.isArray(product.media)\n          ? product.media\n          : [];\n\n        const moodbild = media.find(function (item) {\n          return String(item.alt || \"\")\n            .toLowerCase()\n            .includes(MARKER);\n        });\n\n        if (!moodbild) {\n          return false;\n        }\n\n        const imageUrl =\n          moodbild.preview_image \u0026\u0026\n          moodbild.preview_image.src\n            ? moodbild.preview_image.src\n            : moodbild.src;\n\n        return insertMoodbild(\n          imageUrl,\n          moodbild.alt\n        );\n      })\n      .catch(function () {\n        return false;\n      });\n  }\n\n  function initializeMoodbild() {\n    if (findMoodbildInGallery()) {\n      return;\n    }\n\n    findMoodbildInProductData()\n      .then(function (found) {\n        if (found) return;\n\n        const observer = new MutationObserver(\n          function () {\n            if (findMoodbildInGallery()) {\n              observer.disconnect();\n            }\n          }\n        );\n\n        observer.observe(\n          document.documentElement,\n          {\n            childList: true,\n            subtree: true,\n            attributes: true,\n            attributeFilter: [\n              \"alt\",\n              \"data-alt\",\n              \"src\",\n              \"data-src\",\n              \"srcset\",\n              \"data-srcset\"\n            ]\n          }\n        );\n\n        window.setTimeout(function () {\n          observer.disconnect();\n        }, 15000);\n      });\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeMoodbild\n    );\n  } else {\n    initializeMoodbild();\n  }\n})();\n\u003c\/script\u003e\n\u003c\/div\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle \/ 1 Meter","offer_id":53327306359051,"sku":"550000245","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 2 Meter","offer_id":53327306391819,"sku":"550000246","price":47.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 3 Meter","offer_id":53327306424587,"sku":"550000247","price":69.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 1 Meter","offer_id":53327306457355,"sku":"550000248","price":27.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 2 Meter","offer_id":53327306490123,"sku":"550000249","price":53.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 3 Meter","offer_id":53327306522891,"sku":"550000250","price":77.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-Blatter-Dschungelgrun-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1787874596"},{"product_id":"blatter-dschungel-violett-stoff","title":"Blätter-Dschungel violett – Stoff","description":"\u003c!-- PRODUKTBESCHREIBUNG MIT PARALLAX-LOGO --\u003e\n\n\u003csection class=\"bine-stoff-intro\"\u003e\n\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo-Stoffe-bunt.png?v=1787871207\" alt=\"Bine Brändle Stoffe Logo\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"bine-stoff-text\"\u003e\n\n    \u003ch2\u003eFarbenfrohe Stoffe mit Design von Bine Brändle\u003c\/h2\u003e\n\n    \u003cp class=\"bine-stoff-einleitung\"\u003e\n      Mach dein nächstes Nähprojekt zu etwas ganz Besonderem! Unsere\n      wunderschön bedruckten Stoffe mit einem liebevoll gestalteten Design\n      von \u003cstrong\u003eBine Brändle\u003c\/strong\u003e bringen Farbe, Freude und gute Laune\n      an die Nähmaschine.\n    \u003c\/p\u003e\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"Bine Brändle\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n    \u003cp\u003e\n      Du kannst dein Lieblingsdesign passend zu deinem Projekt als\n      \u003cstrong\u003eBaumwollgewebe\u003c\/strong\u003e oder \u003cstrong\u003eJersey\u003c\/strong\u003e auswählen.\n    \u003c\/p\u003e\n\n    \u003cdiv class=\"bine-stoffarten\"\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Das angenehm glatte und formstabile Baumwollgewebe eignet sich\n          wunderbar für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eHemden und Hosen\u003c\/li\u003e\n          \u003cli\u003eTaschen und Beutel\u003c\/li\u003e\n          \u003cli\u003eKissen und Wohnaccessoires\u003c\/li\u003e\n          \u003cli\u003eDekorationen\u003c\/li\u003e\n          \u003cli\u003ePatchwork-Projekte\u003c\/li\u003e\n          \u003cli\u003eSchultüten und kleine Geschenkideen\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eJersey\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Der weiche und elastische Jersey-Stoff ist besonders angenehm\n          auf der Haut und ideal für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eShirts und Kleider\u003c\/li\u003e\n          \u003cli\u003eHosen und Leggings\u003c\/li\u003e\n          \u003cli\u003eMützen und Halstücher\u003c\/li\u003e\n          \u003cli\u003eKinder- und Babykleidung\u003c\/li\u003e\n          \u003cli\u003eBequeme Lieblingsstücke für Groß und Klein\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"bine-stofflaengen\"\u003e\n      \u003ch3\u003eErhältliche Stofflängen\u003c\/h3\u003e\n\n      \u003cp\u003e\n        Du erhältst den Stoff fertig zugeschnitten in folgenden Längen:\n      \u003c\/p\u003e\n\n      \u003cdiv class=\"bine-laengen-auswahl\"\u003e\n        \u003cspan\u003e1 Meter\u003c\/span\u003e\n        \u003cspan\u003e2 Meter\u003c\/span\u003e\n        \u003cspan\u003e3 Meter\u003c\/span\u003e\n      \u003c\/div\u003e\n\n      \u003cp\u003e\n        Wähle einfach deine gewünschte Stoffart und anschließend die\n        passende Länge aus.\n      \u003c\/p\u003e\n\n      \u003cp class=\"bine-stoff-hinweis\"\u003e\n        \u003cstrong\u003eBitte beachte:\u003c\/strong\u003e Die ausgewählte Länge wird als\n        zusammenhängendes Stoffstück geliefert. Bei der Auswahl von\n        2 Metern erhältst du ein 2 Meter langes Stück, bei 3 Metern ein\n        zusammenhängendes 3-Meter-Stück.\n      \u003c\/p\u003e\n    \u003c\/div\u003e\n\n    \u003cp\u003e\n      Ob als farbenfrohes Kleidungsstück, liebevoll genähtes Geschenk oder\n      besonderer Blickfang für dein Zuhause – mit den Designs von\n      \u003cstrong\u003eBine Brändle\u003c\/strong\u003e wird jedes Nähprojekt herrlich bunt und\n      unverwechselbar.\n    \u003c\/p\u003e\n\n  \u003c\/div\u003e\n\n\u003c\/section\u003e\n\n\u003cstyle\u003e\n  .bine-stoff-intro {\n    margin: 20px 0 35px;\n    overflow: hidden;\n  }\n\n  .bine-logo-parallax {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    min-height: 150px;\n    margin-bottom: 20px;\n    padding: 20px;\n    \n    border-radius: 16px;\n    overflow: hidden;\n  }\n\n  .bine-stoff-logo {\n    display: block;\n    width: auto;\n    max-width: min(320px, 80%);\n    max-height: 130px;\n    object-fit: contain;\n    will-change: transform;\n    transform: translate3d(0, 0, 0);\n  }\n\n  .bine-stoff-text h2 {\n    margin-top: 0;\n    margin-bottom: 15px;\n    color: #08868a;\n    font-size: clamp(24px, 4vw, 34px);\n    line-height: 1.2;\n  }\n\n  .bine-stoff-text h3 {\n    margin-top: 0;\n    margin-bottom: 10px;\n  }\n\n  .bine-stoff-einleitung {\n    font-size: 1.08em;\n    line-height: 1.65;\n  }\n\n  .bine-stoffarten {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 16px;\n    margin: 25px 0;\n  }\n\n  .bine-stoffart {\n    padding: 20px;\n    background: #f7f7f7;\n    border-top: 4px solid #08868a;\n    border-radius: 10px;\n  }\n\n  .bine-stoffart ul {\n    margin-bottom: 0;\n    padding-left: 20px;\n  }\n\n  .bine-stoffart li {\n    margin-bottom: 5px;\n  }\n\n  .bine-stofflaengen {\n    margin: 25px 0;\n    padding: 22px;\n    background: rgba(8, 134, 138, 0.08);\n    border-radius: 12px;\n  }\n\n  .bine-laengen-auswahl {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin: 16px 0;\n  }\n\n  .bine-laengen-auswahl span {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 90px;\n    padding: 9px 15px;\n    background: #08868a;\n    color: #ffffff;\n    border-radius: 20px;\n    font-weight: 700;\n  }\n\n  .bine-stoff-hinweis {\n    margin-bottom: 0;\n    padding: 14px;\n    background: #ffffff;\n    border-left: 4px solid #08868a;\n    border-radius: 5px;\n  }\n\n  @media screen and (max-width: 650px) {\n    .bine-stoffarten {\n      grid-template-columns: 1fr;\n    }\n\n    .bine-logo-parallax {\n      min-height: 120px;\n    }\n\n    .bine-stoff-logo {\n      max-width: 75%;\n      max-height: 100px;\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .bine-stoff-logo {\n      transform: none !important;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const parallaxElements = document.querySelectorAll(\n    \"[data-bine-parallax] .bine-stoff-logo\"\n  );\n\n  if (\n    !parallaxElements.length ||\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  ) {\n    return;\n  }\n\n  let animationFrameRequested = false;\n\n  function updateParallax() {\n    parallaxElements.forEach(function (logo) {\n      const container = logo.closest(\"[data-bine-parallax]\");\n      const position = container.getBoundingClientRect();\n      const windowHeight = window.innerHeight;\n\n      if (\n        position.bottom \u003e= 0 \u0026\u0026\n        position.top \u003c= windowHeight\n      ) {\n        const containerCenter =\n          position.top + position.height \/ 2;\n\n        const screenCenter = windowHeight \/ 2;\n\n        const offset =\n          (containerCenter - screenCenter) * -0.08;\n\n        const limitedOffset = Math.max(\n          -22,\n          Math.min(22, offset)\n        );\n\n        logo.style.transform =\n          \"translate3d(0,\" + limitedOffset + \"px,0)\";\n      }\n    });\n\n    animationFrameRequested = false;\n  }\n\n  function requestParallaxUpdate() {\n    if (!animationFrameRequested) {\n      window.requestAnimationFrame(updateParallax);\n      animationFrameRequested = true;\n    }\n  }\n\n  window.addEventListener(\n    \"scroll\",\n    requestParallaxUpdate,\n    { passive: true }\n  );\n\n  window.addEventListener(\n    \"resize\",\n    requestParallaxUpdate\n  );\n\n  requestParallaxUpdate();\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif02?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eBaumwolle\u003c\/span\u003e\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif01?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eJersey\u003c\/span\u003e\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n   \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e \u003cp\u003e 100 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Rohgewebe aus 100 % Baumwolle,\u003cbr\u003e hergestellt aus Materialien, die gemäß OEKO-TEX® STANDARD 100 zertifiziert sind. \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 2 cm\u003cbr\u003e\u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 150–153 g\/m² \u003c\/p\u003e \u003chr\u003e \n\u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n\n   \u003ch3\u003eJersey\u003c\/h3\u003e \u003cp\u003e \u003cstrong\u003eZusammensetzung:\u003c\/strong\u003e\u003cbr\u003e 92 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche\u003cbr\u003e 8 % Elasthan \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Maschenwaren aus Baumwollgarnen mit Zusatz von Elastangarnen (EL-Anteil bis zu 8 %).\u003cbr\u003e Ausschließlich hergestellt unter Verwendung von Komponenten und Veredelungsverfahren, die gemäß OEKO-TEX® STANDARD 100 vorzertifiziert sind. \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 3 cm\u003cbr\u003e \u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 200–210 g\/m² \u003c\/p\u003e\n\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    flex: 1;\n    padding: 12px 20px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-gif {\n    width: 32px;\n    height: 32px;\n    object-fit: contain;\n    flex-shrink: 0;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content h3 {\n    margin-top: 0;\n  }\n\n  .stoff-tab-content h4 {\n    margin-top: 20px;\n    margin-bottom: 8px;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tab-button {\n      padding: 11px 8px;\n      gap: 6px;\n    }\n\n    .stoff-tab-gif {\n      width: 28px;\n      height: 28px;\n    }\n\n    .stoff-tab-content {\n      padding: 16px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\"Baumwolle\", \"Jersey\"];\n\n  function normalize(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getCorrectName(value) {\n    const wanted = normalize(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalize(stoffart) === wanted;\n    }) || null;\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalize(element.dataset.stoffButton) === normalize(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalize(correctName);\n\n    container\n      .querySelectorAll(\"[data-stoff-button]\")\n      .forEach(function (button) {\n        const active =\n          !button.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(button.dataset.stoffButton) === wanted;\n\n        button.classList.toggle(\"is-active\", active);\n        button.setAttribute(\n          \"aria-selected\",\n          active ? \"true\" : \"false\"\n        );\n      });\n\n    container\n      .querySelectorAll(\"[data-stoff-content]\")\n      .forEach(function (content) {\n        const active =\n          !content.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(content.dataset.stoffContent) === wanted;\n\n        content.classList.toggle(\"is-active\", active);\n        content.hidden = !active;\n      });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(\n        container,\n        firstButton.dataset.stoffButton\n      );\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section =\n      container.closest(\".shopify-section\") || document;\n\n    const selectedFields = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const field of selectedFields) {\n      const name = getCorrectName(field.value);\n\n      if (name) {\n        return name;\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabs() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart =\n        getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabs();\n      return;\n    }\n\n    const availability = {\n      baumwolle: false,\n      jersey: false\n    };\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const options = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      options.forEach(function (optionValue) {\n        const stoffart =\n          getCorrectName(optionValue);\n\n        if (stoffart) {\n          availability[normalize(stoffart)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available =\n          availability[normalize(stoffart)] === true;\n\n        const button = container.querySelector(\n          '[data-stoff-button=\"' + stoffart + '\"]'\n        );\n\n        const content = container.querySelector(\n          '[data-stoff-content=\"' + stoffart + '\"]'\n        );\n\n        if (button) {\n          button.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabs();\n  }\n\n  function loadProductAvailability() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      syncTabs();\n      return;\n    }\n\n    fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        syncTabs();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button =\n      event.target.closest(\"[data-stoff-button]\");\n\n    if (\n      !button ||\n      button.classList.contains(\"is-unavailable\")\n    ) {\n      return;\n    }\n\n    const container =\n      button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(\n        container,\n        button.dataset.stoffButton\n      );\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches(\n        'input[type=\"radio\"], select'\n      )\n    ) {\n      window.setTimeout(syncTabs, 50);\n    }\n  });\n\n  function initializeStoffTabs() {\n    loadProductAvailability();\n    window.setTimeout(syncTabs, 300);\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeStoffTabs\n    );\n  } else {\n    initializeStoffTabs();\n  }\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"dynamisches-moodbild\" data-moodbild\u003e\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .dynamisches-moodbild {\n    display: none;\n    margin: 25px 0;\n    text-align: center;\n  }\n\n  .dynamisches-moodbild.is-visible {\n    display: block;\n  }\n\n  .moodbild-link {\n    display: block;\n    text-decoration: none;\n    border-radius: 14px;\n    overflow: hidden;\n  }\n\n  .dynamisches-moodbild img {\n    display: block;\n    width: 100%;\n    height: auto;\n    margin: 0 auto;\n    border-radius: 14px;\n    transition: transform 0.25s ease;\n  }\n\n  .moodbild-link:hover img {\n    transform: scale(1.015);\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .dynamisches-moodbild img {\n      transition: none;\n    }\n\n    .moodbild-link:hover img {\n      transform: none;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const MARKER = \"#moodbild\";\n\n  const containers = document.querySelectorAll(\n    \"[data-moodbild]\"\n  );\n\n  if (!containers.length) return;\n\n  function cleanAlt(text) {\n    return String(text || \"\")\n      .replace(\/\\s*#Moodbild\\s*\/gi, \"\")\n      .trim();\n  }\n\n  function getImageUrl(image) {\n    if (image.currentSrc) {\n      return image.currentSrc;\n    }\n\n    const srcset =\n      image.getAttribute(\"srcset\") ||\n      image.getAttribute(\"data-srcset\");\n\n    if (srcset) {\n      const sources = srcset\n        .split(\",\")\n        .map(function (entry) {\n          return entry.trim().split(\/\\s+\/)[0];\n        })\n        .filter(Boolean);\n\n      if (sources.length) {\n        return sources[sources.length - 1];\n      }\n    }\n\n    return (\n      image.getAttribute(\"data-src\") ||\n      image.getAttribute(\"src\") ||\n      \"\"\n    );\n  }\n\n  function insertMoodbild(imageUrl, altText) {\n    if (!imageUrl) return false;\n\n    containers.forEach(function (container) {\n      if (\n        container.dataset.moodbildLoaded === \"true\"\n      ) {\n        return;\n      }\n\n      const link = document.createElement(\"a\");\n\n      link.href = \"https:\/\/www.bines-shop.com\";\n      link.target = \"_blank\";\n      link.rel = \"noopener noreferrer\";\n      link.className = \"moodbild-link\";\n\n      link.setAttribute(\n        \"aria-label\",\n        \"Bines Shop in einem neuen Fenster öffnen\"\n      );\n\n      const image = document.createElement(\"img\");\n\n      image.src = imageUrl;\n\n      image.alt =\n        cleanAlt(altText) ||\n        \"Moodbild zum Produkt\";\n\n      image.loading = \"lazy\";\n      image.decoding = \"async\";\n\n      link.appendChild(image);\n      container.replaceChildren(link);\n\n      container.classList.add(\"is-visible\");\n\n      container.dataset.moodbildLoaded = \"true\";\n    });\n\n    return true;\n  }\n\n  function findMoodbildInGallery() {\n    const images = Array.from(\n      document.querySelectorAll(\"img\")\n    ).filter(function (image) {\n      if (image.closest(\"[data-moodbild]\")) {\n        return false;\n      }\n\n      const searchableText = [\n        image.getAttribute(\"alt\"),\n        image.getAttribute(\"data-alt\"),\n        image.getAttribute(\"aria-label\")\n      ]\n        .filter(Boolean)\n        .join(\" \")\n        .toLowerCase();\n\n      return searchableText.includes(MARKER);\n    });\n\n    if (!images.length) {\n      return false;\n    }\n\n    const moodbild = images.find(function (image) {\n      return getImageUrl(image);\n    });\n\n    if (!moodbild) {\n      return false;\n    }\n\n    const altText =\n      moodbild.getAttribute(\"alt\") ||\n      moodbild.getAttribute(\"data-alt\") ||\n      moodbild.getAttribute(\"aria-label\") ||\n      \"\";\n\n    return insertMoodbild(\n      getImageUrl(moodbild),\n      altText\n    );\n  }\n\n  function findMoodbildInProductData() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return Promise.resolve(false);\n    }\n\n    return fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(function (product) {\n        const media = Array.isArray(product.media)\n          ? product.media\n          : [];\n\n        const moodbild = media.find(function (item) {\n          return String(item.alt || \"\")\n            .toLowerCase()\n            .includes(MARKER);\n        });\n\n        if (!moodbild) {\n          return false;\n        }\n\n        const imageUrl =\n          moodbild.preview_image \u0026\u0026\n          moodbild.preview_image.src\n            ? moodbild.preview_image.src\n            : moodbild.src;\n\n        return insertMoodbild(\n          imageUrl,\n          moodbild.alt\n        );\n      })\n      .catch(function () {\n        return false;\n      });\n  }\n\n  function initializeMoodbild() {\n    if (findMoodbildInGallery()) {\n      return;\n    }\n\n    findMoodbildInProductData()\n      .then(function (found) {\n        if (found) return;\n\n        const observer = new MutationObserver(\n          function () {\n            if (findMoodbildInGallery()) {\n              observer.disconnect();\n            }\n          }\n        );\n\n        observer.observe(\n          document.documentElement,\n          {\n            childList: true,\n            subtree: true,\n            attributes: true,\n            attributeFilter: [\n              \"alt\",\n              \"data-alt\",\n              \"src\",\n              \"data-src\",\n              \"srcset\",\n              \"data-srcset\"\n            ]\n          }\n        );\n\n        window.setTimeout(function () {\n          observer.disconnect();\n        }, 15000);\n      });\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeMoodbild\n    );\n  } else {\n    initializeMoodbild();\n  }\n})();\n\u003c\/script\u003e\n\u003c\/div\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle \/ 1 Meter","offer_id":53327308587275,"sku":"550000239","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 2 Meter","offer_id":53327308620043,"sku":"550000240","price":47.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 3 Meter","offer_id":53327308652811,"sku":"550000241","price":69.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 1 Meter","offer_id":53327308685579,"sku":"550000242","price":27.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 2 Meter","offer_id":53327308718347,"sku":"550000243","price":53.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 3 Meter","offer_id":53327308751115,"sku":"550000244","price":77.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-Blatter-Dschungelviolett-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1787874949"},{"product_id":"blumchenwiese-stoff","title":"Blümchenwiese – Stoff","description":"\u003c!-- PRODUKTBESCHREIBUNG MIT PARALLAX-LOGO --\u003e\n\n\u003csection class=\"bine-stoff-intro\"\u003e\n\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo-Stoffe-bunt.png?v=1787871207\" alt=\"Bine Brändle Stoffe Logo\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"bine-stoff-text\"\u003e\n\n    \u003ch2\u003eFarbenfrohe Stoffe mit Design von Bine Brändle\u003c\/h2\u003e\n\n    \u003cp class=\"bine-stoff-einleitung\"\u003e\n      Mach dein nächstes Nähprojekt zu etwas ganz Besonderem! Unsere\n      wunderschön bedruckten Stoffe mit einem liebevoll gestalteten Design\n      von \u003cstrong\u003eBine Brändle\u003c\/strong\u003e bringen Farbe, Freude und gute Laune\n      an die Nähmaschine.\n    \u003c\/p\u003e\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"Bine Brändle\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n    \u003cp\u003e\n      Du kannst dein Lieblingsdesign passend zu deinem Projekt als\n      \u003cstrong\u003eBaumwollgewebe\u003c\/strong\u003e oder \u003cstrong\u003eJersey\u003c\/strong\u003e auswählen.\n    \u003c\/p\u003e\n\n    \u003cdiv class=\"bine-stoffarten\"\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Das angenehm glatte und formstabile Baumwollgewebe eignet sich\n          wunderbar für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eHemden und Hosen\u003c\/li\u003e\n          \u003cli\u003eTaschen und Beutel\u003c\/li\u003e\n          \u003cli\u003eKissen und Wohnaccessoires\u003c\/li\u003e\n          \u003cli\u003eDekorationen\u003c\/li\u003e\n          \u003cli\u003ePatchwork-Projekte\u003c\/li\u003e\n          \u003cli\u003eSchultüten und kleine Geschenkideen\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eJersey\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Der weiche und elastische Jersey-Stoff ist besonders angenehm\n          auf der Haut und ideal für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eShirts und Kleider\u003c\/li\u003e\n          \u003cli\u003eHosen und Leggings\u003c\/li\u003e\n          \u003cli\u003eMützen und Halstücher\u003c\/li\u003e\n          \u003cli\u003eKinder- und Babykleidung\u003c\/li\u003e\n          \u003cli\u003eBequeme Lieblingsstücke für Groß und Klein\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"bine-stofflaengen\"\u003e\n      \u003ch3\u003eErhältliche Stofflängen\u003c\/h3\u003e\n\n      \u003cp\u003e\n        Du erhältst den Stoff fertig zugeschnitten in folgenden Längen:\n      \u003c\/p\u003e\n\n      \u003cdiv class=\"bine-laengen-auswahl\"\u003e\n        \u003cspan\u003e1 Meter\u003c\/span\u003e\n        \u003cspan\u003e2 Meter\u003c\/span\u003e\n        \u003cspan\u003e3 Meter\u003c\/span\u003e\n      \u003c\/div\u003e\n\n      \u003cp\u003e\n        Wähle einfach deine gewünschte Stoffart und anschließend die\n        passende Länge aus.\n      \u003c\/p\u003e\n\n      \u003cp class=\"bine-stoff-hinweis\"\u003e\n        \u003cstrong\u003eBitte beachte:\u003c\/strong\u003e Die ausgewählte Länge wird als\n        zusammenhängendes Stoffstück geliefert. Bei der Auswahl von\n        2 Metern erhältst du ein 2 Meter langes Stück, bei 3 Metern ein\n        zusammenhängendes 3-Meter-Stück.\n      \u003c\/p\u003e\n    \u003c\/div\u003e\n\n    \u003cp\u003e\n      Ob als farbenfrohes Kleidungsstück, liebevoll genähtes Geschenk oder\n      besonderer Blickfang für dein Zuhause – mit den Designs von\n      \u003cstrong\u003eBine Brändle\u003c\/strong\u003e wird jedes Nähprojekt herrlich bunt und\n      unverwechselbar.\n    \u003c\/p\u003e\n\n  \u003c\/div\u003e\n\n\u003c\/section\u003e\n\n\u003cstyle\u003e\n  .bine-stoff-intro {\n    margin: 20px 0 35px;\n    overflow: hidden;\n  }\n\n  .bine-logo-parallax {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    min-height: 150px;\n    margin-bottom: 20px;\n    padding: 20px;\n    \n    border-radius: 16px;\n    overflow: hidden;\n  }\n\n  .bine-stoff-logo {\n    display: block;\n    width: auto;\n    max-width: min(320px, 80%);\n    max-height: 130px;\n    object-fit: contain;\n    will-change: transform;\n    transform: translate3d(0, 0, 0);\n  }\n\n  .bine-stoff-text h2 {\n    margin-top: 0;\n    margin-bottom: 15px;\n    color: #08868a;\n    font-size: clamp(24px, 4vw, 34px);\n    line-height: 1.2;\n  }\n\n  .bine-stoff-text h3 {\n    margin-top: 0;\n    margin-bottom: 10px;\n  }\n\n  .bine-stoff-einleitung {\n    font-size: 1.08em;\n    line-height: 1.65;\n  }\n\n  .bine-stoffarten {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 16px;\n    margin: 25px 0;\n  }\n\n  .bine-stoffart {\n    padding: 20px;\n    background: #f7f7f7;\n    border-top: 4px solid #08868a;\n    border-radius: 10px;\n  }\n\n  .bine-stoffart ul {\n    margin-bottom: 0;\n    padding-left: 20px;\n  }\n\n  .bine-stoffart li {\n    margin-bottom: 5px;\n  }\n\n  .bine-stofflaengen {\n    margin: 25px 0;\n    padding: 22px;\n    background: rgba(8, 134, 138, 0.08);\n    border-radius: 12px;\n  }\n\n  .bine-laengen-auswahl {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin: 16px 0;\n  }\n\n  .bine-laengen-auswahl span {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 90px;\n    padding: 9px 15px;\n    background: #08868a;\n    color: #ffffff;\n    border-radius: 20px;\n    font-weight: 700;\n  }\n\n  .bine-stoff-hinweis {\n    margin-bottom: 0;\n    padding: 14px;\n    background: #ffffff;\n    border-left: 4px solid #08868a;\n    border-radius: 5px;\n  }\n\n  @media screen and (max-width: 650px) {\n    .bine-stoffarten {\n      grid-template-columns: 1fr;\n    }\n\n    .bine-logo-parallax {\n      min-height: 120px;\n    }\n\n    .bine-stoff-logo {\n      max-width: 75%;\n      max-height: 100px;\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .bine-stoff-logo {\n      transform: none !important;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const parallaxElements = document.querySelectorAll(\n    \"[data-bine-parallax] .bine-stoff-logo\"\n  );\n\n  if (\n    !parallaxElements.length ||\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  ) {\n    return;\n  }\n\n  let animationFrameRequested = false;\n\n  function updateParallax() {\n    parallaxElements.forEach(function (logo) {\n      const container = logo.closest(\"[data-bine-parallax]\");\n      const position = container.getBoundingClientRect();\n      const windowHeight = window.innerHeight;\n\n      if (\n        position.bottom \u003e= 0 \u0026\u0026\n        position.top \u003c= windowHeight\n      ) {\n        const containerCenter =\n          position.top + position.height \/ 2;\n\n        const screenCenter = windowHeight \/ 2;\n\n        const offset =\n          (containerCenter - screenCenter) * -0.08;\n\n        const limitedOffset = Math.max(\n          -22,\n          Math.min(22, offset)\n        );\n\n        logo.style.transform =\n          \"translate3d(0,\" + limitedOffset + \"px,0)\";\n      }\n    });\n\n    animationFrameRequested = false;\n  }\n\n  function requestParallaxUpdate() {\n    if (!animationFrameRequested) {\n      window.requestAnimationFrame(updateParallax);\n      animationFrameRequested = true;\n    }\n  }\n\n  window.addEventListener(\n    \"scroll\",\n    requestParallaxUpdate,\n    { passive: true }\n  );\n\n  window.addEventListener(\n    \"resize\",\n    requestParallaxUpdate\n  );\n\n  requestParallaxUpdate();\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif02?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eBaumwolle\u003c\/span\u003e\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif01?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eJersey\u003c\/span\u003e\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n   \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e \u003cp\u003e 100 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Rohgewebe aus 100 % Baumwolle,\u003cbr\u003e hergestellt aus Materialien, die gemäß OEKO-TEX® STANDARD 100 zertifiziert sind. \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 2 cm\u003cbr\u003e\u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 150–153 g\/m² \u003c\/p\u003e \u003chr\u003e \n\u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n\n   \u003ch3\u003eJersey\u003c\/h3\u003e \u003cp\u003e \u003cstrong\u003eZusammensetzung:\u003c\/strong\u003e\u003cbr\u003e 92 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche\u003cbr\u003e 8 % Elasthan \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Maschenwaren aus Baumwollgarnen mit Zusatz von Elastangarnen (EL-Anteil bis zu 8 %).\u003cbr\u003e Ausschließlich hergestellt unter Verwendung von Komponenten und Veredelungsverfahren, die gemäß OEKO-TEX® STANDARD 100 vorzertifiziert sind. \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 3 cm\u003cbr\u003e \u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 200–210 g\/m² \u003c\/p\u003e\n\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    flex: 1;\n    padding: 12px 20px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-gif {\n    width: 32px;\n    height: 32px;\n    object-fit: contain;\n    flex-shrink: 0;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content h3 {\n    margin-top: 0;\n  }\n\n  .stoff-tab-content h4 {\n    margin-top: 20px;\n    margin-bottom: 8px;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tab-button {\n      padding: 11px 8px;\n      gap: 6px;\n    }\n\n    .stoff-tab-gif {\n      width: 28px;\n      height: 28px;\n    }\n\n    .stoff-tab-content {\n      padding: 16px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\"Baumwolle\", \"Jersey\"];\n\n  function normalize(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getCorrectName(value) {\n    const wanted = normalize(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalize(stoffart) === wanted;\n    }) || null;\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalize(element.dataset.stoffButton) === normalize(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalize(correctName);\n\n    container\n      .querySelectorAll(\"[data-stoff-button]\")\n      .forEach(function (button) {\n        const active =\n          !button.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(button.dataset.stoffButton) === wanted;\n\n        button.classList.toggle(\"is-active\", active);\n        button.setAttribute(\n          \"aria-selected\",\n          active ? \"true\" : \"false\"\n        );\n      });\n\n    container\n      .querySelectorAll(\"[data-stoff-content]\")\n      .forEach(function (content) {\n        const active =\n          !content.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(content.dataset.stoffContent) === wanted;\n\n        content.classList.toggle(\"is-active\", active);\n        content.hidden = !active;\n      });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(\n        container,\n        firstButton.dataset.stoffButton\n      );\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section =\n      container.closest(\".shopify-section\") || document;\n\n    const selectedFields = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const field of selectedFields) {\n      const name = getCorrectName(field.value);\n\n      if (name) {\n        return name;\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabs() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart =\n        getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabs();\n      return;\n    }\n\n    const availability = {\n      baumwolle: false,\n      jersey: false\n    };\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const options = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      options.forEach(function (optionValue) {\n        const stoffart =\n          getCorrectName(optionValue);\n\n        if (stoffart) {\n          availability[normalize(stoffart)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available =\n          availability[normalize(stoffart)] === true;\n\n        const button = container.querySelector(\n          '[data-stoff-button=\"' + stoffart + '\"]'\n        );\n\n        const content = container.querySelector(\n          '[data-stoff-content=\"' + stoffart + '\"]'\n        );\n\n        if (button) {\n          button.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabs();\n  }\n\n  function loadProductAvailability() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      syncTabs();\n      return;\n    }\n\n    fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        syncTabs();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button =\n      event.target.closest(\"[data-stoff-button]\");\n\n    if (\n      !button ||\n      button.classList.contains(\"is-unavailable\")\n    ) {\n      return;\n    }\n\n    const container =\n      button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(\n        container,\n        button.dataset.stoffButton\n      );\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches(\n        'input[type=\"radio\"], select'\n      )\n    ) {\n      window.setTimeout(syncTabs, 50);\n    }\n  });\n\n  function initializeStoffTabs() {\n    loadProductAvailability();\n    window.setTimeout(syncTabs, 300);\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeStoffTabs\n    );\n  } else {\n    initializeStoffTabs();\n  }\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"dynamisches-moodbild\" data-moodbild\u003e\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .dynamisches-moodbild {\n    display: none;\n    margin: 25px 0;\n    text-align: center;\n  }\n\n  .dynamisches-moodbild.is-visible {\n    display: block;\n  }\n\n  .moodbild-link {\n    display: block;\n    text-decoration: none;\n    border-radius: 14px;\n    overflow: hidden;\n  }\n\n  .dynamisches-moodbild img {\n    display: block;\n    width: 100%;\n    height: auto;\n    margin: 0 auto;\n    border-radius: 14px;\n    transition: transform 0.25s ease;\n  }\n\n  .moodbild-link:hover img {\n    transform: scale(1.015);\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .dynamisches-moodbild img {\n      transition: none;\n    }\n\n    .moodbild-link:hover img {\n      transform: none;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const MARKER = \"#moodbild\";\n\n  const containers = document.querySelectorAll(\n    \"[data-moodbild]\"\n  );\n\n  if (!containers.length) return;\n\n  function cleanAlt(text) {\n    return String(text || \"\")\n      .replace(\/\\s*#Moodbild\\s*\/gi, \"\")\n      .trim();\n  }\n\n  function getImageUrl(image) {\n    if (image.currentSrc) {\n      return image.currentSrc;\n    }\n\n    const srcset =\n      image.getAttribute(\"srcset\") ||\n      image.getAttribute(\"data-srcset\");\n\n    if (srcset) {\n      const sources = srcset\n        .split(\",\")\n        .map(function (entry) {\n          return entry.trim().split(\/\\s+\/)[0];\n        })\n        .filter(Boolean);\n\n      if (sources.length) {\n        return sources[sources.length - 1];\n      }\n    }\n\n    return (\n      image.getAttribute(\"data-src\") ||\n      image.getAttribute(\"src\") ||\n      \"\"\n    );\n  }\n\n  function insertMoodbild(imageUrl, altText) {\n    if (!imageUrl) return false;\n\n    containers.forEach(function (container) {\n      if (\n        container.dataset.moodbildLoaded === \"true\"\n      ) {\n        return;\n      }\n\n      const link = document.createElement(\"a\");\n\n      link.href = \"https:\/\/www.bines-shop.com\";\n      link.target = \"_blank\";\n      link.rel = \"noopener noreferrer\";\n      link.className = \"moodbild-link\";\n\n      link.setAttribute(\n        \"aria-label\",\n        \"Bines Shop in einem neuen Fenster öffnen\"\n      );\n\n      const image = document.createElement(\"img\");\n\n      image.src = imageUrl;\n\n      image.alt =\n        cleanAlt(altText) ||\n        \"Moodbild zum Produkt\";\n\n      image.loading = \"lazy\";\n      image.decoding = \"async\";\n\n      link.appendChild(image);\n      container.replaceChildren(link);\n\n      container.classList.add(\"is-visible\");\n\n      container.dataset.moodbildLoaded = \"true\";\n    });\n\n    return true;\n  }\n\n  function findMoodbildInGallery() {\n    const images = Array.from(\n      document.querySelectorAll(\"img\")\n    ).filter(function (image) {\n      if (image.closest(\"[data-moodbild]\")) {\n        return false;\n      }\n\n      const searchableText = [\n        image.getAttribute(\"alt\"),\n        image.getAttribute(\"data-alt\"),\n        image.getAttribute(\"aria-label\")\n      ]\n        .filter(Boolean)\n        .join(\" \")\n        .toLowerCase();\n\n      return searchableText.includes(MARKER);\n    });\n\n    if (!images.length) {\n      return false;\n    }\n\n    const moodbild = images.find(function (image) {\n      return getImageUrl(image);\n    });\n\n    if (!moodbild) {\n      return false;\n    }\n\n    const altText =\n      moodbild.getAttribute(\"alt\") ||\n      moodbild.getAttribute(\"data-alt\") ||\n      moodbild.getAttribute(\"aria-label\") ||\n      \"\";\n\n    return insertMoodbild(\n      getImageUrl(moodbild),\n      altText\n    );\n  }\n\n  function findMoodbildInProductData() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return Promise.resolve(false);\n    }\n\n    return fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(function (product) {\n        const media = Array.isArray(product.media)\n          ? product.media\n          : [];\n\n        const moodbild = media.find(function (item) {\n          return String(item.alt || \"\")\n            .toLowerCase()\n            .includes(MARKER);\n        });\n\n        if (!moodbild) {\n          return false;\n        }\n\n        const imageUrl =\n          moodbild.preview_image \u0026\u0026\n          moodbild.preview_image.src\n            ? moodbild.preview_image.src\n            : moodbild.src;\n\n        return insertMoodbild(\n          imageUrl,\n          moodbild.alt\n        );\n      })\n      .catch(function () {\n        return false;\n      });\n  }\n\n  function initializeMoodbild() {\n    if (findMoodbildInGallery()) {\n      return;\n    }\n\n    findMoodbildInProductData()\n      .then(function (found) {\n        if (found) return;\n\n        const observer = new MutationObserver(\n          function () {\n            if (findMoodbildInGallery()) {\n              observer.disconnect();\n            }\n          }\n        );\n\n        observer.observe(\n          document.documentElement,\n          {\n            childList: true,\n            subtree: true,\n            attributes: true,\n            attributeFilter: [\n              \"alt\",\n              \"data-alt\",\n              \"src\",\n              \"data-src\",\n              \"srcset\",\n              \"data-srcset\"\n            ]\n          }\n        );\n\n        window.setTimeout(function () {\n          observer.disconnect();\n        }, 15000);\n      });\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeMoodbild\n    );\n  } else {\n    initializeMoodbild();\n  }\n})();\n\u003c\/script\u003e\n\u003c\/div\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle \/ 1 Meter","offer_id":53327310717195,"sku":"550000233","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 2 Meter","offer_id":53327310749963,"sku":"550000234","price":47.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 3 Meter","offer_id":53327310782731,"sku":"550000235","price":69.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 1 Meter","offer_id":53327310815499,"sku":"550000236","price":27.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 2 Meter","offer_id":53327310848267,"sku":"550000237","price":53.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 3 Meter","offer_id":53327310881035,"sku":"550000238","price":77.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-Blumchenwiese-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1787875130"},{"product_id":"blumenmeer-dunkelblau-stoff","title":"Blumenmeer Dunkelblau – Stoff","description":"\u003cdiv style=\"background: #e8f6f6; border: 2px solid #08868a; border-radius: 12px; padding: 14px 16px; margin: 0 0 22px 0; color: #333; line-height: 1.5;\"\u003e\n  \u003cdiv style=\"font-size: 17px; font-weight: 700; color: #08868a; margin-bottom: 4px;\"\u003e\n    ⚠️ Bitte beachten: Rapport kann variieren\n  \u003c\/div\u003e\n  \u003cdiv style=\"font-size: 15px;\"\u003e\n    Wie das Muster auf deinem zugeschnittenen Stoffstück sitzt, kann unterschiedlich ausfallen und von der Produktabbildung abweichen.\n  \u003c\/div\u003e\n\u003c\/div\u003e\n\n\u003c!-- PRODUKTBESCHREIBUNG MIT PARALLAX-LOGO --\u003e\n\n\u003csection class=\"bine-stoff-intro\"\u003e\n\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo-Stoffe-bunt.png?v=1787871207\" alt=\"Bine Brändle Stoffe Logo\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"bine-stoff-text\"\u003e\n\n    \u003ch2\u003eFarbenfrohe Stoffe mit Design von Bine Brändle\u003c\/h2\u003e\n\n    \u003cp class=\"bine-stoff-einleitung\"\u003e\n      Mach dein nächstes Nähprojekt zu etwas ganz Besonderem! Unsere\n      wunderschön bedruckten Stoffe mit einem liebevoll gestalteten Design\n      von \u003cstrong\u003eBine Brändle\u003c\/strong\u003e bringen Farbe, Freude und gute Laune\n      an die Nähmaschine.\n    \u003c\/p\u003e\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"Bine Brändle\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n    \u003cp\u003e\n      Du kannst dein Lieblingsdesign passend zu deinem Projekt als\n      \u003cstrong\u003eBaumwollgewebe\u003c\/strong\u003e oder \u003cstrong\u003eJersey\u003c\/strong\u003e auswählen.\n    \u003c\/p\u003e\n\n    \u003cdiv class=\"bine-stoffarten\"\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Das angenehm glatte und formstabile Baumwollgewebe eignet sich\n          wunderbar für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eHemden und Hosen\u003c\/li\u003e\n          \u003cli\u003eTaschen und Beutel\u003c\/li\u003e\n          \u003cli\u003eKissen und Wohnaccessoires\u003c\/li\u003e\n          \u003cli\u003eDekorationen\u003c\/li\u003e\n          \u003cli\u003ePatchwork-Projekte\u003c\/li\u003e\n          \u003cli\u003eSchultüten und kleine Geschenkideen\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eJersey\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Der weiche und elastische Jersey-Stoff ist besonders angenehm\n          auf der Haut und ideal für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eShirts und Kleider\u003c\/li\u003e\n          \u003cli\u003eHosen und Leggings\u003c\/li\u003e\n          \u003cli\u003eMützen und Halstücher\u003c\/li\u003e\n          \u003cli\u003eKinder- und Babykleidung\u003c\/li\u003e\n          \u003cli\u003eBequeme Lieblingsstücke für Groß und Klein\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"bine-stofflaengen\"\u003e\n      \u003ch3\u003eErhältliche Stofflängen\u003c\/h3\u003e\n\n      \u003cp\u003e\n        Du erhältst den Stoff fertig zugeschnitten in folgenden Längen:\n      \u003c\/p\u003e\n\n      \u003cdiv class=\"bine-laengen-auswahl\"\u003e\n        \u003cspan\u003e1 Meter\u003c\/span\u003e\n        \u003cspan\u003e2 Meter\u003c\/span\u003e\n        \u003cspan\u003e3 Meter\u003c\/span\u003e\n      \u003c\/div\u003e\n\n      \u003cp\u003e\n        Wähle einfach deine gewünschte Stoffart und anschließend die\n        passende Länge aus.\n      \u003c\/p\u003e\n\n      \u003cp class=\"bine-stoff-hinweis\"\u003e\n        \u003cstrong\u003eBitte beachte:\u003c\/strong\u003e Die ausgewählte Länge wird als\n        zusammenhängendes Stoffstück geliefert. Bei der Auswahl von\n        2 Metern erhältst du ein 2 Meter langes Stück, bei 3 Metern ein\n        zusammenhängendes 3-Meter-Stück.\n      \u003c\/p\u003e\n    \u003c\/div\u003e\n\n    \u003cp\u003e\n      Ob als farbenfrohes Kleidungsstück, liebevoll genähtes Geschenk oder\n      besonderer Blickfang für dein Zuhause – mit den Designs von\n      \u003cstrong\u003eBine Brändle\u003c\/strong\u003e wird jedes Nähprojekt herrlich bunt und\n      unverwechselbar.\n    \u003c\/p\u003e\n\n  \u003c\/div\u003e\n\n\u003c\/section\u003e\n\n\u003cstyle\u003e\n  .bine-stoff-intro {\n    margin: 20px 0 35px;\n    overflow: hidden;\n  }\n\n  .bine-logo-parallax {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    min-height: 150px;\n    margin-bottom: 20px;\n    padding: 20px;\n    \n    border-radius: 16px;\n    overflow: hidden;\n  }\n\n  .bine-stoff-logo {\n    display: block;\n    width: auto;\n    max-width: min(320px, 80%);\n    max-height: 130px;\n    object-fit: contain;\n    will-change: transform;\n    transform: translate3d(0, 0, 0);\n  }\n\n  .bine-stoff-text h2 {\n    margin-top: 0;\n    margin-bottom: 15px;\n    color: #08868a;\n    font-size: clamp(24px, 4vw, 34px);\n    line-height: 1.2;\n  }\n\n  .bine-stoff-text h3 {\n    margin-top: 0;\n    margin-bottom: 10px;\n  }\n\n  .bine-stoff-einleitung {\n    font-size: 1.08em;\n    line-height: 1.65;\n  }\n\n  .bine-stoffarten {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 16px;\n    margin: 25px 0;\n  }\n\n  .bine-stoffart {\n    padding: 20px;\n    background: #f7f7f7;\n    border-top: 4px solid #08868a;\n    border-radius: 10px;\n  }\n\n  .bine-stoffart ul {\n    margin-bottom: 0;\n    padding-left: 20px;\n  }\n\n  .bine-stoffart li {\n    margin-bottom: 5px;\n  }\n\n  .bine-stofflaengen {\n    margin: 25px 0;\n    padding: 22px;\n    background: rgba(8, 134, 138, 0.08);\n    border-radius: 12px;\n  }\n\n  .bine-laengen-auswahl {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin: 16px 0;\n  }\n\n  .bine-laengen-auswahl span {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 90px;\n    padding: 9px 15px;\n    background: #08868a;\n    color: #ffffff;\n    border-radius: 20px;\n    font-weight: 700;\n  }\n\n  .bine-stoff-hinweis {\n    margin-bottom: 0;\n    padding: 14px;\n    background: #ffffff;\n    border-left: 4px solid #08868a;\n    border-radius: 5px;\n  }\n\n  @media screen and (max-width: 650px) {\n    .bine-stoffarten {\n      grid-template-columns: 1fr;\n    }\n\n    .bine-logo-parallax {\n      min-height: 120px;\n    }\n\n    .bine-stoff-logo {\n      max-width: 75%;\n      max-height: 100px;\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .bine-stoff-logo {\n      transform: none !important;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const parallaxElements = document.querySelectorAll(\n    \"[data-bine-parallax] .bine-stoff-logo\"\n  );\n\n  if (\n    !parallaxElements.length ||\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  ) {\n    return;\n  }\n\n  let animationFrameRequested = false;\n\n  function updateParallax() {\n    parallaxElements.forEach(function (logo) {\n      const container = logo.closest(\"[data-bine-parallax]\");\n      const position = container.getBoundingClientRect();\n      const windowHeight = window.innerHeight;\n\n      if (\n        position.bottom \u003e= 0 \u0026\u0026\n        position.top \u003c= windowHeight\n      ) {\n        const containerCenter =\n          position.top + position.height \/ 2;\n\n        const screenCenter = windowHeight \/ 2;\n\n        const offset =\n          (containerCenter - screenCenter) * -0.08;\n\n        const limitedOffset = Math.max(\n          -22,\n          Math.min(22, offset)\n        );\n\n        logo.style.transform =\n          \"translate3d(0,\" + limitedOffset + \"px,0)\";\n      }\n    });\n\n    animationFrameRequested = false;\n  }\n\n  function requestParallaxUpdate() {\n    if (!animationFrameRequested) {\n      window.requestAnimationFrame(updateParallax);\n      animationFrameRequested = true;\n    }\n  }\n\n  window.addEventListener(\n    \"scroll\",\n    requestParallaxUpdate,\n    { passive: true }\n  );\n\n  window.addEventListener(\n    \"resize\",\n    requestParallaxUpdate\n  );\n\n  requestParallaxUpdate();\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif02?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eBaumwolle\u003c\/span\u003e\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif01?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eJersey\u003c\/span\u003e\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n   \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e \u003cp\u003e 100 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Rohgewebe aus 100 % Baumwolle,\u003cbr\u003e hergestellt aus Materialien, die gemäß OEKO-TEX® STANDARD 100 zertifiziert sind. \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 2 cm\u003cbr\u003e\u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 150–153 g\/m² \u003c\/p\u003e \u003chr\u003e \n\u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n\n   \u003ch3\u003eJersey\u003c\/h3\u003e \u003cp\u003e \u003cstrong\u003eZusammensetzung:\u003c\/strong\u003e\u003cbr\u003e 92 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche\u003cbr\u003e 8 % Elasthan \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Maschenwaren aus Baumwollgarnen mit Zusatz von Elastangarnen (EL-Anteil bis zu 8 %).\u003cbr\u003e Ausschließlich hergestellt unter Verwendung von Komponenten und Veredelungsverfahren, die gemäß OEKO-TEX® STANDARD 100 vorzertifiziert sind. \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 3 cm\u003cbr\u003e \u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 200–210 g\/m² \u003c\/p\u003e\n\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    flex: 1;\n    padding: 12px 20px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-gif {\n    width: 32px;\n    height: 32px;\n    object-fit: contain;\n    flex-shrink: 0;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content h3 {\n    margin-top: 0;\n  }\n\n  .stoff-tab-content h4 {\n    margin-top: 20px;\n    margin-bottom: 8px;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tab-button {\n      padding: 11px 8px;\n      gap: 6px;\n    }\n\n    .stoff-tab-gif {\n      width: 28px;\n      height: 28px;\n    }\n\n    .stoff-tab-content {\n      padding: 16px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\"Baumwolle\", \"Jersey\"];\n\n  function normalize(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getCorrectName(value) {\n    const wanted = normalize(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalize(stoffart) === wanted;\n    }) || null;\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalize(element.dataset.stoffButton) === normalize(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalize(correctName);\n\n    container\n      .querySelectorAll(\"[data-stoff-button]\")\n      .forEach(function (button) {\n        const active =\n          !button.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(button.dataset.stoffButton) === wanted;\n\n        button.classList.toggle(\"is-active\", active);\n        button.setAttribute(\n          \"aria-selected\",\n          active ? \"true\" : \"false\"\n        );\n      });\n\n    container\n      .querySelectorAll(\"[data-stoff-content]\")\n      .forEach(function (content) {\n        const active =\n          !content.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(content.dataset.stoffContent) === wanted;\n\n        content.classList.toggle(\"is-active\", active);\n        content.hidden = !active;\n      });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(\n        container,\n        firstButton.dataset.stoffButton\n      );\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section =\n      container.closest(\".shopify-section\") || document;\n\n    const selectedFields = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const field of selectedFields) {\n      const name = getCorrectName(field.value);\n\n      if (name) {\n        return name;\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabs() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart =\n        getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabs();\n      return;\n    }\n\n    const availability = {\n      baumwolle: false,\n      jersey: false\n    };\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const options = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      options.forEach(function (optionValue) {\n        const stoffart =\n          getCorrectName(optionValue);\n\n        if (stoffart) {\n          availability[normalize(stoffart)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available =\n          availability[normalize(stoffart)] === true;\n\n        const button = container.querySelector(\n          '[data-stoff-button=\"' + stoffart + '\"]'\n        );\n\n        const content = container.querySelector(\n          '[data-stoff-content=\"' + stoffart + '\"]'\n        );\n\n        if (button) {\n          button.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabs();\n  }\n\n  function loadProductAvailability() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      syncTabs();\n      return;\n    }\n\n    fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        syncTabs();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button =\n      event.target.closest(\"[data-stoff-button]\");\n\n    if (\n      !button ||\n      button.classList.contains(\"is-unavailable\")\n    ) {\n      return;\n    }\n\n    const container =\n      button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(\n        container,\n        button.dataset.stoffButton\n      );\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches(\n        'input[type=\"radio\"], select'\n      )\n    ) {\n      window.setTimeout(syncTabs, 50);\n    }\n  });\n\n  function initializeStoffTabs() {\n    loadProductAvailability();\n    window.setTimeout(syncTabs, 300);\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeStoffTabs\n    );\n  } else {\n    initializeStoffTabs();\n  }\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"dynamisches-moodbild\" data-moodbild\u003e\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .dynamisches-moodbild {\n    display: none;\n    margin: 25px 0;\n    text-align: center;\n  }\n\n  .dynamisches-moodbild.is-visible {\n    display: block;\n  }\n\n  .moodbild-link {\n    display: block;\n    text-decoration: none;\n    border-radius: 14px;\n    overflow: hidden;\n  }\n\n  .dynamisches-moodbild img {\n    display: block;\n    width: 100%;\n    height: auto;\n    margin: 0 auto;\n    border-radius: 14px;\n    transition: transform 0.25s ease;\n  }\n\n  .moodbild-link:hover img {\n    transform: scale(1.015);\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .dynamisches-moodbild img {\n      transition: none;\n    }\n\n    .moodbild-link:hover img {\n      transform: none;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const MARKER = \"#moodbild\";\n\n  const containers = document.querySelectorAll(\n    \"[data-moodbild]\"\n  );\n\n  if (!containers.length) return;\n\n  function cleanAlt(text) {\n    return String(text || \"\")\n      .replace(\/\\s*#Moodbild\\s*\/gi, \"\")\n      .trim();\n  }\n\n  function getImageUrl(image) {\n    if (image.currentSrc) {\n      return image.currentSrc;\n    }\n\n    const srcset =\n      image.getAttribute(\"srcset\") ||\n      image.getAttribute(\"data-srcset\");\n\n    if (srcset) {\n      const sources = srcset\n        .split(\",\")\n        .map(function (entry) {\n          return entry.trim().split(\/\\s+\/)[0];\n        })\n        .filter(Boolean);\n\n      if (sources.length) {\n        return sources[sources.length - 1];\n      }\n    }\n\n    return (\n      image.getAttribute(\"data-src\") ||\n      image.getAttribute(\"src\") ||\n      \"\"\n    );\n  }\n\n  function insertMoodbild(imageUrl, altText) {\n    if (!imageUrl) return false;\n\n    containers.forEach(function (container) {\n      if (\n        container.dataset.moodbildLoaded === \"true\"\n      ) {\n        return;\n      }\n\n      const link = document.createElement(\"a\");\n\n      link.href = \"https:\/\/www.bines-shop.com\";\n      link.target = \"_blank\";\n      link.rel = \"noopener noreferrer\";\n      link.className = \"moodbild-link\";\n\n      link.setAttribute(\n        \"aria-label\",\n        \"Bines Shop in einem neuen Fenster öffnen\"\n      );\n\n      const image = document.createElement(\"img\");\n\n      image.src = imageUrl;\n\n      image.alt =\n        cleanAlt(altText) ||\n        \"Moodbild zum Produkt\";\n\n      image.loading = \"lazy\";\n      image.decoding = \"async\";\n\n      link.appendChild(image);\n      container.replaceChildren(link);\n\n      container.classList.add(\"is-visible\");\n\n      container.dataset.moodbildLoaded = \"true\";\n    });\n\n    return true;\n  }\n\n  function findMoodbildInGallery() {\n    const images = Array.from(\n      document.querySelectorAll(\"img\")\n    ).filter(function (image) {\n      if (image.closest(\"[data-moodbild]\")) {\n        return false;\n      }\n\n      const searchableText = [\n        image.getAttribute(\"alt\"),\n        image.getAttribute(\"data-alt\"),\n        image.getAttribute(\"aria-label\")\n      ]\n        .filter(Boolean)\n        .join(\" \")\n        .toLowerCase();\n\n      return searchableText.includes(MARKER);\n    });\n\n    if (!images.length) {\n      return false;\n    }\n\n    const moodbild = images.find(function (image) {\n      return getImageUrl(image);\n    });\n\n    if (!moodbild) {\n      return false;\n    }\n\n    const altText =\n      moodbild.getAttribute(\"alt\") ||\n      moodbild.getAttribute(\"data-alt\") ||\n      moodbild.getAttribute(\"aria-label\") ||\n      \"\";\n\n    return insertMoodbild(\n      getImageUrl(moodbild),\n      altText\n    );\n  }\n\n  function findMoodbildInProductData() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return Promise.resolve(false);\n    }\n\n    return fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(function (product) {\n        const media = Array.isArray(product.media)\n          ? product.media\n          : [];\n\n        const moodbild = media.find(function (item) {\n          return String(item.alt || \"\")\n            .toLowerCase()\n            .includes(MARKER);\n        });\n\n        if (!moodbild) {\n          return false;\n        }\n\n        const imageUrl =\n          moodbild.preview_image \u0026\u0026\n          moodbild.preview_image.src\n            ? moodbild.preview_image.src\n            : moodbild.src;\n\n        return insertMoodbild(\n          imageUrl,\n          moodbild.alt\n        );\n      })\n      .catch(function () {\n        return false;\n      });\n  }\n\n  function initializeMoodbild() {\n    if (findMoodbildInGallery()) {\n      return;\n    }\n\n    findMoodbildInProductData()\n      .then(function (found) {\n        if (found) return;\n\n        const observer = new MutationObserver(\n          function () {\n            if (findMoodbildInGallery()) {\n              observer.disconnect();\n            }\n          }\n        );\n\n        observer.observe(\n          document.documentElement,\n          {\n            childList: true,\n            subtree: true,\n            attributes: true,\n            attributeFilter: [\n              \"alt\",\n              \"data-alt\",\n              \"src\",\n              \"data-src\",\n              \"srcset\",\n              \"data-srcset\"\n            ]\n          }\n        );\n\n        window.setTimeout(function () {\n          observer.disconnect();\n        }, 15000);\n      });\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeMoodbild\n    );\n  } else {\n    initializeMoodbild();\n  }\n})();\n\u003c\/script\u003e\n\u003c\/div\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle \/ 1 Meter","offer_id":53327312224523,"sku":"550000227","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 2 Meter","offer_id":53327312257291,"sku":"550000228","price":47.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 3 Meter","offer_id":53327312290059,"sku":"550000229","price":69.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 1 Meter","offer_id":53327312322827,"sku":"550000230","price":27.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 2 Meter","offer_id":53327312355595,"sku":"550000231","price":53.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 3 Meter","offer_id":53327312388363,"sku":"550000232","price":77.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-BlumenmeerDunkelblau-Stoff-Baumwolle-Jersey-nahen-bunt-Muster_d40b5e17-6306-4436-9cf2-e324d112e0fb.jpg?v=1787875331"},{"product_id":"blumenmeer-koralle-stoff","title":"Blumenmeer Koralle – Stoff","description":"\u003cdiv style=\"background: #e8f6f6; border: 2px solid #08868a; border-radius: 12px; padding: 14px 16px; margin: 0 0 22px 0; color: #333; line-height: 1.5;\"\u003e\n  \u003cdiv style=\"font-size: 17px; font-weight: 700; color: #08868a; margin-bottom: 4px;\"\u003e\n    ⚠️ Bitte beachten: Rapport kann variieren\n  \u003c\/div\u003e\n  \u003cdiv style=\"font-size: 15px;\"\u003e\n    Wie das Muster auf deinem zugeschnittenen Stoffstück sitzt, kann unterschiedlich ausfallen und von der Produktabbildung abweichen.\n  \u003c\/div\u003e\n\u003c\/div\u003e\n\n\u003c!-- PRODUKTBESCHREIBUNG MIT PARALLAX-LOGO --\u003e\n\n\u003csection class=\"bine-stoff-intro\"\u003e\n\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo-Stoffe-bunt.png?v=1787871207\" alt=\"Bine Brändle Stoffe Logo\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"bine-stoff-text\"\u003e\n\n    \u003ch2\u003eFarbenfrohe Stoffe mit Design von Bine Brändle\u003c\/h2\u003e\n\n    \u003cp class=\"bine-stoff-einleitung\"\u003e\n      Mach dein nächstes Nähprojekt zu etwas ganz Besonderem! Unsere\n      wunderschön bedruckten Stoffe mit einem liebevoll gestalteten Design\n      von \u003cstrong\u003eBine Brändle\u003c\/strong\u003e bringen Farbe, Freude und gute Laune\n      an die Nähmaschine.\n    \u003c\/p\u003e\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"Bine Brändle\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n    \u003cp\u003e\n      Du kannst dein Lieblingsdesign passend zu deinem Projekt als\n      \u003cstrong\u003eBaumwollgewebe\u003c\/strong\u003e oder \u003cstrong\u003eJersey\u003c\/strong\u003e auswählen.\n    \u003c\/p\u003e\n\n    \u003cdiv class=\"bine-stoffarten\"\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Das angenehm glatte und formstabile Baumwollgewebe eignet sich\n          wunderbar für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eHemden und Hosen\u003c\/li\u003e\n          \u003cli\u003eTaschen und Beutel\u003c\/li\u003e\n          \u003cli\u003eKissen und Wohnaccessoires\u003c\/li\u003e\n          \u003cli\u003eDekorationen\u003c\/li\u003e\n          \u003cli\u003ePatchwork-Projekte\u003c\/li\u003e\n          \u003cli\u003eSchultüten und kleine Geschenkideen\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eJersey\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Der weiche und elastische Jersey-Stoff ist besonders angenehm\n          auf der Haut und ideal für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eShirts und Kleider\u003c\/li\u003e\n          \u003cli\u003eHosen und Leggings\u003c\/li\u003e\n          \u003cli\u003eMützen und Halstücher\u003c\/li\u003e\n          \u003cli\u003eKinder- und Babykleidung\u003c\/li\u003e\n          \u003cli\u003eBequeme Lieblingsstücke für Groß und Klein\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"bine-stofflaengen\"\u003e\n      \u003ch3\u003eErhältliche Stofflängen\u003c\/h3\u003e\n\n      \u003cp\u003e\n        Du erhältst den Stoff fertig zugeschnitten in folgenden Längen:\n      \u003c\/p\u003e\n\n      \u003cdiv class=\"bine-laengen-auswahl\"\u003e\n        \u003cspan\u003e1 Meter\u003c\/span\u003e\n        \u003cspan\u003e2 Meter\u003c\/span\u003e\n        \u003cspan\u003e3 Meter\u003c\/span\u003e\n      \u003c\/div\u003e\n\n      \u003cp\u003e\n        Wähle einfach deine gewünschte Stoffart und anschließend die\n        passende Länge aus.\n      \u003c\/p\u003e\n\n      \u003cp class=\"bine-stoff-hinweis\"\u003e\n        \u003cstrong\u003eBitte beachte:\u003c\/strong\u003e Die ausgewählte Länge wird als\n        zusammenhängendes Stoffstück geliefert. Bei der Auswahl von\n        2 Metern erhältst du ein 2 Meter langes Stück, bei 3 Metern ein\n        zusammenhängendes 3-Meter-Stück.\n      \u003c\/p\u003e\n    \u003c\/div\u003e\n\n    \u003cp\u003e\n      Ob als farbenfrohes Kleidungsstück, liebevoll genähtes Geschenk oder\n      besonderer Blickfang für dein Zuhause – mit den Designs von\n      \u003cstrong\u003eBine Brändle\u003c\/strong\u003e wird jedes Nähprojekt herrlich bunt und\n      unverwechselbar.\n    \u003c\/p\u003e\n\n  \u003c\/div\u003e\n\n\u003c\/section\u003e\n\n\u003cstyle\u003e\n  .bine-stoff-intro {\n    margin: 20px 0 35px;\n    overflow: hidden;\n  }\n\n  .bine-logo-parallax {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    min-height: 150px;\n    margin-bottom: 20px;\n    padding: 20px;\n    \n    border-radius: 16px;\n    overflow: hidden;\n  }\n\n  .bine-stoff-logo {\n    display: block;\n    width: auto;\n    max-width: min(320px, 80%);\n    max-height: 130px;\n    object-fit: contain;\n    will-change: transform;\n    transform: translate3d(0, 0, 0);\n  }\n\n  .bine-stoff-text h2 {\n    margin-top: 0;\n    margin-bottom: 15px;\n    color: #08868a;\n    font-size: clamp(24px, 4vw, 34px);\n    line-height: 1.2;\n  }\n\n  .bine-stoff-text h3 {\n    margin-top: 0;\n    margin-bottom: 10px;\n  }\n\n  .bine-stoff-einleitung {\n    font-size: 1.08em;\n    line-height: 1.65;\n  }\n\n  .bine-stoffarten {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 16px;\n    margin: 25px 0;\n  }\n\n  .bine-stoffart {\n    padding: 20px;\n    background: #f7f7f7;\n    border-top: 4px solid #08868a;\n    border-radius: 10px;\n  }\n\n  .bine-stoffart ul {\n    margin-bottom: 0;\n    padding-left: 20px;\n  }\n\n  .bine-stoffart li {\n    margin-bottom: 5px;\n  }\n\n  .bine-stofflaengen {\n    margin: 25px 0;\n    padding: 22px;\n    background: rgba(8, 134, 138, 0.08);\n    border-radius: 12px;\n  }\n\n  .bine-laengen-auswahl {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin: 16px 0;\n  }\n\n  .bine-laengen-auswahl span {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 90px;\n    padding: 9px 15px;\n    background: #08868a;\n    color: #ffffff;\n    border-radius: 20px;\n    font-weight: 700;\n  }\n\n  .bine-stoff-hinweis {\n    margin-bottom: 0;\n    padding: 14px;\n    background: #ffffff;\n    border-left: 4px solid #08868a;\n    border-radius: 5px;\n  }\n\n  @media screen and (max-width: 650px) {\n    .bine-stoffarten {\n      grid-template-columns: 1fr;\n    }\n\n    .bine-logo-parallax {\n      min-height: 120px;\n    }\n\n    .bine-stoff-logo {\n      max-width: 75%;\n      max-height: 100px;\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .bine-stoff-logo {\n      transform: none !important;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const parallaxElements = document.querySelectorAll(\n    \"[data-bine-parallax] .bine-stoff-logo\"\n  );\n\n  if (\n    !parallaxElements.length ||\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  ) {\n    return;\n  }\n\n  let animationFrameRequested = false;\n\n  function updateParallax() {\n    parallaxElements.forEach(function (logo) {\n      const container = logo.closest(\"[data-bine-parallax]\");\n      const position = container.getBoundingClientRect();\n      const windowHeight = window.innerHeight;\n\n      if (\n        position.bottom \u003e= 0 \u0026\u0026\n        position.top \u003c= windowHeight\n      ) {\n        const containerCenter =\n          position.top + position.height \/ 2;\n\n        const screenCenter = windowHeight \/ 2;\n\n        const offset =\n          (containerCenter - screenCenter) * -0.08;\n\n        const limitedOffset = Math.max(\n          -22,\n          Math.min(22, offset)\n        );\n\n        logo.style.transform =\n          \"translate3d(0,\" + limitedOffset + \"px,0)\";\n      }\n    });\n\n    animationFrameRequested = false;\n  }\n\n  function requestParallaxUpdate() {\n    if (!animationFrameRequested) {\n      window.requestAnimationFrame(updateParallax);\n      animationFrameRequested = true;\n    }\n  }\n\n  window.addEventListener(\n    \"scroll\",\n    requestParallaxUpdate,\n    { passive: true }\n  );\n\n  window.addEventListener(\n    \"resize\",\n    requestParallaxUpdate\n  );\n\n  requestParallaxUpdate();\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif02?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eBaumwolle\u003c\/span\u003e\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif01?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eJersey\u003c\/span\u003e\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n   \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e \u003cp\u003e 100 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Rohgewebe aus 100 % Baumwolle,\u003cbr\u003e hergestellt aus Materialien, die gemäß OEKO-TEX® STANDARD 100 zertifiziert sind. \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 2 cm\u003cbr\u003e\u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 150–153 g\/m² \u003c\/p\u003e \u003chr\u003e \n\u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n\n   \u003ch3\u003eJersey\u003c\/h3\u003e \u003cp\u003e \u003cstrong\u003eZusammensetzung:\u003c\/strong\u003e\u003cbr\u003e 92 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche\u003cbr\u003e 8 % Elasthan \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Maschenwaren aus Baumwollgarnen mit Zusatz von Elastangarnen (EL-Anteil bis zu 8 %).\u003cbr\u003e Ausschließlich hergestellt unter Verwendung von Komponenten und Veredelungsverfahren, die gemäß OEKO-TEX® STANDARD 100 vorzertifiziert sind. \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 3 cm\u003cbr\u003e \u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 200–210 g\/m² \u003c\/p\u003e\n\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    flex: 1;\n    padding: 12px 20px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-gif {\n    width: 32px;\n    height: 32px;\n    object-fit: contain;\n    flex-shrink: 0;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content h3 {\n    margin-top: 0;\n  }\n\n  .stoff-tab-content h4 {\n    margin-top: 20px;\n    margin-bottom: 8px;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tab-button {\n      padding: 11px 8px;\n      gap: 6px;\n    }\n\n    .stoff-tab-gif {\n      width: 28px;\n      height: 28px;\n    }\n\n    .stoff-tab-content {\n      padding: 16px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\"Baumwolle\", \"Jersey\"];\n\n  function normalize(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getCorrectName(value) {\n    const wanted = normalize(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalize(stoffart) === wanted;\n    }) || null;\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalize(element.dataset.stoffButton) === normalize(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalize(correctName);\n\n    container\n      .querySelectorAll(\"[data-stoff-button]\")\n      .forEach(function (button) {\n        const active =\n          !button.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(button.dataset.stoffButton) === wanted;\n\n        button.classList.toggle(\"is-active\", active);\n        button.setAttribute(\n          \"aria-selected\",\n          active ? \"true\" : \"false\"\n        );\n      });\n\n    container\n      .querySelectorAll(\"[data-stoff-content]\")\n      .forEach(function (content) {\n        const active =\n          !content.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(content.dataset.stoffContent) === wanted;\n\n        content.classList.toggle(\"is-active\", active);\n        content.hidden = !active;\n      });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(\n        container,\n        firstButton.dataset.stoffButton\n      );\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section =\n      container.closest(\".shopify-section\") || document;\n\n    const selectedFields = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const field of selectedFields) {\n      const name = getCorrectName(field.value);\n\n      if (name) {\n        return name;\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabs() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart =\n        getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabs();\n      return;\n    }\n\n    const availability = {\n      baumwolle: false,\n      jersey: false\n    };\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const options = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      options.forEach(function (optionValue) {\n        const stoffart =\n          getCorrectName(optionValue);\n\n        if (stoffart) {\n          availability[normalize(stoffart)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available =\n          availability[normalize(stoffart)] === true;\n\n        const button = container.querySelector(\n          '[data-stoff-button=\"' + stoffart + '\"]'\n        );\n\n        const content = container.querySelector(\n          '[data-stoff-content=\"' + stoffart + '\"]'\n        );\n\n        if (button) {\n          button.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabs();\n  }\n\n  function loadProductAvailability() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      syncTabs();\n      return;\n    }\n\n    fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        syncTabs();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button =\n      event.target.closest(\"[data-stoff-button]\");\n\n    if (\n      !button ||\n      button.classList.contains(\"is-unavailable\")\n    ) {\n      return;\n    }\n\n    const container =\n      button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(\n        container,\n        button.dataset.stoffButton\n      );\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches(\n        'input[type=\"radio\"], select'\n      )\n    ) {\n      window.setTimeout(syncTabs, 50);\n    }\n  });\n\n  function initializeStoffTabs() {\n    loadProductAvailability();\n    window.setTimeout(syncTabs, 300);\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeStoffTabs\n    );\n  } else {\n    initializeStoffTabs();\n  }\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"dynamisches-moodbild\" data-moodbild\u003e\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .dynamisches-moodbild {\n    display: none;\n    margin: 25px 0;\n    text-align: center;\n  }\n\n  .dynamisches-moodbild.is-visible {\n    display: block;\n  }\n\n  .moodbild-link {\n    display: block;\n    text-decoration: none;\n    border-radius: 14px;\n    overflow: hidden;\n  }\n\n  .dynamisches-moodbild img {\n    display: block;\n    width: 100%;\n    height: auto;\n    margin: 0 auto;\n    border-radius: 14px;\n    transition: transform 0.25s ease;\n  }\n\n  .moodbild-link:hover img {\n    transform: scale(1.015);\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .dynamisches-moodbild img {\n      transition: none;\n    }\n\n    .moodbild-link:hover img {\n      transform: none;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const MARKER = \"#moodbild\";\n\n  const containers = document.querySelectorAll(\n    \"[data-moodbild]\"\n  );\n\n  if (!containers.length) return;\n\n  function cleanAlt(text) {\n    return String(text || \"\")\n      .replace(\/\\s*#Moodbild\\s*\/gi, \"\")\n      .trim();\n  }\n\n  function getImageUrl(image) {\n    if (image.currentSrc) {\n      return image.currentSrc;\n    }\n\n    const srcset =\n      image.getAttribute(\"srcset\") ||\n      image.getAttribute(\"data-srcset\");\n\n    if (srcset) {\n      const sources = srcset\n        .split(\",\")\n        .map(function (entry) {\n          return entry.trim().split(\/\\s+\/)[0];\n        })\n        .filter(Boolean);\n\n      if (sources.length) {\n        return sources[sources.length - 1];\n      }\n    }\n\n    return (\n      image.getAttribute(\"data-src\") ||\n      image.getAttribute(\"src\") ||\n      \"\"\n    );\n  }\n\n  function insertMoodbild(imageUrl, altText) {\n    if (!imageUrl) return false;\n\n    containers.forEach(function (container) {\n      if (\n        container.dataset.moodbildLoaded === \"true\"\n      ) {\n        return;\n      }\n\n      const link = document.createElement(\"a\");\n\n      link.href = \"https:\/\/www.bines-shop.com\";\n      link.target = \"_blank\";\n      link.rel = \"noopener noreferrer\";\n      link.className = \"moodbild-link\";\n\n      link.setAttribute(\n        \"aria-label\",\n        \"Bines Shop in einem neuen Fenster öffnen\"\n      );\n\n      const image = document.createElement(\"img\");\n\n      image.src = imageUrl;\n\n      image.alt =\n        cleanAlt(altText) ||\n        \"Moodbild zum Produkt\";\n\n      image.loading = \"lazy\";\n      image.decoding = \"async\";\n\n      link.appendChild(image);\n      container.replaceChildren(link);\n\n      container.classList.add(\"is-visible\");\n\n      container.dataset.moodbildLoaded = \"true\";\n    });\n\n    return true;\n  }\n\n  function findMoodbildInGallery() {\n    const images = Array.from(\n      document.querySelectorAll(\"img\")\n    ).filter(function (image) {\n      if (image.closest(\"[data-moodbild]\")) {\n        return false;\n      }\n\n      const searchableText = [\n        image.getAttribute(\"alt\"),\n        image.getAttribute(\"data-alt\"),\n        image.getAttribute(\"aria-label\")\n      ]\n        .filter(Boolean)\n        .join(\" \")\n        .toLowerCase();\n\n      return searchableText.includes(MARKER);\n    });\n\n    if (!images.length) {\n      return false;\n    }\n\n    const moodbild = images.find(function (image) {\n      return getImageUrl(image);\n    });\n\n    if (!moodbild) {\n      return false;\n    }\n\n    const altText =\n      moodbild.getAttribute(\"alt\") ||\n      moodbild.getAttribute(\"data-alt\") ||\n      moodbild.getAttribute(\"aria-label\") ||\n      \"\";\n\n    return insertMoodbild(\n      getImageUrl(moodbild),\n      altText\n    );\n  }\n\n  function findMoodbildInProductData() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return Promise.resolve(false);\n    }\n\n    return fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(function (product) {\n        const media = Array.isArray(product.media)\n          ? product.media\n          : [];\n\n        const moodbild = media.find(function (item) {\n          return String(item.alt || \"\")\n            .toLowerCase()\n            .includes(MARKER);\n        });\n\n        if (!moodbild) {\n          return false;\n        }\n\n        const imageUrl =\n          moodbild.preview_image \u0026\u0026\n          moodbild.preview_image.src\n            ? moodbild.preview_image.src\n            : moodbild.src;\n\n        return insertMoodbild(\n          imageUrl,\n          moodbild.alt\n        );\n      })\n      .catch(function () {\n        return false;\n      });\n  }\n\n  function initializeMoodbild() {\n    if (findMoodbildInGallery()) {\n      return;\n    }\n\n    findMoodbildInProductData()\n      .then(function (found) {\n        if (found) return;\n\n        const observer = new MutationObserver(\n          function () {\n            if (findMoodbildInGallery()) {\n              observer.disconnect();\n            }\n          }\n        );\n\n        observer.observe(\n          document.documentElement,\n          {\n            childList: true,\n            subtree: true,\n            attributes: true,\n            attributeFilter: [\n              \"alt\",\n              \"data-alt\",\n              \"src\",\n              \"data-src\",\n              \"srcset\",\n              \"data-srcset\"\n            ]\n          }\n        );\n\n        window.setTimeout(function () {\n          observer.disconnect();\n        }, 15000);\n      });\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeMoodbild\n    );\n  } else {\n    initializeMoodbild();\n  }\n})();\n\u003c\/script\u003e\n\u003c\/div\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle \/ 1 Meter","offer_id":53327315992843,"sku":"550000221","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 2 Meter","offer_id":53327316025611,"sku":"550000222","price":47.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 3 Meter","offer_id":53327316058379,"sku":"550000223","price":69.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 1 Meter","offer_id":53327316091147,"sku":"550000224","price":27.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 2 Meter","offer_id":53327316123915,"sku":"550000225","price":53.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 3 Meter","offer_id":53327316156683,"sku":"550000226","price":77.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-BlumenmeerKoralle-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1787875546"},{"product_id":"blumenmeer-weiss-rot-stoff","title":"Blumenmeer Weiß-Rot – Stoff","description":"\u003cdiv style=\"background: #e8f6f6; border: 2px solid #08868a; border-radius: 12px; padding: 14px 16px; margin: 0 0 22px 0; color: #333; line-height: 1.5;\"\u003e\n  \u003cdiv style=\"font-size: 17px; font-weight: 700; color: #08868a; margin-bottom: 4px;\"\u003e\n    ⚠️ Bitte beachten: Rapport kann variieren\n  \u003c\/div\u003e\n  \u003cdiv style=\"font-size: 15px;\"\u003e\n    Wie das Muster auf deinem zugeschnittenen Stoffstück sitzt, kann unterschiedlich ausfallen und von der Produktabbildung abweichen.\n  \u003c\/div\u003e\n\u003c\/div\u003e\n\n\u003c!-- PRODUKTBESCHREIBUNG MIT PARALLAX-LOGO --\u003e\n\n\u003csection class=\"bine-stoff-intro\"\u003e\n\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo-Stoffe-bunt.png?v=1787871207\" alt=\"Bine Brändle Stoffe Logo\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"bine-stoff-text\"\u003e\n\n    \u003ch2\u003eFarbenfrohe Stoffe mit Design von Bine Brändle\u003c\/h2\u003e\n\n    \u003cp class=\"bine-stoff-einleitung\"\u003e\n      Mach dein nächstes Nähprojekt zu etwas ganz Besonderem! Unsere\n      wunderschön bedruckten Stoffe mit einem liebevoll gestalteten Design\n      von \u003cstrong\u003eBine Brändle\u003c\/strong\u003e bringen Farbe, Freude und gute Laune\n      an die Nähmaschine.\n    \u003c\/p\u003e\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"Bine Brändle\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n    \u003cp\u003e\n      Du kannst dein Lieblingsdesign passend zu deinem Projekt als\n      \u003cstrong\u003eBaumwollgewebe\u003c\/strong\u003e oder \u003cstrong\u003eJersey\u003c\/strong\u003e auswählen.\n    \u003c\/p\u003e\n\n    \u003cdiv class=\"bine-stoffarten\"\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Das angenehm glatte und formstabile Baumwollgewebe eignet sich\n          wunderbar für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eHemden und Hosen\u003c\/li\u003e\n          \u003cli\u003eTaschen und Beutel\u003c\/li\u003e\n          \u003cli\u003eKissen und Wohnaccessoires\u003c\/li\u003e\n          \u003cli\u003eDekorationen\u003c\/li\u003e\n          \u003cli\u003ePatchwork-Projekte\u003c\/li\u003e\n          \u003cli\u003eSchultüten und kleine Geschenkideen\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eJersey\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Der weiche und elastische Jersey-Stoff ist besonders angenehm\n          auf der Haut und ideal für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eShirts und Kleider\u003c\/li\u003e\n          \u003cli\u003eHosen und Leggings\u003c\/li\u003e\n          \u003cli\u003eMützen und Halstücher\u003c\/li\u003e\n          \u003cli\u003eKinder- und Babykleidung\u003c\/li\u003e\n          \u003cli\u003eBequeme Lieblingsstücke für Groß und Klein\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"bine-stofflaengen\"\u003e\n      \u003ch3\u003eErhältliche Stofflängen\u003c\/h3\u003e\n\n      \u003cp\u003e\n        Du erhältst den Stoff fertig zugeschnitten in folgenden Längen:\n      \u003c\/p\u003e\n\n      \u003cdiv class=\"bine-laengen-auswahl\"\u003e\n        \u003cspan\u003e1 Meter\u003c\/span\u003e\n        \u003cspan\u003e2 Meter\u003c\/span\u003e\n        \u003cspan\u003e3 Meter\u003c\/span\u003e\n      \u003c\/div\u003e\n\n      \u003cp\u003e\n        Wähle einfach deine gewünschte Stoffart und anschließend die\n        passende Länge aus.\n      \u003c\/p\u003e\n\n      \u003cp class=\"bine-stoff-hinweis\"\u003e\n        \u003cstrong\u003eBitte beachte:\u003c\/strong\u003e Die ausgewählte Länge wird als\n        zusammenhängendes Stoffstück geliefert. Bei der Auswahl von\n        2 Metern erhältst du ein 2 Meter langes Stück, bei 3 Metern ein\n        zusammenhängendes 3-Meter-Stück.\n      \u003c\/p\u003e\n    \u003c\/div\u003e\n\n    \u003cp\u003e\n      Ob als farbenfrohes Kleidungsstück, liebevoll genähtes Geschenk oder\n      besonderer Blickfang für dein Zuhause – mit den Designs von\n      \u003cstrong\u003eBine Brändle\u003c\/strong\u003e wird jedes Nähprojekt herrlich bunt und\n      unverwechselbar.\n    \u003c\/p\u003e\n\n  \u003c\/div\u003e\n\n\u003c\/section\u003e\n\n\u003cstyle\u003e\n  .bine-stoff-intro {\n    margin: 20px 0 35px;\n    overflow: hidden;\n  }\n\n  .bine-logo-parallax {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    min-height: 150px;\n    margin-bottom: 20px;\n    padding: 20px;\n    \n    border-radius: 16px;\n    overflow: hidden;\n  }\n\n  .bine-stoff-logo {\n    display: block;\n    width: auto;\n    max-width: min(320px, 80%);\n    max-height: 130px;\n    object-fit: contain;\n    will-change: transform;\n    transform: translate3d(0, 0, 0);\n  }\n\n  .bine-stoff-text h2 {\n    margin-top: 0;\n    margin-bottom: 15px;\n    color: #08868a;\n    font-size: clamp(24px, 4vw, 34px);\n    line-height: 1.2;\n  }\n\n  .bine-stoff-text h3 {\n    margin-top: 0;\n    margin-bottom: 10px;\n  }\n\n  .bine-stoff-einleitung {\n    font-size: 1.08em;\n    line-height: 1.65;\n  }\n\n  .bine-stoffarten {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 16px;\n    margin: 25px 0;\n  }\n\n  .bine-stoffart {\n    padding: 20px;\n    background: #f7f7f7;\n    border-top: 4px solid #08868a;\n    border-radius: 10px;\n  }\n\n  .bine-stoffart ul {\n    margin-bottom: 0;\n    padding-left: 20px;\n  }\n\n  .bine-stoffart li {\n    margin-bottom: 5px;\n  }\n\n  .bine-stofflaengen {\n    margin: 25px 0;\n    padding: 22px;\n    background: rgba(8, 134, 138, 0.08);\n    border-radius: 12px;\n  }\n\n  .bine-laengen-auswahl {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin: 16px 0;\n  }\n\n  .bine-laengen-auswahl span {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 90px;\n    padding: 9px 15px;\n    background: #08868a;\n    color: #ffffff;\n    border-radius: 20px;\n    font-weight: 700;\n  }\n\n  .bine-stoff-hinweis {\n    margin-bottom: 0;\n    padding: 14px;\n    background: #ffffff;\n    border-left: 4px solid #08868a;\n    border-radius: 5px;\n  }\n\n  @media screen and (max-width: 650px) {\n    .bine-stoffarten {\n      grid-template-columns: 1fr;\n    }\n\n    .bine-logo-parallax {\n      min-height: 120px;\n    }\n\n    .bine-stoff-logo {\n      max-width: 75%;\n      max-height: 100px;\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .bine-stoff-logo {\n      transform: none !important;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const parallaxElements = document.querySelectorAll(\n    \"[data-bine-parallax] .bine-stoff-logo\"\n  );\n\n  if (\n    !parallaxElements.length ||\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  ) {\n    return;\n  }\n\n  let animationFrameRequested = false;\n\n  function updateParallax() {\n    parallaxElements.forEach(function (logo) {\n      const container = logo.closest(\"[data-bine-parallax]\");\n      const position = container.getBoundingClientRect();\n      const windowHeight = window.innerHeight;\n\n      if (\n        position.bottom \u003e= 0 \u0026\u0026\n        position.top \u003c= windowHeight\n      ) {\n        const containerCenter =\n          position.top + position.height \/ 2;\n\n        const screenCenter = windowHeight \/ 2;\n\n        const offset =\n          (containerCenter - screenCenter) * -0.08;\n\n        const limitedOffset = Math.max(\n          -22,\n          Math.min(22, offset)\n        );\n\n        logo.style.transform =\n          \"translate3d(0,\" + limitedOffset + \"px,0)\";\n      }\n    });\n\n    animationFrameRequested = false;\n  }\n\n  function requestParallaxUpdate() {\n    if (!animationFrameRequested) {\n      window.requestAnimationFrame(updateParallax);\n      animationFrameRequested = true;\n    }\n  }\n\n  window.addEventListener(\n    \"scroll\",\n    requestParallaxUpdate,\n    { passive: true }\n  );\n\n  window.addEventListener(\n    \"resize\",\n    requestParallaxUpdate\n  );\n\n  requestParallaxUpdate();\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif02?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eBaumwolle\u003c\/span\u003e\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif01?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eJersey\u003c\/span\u003e\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n   \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e \u003cp\u003e 100 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Rohgewebe aus 100 % Baumwolle,\u003cbr\u003e hergestellt aus Materialien, die gemäß OEKO-TEX® STANDARD 100 zertifiziert sind. \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 2 cm\u003cbr\u003e\u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 150–153 g\/m² \u003c\/p\u003e \u003chr\u003e \n\u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n\n   \u003ch3\u003eJersey\u003c\/h3\u003e \u003cp\u003e \u003cstrong\u003eZusammensetzung:\u003c\/strong\u003e\u003cbr\u003e 92 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche\u003cbr\u003e 8 % Elasthan \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Maschenwaren aus Baumwollgarnen mit Zusatz von Elastangarnen (EL-Anteil bis zu 8 %).\u003cbr\u003e Ausschließlich hergestellt unter Verwendung von Komponenten und Veredelungsverfahren, die gemäß OEKO-TEX® STANDARD 100 vorzertifiziert sind. \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 3 cm\u003cbr\u003e \u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 200–210 g\/m² \u003c\/p\u003e\n\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    flex: 1;\n    padding: 12px 20px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-gif {\n    width: 32px;\n    height: 32px;\n    object-fit: contain;\n    flex-shrink: 0;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content h3 {\n    margin-top: 0;\n  }\n\n  .stoff-tab-content h4 {\n    margin-top: 20px;\n    margin-bottom: 8px;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tab-button {\n      padding: 11px 8px;\n      gap: 6px;\n    }\n\n    .stoff-tab-gif {\n      width: 28px;\n      height: 28px;\n    }\n\n    .stoff-tab-content {\n      padding: 16px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\"Baumwolle\", \"Jersey\"];\n\n  function normalize(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getCorrectName(value) {\n    const wanted = normalize(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalize(stoffart) === wanted;\n    }) || null;\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalize(element.dataset.stoffButton) === normalize(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalize(correctName);\n\n    container\n      .querySelectorAll(\"[data-stoff-button]\")\n      .forEach(function (button) {\n        const active =\n          !button.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(button.dataset.stoffButton) === wanted;\n\n        button.classList.toggle(\"is-active\", active);\n        button.setAttribute(\n          \"aria-selected\",\n          active ? \"true\" : \"false\"\n        );\n      });\n\n    container\n      .querySelectorAll(\"[data-stoff-content]\")\n      .forEach(function (content) {\n        const active =\n          !content.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(content.dataset.stoffContent) === wanted;\n\n        content.classList.toggle(\"is-active\", active);\n        content.hidden = !active;\n      });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(\n        container,\n        firstButton.dataset.stoffButton\n      );\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section =\n      container.closest(\".shopify-section\") || document;\n\n    const selectedFields = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const field of selectedFields) {\n      const name = getCorrectName(field.value);\n\n      if (name) {\n        return name;\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabs() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart =\n        getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabs();\n      return;\n    }\n\n    const availability = {\n      baumwolle: false,\n      jersey: false\n    };\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const options = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      options.forEach(function (optionValue) {\n        const stoffart =\n          getCorrectName(optionValue);\n\n        if (stoffart) {\n          availability[normalize(stoffart)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available =\n          availability[normalize(stoffart)] === true;\n\n        const button = container.querySelector(\n          '[data-stoff-button=\"' + stoffart + '\"]'\n        );\n\n        const content = container.querySelector(\n          '[data-stoff-content=\"' + stoffart + '\"]'\n        );\n\n        if (button) {\n          button.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabs();\n  }\n\n  function loadProductAvailability() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      syncTabs();\n      return;\n    }\n\n    fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        syncTabs();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button =\n      event.target.closest(\"[data-stoff-button]\");\n\n    if (\n      !button ||\n      button.classList.contains(\"is-unavailable\")\n    ) {\n      return;\n    }\n\n    const container =\n      button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(\n        container,\n        button.dataset.stoffButton\n      );\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches(\n        'input[type=\"radio\"], select'\n      )\n    ) {\n      window.setTimeout(syncTabs, 50);\n    }\n  });\n\n  function initializeStoffTabs() {\n    loadProductAvailability();\n    window.setTimeout(syncTabs, 300);\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeStoffTabs\n    );\n  } else {\n    initializeStoffTabs();\n  }\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"dynamisches-moodbild\" data-moodbild\u003e\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .dynamisches-moodbild {\n    display: none;\n    margin: 25px 0;\n    text-align: center;\n  }\n\n  .dynamisches-moodbild.is-visible {\n    display: block;\n  }\n\n  .moodbild-link {\n    display: block;\n    text-decoration: none;\n    border-radius: 14px;\n    overflow: hidden;\n  }\n\n  .dynamisches-moodbild img {\n    display: block;\n    width: 100%;\n    height: auto;\n    margin: 0 auto;\n    border-radius: 14px;\n    transition: transform 0.25s ease;\n  }\n\n  .moodbild-link:hover img {\n    transform: scale(1.015);\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .dynamisches-moodbild img {\n      transition: none;\n    }\n\n    .moodbild-link:hover img {\n      transform: none;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const MARKER = \"#moodbild\";\n\n  const containers = document.querySelectorAll(\n    \"[data-moodbild]\"\n  );\n\n  if (!containers.length) return;\n\n  function cleanAlt(text) {\n    return String(text || \"\")\n      .replace(\/\\s*#Moodbild\\s*\/gi, \"\")\n      .trim();\n  }\n\n  function getImageUrl(image) {\n    if (image.currentSrc) {\n      return image.currentSrc;\n    }\n\n    const srcset =\n      image.getAttribute(\"srcset\") ||\n      image.getAttribute(\"data-srcset\");\n\n    if (srcset) {\n      const sources = srcset\n        .split(\",\")\n        .map(function (entry) {\n          return entry.trim().split(\/\\s+\/)[0];\n        })\n        .filter(Boolean);\n\n      if (sources.length) {\n        return sources[sources.length - 1];\n      }\n    }\n\n    return (\n      image.getAttribute(\"data-src\") ||\n      image.getAttribute(\"src\") ||\n      \"\"\n    );\n  }\n\n  function insertMoodbild(imageUrl, altText) {\n    if (!imageUrl) return false;\n\n    containers.forEach(function (container) {\n      if (\n        container.dataset.moodbildLoaded === \"true\"\n      ) {\n        return;\n      }\n\n      const link = document.createElement(\"a\");\n\n      link.href = \"https:\/\/www.bines-shop.com\";\n      link.target = \"_blank\";\n      link.rel = \"noopener noreferrer\";\n      link.className = \"moodbild-link\";\n\n      link.setAttribute(\n        \"aria-label\",\n        \"Bines Shop in einem neuen Fenster öffnen\"\n      );\n\n      const image = document.createElement(\"img\");\n\n      image.src = imageUrl;\n\n      image.alt =\n        cleanAlt(altText) ||\n        \"Moodbild zum Produkt\";\n\n      image.loading = \"lazy\";\n      image.decoding = \"async\";\n\n      link.appendChild(image);\n      container.replaceChildren(link);\n\n      container.classList.add(\"is-visible\");\n\n      container.dataset.moodbildLoaded = \"true\";\n    });\n\n    return true;\n  }\n\n  function findMoodbildInGallery() {\n    const images = Array.from(\n      document.querySelectorAll(\"img\")\n    ).filter(function (image) {\n      if (image.closest(\"[data-moodbild]\")) {\n        return false;\n      }\n\n      const searchableText = [\n        image.getAttribute(\"alt\"),\n        image.getAttribute(\"data-alt\"),\n        image.getAttribute(\"aria-label\")\n      ]\n        .filter(Boolean)\n        .join(\" \")\n        .toLowerCase();\n\n      return searchableText.includes(MARKER);\n    });\n\n    if (!images.length) {\n      return false;\n    }\n\n    const moodbild = images.find(function (image) {\n      return getImageUrl(image);\n    });\n\n    if (!moodbild) {\n      return false;\n    }\n\n    const altText =\n      moodbild.getAttribute(\"alt\") ||\n      moodbild.getAttribute(\"data-alt\") ||\n      moodbild.getAttribute(\"aria-label\") ||\n      \"\";\n\n    return insertMoodbild(\n      getImageUrl(moodbild),\n      altText\n    );\n  }\n\n  function findMoodbildInProductData() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return Promise.resolve(false);\n    }\n\n    return fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(function (product) {\n        const media = Array.isArray(product.media)\n          ? product.media\n          : [];\n\n        const moodbild = media.find(function (item) {\n          return String(item.alt || \"\")\n            .toLowerCase()\n            .includes(MARKER);\n        });\n\n        if (!moodbild) {\n          return false;\n        }\n\n        const imageUrl =\n          moodbild.preview_image \u0026\u0026\n          moodbild.preview_image.src\n            ? moodbild.preview_image.src\n            : moodbild.src;\n\n        return insertMoodbild(\n          imageUrl,\n          moodbild.alt\n        );\n      })\n      .catch(function () {\n        return false;\n      });\n  }\n\n  function initializeMoodbild() {\n    if (findMoodbildInGallery()) {\n      return;\n    }\n\n    findMoodbildInProductData()\n      .then(function (found) {\n        if (found) return;\n\n        const observer = new MutationObserver(\n          function () {\n            if (findMoodbildInGallery()) {\n              observer.disconnect();\n            }\n          }\n        );\n\n        observer.observe(\n          document.documentElement,\n          {\n            childList: true,\n            subtree: true,\n            attributes: true,\n            attributeFilter: [\n              \"alt\",\n              \"data-alt\",\n              \"src\",\n              \"data-src\",\n              \"srcset\",\n              \"data-srcset\"\n            ]\n          }\n        );\n\n        window.setTimeout(function () {\n          observer.disconnect();\n        }, 15000);\n      });\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeMoodbild\n    );\n  } else {\n    initializeMoodbild();\n  }\n})();\n\u003c\/script\u003e\n\u003c\/div\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle \/ 1 Meter","offer_id":53327317270795,"sku":"550000215","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 2 Meter","offer_id":53327317303563,"sku":"550000216","price":47.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 3 Meter","offer_id":53327317336331,"sku":"550000217","price":69.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 1 Meter","offer_id":53327317369099,"sku":"550000218","price":27.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 2 Meter","offer_id":53327317401867,"sku":"550000219","price":53.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 3 Meter","offer_id":53327317434635,"sku":"550000220","price":77.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-BlumenmeerWeiss-Rot-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1787875686"},{"product_id":"blumenzauber-mini-blau-stoff","title":"Blumenzauber MINI blau – Stoff","description":"\u003c!-- PRODUKTBESCHREIBUNG MIT PARALLAX-LOGO --\u003e\n\n\u003csection class=\"bine-stoff-intro\"\u003e\n\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo-Stoffe-bunt.png?v=1787871207\" alt=\"Bine Brändle Stoffe Logo\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"bine-stoff-text\"\u003e\n\n    \u003ch2\u003eFarbenfrohe Stoffe mit Design von Bine Brändle\u003c\/h2\u003e\n\n    \u003cp class=\"bine-stoff-einleitung\"\u003e\n      Mach dein nächstes Nähprojekt zu etwas ganz Besonderem! Unsere\n      wunderschön bedruckten Stoffe mit einem liebevoll gestalteten Design\n      von \u003cstrong\u003eBine Brändle\u003c\/strong\u003e bringen Farbe, Freude und gute Laune\n      an die Nähmaschine.\n    \u003c\/p\u003e\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"Bine Brändle\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n    \u003cp\u003e\n      Du kannst dein Lieblingsdesign passend zu deinem Projekt als\n      \u003cstrong\u003eBaumwollgewebe\u003c\/strong\u003e oder \u003cstrong\u003eJersey\u003c\/strong\u003e auswählen.\n    \u003c\/p\u003e\n\n    \u003cdiv class=\"bine-stoffarten\"\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Das angenehm glatte und formstabile Baumwollgewebe eignet sich\n          wunderbar für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eHemden und Hosen\u003c\/li\u003e\n          \u003cli\u003eTaschen und Beutel\u003c\/li\u003e\n          \u003cli\u003eKissen und Wohnaccessoires\u003c\/li\u003e\n          \u003cli\u003eDekorationen\u003c\/li\u003e\n          \u003cli\u003ePatchwork-Projekte\u003c\/li\u003e\n          \u003cli\u003eSchultüten und kleine Geschenkideen\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eJersey\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Der weiche und elastische Jersey-Stoff ist besonders angenehm\n          auf der Haut und ideal für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eShirts und Kleider\u003c\/li\u003e\n          \u003cli\u003eHosen und Leggings\u003c\/li\u003e\n          \u003cli\u003eMützen und Halstücher\u003c\/li\u003e\n          \u003cli\u003eKinder- und Babykleidung\u003c\/li\u003e\n          \u003cli\u003eBequeme Lieblingsstücke für Groß und Klein\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"bine-stofflaengen\"\u003e\n      \u003ch3\u003eErhältliche Stofflängen\u003c\/h3\u003e\n\n      \u003cp\u003e\n        Du erhältst den Stoff fertig zugeschnitten in folgenden Längen:\n      \u003c\/p\u003e\n\n      \u003cdiv class=\"bine-laengen-auswahl\"\u003e\n        \u003cspan\u003e1 Meter\u003c\/span\u003e\n        \u003cspan\u003e2 Meter\u003c\/span\u003e\n        \u003cspan\u003e3 Meter\u003c\/span\u003e\n      \u003c\/div\u003e\n\n      \u003cp\u003e\n        Wähle einfach deine gewünschte Stoffart und anschließend die\n        passende Länge aus.\n      \u003c\/p\u003e\n\n      \u003cp class=\"bine-stoff-hinweis\"\u003e\n        \u003cstrong\u003eBitte beachte:\u003c\/strong\u003e Die ausgewählte Länge wird als\n        zusammenhängendes Stoffstück geliefert. Bei der Auswahl von\n        2 Metern erhältst du ein 2 Meter langes Stück, bei 3 Metern ein\n        zusammenhängendes 3-Meter-Stück.\n      \u003c\/p\u003e\n    \u003c\/div\u003e\n\n    \u003cp\u003e\n      Ob als farbenfrohes Kleidungsstück, liebevoll genähtes Geschenk oder\n      besonderer Blickfang für dein Zuhause – mit den Designs von\n      \u003cstrong\u003eBine Brändle\u003c\/strong\u003e wird jedes Nähprojekt herrlich bunt und\n      unverwechselbar.\n    \u003c\/p\u003e\n\n  \u003c\/div\u003e\n\n\u003c\/section\u003e\n\n\u003cstyle\u003e\n  .bine-stoff-intro {\n    margin: 20px 0 35px;\n    overflow: hidden;\n  }\n\n  .bine-logo-parallax {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    min-height: 150px;\n    margin-bottom: 20px;\n    padding: 20px;\n    \n    border-radius: 16px;\n    overflow: hidden;\n  }\n\n  .bine-stoff-logo {\n    display: block;\n    width: auto;\n    max-width: min(320px, 80%);\n    max-height: 130px;\n    object-fit: contain;\n    will-change: transform;\n    transform: translate3d(0, 0, 0);\n  }\n\n  .bine-stoff-text h2 {\n    margin-top: 0;\n    margin-bottom: 15px;\n    color: #08868a;\n    font-size: clamp(24px, 4vw, 34px);\n    line-height: 1.2;\n  }\n\n  .bine-stoff-text h3 {\n    margin-top: 0;\n    margin-bottom: 10px;\n  }\n\n  .bine-stoff-einleitung {\n    font-size: 1.08em;\n    line-height: 1.65;\n  }\n\n  .bine-stoffarten {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 16px;\n    margin: 25px 0;\n  }\n\n  .bine-stoffart {\n    padding: 20px;\n    background: #f7f7f7;\n    border-top: 4px solid #08868a;\n    border-radius: 10px;\n  }\n\n  .bine-stoffart ul {\n    margin-bottom: 0;\n    padding-left: 20px;\n  }\n\n  .bine-stoffart li {\n    margin-bottom: 5px;\n  }\n\n  .bine-stofflaengen {\n    margin: 25px 0;\n    padding: 22px;\n    background: rgba(8, 134, 138, 0.08);\n    border-radius: 12px;\n  }\n\n  .bine-laengen-auswahl {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin: 16px 0;\n  }\n\n  .bine-laengen-auswahl span {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 90px;\n    padding: 9px 15px;\n    background: #08868a;\n    color: #ffffff;\n    border-radius: 20px;\n    font-weight: 700;\n  }\n\n  .bine-stoff-hinweis {\n    margin-bottom: 0;\n    padding: 14px;\n    background: #ffffff;\n    border-left: 4px solid #08868a;\n    border-radius: 5px;\n  }\n\n  @media screen and (max-width: 650px) {\n    .bine-stoffarten {\n      grid-template-columns: 1fr;\n    }\n\n    .bine-logo-parallax {\n      min-height: 120px;\n    }\n\n    .bine-stoff-logo {\n      max-width: 75%;\n      max-height: 100px;\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .bine-stoff-logo {\n      transform: none !important;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const parallaxElements = document.querySelectorAll(\n    \"[data-bine-parallax] .bine-stoff-logo\"\n  );\n\n  if (\n    !parallaxElements.length ||\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  ) {\n    return;\n  }\n\n  let animationFrameRequested = false;\n\n  function updateParallax() {\n    parallaxElements.forEach(function (logo) {\n      const container = logo.closest(\"[data-bine-parallax]\");\n      const position = container.getBoundingClientRect();\n      const windowHeight = window.innerHeight;\n\n      if (\n        position.bottom \u003e= 0 \u0026\u0026\n        position.top \u003c= windowHeight\n      ) {\n        const containerCenter =\n          position.top + position.height \/ 2;\n\n        const screenCenter = windowHeight \/ 2;\n\n        const offset =\n          (containerCenter - screenCenter) * -0.08;\n\n        const limitedOffset = Math.max(\n          -22,\n          Math.min(22, offset)\n        );\n\n        logo.style.transform =\n          \"translate3d(0,\" + limitedOffset + \"px,0)\";\n      }\n    });\n\n    animationFrameRequested = false;\n  }\n\n  function requestParallaxUpdate() {\n    if (!animationFrameRequested) {\n      window.requestAnimationFrame(updateParallax);\n      animationFrameRequested = true;\n    }\n  }\n\n  window.addEventListener(\n    \"scroll\",\n    requestParallaxUpdate,\n    { passive: true }\n  );\n\n  window.addEventListener(\n    \"resize\",\n    requestParallaxUpdate\n  );\n\n  requestParallaxUpdate();\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif02?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eBaumwolle\u003c\/span\u003e\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif01?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eJersey\u003c\/span\u003e\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n   \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e \u003cp\u003e 100 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Rohgewebe aus 100 % Baumwolle,\u003cbr\u003e hergestellt aus Materialien, die gemäß OEKO-TEX® STANDARD 100 zertifiziert sind. \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 2 cm\u003cbr\u003e\u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 150–153 g\/m² \u003c\/p\u003e \u003chr\u003e \n\u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n\n   \u003ch3\u003eJersey\u003c\/h3\u003e \u003cp\u003e \u003cstrong\u003eZusammensetzung:\u003c\/strong\u003e\u003cbr\u003e 92 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche\u003cbr\u003e 8 % Elasthan \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Maschenwaren aus Baumwollgarnen mit Zusatz von Elastangarnen (EL-Anteil bis zu 8 %).\u003cbr\u003e Ausschließlich hergestellt unter Verwendung von Komponenten und Veredelungsverfahren, die gemäß OEKO-TEX® STANDARD 100 vorzertifiziert sind. \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 3 cm\u003cbr\u003e \u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 200–210 g\/m² \u003c\/p\u003e\n\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    flex: 1;\n    padding: 12px 20px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-gif {\n    width: 32px;\n    height: 32px;\n    object-fit: contain;\n    flex-shrink: 0;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content h3 {\n    margin-top: 0;\n  }\n\n  .stoff-tab-content h4 {\n    margin-top: 20px;\n    margin-bottom: 8px;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tab-button {\n      padding: 11px 8px;\n      gap: 6px;\n    }\n\n    .stoff-tab-gif {\n      width: 28px;\n      height: 28px;\n    }\n\n    .stoff-tab-content {\n      padding: 16px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\"Baumwolle\", \"Jersey\"];\n\n  function normalize(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getCorrectName(value) {\n    const wanted = normalize(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalize(stoffart) === wanted;\n    }) || null;\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalize(element.dataset.stoffButton) === normalize(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalize(correctName);\n\n    container\n      .querySelectorAll(\"[data-stoff-button]\")\n      .forEach(function (button) {\n        const active =\n          !button.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(button.dataset.stoffButton) === wanted;\n\n        button.classList.toggle(\"is-active\", active);\n        button.setAttribute(\n          \"aria-selected\",\n          active ? \"true\" : \"false\"\n        );\n      });\n\n    container\n      .querySelectorAll(\"[data-stoff-content]\")\n      .forEach(function (content) {\n        const active =\n          !content.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(content.dataset.stoffContent) === wanted;\n\n        content.classList.toggle(\"is-active\", active);\n        content.hidden = !active;\n      });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(\n        container,\n        firstButton.dataset.stoffButton\n      );\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section =\n      container.closest(\".shopify-section\") || document;\n\n    const selectedFields = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const field of selectedFields) {\n      const name = getCorrectName(field.value);\n\n      if (name) {\n        return name;\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabs() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart =\n        getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabs();\n      return;\n    }\n\n    const availability = {\n      baumwolle: false,\n      jersey: false\n    };\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const options = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      options.forEach(function (optionValue) {\n        const stoffart =\n          getCorrectName(optionValue);\n\n        if (stoffart) {\n          availability[normalize(stoffart)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available =\n          availability[normalize(stoffart)] === true;\n\n        const button = container.querySelector(\n          '[data-stoff-button=\"' + stoffart + '\"]'\n        );\n\n        const content = container.querySelector(\n          '[data-stoff-content=\"' + stoffart + '\"]'\n        );\n\n        if (button) {\n          button.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabs();\n  }\n\n  function loadProductAvailability() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      syncTabs();\n      return;\n    }\n\n    fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        syncTabs();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button =\n      event.target.closest(\"[data-stoff-button]\");\n\n    if (\n      !button ||\n      button.classList.contains(\"is-unavailable\")\n    ) {\n      return;\n    }\n\n    const container =\n      button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(\n        container,\n        button.dataset.stoffButton\n      );\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches(\n        'input[type=\"radio\"], select'\n      )\n    ) {\n      window.setTimeout(syncTabs, 50);\n    }\n  });\n\n  function initializeStoffTabs() {\n    loadProductAvailability();\n    window.setTimeout(syncTabs, 300);\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeStoffTabs\n    );\n  } else {\n    initializeStoffTabs();\n  }\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"dynamisches-moodbild\" data-moodbild\u003e\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .dynamisches-moodbild {\n    display: none;\n    margin: 25px 0;\n    text-align: center;\n  }\n\n  .dynamisches-moodbild.is-visible {\n    display: block;\n  }\n\n  .moodbild-link {\n    display: block;\n    text-decoration: none;\n    border-radius: 14px;\n    overflow: hidden;\n  }\n\n  .dynamisches-moodbild img {\n    display: block;\n    width: 100%;\n    height: auto;\n    margin: 0 auto;\n    border-radius: 14px;\n    transition: transform 0.25s ease;\n  }\n\n  .moodbild-link:hover img {\n    transform: scale(1.015);\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .dynamisches-moodbild img {\n      transition: none;\n    }\n\n    .moodbild-link:hover img {\n      transform: none;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const MARKER = \"#moodbild\";\n\n  const containers = document.querySelectorAll(\n    \"[data-moodbild]\"\n  );\n\n  if (!containers.length) return;\n\n  function cleanAlt(text) {\n    return String(text || \"\")\n      .replace(\/\\s*#Moodbild\\s*\/gi, \"\")\n      .trim();\n  }\n\n  function getImageUrl(image) {\n    if (image.currentSrc) {\n      return image.currentSrc;\n    }\n\n    const srcset =\n      image.getAttribute(\"srcset\") ||\n      image.getAttribute(\"data-srcset\");\n\n    if (srcset) {\n      const sources = srcset\n        .split(\",\")\n        .map(function (entry) {\n          return entry.trim().split(\/\\s+\/)[0];\n        })\n        .filter(Boolean);\n\n      if (sources.length) {\n        return sources[sources.length - 1];\n      }\n    }\n\n    return (\n      image.getAttribute(\"data-src\") ||\n      image.getAttribute(\"src\") ||\n      \"\"\n    );\n  }\n\n  function insertMoodbild(imageUrl, altText) {\n    if (!imageUrl) return false;\n\n    containers.forEach(function (container) {\n      if (\n        container.dataset.moodbildLoaded === \"true\"\n      ) {\n        return;\n      }\n\n      const link = document.createElement(\"a\");\n\n      link.href = \"https:\/\/www.bines-shop.com\";\n      link.target = \"_blank\";\n      link.rel = \"noopener noreferrer\";\n      link.className = \"moodbild-link\";\n\n      link.setAttribute(\n        \"aria-label\",\n        \"Bines Shop in einem neuen Fenster öffnen\"\n      );\n\n      const image = document.createElement(\"img\");\n\n      image.src = imageUrl;\n\n      image.alt =\n        cleanAlt(altText) ||\n        \"Moodbild zum Produkt\";\n\n      image.loading = \"lazy\";\n      image.decoding = \"async\";\n\n      link.appendChild(image);\n      container.replaceChildren(link);\n\n      container.classList.add(\"is-visible\");\n\n      container.dataset.moodbildLoaded = \"true\";\n    });\n\n    return true;\n  }\n\n  function findMoodbildInGallery() {\n    const images = Array.from(\n      document.querySelectorAll(\"img\")\n    ).filter(function (image) {\n      if (image.closest(\"[data-moodbild]\")) {\n        return false;\n      }\n\n      const searchableText = [\n        image.getAttribute(\"alt\"),\n        image.getAttribute(\"data-alt\"),\n        image.getAttribute(\"aria-label\")\n      ]\n        .filter(Boolean)\n        .join(\" \")\n        .toLowerCase();\n\n      return searchableText.includes(MARKER);\n    });\n\n    if (!images.length) {\n      return false;\n    }\n\n    const moodbild = images.find(function (image) {\n      return getImageUrl(image);\n    });\n\n    if (!moodbild) {\n      return false;\n    }\n\n    const altText =\n      moodbild.getAttribute(\"alt\") ||\n      moodbild.getAttribute(\"data-alt\") ||\n      moodbild.getAttribute(\"aria-label\") ||\n      \"\";\n\n    return insertMoodbild(\n      getImageUrl(moodbild),\n      altText\n    );\n  }\n\n  function findMoodbildInProductData() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return Promise.resolve(false);\n    }\n\n    return fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(function (product) {\n        const media = Array.isArray(product.media)\n          ? product.media\n          : [];\n\n        const moodbild = media.find(function (item) {\n          return String(item.alt || \"\")\n            .toLowerCase()\n            .includes(MARKER);\n        });\n\n        if (!moodbild) {\n          return false;\n        }\n\n        const imageUrl =\n          moodbild.preview_image \u0026\u0026\n          moodbild.preview_image.src\n            ? moodbild.preview_image.src\n            : moodbild.src;\n\n        return insertMoodbild(\n          imageUrl,\n          moodbild.alt\n        );\n      })\n      .catch(function () {\n        return false;\n      });\n  }\n\n  function initializeMoodbild() {\n    if (findMoodbildInGallery()) {\n      return;\n    }\n\n    findMoodbildInProductData()\n      .then(function (found) {\n        if (found) return;\n\n        const observer = new MutationObserver(\n          function () {\n            if (findMoodbildInGallery()) {\n              observer.disconnect();\n            }\n          }\n        );\n\n        observer.observe(\n          document.documentElement,\n          {\n            childList: true,\n            subtree: true,\n            attributes: true,\n            attributeFilter: [\n              \"alt\",\n              \"data-alt\",\n              \"src\",\n              \"data-src\",\n              \"srcset\",\n              \"data-srcset\"\n            ]\n          }\n        );\n\n        window.setTimeout(function () {\n          observer.disconnect();\n        }, 15000);\n      });\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeMoodbild\n    );\n  } else {\n    initializeMoodbild();\n  }\n})();\n\u003c\/script\u003e\n\u003c\/div\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle \/ 1 Meter","offer_id":53327319269643,"sku":"550000209","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 2 Meter","offer_id":53327319302411,"sku":"550000210","price":47.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 3 Meter","offer_id":53327319335179,"sku":"550000211","price":69.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 1 Meter","offer_id":53327319367947,"sku":"550000212","price":27.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 2 Meter","offer_id":53327319400715,"sku":"550000213","price":53.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 3 Meter","offer_id":53327319433483,"sku":"550000214","price":77.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-BlumenzauberMINIblau-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1787875879"},{"product_id":"blumenzauber-mini-gelb-stoff","title":"Blumenzauber MINI gelb – Stoff","description":"\u003c!-- PRODUKTBESCHREIBUNG MIT PARALLAX-LOGO --\u003e\n\n\u003csection class=\"bine-stoff-intro\"\u003e\n\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo-Stoffe-bunt.png?v=1787871207\" alt=\"Bine Brändle Stoffe Logo\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"bine-stoff-text\"\u003e\n\n    \u003ch2\u003eFarbenfrohe Stoffe mit Design von Bine Brändle\u003c\/h2\u003e\n\n    \u003cp class=\"bine-stoff-einleitung\"\u003e\n      Mach dein nächstes Nähprojekt zu etwas ganz Besonderem! Unsere\n      wunderschön bedruckten Stoffe mit einem liebevoll gestalteten Design\n      von \u003cstrong\u003eBine Brändle\u003c\/strong\u003e bringen Farbe, Freude und gute Laune\n      an die Nähmaschine.\n    \u003c\/p\u003e\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"Bine Brändle\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n    \u003cp\u003e\n      Du kannst dein Lieblingsdesign passend zu deinem Projekt als\n      \u003cstrong\u003eBaumwollgewebe\u003c\/strong\u003e oder \u003cstrong\u003eJersey\u003c\/strong\u003e auswählen.\n    \u003c\/p\u003e\n\n    \u003cdiv class=\"bine-stoffarten\"\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Das angenehm glatte und formstabile Baumwollgewebe eignet sich\n          wunderbar für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eHemden und Hosen\u003c\/li\u003e\n          \u003cli\u003eTaschen und Beutel\u003c\/li\u003e\n          \u003cli\u003eKissen und Wohnaccessoires\u003c\/li\u003e\n          \u003cli\u003eDekorationen\u003c\/li\u003e\n          \u003cli\u003ePatchwork-Projekte\u003c\/li\u003e\n          \u003cli\u003eSchultüten und kleine Geschenkideen\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eJersey\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Der weiche und elastische Jersey-Stoff ist besonders angenehm\n          auf der Haut und ideal für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eShirts und Kleider\u003c\/li\u003e\n          \u003cli\u003eHosen und Leggings\u003c\/li\u003e\n          \u003cli\u003eMützen und Halstücher\u003c\/li\u003e\n          \u003cli\u003eKinder- und Babykleidung\u003c\/li\u003e\n          \u003cli\u003eBequeme Lieblingsstücke für Groß und Klein\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"bine-stofflaengen\"\u003e\n      \u003ch3\u003eErhältliche Stofflängen\u003c\/h3\u003e\n\n      \u003cp\u003e\n        Du erhältst den Stoff fertig zugeschnitten in folgenden Längen:\n      \u003c\/p\u003e\n\n      \u003cdiv class=\"bine-laengen-auswahl\"\u003e\n        \u003cspan\u003e1 Meter\u003c\/span\u003e\n        \u003cspan\u003e2 Meter\u003c\/span\u003e\n        \u003cspan\u003e3 Meter\u003c\/span\u003e\n      \u003c\/div\u003e\n\n      \u003cp\u003e\n        Wähle einfach deine gewünschte Stoffart und anschließend die\n        passende Länge aus.\n      \u003c\/p\u003e\n\n      \u003cp class=\"bine-stoff-hinweis\"\u003e\n        \u003cstrong\u003eBitte beachte:\u003c\/strong\u003e Die ausgewählte Länge wird als\n        zusammenhängendes Stoffstück geliefert. Bei der Auswahl von\n        2 Metern erhältst du ein 2 Meter langes Stück, bei 3 Metern ein\n        zusammenhängendes 3-Meter-Stück.\n      \u003c\/p\u003e\n    \u003c\/div\u003e\n\n    \u003cp\u003e\n      Ob als farbenfrohes Kleidungsstück, liebevoll genähtes Geschenk oder\n      besonderer Blickfang für dein Zuhause – mit den Designs von\n      \u003cstrong\u003eBine Brändle\u003c\/strong\u003e wird jedes Nähprojekt herrlich bunt und\n      unverwechselbar.\n    \u003c\/p\u003e\n\n  \u003c\/div\u003e\n\n\u003c\/section\u003e\n\n\u003cstyle\u003e\n  .bine-stoff-intro {\n    margin: 20px 0 35px;\n    overflow: hidden;\n  }\n\n  .bine-logo-parallax {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    min-height: 150px;\n    margin-bottom: 20px;\n    padding: 20px;\n    \n    border-radius: 16px;\n    overflow: hidden;\n  }\n\n  .bine-stoff-logo {\n    display: block;\n    width: auto;\n    max-width: min(320px, 80%);\n    max-height: 130px;\n    object-fit: contain;\n    will-change: transform;\n    transform: translate3d(0, 0, 0);\n  }\n\n  .bine-stoff-text h2 {\n    margin-top: 0;\n    margin-bottom: 15px;\n    color: #08868a;\n    font-size: clamp(24px, 4vw, 34px);\n    line-height: 1.2;\n  }\n\n  .bine-stoff-text h3 {\n    margin-top: 0;\n    margin-bottom: 10px;\n  }\n\n  .bine-stoff-einleitung {\n    font-size: 1.08em;\n    line-height: 1.65;\n  }\n\n  .bine-stoffarten {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 16px;\n    margin: 25px 0;\n  }\n\n  .bine-stoffart {\n    padding: 20px;\n    background: #f7f7f7;\n    border-top: 4px solid #08868a;\n    border-radius: 10px;\n  }\n\n  .bine-stoffart ul {\n    margin-bottom: 0;\n    padding-left: 20px;\n  }\n\n  .bine-stoffart li {\n    margin-bottom: 5px;\n  }\n\n  .bine-stofflaengen {\n    margin: 25px 0;\n    padding: 22px;\n    background: rgba(8, 134, 138, 0.08);\n    border-radius: 12px;\n  }\n\n  .bine-laengen-auswahl {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin: 16px 0;\n  }\n\n  .bine-laengen-auswahl span {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 90px;\n    padding: 9px 15px;\n    background: #08868a;\n    color: #ffffff;\n    border-radius: 20px;\n    font-weight: 700;\n  }\n\n  .bine-stoff-hinweis {\n    margin-bottom: 0;\n    padding: 14px;\n    background: #ffffff;\n    border-left: 4px solid #08868a;\n    border-radius: 5px;\n  }\n\n  @media screen and (max-width: 650px) {\n    .bine-stoffarten {\n      grid-template-columns: 1fr;\n    }\n\n    .bine-logo-parallax {\n      min-height: 120px;\n    }\n\n    .bine-stoff-logo {\n      max-width: 75%;\n      max-height: 100px;\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .bine-stoff-logo {\n      transform: none !important;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const parallaxElements = document.querySelectorAll(\n    \"[data-bine-parallax] .bine-stoff-logo\"\n  );\n\n  if (\n    !parallaxElements.length ||\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  ) {\n    return;\n  }\n\n  let animationFrameRequested = false;\n\n  function updateParallax() {\n    parallaxElements.forEach(function (logo) {\n      const container = logo.closest(\"[data-bine-parallax]\");\n      const position = container.getBoundingClientRect();\n      const windowHeight = window.innerHeight;\n\n      if (\n        position.bottom \u003e= 0 \u0026\u0026\n        position.top \u003c= windowHeight\n      ) {\n        const containerCenter =\n          position.top + position.height \/ 2;\n\n        const screenCenter = windowHeight \/ 2;\n\n        const offset =\n          (containerCenter - screenCenter) * -0.08;\n\n        const limitedOffset = Math.max(\n          -22,\n          Math.min(22, offset)\n        );\n\n        logo.style.transform =\n          \"translate3d(0,\" + limitedOffset + \"px,0)\";\n      }\n    });\n\n    animationFrameRequested = false;\n  }\n\n  function requestParallaxUpdate() {\n    if (!animationFrameRequested) {\n      window.requestAnimationFrame(updateParallax);\n      animationFrameRequested = true;\n    }\n  }\n\n  window.addEventListener(\n    \"scroll\",\n    requestParallaxUpdate,\n    { passive: true }\n  );\n\n  window.addEventListener(\n    \"resize\",\n    requestParallaxUpdate\n  );\n\n  requestParallaxUpdate();\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif02?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eBaumwolle\u003c\/span\u003e\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif01?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eJersey\u003c\/span\u003e\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n   \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e \u003cp\u003e 100 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Rohgewebe aus 100 % Baumwolle,\u003cbr\u003e hergestellt aus Materialien, die gemäß OEKO-TEX® STANDARD 100 zertifiziert sind. \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 2 cm\u003cbr\u003e\u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 150–153 g\/m² \u003c\/p\u003e \u003chr\u003e \n\u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n\n   \u003ch3\u003eJersey\u003c\/h3\u003e \u003cp\u003e \u003cstrong\u003eZusammensetzung:\u003c\/strong\u003e\u003cbr\u003e 92 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche\u003cbr\u003e 8 % Elasthan \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Maschenwaren aus Baumwollgarnen mit Zusatz von Elastangarnen (EL-Anteil bis zu 8 %).\u003cbr\u003e Ausschließlich hergestellt unter Verwendung von Komponenten und Veredelungsverfahren, die gemäß OEKO-TEX® STANDARD 100 vorzertifiziert sind. \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 3 cm\u003cbr\u003e \u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 200–210 g\/m² \u003c\/p\u003e\n\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    flex: 1;\n    padding: 12px 20px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-gif {\n    width: 32px;\n    height: 32px;\n    object-fit: contain;\n    flex-shrink: 0;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content h3 {\n    margin-top: 0;\n  }\n\n  .stoff-tab-content h4 {\n    margin-top: 20px;\n    margin-bottom: 8px;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tab-button {\n      padding: 11px 8px;\n      gap: 6px;\n    }\n\n    .stoff-tab-gif {\n      width: 28px;\n      height: 28px;\n    }\n\n    .stoff-tab-content {\n      padding: 16px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\"Baumwolle\", \"Jersey\"];\n\n  function normalize(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getCorrectName(value) {\n    const wanted = normalize(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalize(stoffart) === wanted;\n    }) || null;\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalize(element.dataset.stoffButton) === normalize(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalize(correctName);\n\n    container\n      .querySelectorAll(\"[data-stoff-button]\")\n      .forEach(function (button) {\n        const active =\n          !button.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(button.dataset.stoffButton) === wanted;\n\n        button.classList.toggle(\"is-active\", active);\n        button.setAttribute(\n          \"aria-selected\",\n          active ? \"true\" : \"false\"\n        );\n      });\n\n    container\n      .querySelectorAll(\"[data-stoff-content]\")\n      .forEach(function (content) {\n        const active =\n          !content.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(content.dataset.stoffContent) === wanted;\n\n        content.classList.toggle(\"is-active\", active);\n        content.hidden = !active;\n      });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(\n        container,\n        firstButton.dataset.stoffButton\n      );\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section =\n      container.closest(\".shopify-section\") || document;\n\n    const selectedFields = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const field of selectedFields) {\n      const name = getCorrectName(field.value);\n\n      if (name) {\n        return name;\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabs() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart =\n        getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabs();\n      return;\n    }\n\n    const availability = {\n      baumwolle: false,\n      jersey: false\n    };\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const options = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      options.forEach(function (optionValue) {\n        const stoffart =\n          getCorrectName(optionValue);\n\n        if (stoffart) {\n          availability[normalize(stoffart)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available =\n          availability[normalize(stoffart)] === true;\n\n        const button = container.querySelector(\n          '[data-stoff-button=\"' + stoffart + '\"]'\n        );\n\n        const content = container.querySelector(\n          '[data-stoff-content=\"' + stoffart + '\"]'\n        );\n\n        if (button) {\n          button.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabs();\n  }\n\n  function loadProductAvailability() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      syncTabs();\n      return;\n    }\n\n    fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        syncTabs();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button =\n      event.target.closest(\"[data-stoff-button]\");\n\n    if (\n      !button ||\n      button.classList.contains(\"is-unavailable\")\n    ) {\n      return;\n    }\n\n    const container =\n      button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(\n        container,\n        button.dataset.stoffButton\n      );\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches(\n        'input[type=\"radio\"], select'\n      )\n    ) {\n      window.setTimeout(syncTabs, 50);\n    }\n  });\n\n  function initializeStoffTabs() {\n    loadProductAvailability();\n    window.setTimeout(syncTabs, 300);\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeStoffTabs\n    );\n  } else {\n    initializeStoffTabs();\n  }\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"dynamisches-moodbild\" data-moodbild\u003e\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .dynamisches-moodbild {\n    display: none;\n    margin: 25px 0;\n    text-align: center;\n  }\n\n  .dynamisches-moodbild.is-visible {\n    display: block;\n  }\n\n  .moodbild-link {\n    display: block;\n    text-decoration: none;\n    border-radius: 14px;\n    overflow: hidden;\n  }\n\n  .dynamisches-moodbild img {\n    display: block;\n    width: 100%;\n    height: auto;\n    margin: 0 auto;\n    border-radius: 14px;\n    transition: transform 0.25s ease;\n  }\n\n  .moodbild-link:hover img {\n    transform: scale(1.015);\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .dynamisches-moodbild img {\n      transition: none;\n    }\n\n    .moodbild-link:hover img {\n      transform: none;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const MARKER = \"#moodbild\";\n\n  const containers = document.querySelectorAll(\n    \"[data-moodbild]\"\n  );\n\n  if (!containers.length) return;\n\n  function cleanAlt(text) {\n    return String(text || \"\")\n      .replace(\/\\s*#Moodbild\\s*\/gi, \"\")\n      .trim();\n  }\n\n  function getImageUrl(image) {\n    if (image.currentSrc) {\n      return image.currentSrc;\n    }\n\n    const srcset =\n      image.getAttribute(\"srcset\") ||\n      image.getAttribute(\"data-srcset\");\n\n    if (srcset) {\n      const sources = srcset\n        .split(\",\")\n        .map(function (entry) {\n          return entry.trim().split(\/\\s+\/)[0];\n        })\n        .filter(Boolean);\n\n      if (sources.length) {\n        return sources[sources.length - 1];\n      }\n    }\n\n    return (\n      image.getAttribute(\"data-src\") ||\n      image.getAttribute(\"src\") ||\n      \"\"\n    );\n  }\n\n  function insertMoodbild(imageUrl, altText) {\n    if (!imageUrl) return false;\n\n    containers.forEach(function (container) {\n      if (\n        container.dataset.moodbildLoaded === \"true\"\n      ) {\n        return;\n      }\n\n      const link = document.createElement(\"a\");\n\n      link.href = \"https:\/\/www.bines-shop.com\";\n      link.target = \"_blank\";\n      link.rel = \"noopener noreferrer\";\n      link.className = \"moodbild-link\";\n\n      link.setAttribute(\n        \"aria-label\",\n        \"Bines Shop in einem neuen Fenster öffnen\"\n      );\n\n      const image = document.createElement(\"img\");\n\n      image.src = imageUrl;\n\n      image.alt =\n        cleanAlt(altText) ||\n        \"Moodbild zum Produkt\";\n\n      image.loading = \"lazy\";\n      image.decoding = \"async\";\n\n      link.appendChild(image);\n      container.replaceChildren(link);\n\n      container.classList.add(\"is-visible\");\n\n      container.dataset.moodbildLoaded = \"true\";\n    });\n\n    return true;\n  }\n\n  function findMoodbildInGallery() {\n    const images = Array.from(\n      document.querySelectorAll(\"img\")\n    ).filter(function (image) {\n      if (image.closest(\"[data-moodbild]\")) {\n        return false;\n      }\n\n      const searchableText = [\n        image.getAttribute(\"alt\"),\n        image.getAttribute(\"data-alt\"),\n        image.getAttribute(\"aria-label\")\n      ]\n        .filter(Boolean)\n        .join(\" \")\n        .toLowerCase();\n\n      return searchableText.includes(MARKER);\n    });\n\n    if (!images.length) {\n      return false;\n    }\n\n    const moodbild = images.find(function (image) {\n      return getImageUrl(image);\n    });\n\n    if (!moodbild) {\n      return false;\n    }\n\n    const altText =\n      moodbild.getAttribute(\"alt\") ||\n      moodbild.getAttribute(\"data-alt\") ||\n      moodbild.getAttribute(\"aria-label\") ||\n      \"\";\n\n    return insertMoodbild(\n      getImageUrl(moodbild),\n      altText\n    );\n  }\n\n  function findMoodbildInProductData() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return Promise.resolve(false);\n    }\n\n    return fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(function (product) {\n        const media = Array.isArray(product.media)\n          ? product.media\n          : [];\n\n        const moodbild = media.find(function (item) {\n          return String(item.alt || \"\")\n            .toLowerCase()\n            .includes(MARKER);\n        });\n\n        if (!moodbild) {\n          return false;\n        }\n\n        const imageUrl =\n          moodbild.preview_image \u0026\u0026\n          moodbild.preview_image.src\n            ? moodbild.preview_image.src\n            : moodbild.src;\n\n        return insertMoodbild(\n          imageUrl,\n          moodbild.alt\n        );\n      })\n      .catch(function () {\n        return false;\n      });\n  }\n\n  function initializeMoodbild() {\n    if (findMoodbildInGallery()) {\n      return;\n    }\n\n    findMoodbildInProductData()\n      .then(function (found) {\n        if (found) return;\n\n        const observer = new MutationObserver(\n          function () {\n            if (findMoodbildInGallery()) {\n              observer.disconnect();\n            }\n          }\n        );\n\n        observer.observe(\n          document.documentElement,\n          {\n            childList: true,\n            subtree: true,\n            attributes: true,\n            attributeFilter: [\n              \"alt\",\n              \"data-alt\",\n              \"src\",\n              \"data-src\",\n              \"srcset\",\n              \"data-srcset\"\n            ]\n          }\n        );\n\n        window.setTimeout(function () {\n          observer.disconnect();\n        }, 15000);\n      });\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeMoodbild\n    );\n  } else {\n    initializeMoodbild();\n  }\n})();\n\u003c\/script\u003e\n\u003c\/div\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle \/ 1 Meter","offer_id":53327321727243,"sku":"550000203","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 2 Meter","offer_id":53327321760011,"sku":"550000204","price":47.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 3 Meter","offer_id":53327321792779,"sku":"550000205","price":69.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 1 Meter","offer_id":53327321825547,"sku":"550000206","price":27.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 2 Meter","offer_id":53327321858315,"sku":"550000207","price":53.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 3 Meter","offer_id":53327321891083,"sku":"550000208","price":77.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-BlumenzauberMINIgelb-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1787876169"},{"product_id":"blumenzauber-mini-hellblau-stoff","title":"Blumenzauber MINI hellblau – Stoff","description":"\u003c!-- PRODUKTBESCHREIBUNG MIT PARALLAX-LOGO --\u003e\n\n\u003csection class=\"bine-stoff-intro\"\u003e\n\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo-Stoffe-bunt.png?v=1787871207\" alt=\"Bine Brändle Stoffe Logo\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"bine-stoff-text\"\u003e\n\n    \u003ch2\u003eFarbenfrohe Stoffe mit Design von Bine Brändle\u003c\/h2\u003e\n\n    \u003cp class=\"bine-stoff-einleitung\"\u003e\n      Mach dein nächstes Nähprojekt zu etwas ganz Besonderem! Unsere\n      wunderschön bedruckten Stoffe mit einem liebevoll gestalteten Design\n      von \u003cstrong\u003eBine Brändle\u003c\/strong\u003e bringen Farbe, Freude und gute Laune\n      an die Nähmaschine.\n    \u003c\/p\u003e\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"Bine Brändle\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n    \u003cp\u003e\n      Du kannst dein Lieblingsdesign passend zu deinem Projekt als\n      \u003cstrong\u003eBaumwollgewebe\u003c\/strong\u003e oder \u003cstrong\u003eJersey\u003c\/strong\u003e auswählen.\n    \u003c\/p\u003e\n\n    \u003cdiv class=\"bine-stoffarten\"\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Das angenehm glatte und formstabile Baumwollgewebe eignet sich\n          wunderbar für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eHemden und Hosen\u003c\/li\u003e\n          \u003cli\u003eTaschen und Beutel\u003c\/li\u003e\n          \u003cli\u003eKissen und Wohnaccessoires\u003c\/li\u003e\n          \u003cli\u003eDekorationen\u003c\/li\u003e\n          \u003cli\u003ePatchwork-Projekte\u003c\/li\u003e\n          \u003cli\u003eSchultüten und kleine Geschenkideen\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eJersey\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Der weiche und elastische Jersey-Stoff ist besonders angenehm\n          auf der Haut und ideal für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eShirts und Kleider\u003c\/li\u003e\n          \u003cli\u003eHosen und Leggings\u003c\/li\u003e\n          \u003cli\u003eMützen und Halstücher\u003c\/li\u003e\n          \u003cli\u003eKinder- und Babykleidung\u003c\/li\u003e\n          \u003cli\u003eBequeme Lieblingsstücke für Groß und Klein\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"bine-stofflaengen\"\u003e\n      \u003ch3\u003eErhältliche Stofflängen\u003c\/h3\u003e\n\n      \u003cp\u003e\n        Du erhältst den Stoff fertig zugeschnitten in folgenden Längen:\n      \u003c\/p\u003e\n\n      \u003cdiv class=\"bine-laengen-auswahl\"\u003e\n        \u003cspan\u003e1 Meter\u003c\/span\u003e\n        \u003cspan\u003e2 Meter\u003c\/span\u003e\n        \u003cspan\u003e3 Meter\u003c\/span\u003e\n      \u003c\/div\u003e\n\n      \u003cp\u003e\n        Wähle einfach deine gewünschte Stoffart und anschließend die\n        passende Länge aus.\n      \u003c\/p\u003e\n\n      \u003cp class=\"bine-stoff-hinweis\"\u003e\n        \u003cstrong\u003eBitte beachte:\u003c\/strong\u003e Die ausgewählte Länge wird als\n        zusammenhängendes Stoffstück geliefert. Bei der Auswahl von\n        2 Metern erhältst du ein 2 Meter langes Stück, bei 3 Metern ein\n        zusammenhängendes 3-Meter-Stück.\n      \u003c\/p\u003e\n    \u003c\/div\u003e\n\n    \u003cp\u003e\n      Ob als farbenfrohes Kleidungsstück, liebevoll genähtes Geschenk oder\n      besonderer Blickfang für dein Zuhause – mit den Designs von\n      \u003cstrong\u003eBine Brändle\u003c\/strong\u003e wird jedes Nähprojekt herrlich bunt und\n      unverwechselbar.\n    \u003c\/p\u003e\n\n  \u003c\/div\u003e\n\n\u003c\/section\u003e\n\n\u003cstyle\u003e\n  .bine-stoff-intro {\n    margin: 20px 0 35px;\n    overflow: hidden;\n  }\n\n  .bine-logo-parallax {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    min-height: 150px;\n    margin-bottom: 20px;\n    padding: 20px;\n    \n    border-radius: 16px;\n    overflow: hidden;\n  }\n\n  .bine-stoff-logo {\n    display: block;\n    width: auto;\n    max-width: min(320px, 80%);\n    max-height: 130px;\n    object-fit: contain;\n    will-change: transform;\n    transform: translate3d(0, 0, 0);\n  }\n\n  .bine-stoff-text h2 {\n    margin-top: 0;\n    margin-bottom: 15px;\n    color: #08868a;\n    font-size: clamp(24px, 4vw, 34px);\n    line-height: 1.2;\n  }\n\n  .bine-stoff-text h3 {\n    margin-top: 0;\n    margin-bottom: 10px;\n  }\n\n  .bine-stoff-einleitung {\n    font-size: 1.08em;\n    line-height: 1.65;\n  }\n\n  .bine-stoffarten {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 16px;\n    margin: 25px 0;\n  }\n\n  .bine-stoffart {\n    padding: 20px;\n    background: #f7f7f7;\n    border-top: 4px solid #08868a;\n    border-radius: 10px;\n  }\n\n  .bine-stoffart ul {\n    margin-bottom: 0;\n    padding-left: 20px;\n  }\n\n  .bine-stoffart li {\n    margin-bottom: 5px;\n  }\n\n  .bine-stofflaengen {\n    margin: 25px 0;\n    padding: 22px;\n    background: rgba(8, 134, 138, 0.08);\n    border-radius: 12px;\n  }\n\n  .bine-laengen-auswahl {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin: 16px 0;\n  }\n\n  .bine-laengen-auswahl span {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 90px;\n    padding: 9px 15px;\n    background: #08868a;\n    color: #ffffff;\n    border-radius: 20px;\n    font-weight: 700;\n  }\n\n  .bine-stoff-hinweis {\n    margin-bottom: 0;\n    padding: 14px;\n    background: #ffffff;\n    border-left: 4px solid #08868a;\n    border-radius: 5px;\n  }\n\n  @media screen and (max-width: 650px) {\n    .bine-stoffarten {\n      grid-template-columns: 1fr;\n    }\n\n    .bine-logo-parallax {\n      min-height: 120px;\n    }\n\n    .bine-stoff-logo {\n      max-width: 75%;\n      max-height: 100px;\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .bine-stoff-logo {\n      transform: none !important;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const parallaxElements = document.querySelectorAll(\n    \"[data-bine-parallax] .bine-stoff-logo\"\n  );\n\n  if (\n    !parallaxElements.length ||\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  ) {\n    return;\n  }\n\n  let animationFrameRequested = false;\n\n  function updateParallax() {\n    parallaxElements.forEach(function (logo) {\n      const container = logo.closest(\"[data-bine-parallax]\");\n      const position = container.getBoundingClientRect();\n      const windowHeight = window.innerHeight;\n\n      if (\n        position.bottom \u003e= 0 \u0026\u0026\n        position.top \u003c= windowHeight\n      ) {\n        const containerCenter =\n          position.top + position.height \/ 2;\n\n        const screenCenter = windowHeight \/ 2;\n\n        const offset =\n          (containerCenter - screenCenter) * -0.08;\n\n        const limitedOffset = Math.max(\n          -22,\n          Math.min(22, offset)\n        );\n\n        logo.style.transform =\n          \"translate3d(0,\" + limitedOffset + \"px,0)\";\n      }\n    });\n\n    animationFrameRequested = false;\n  }\n\n  function requestParallaxUpdate() {\n    if (!animationFrameRequested) {\n      window.requestAnimationFrame(updateParallax);\n      animationFrameRequested = true;\n    }\n  }\n\n  window.addEventListener(\n    \"scroll\",\n    requestParallaxUpdate,\n    { passive: true }\n  );\n\n  window.addEventListener(\n    \"resize\",\n    requestParallaxUpdate\n  );\n\n  requestParallaxUpdate();\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif02?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eBaumwolle\u003c\/span\u003e\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif01?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eJersey\u003c\/span\u003e\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n   \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e \u003cp\u003e 100 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Rohgewebe aus 100 % Baumwolle,\u003cbr\u003e hergestellt aus Materialien, die gemäß OEKO-TEX® STANDARD 100 zertifiziert sind. \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 2 cm\u003cbr\u003e\u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 150–153 g\/m² \u003c\/p\u003e \u003chr\u003e \n\u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n\n   \u003ch3\u003eJersey\u003c\/h3\u003e \u003cp\u003e \u003cstrong\u003eZusammensetzung:\u003c\/strong\u003e\u003cbr\u003e 92 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche\u003cbr\u003e 8 % Elasthan \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Maschenwaren aus Baumwollgarnen mit Zusatz von Elastangarnen (EL-Anteil bis zu 8 %).\u003cbr\u003e Ausschließlich hergestellt unter Verwendung von Komponenten und Veredelungsverfahren, die gemäß OEKO-TEX® STANDARD 100 vorzertifiziert sind. \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 3 cm\u003cbr\u003e \u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 200–210 g\/m² \u003c\/p\u003e\n\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    flex: 1;\n    padding: 12px 20px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-gif {\n    width: 32px;\n    height: 32px;\n    object-fit: contain;\n    flex-shrink: 0;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content h3 {\n    margin-top: 0;\n  }\n\n  .stoff-tab-content h4 {\n    margin-top: 20px;\n    margin-bottom: 8px;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tab-button {\n      padding: 11px 8px;\n      gap: 6px;\n    }\n\n    .stoff-tab-gif {\n      width: 28px;\n      height: 28px;\n    }\n\n    .stoff-tab-content {\n      padding: 16px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\"Baumwolle\", \"Jersey\"];\n\n  function normalize(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getCorrectName(value) {\n    const wanted = normalize(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalize(stoffart) === wanted;\n    }) || null;\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalize(element.dataset.stoffButton) === normalize(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalize(correctName);\n\n    container\n      .querySelectorAll(\"[data-stoff-button]\")\n      .forEach(function (button) {\n        const active =\n          !button.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(button.dataset.stoffButton) === wanted;\n\n        button.classList.toggle(\"is-active\", active);\n        button.setAttribute(\n          \"aria-selected\",\n          active ? \"true\" : \"false\"\n        );\n      });\n\n    container\n      .querySelectorAll(\"[data-stoff-content]\")\n      .forEach(function (content) {\n        const active =\n          !content.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(content.dataset.stoffContent) === wanted;\n\n        content.classList.toggle(\"is-active\", active);\n        content.hidden = !active;\n      });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(\n        container,\n        firstButton.dataset.stoffButton\n      );\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section =\n      container.closest(\".shopify-section\") || document;\n\n    const selectedFields = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const field of selectedFields) {\n      const name = getCorrectName(field.value);\n\n      if (name) {\n        return name;\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabs() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart =\n        getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabs();\n      return;\n    }\n\n    const availability = {\n      baumwolle: false,\n      jersey: false\n    };\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const options = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      options.forEach(function (optionValue) {\n        const stoffart =\n          getCorrectName(optionValue);\n\n        if (stoffart) {\n          availability[normalize(stoffart)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available =\n          availability[normalize(stoffart)] === true;\n\n        const button = container.querySelector(\n          '[data-stoff-button=\"' + stoffart + '\"]'\n        );\n\n        const content = container.querySelector(\n          '[data-stoff-content=\"' + stoffart + '\"]'\n        );\n\n        if (button) {\n          button.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabs();\n  }\n\n  function loadProductAvailability() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      syncTabs();\n      return;\n    }\n\n    fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        syncTabs();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button =\n      event.target.closest(\"[data-stoff-button]\");\n\n    if (\n      !button ||\n      button.classList.contains(\"is-unavailable\")\n    ) {\n      return;\n    }\n\n    const container =\n      button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(\n        container,\n        button.dataset.stoffButton\n      );\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches(\n        'input[type=\"radio\"], select'\n      )\n    ) {\n      window.setTimeout(syncTabs, 50);\n    }\n  });\n\n  function initializeStoffTabs() {\n    loadProductAvailability();\n    window.setTimeout(syncTabs, 300);\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeStoffTabs\n    );\n  } else {\n    initializeStoffTabs();\n  }\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"dynamisches-moodbild\" data-moodbild\u003e\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .dynamisches-moodbild {\n    display: none;\n    margin: 25px 0;\n    text-align: center;\n  }\n\n  .dynamisches-moodbild.is-visible {\n    display: block;\n  }\n\n  .moodbild-link {\n    display: block;\n    text-decoration: none;\n    border-radius: 14px;\n    overflow: hidden;\n  }\n\n  .dynamisches-moodbild img {\n    display: block;\n    width: 100%;\n    height: auto;\n    margin: 0 auto;\n    border-radius: 14px;\n    transition: transform 0.25s ease;\n  }\n\n  .moodbild-link:hover img {\n    transform: scale(1.015);\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .dynamisches-moodbild img {\n      transition: none;\n    }\n\n    .moodbild-link:hover img {\n      transform: none;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const MARKER = \"#moodbild\";\n\n  const containers = document.querySelectorAll(\n    \"[data-moodbild]\"\n  );\n\n  if (!containers.length) return;\n\n  function cleanAlt(text) {\n    return String(text || \"\")\n      .replace(\/\\s*#Moodbild\\s*\/gi, \"\")\n      .trim();\n  }\n\n  function getImageUrl(image) {\n    if (image.currentSrc) {\n      return image.currentSrc;\n    }\n\n    const srcset =\n      image.getAttribute(\"srcset\") ||\n      image.getAttribute(\"data-srcset\");\n\n    if (srcset) {\n      const sources = srcset\n        .split(\",\")\n        .map(function (entry) {\n          return entry.trim().split(\/\\s+\/)[0];\n        })\n        .filter(Boolean);\n\n      if (sources.length) {\n        return sources[sources.length - 1];\n      }\n    }\n\n    return (\n      image.getAttribute(\"data-src\") ||\n      image.getAttribute(\"src\") ||\n      \"\"\n    );\n  }\n\n  function insertMoodbild(imageUrl, altText) {\n    if (!imageUrl) return false;\n\n    containers.forEach(function (container) {\n      if (\n        container.dataset.moodbildLoaded === \"true\"\n      ) {\n        return;\n      }\n\n      const link = document.createElement(\"a\");\n\n      link.href = \"https:\/\/www.bines-shop.com\";\n      link.target = \"_blank\";\n      link.rel = \"noopener noreferrer\";\n      link.className = \"moodbild-link\";\n\n      link.setAttribute(\n        \"aria-label\",\n        \"Bines Shop in einem neuen Fenster öffnen\"\n      );\n\n      const image = document.createElement(\"img\");\n\n      image.src = imageUrl;\n\n      image.alt =\n        cleanAlt(altText) ||\n        \"Moodbild zum Produkt\";\n\n      image.loading = \"lazy\";\n      image.decoding = \"async\";\n\n      link.appendChild(image);\n      container.replaceChildren(link);\n\n      container.classList.add(\"is-visible\");\n\n      container.dataset.moodbildLoaded = \"true\";\n    });\n\n    return true;\n  }\n\n  function findMoodbildInGallery() {\n    const images = Array.from(\n      document.querySelectorAll(\"img\")\n    ).filter(function (image) {\n      if (image.closest(\"[data-moodbild]\")) {\n        return false;\n      }\n\n      const searchableText = [\n        image.getAttribute(\"alt\"),\n        image.getAttribute(\"data-alt\"),\n        image.getAttribute(\"aria-label\")\n      ]\n        .filter(Boolean)\n        .join(\" \")\n        .toLowerCase();\n\n      return searchableText.includes(MARKER);\n    });\n\n    if (!images.length) {\n      return false;\n    }\n\n    const moodbild = images.find(function (image) {\n      return getImageUrl(image);\n    });\n\n    if (!moodbild) {\n      return false;\n    }\n\n    const altText =\n      moodbild.getAttribute(\"alt\") ||\n      moodbild.getAttribute(\"data-alt\") ||\n      moodbild.getAttribute(\"aria-label\") ||\n      \"\";\n\n    return insertMoodbild(\n      getImageUrl(moodbild),\n      altText\n    );\n  }\n\n  function findMoodbildInProductData() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return Promise.resolve(false);\n    }\n\n    return fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(function (product) {\n        const media = Array.isArray(product.media)\n          ? product.media\n          : [];\n\n        const moodbild = media.find(function (item) {\n          return String(item.alt || \"\")\n            .toLowerCase()\n            .includes(MARKER);\n        });\n\n        if (!moodbild) {\n          return false;\n        }\n\n        const imageUrl =\n          moodbild.preview_image \u0026\u0026\n          moodbild.preview_image.src\n            ? moodbild.preview_image.src\n            : moodbild.src;\n\n        return insertMoodbild(\n          imageUrl,\n          moodbild.alt\n        );\n      })\n      .catch(function () {\n        return false;\n      });\n  }\n\n  function initializeMoodbild() {\n    if (findMoodbildInGallery()) {\n      return;\n    }\n\n    findMoodbildInProductData()\n      .then(function (found) {\n        if (found) return;\n\n        const observer = new MutationObserver(\n          function () {\n            if (findMoodbildInGallery()) {\n              observer.disconnect();\n            }\n          }\n        );\n\n        observer.observe(\n          document.documentElement,\n          {\n            childList: true,\n            subtree: true,\n            attributes: true,\n            attributeFilter: [\n              \"alt\",\n              \"data-alt\",\n              \"src\",\n              \"data-src\",\n              \"srcset\",\n              \"data-srcset\"\n            ]\n          }\n        );\n\n        window.setTimeout(function () {\n          observer.disconnect();\n        }, 15000);\n      });\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeMoodbild\n    );\n  } else {\n    initializeMoodbild();\n  }\n})();\n\u003c\/script\u003e\n\u003c\/div\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle \/ 1 Meter","offer_id":53327322710283,"sku":"550000197","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 2 Meter","offer_id":53327322743051,"sku":"550000198","price":47.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 3 Meter","offer_id":53327322775819,"sku":"550000199","price":69.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 1 Meter","offer_id":53327322808587,"sku":"550000200","price":27.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 2 Meter","offer_id":53327322841355,"sku":"550000201","price":53.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 3 Meter","offer_id":53327322874123,"sku":"550000202","price":77.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-BlumenzauberMINIhellblau-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1787876340"},{"product_id":"blumenzauber-mini-rosa-stoff","title":"Blumenzauber MINI rosa – Stoff","description":"\u003c!-- PRODUKTBESCHREIBUNG MIT PARALLAX-LOGO --\u003e\n\n\u003csection class=\"bine-stoff-intro\"\u003e\n\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo-Stoffe-bunt.png?v=1787871207\" alt=\"Bine Brändle Stoffe Logo\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"bine-stoff-text\"\u003e\n\n    \u003ch2\u003eFarbenfrohe Stoffe mit Design von Bine Brändle\u003c\/h2\u003e\n\n    \u003cp class=\"bine-stoff-einleitung\"\u003e\n      Mach dein nächstes Nähprojekt zu etwas ganz Besonderem! Unsere\n      wunderschön bedruckten Stoffe mit einem liebevoll gestalteten Design\n      von \u003cstrong\u003eBine Brändle\u003c\/strong\u003e bringen Farbe, Freude und gute Laune\n      an die Nähmaschine.\n    \u003c\/p\u003e\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"Bine Brändle\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n    \u003cp\u003e\n      Du kannst dein Lieblingsdesign passend zu deinem Projekt als\n      \u003cstrong\u003eBaumwollgewebe\u003c\/strong\u003e oder \u003cstrong\u003eJersey\u003c\/strong\u003e auswählen.\n    \u003c\/p\u003e\n\n    \u003cdiv class=\"bine-stoffarten\"\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Das angenehm glatte und formstabile Baumwollgewebe eignet sich\n          wunderbar für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eHemden und Hosen\u003c\/li\u003e\n          \u003cli\u003eTaschen und Beutel\u003c\/li\u003e\n          \u003cli\u003eKissen und Wohnaccessoires\u003c\/li\u003e\n          \u003cli\u003eDekorationen\u003c\/li\u003e\n          \u003cli\u003ePatchwork-Projekte\u003c\/li\u003e\n          \u003cli\u003eSchultüten und kleine Geschenkideen\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eJersey\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Der weiche und elastische Jersey-Stoff ist besonders angenehm\n          auf der Haut und ideal für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eShirts und Kleider\u003c\/li\u003e\n          \u003cli\u003eHosen und Leggings\u003c\/li\u003e\n          \u003cli\u003eMützen und Halstücher\u003c\/li\u003e\n          \u003cli\u003eKinder- und Babykleidung\u003c\/li\u003e\n          \u003cli\u003eBequeme Lieblingsstücke für Groß und Klein\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"bine-stofflaengen\"\u003e\n      \u003ch3\u003eErhältliche Stofflängen\u003c\/h3\u003e\n\n      \u003cp\u003e\n        Du erhältst den Stoff fertig zugeschnitten in folgenden Längen:\n      \u003c\/p\u003e\n\n      \u003cdiv class=\"bine-laengen-auswahl\"\u003e\n        \u003cspan\u003e1 Meter\u003c\/span\u003e\n        \u003cspan\u003e2 Meter\u003c\/span\u003e\n        \u003cspan\u003e3 Meter\u003c\/span\u003e\n      \u003c\/div\u003e\n\n      \u003cp\u003e\n        Wähle einfach deine gewünschte Stoffart und anschließend die\n        passende Länge aus.\n      \u003c\/p\u003e\n\n      \u003cp class=\"bine-stoff-hinweis\"\u003e\n        \u003cstrong\u003eBitte beachte:\u003c\/strong\u003e Die ausgewählte Länge wird als\n        zusammenhängendes Stoffstück geliefert. Bei der Auswahl von\n        2 Metern erhältst du ein 2 Meter langes Stück, bei 3 Metern ein\n        zusammenhängendes 3-Meter-Stück.\n      \u003c\/p\u003e\n    \u003c\/div\u003e\n\n    \u003cp\u003e\n      Ob als farbenfrohes Kleidungsstück, liebevoll genähtes Geschenk oder\n      besonderer Blickfang für dein Zuhause – mit den Designs von\n      \u003cstrong\u003eBine Brändle\u003c\/strong\u003e wird jedes Nähprojekt herrlich bunt und\n      unverwechselbar.\n    \u003c\/p\u003e\n\n  \u003c\/div\u003e\n\n\u003c\/section\u003e\n\n\u003cstyle\u003e\n  .bine-stoff-intro {\n    margin: 20px 0 35px;\n    overflow: hidden;\n  }\n\n  .bine-logo-parallax {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    min-height: 150px;\n    margin-bottom: 20px;\n    padding: 20px;\n    \n    border-radius: 16px;\n    overflow: hidden;\n  }\n\n  .bine-stoff-logo {\n    display: block;\n    width: auto;\n    max-width: min(320px, 80%);\n    max-height: 130px;\n    object-fit: contain;\n    will-change: transform;\n    transform: translate3d(0, 0, 0);\n  }\n\n  .bine-stoff-text h2 {\n    margin-top: 0;\n    margin-bottom: 15px;\n    color: #08868a;\n    font-size: clamp(24px, 4vw, 34px);\n    line-height: 1.2;\n  }\n\n  .bine-stoff-text h3 {\n    margin-top: 0;\n    margin-bottom: 10px;\n  }\n\n  .bine-stoff-einleitung {\n    font-size: 1.08em;\n    line-height: 1.65;\n  }\n\n  .bine-stoffarten {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 16px;\n    margin: 25px 0;\n  }\n\n  .bine-stoffart {\n    padding: 20px;\n    background: #f7f7f7;\n    border-top: 4px solid #08868a;\n    border-radius: 10px;\n  }\n\n  .bine-stoffart ul {\n    margin-bottom: 0;\n    padding-left: 20px;\n  }\n\n  .bine-stoffart li {\n    margin-bottom: 5px;\n  }\n\n  .bine-stofflaengen {\n    margin: 25px 0;\n    padding: 22px;\n    background: rgba(8, 134, 138, 0.08);\n    border-radius: 12px;\n  }\n\n  .bine-laengen-auswahl {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin: 16px 0;\n  }\n\n  .bine-laengen-auswahl span {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 90px;\n    padding: 9px 15px;\n    background: #08868a;\n    color: #ffffff;\n    border-radius: 20px;\n    font-weight: 700;\n  }\n\n  .bine-stoff-hinweis {\n    margin-bottom: 0;\n    padding: 14px;\n    background: #ffffff;\n    border-left: 4px solid #08868a;\n    border-radius: 5px;\n  }\n\n  @media screen and (max-width: 650px) {\n    .bine-stoffarten {\n      grid-template-columns: 1fr;\n    }\n\n    .bine-logo-parallax {\n      min-height: 120px;\n    }\n\n    .bine-stoff-logo {\n      max-width: 75%;\n      max-height: 100px;\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .bine-stoff-logo {\n      transform: none !important;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const parallaxElements = document.querySelectorAll(\n    \"[data-bine-parallax] .bine-stoff-logo\"\n  );\n\n  if (\n    !parallaxElements.length ||\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  ) {\n    return;\n  }\n\n  let animationFrameRequested = false;\n\n  function updateParallax() {\n    parallaxElements.forEach(function (logo) {\n      const container = logo.closest(\"[data-bine-parallax]\");\n      const position = container.getBoundingClientRect();\n      const windowHeight = window.innerHeight;\n\n      if (\n        position.bottom \u003e= 0 \u0026\u0026\n        position.top \u003c= windowHeight\n      ) {\n        const containerCenter =\n          position.top + position.height \/ 2;\n\n        const screenCenter = windowHeight \/ 2;\n\n        const offset =\n          (containerCenter - screenCenter) * -0.08;\n\n        const limitedOffset = Math.max(\n          -22,\n          Math.min(22, offset)\n        );\n\n        logo.style.transform =\n          \"translate3d(0,\" + limitedOffset + \"px,0)\";\n      }\n    });\n\n    animationFrameRequested = false;\n  }\n\n  function requestParallaxUpdate() {\n    if (!animationFrameRequested) {\n      window.requestAnimationFrame(updateParallax);\n      animationFrameRequested = true;\n    }\n  }\n\n  window.addEventListener(\n    \"scroll\",\n    requestParallaxUpdate,\n    { passive: true }\n  );\n\n  window.addEventListener(\n    \"resize\",\n    requestParallaxUpdate\n  );\n\n  requestParallaxUpdate();\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif02?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eBaumwolle\u003c\/span\u003e\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif01?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eJersey\u003c\/span\u003e\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n   \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e \u003cp\u003e 100 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Rohgewebe aus 100 % Baumwolle,\u003cbr\u003e hergestellt aus Materialien, die gemäß OEKO-TEX® STANDARD 100 zertifiziert sind. \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 2 cm\u003cbr\u003e\u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 150–153 g\/m² \u003c\/p\u003e \u003chr\u003e \n\u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n\n   \u003ch3\u003eJersey\u003c\/h3\u003e \u003cp\u003e \u003cstrong\u003eZusammensetzung:\u003c\/strong\u003e\u003cbr\u003e 92 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche\u003cbr\u003e 8 % Elasthan \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Maschenwaren aus Baumwollgarnen mit Zusatz von Elastangarnen (EL-Anteil bis zu 8 %).\u003cbr\u003e Ausschließlich hergestellt unter Verwendung von Komponenten und Veredelungsverfahren, die gemäß OEKO-TEX® STANDARD 100 vorzertifiziert sind. \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 3 cm\u003cbr\u003e \u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 200–210 g\/m² \u003c\/p\u003e\n\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    flex: 1;\n    padding: 12px 20px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-gif {\n    width: 32px;\n    height: 32px;\n    object-fit: contain;\n    flex-shrink: 0;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content h3 {\n    margin-top: 0;\n  }\n\n  .stoff-tab-content h4 {\n    margin-top: 20px;\n    margin-bottom: 8px;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tab-button {\n      padding: 11px 8px;\n      gap: 6px;\n    }\n\n    .stoff-tab-gif {\n      width: 28px;\n      height: 28px;\n    }\n\n    .stoff-tab-content {\n      padding: 16px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\"Baumwolle\", \"Jersey\"];\n\n  function normalize(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getCorrectName(value) {\n    const wanted = normalize(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalize(stoffart) === wanted;\n    }) || null;\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalize(element.dataset.stoffButton) === normalize(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalize(correctName);\n\n    container\n      .querySelectorAll(\"[data-stoff-button]\")\n      .forEach(function (button) {\n        const active =\n          !button.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(button.dataset.stoffButton) === wanted;\n\n        button.classList.toggle(\"is-active\", active);\n        button.setAttribute(\n          \"aria-selected\",\n          active ? \"true\" : \"false\"\n        );\n      });\n\n    container\n      .querySelectorAll(\"[data-stoff-content]\")\n      .forEach(function (content) {\n        const active =\n          !content.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(content.dataset.stoffContent) === wanted;\n\n        content.classList.toggle(\"is-active\", active);\n        content.hidden = !active;\n      });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(\n        container,\n        firstButton.dataset.stoffButton\n      );\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section =\n      container.closest(\".shopify-section\") || document;\n\n    const selectedFields = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const field of selectedFields) {\n      const name = getCorrectName(field.value);\n\n      if (name) {\n        return name;\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabs() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart =\n        getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabs();\n      return;\n    }\n\n    const availability = {\n      baumwolle: false,\n      jersey: false\n    };\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const options = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      options.forEach(function (optionValue) {\n        const stoffart =\n          getCorrectName(optionValue);\n\n        if (stoffart) {\n          availability[normalize(stoffart)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available =\n          availability[normalize(stoffart)] === true;\n\n        const button = container.querySelector(\n          '[data-stoff-button=\"' + stoffart + '\"]'\n        );\n\n        const content = container.querySelector(\n          '[data-stoff-content=\"' + stoffart + '\"]'\n        );\n\n        if (button) {\n          button.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabs();\n  }\n\n  function loadProductAvailability() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      syncTabs();\n      return;\n    }\n\n    fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        syncTabs();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button =\n      event.target.closest(\"[data-stoff-button]\");\n\n    if (\n      !button ||\n      button.classList.contains(\"is-unavailable\")\n    ) {\n      return;\n    }\n\n    const container =\n      button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(\n        container,\n        button.dataset.stoffButton\n      );\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches(\n        'input[type=\"radio\"], select'\n      )\n    ) {\n      window.setTimeout(syncTabs, 50);\n    }\n  });\n\n  function initializeStoffTabs() {\n    loadProductAvailability();\n    window.setTimeout(syncTabs, 300);\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeStoffTabs\n    );\n  } else {\n    initializeStoffTabs();\n  }\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"dynamisches-moodbild\" data-moodbild\u003e\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .dynamisches-moodbild {\n    display: none;\n    margin: 25px 0;\n    text-align: center;\n  }\n\n  .dynamisches-moodbild.is-visible {\n    display: block;\n  }\n\n  .moodbild-link {\n    display: block;\n    text-decoration: none;\n    border-radius: 14px;\n    overflow: hidden;\n  }\n\n  .dynamisches-moodbild img {\n    display: block;\n    width: 100%;\n    height: auto;\n    margin: 0 auto;\n    border-radius: 14px;\n    transition: transform 0.25s ease;\n  }\n\n  .moodbild-link:hover img {\n    transform: scale(1.015);\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .dynamisches-moodbild img {\n      transition: none;\n    }\n\n    .moodbild-link:hover img {\n      transform: none;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const MARKER = \"#moodbild\";\n\n  const containers = document.querySelectorAll(\n    \"[data-moodbild]\"\n  );\n\n  if (!containers.length) return;\n\n  function cleanAlt(text) {\n    return String(text || \"\")\n      .replace(\/\\s*#Moodbild\\s*\/gi, \"\")\n      .trim();\n  }\n\n  function getImageUrl(image) {\n    if (image.currentSrc) {\n      return image.currentSrc;\n    }\n\n    const srcset =\n      image.getAttribute(\"srcset\") ||\n      image.getAttribute(\"data-srcset\");\n\n    if (srcset) {\n      const sources = srcset\n        .split(\",\")\n        .map(function (entry) {\n          return entry.trim().split(\/\\s+\/)[0];\n        })\n        .filter(Boolean);\n\n      if (sources.length) {\n        return sources[sources.length - 1];\n      }\n    }\n\n    return (\n      image.getAttribute(\"data-src\") ||\n      image.getAttribute(\"src\") ||\n      \"\"\n    );\n  }\n\n  function insertMoodbild(imageUrl, altText) {\n    if (!imageUrl) return false;\n\n    containers.forEach(function (container) {\n      if (\n        container.dataset.moodbildLoaded === \"true\"\n      ) {\n        return;\n      }\n\n      const link = document.createElement(\"a\");\n\n      link.href = \"https:\/\/www.bines-shop.com\";\n      link.target = \"_blank\";\n      link.rel = \"noopener noreferrer\";\n      link.className = \"moodbild-link\";\n\n      link.setAttribute(\n        \"aria-label\",\n        \"Bines Shop in einem neuen Fenster öffnen\"\n      );\n\n      const image = document.createElement(\"img\");\n\n      image.src = imageUrl;\n\n      image.alt =\n        cleanAlt(altText) ||\n        \"Moodbild zum Produkt\";\n\n      image.loading = \"lazy\";\n      image.decoding = \"async\";\n\n      link.appendChild(image);\n      container.replaceChildren(link);\n\n      container.classList.add(\"is-visible\");\n\n      container.dataset.moodbildLoaded = \"true\";\n    });\n\n    return true;\n  }\n\n  function findMoodbildInGallery() {\n    const images = Array.from(\n      document.querySelectorAll(\"img\")\n    ).filter(function (image) {\n      if (image.closest(\"[data-moodbild]\")) {\n        return false;\n      }\n\n      const searchableText = [\n        image.getAttribute(\"alt\"),\n        image.getAttribute(\"data-alt\"),\n        image.getAttribute(\"aria-label\")\n      ]\n        .filter(Boolean)\n        .join(\" \")\n        .toLowerCase();\n\n      return searchableText.includes(MARKER);\n    });\n\n    if (!images.length) {\n      return false;\n    }\n\n    const moodbild = images.find(function (image) {\n      return getImageUrl(image);\n    });\n\n    if (!moodbild) {\n      return false;\n    }\n\n    const altText =\n      moodbild.getAttribute(\"alt\") ||\n      moodbild.getAttribute(\"data-alt\") ||\n      moodbild.getAttribute(\"aria-label\") ||\n      \"\";\n\n    return insertMoodbild(\n      getImageUrl(moodbild),\n      altText\n    );\n  }\n\n  function findMoodbildInProductData() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return Promise.resolve(false);\n    }\n\n    return fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(function (product) {\n        const media = Array.isArray(product.media)\n          ? product.media\n          : [];\n\n        const moodbild = media.find(function (item) {\n          return String(item.alt || \"\")\n            .toLowerCase()\n            .includes(MARKER);\n        });\n\n        if (!moodbild) {\n          return false;\n        }\n\n        const imageUrl =\n          moodbild.preview_image \u0026\u0026\n          moodbild.preview_image.src\n            ? moodbild.preview_image.src\n            : moodbild.src;\n\n        return insertMoodbild(\n          imageUrl,\n          moodbild.alt\n        );\n      })\n      .catch(function () {\n        return false;\n      });\n  }\n\n  function initializeMoodbild() {\n    if (findMoodbildInGallery()) {\n      return;\n    }\n\n    findMoodbildInProductData()\n      .then(function (found) {\n        if (found) return;\n\n        const observer = new MutationObserver(\n          function () {\n            if (findMoodbildInGallery()) {\n              observer.disconnect();\n            }\n          }\n        );\n\n        observer.observe(\n          document.documentElement,\n          {\n            childList: true,\n            subtree: true,\n            attributes: true,\n            attributeFilter: [\n              \"alt\",\n              \"data-alt\",\n              \"src\",\n              \"data-src\",\n              \"srcset\",\n              \"data-srcset\"\n            ]\n          }\n        );\n\n        window.setTimeout(function () {\n          observer.disconnect();\n        }, 15000);\n      });\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeMoodbild\n    );\n  } else {\n    initializeMoodbild();\n  }\n})();\n\u003c\/script\u003e\n\u003c\/div\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle \/ 1 Meter","offer_id":53327326249227,"sku":"550000191","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 2 Meter","offer_id":53327326281995,"sku":"550000192","price":47.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 3 Meter","offer_id":53327326314763,"sku":"550000193","price":69.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 1 Meter","offer_id":53327326347531,"sku":"550000194","price":27.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 2 Meter","offer_id":53327326380299,"sku":"550000195","price":53.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 3 Meter","offer_id":53327326413067,"sku":"550000196","price":77.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-BlumenzauberMINIrosa-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1787876626"},{"product_id":"blumenzauber-mini-rot-stoff","title":"Blumenzauber MINI rot – Stoff","description":"\u003c!-- PRODUKTBESCHREIBUNG MIT PARALLAX-LOGO --\u003e\n\n\u003csection class=\"bine-stoff-intro\"\u003e\n\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo-Stoffe-bunt.png?v=1787871207\" alt=\"Bine Brändle Stoffe Logo\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"bine-stoff-text\"\u003e\n\n    \u003ch2\u003eFarbenfrohe Stoffe mit Design von Bine Brändle\u003c\/h2\u003e\n\n    \u003cp class=\"bine-stoff-einleitung\"\u003e\n      Mach dein nächstes Nähprojekt zu etwas ganz Besonderem! Unsere\n      wunderschön bedruckten Stoffe mit einem liebevoll gestalteten Design\n      von \u003cstrong\u003eBine Brändle\u003c\/strong\u003e bringen Farbe, Freude und gute Laune\n      an die Nähmaschine.\n    \u003c\/p\u003e\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"Bine Brändle\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n    \u003cp\u003e\n      Du kannst dein Lieblingsdesign passend zu deinem Projekt als\n      \u003cstrong\u003eBaumwollgewebe\u003c\/strong\u003e oder \u003cstrong\u003eJersey\u003c\/strong\u003e auswählen.\n    \u003c\/p\u003e\n\n    \u003cdiv class=\"bine-stoffarten\"\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Das angenehm glatte und formstabile Baumwollgewebe eignet sich\n          wunderbar für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eHemden und Hosen\u003c\/li\u003e\n          \u003cli\u003eTaschen und Beutel\u003c\/li\u003e\n          \u003cli\u003eKissen und Wohnaccessoires\u003c\/li\u003e\n          \u003cli\u003eDekorationen\u003c\/li\u003e\n          \u003cli\u003ePatchwork-Projekte\u003c\/li\u003e\n          \u003cli\u003eSchultüten und kleine Geschenkideen\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eJersey\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Der weiche und elastische Jersey-Stoff ist besonders angenehm\n          auf der Haut und ideal für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eShirts und Kleider\u003c\/li\u003e\n          \u003cli\u003eHosen und Leggings\u003c\/li\u003e\n          \u003cli\u003eMützen und Halstücher\u003c\/li\u003e\n          \u003cli\u003eKinder- und Babykleidung\u003c\/li\u003e\n          \u003cli\u003eBequeme Lieblingsstücke für Groß und Klein\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"bine-stofflaengen\"\u003e\n      \u003ch3\u003eErhältliche Stofflängen\u003c\/h3\u003e\n\n      \u003cp\u003e\n        Du erhältst den Stoff fertig zugeschnitten in folgenden Längen:\n      \u003c\/p\u003e\n\n      \u003cdiv class=\"bine-laengen-auswahl\"\u003e\n        \u003cspan\u003e1 Meter\u003c\/span\u003e\n        \u003cspan\u003e2 Meter\u003c\/span\u003e\n        \u003cspan\u003e3 Meter\u003c\/span\u003e\n      \u003c\/div\u003e\n\n      \u003cp\u003e\n        Wähle einfach deine gewünschte Stoffart und anschließend die\n        passende Länge aus.\n      \u003c\/p\u003e\n\n      \u003cp class=\"bine-stoff-hinweis\"\u003e\n        \u003cstrong\u003eBitte beachte:\u003c\/strong\u003e Die ausgewählte Länge wird als\n        zusammenhängendes Stoffstück geliefert. Bei der Auswahl von\n        2 Metern erhältst du ein 2 Meter langes Stück, bei 3 Metern ein\n        zusammenhängendes 3-Meter-Stück.\n      \u003c\/p\u003e\n    \u003c\/div\u003e\n\n    \u003cp\u003e\n      Ob als farbenfrohes Kleidungsstück, liebevoll genähtes Geschenk oder\n      besonderer Blickfang für dein Zuhause – mit den Designs von\n      \u003cstrong\u003eBine Brändle\u003c\/strong\u003e wird jedes Nähprojekt herrlich bunt und\n      unverwechselbar.\n    \u003c\/p\u003e\n\n  \u003c\/div\u003e\n\n\u003c\/section\u003e\n\n\u003cstyle\u003e\n  .bine-stoff-intro {\n    margin: 20px 0 35px;\n    overflow: hidden;\n  }\n\n  .bine-logo-parallax {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    min-height: 150px;\n    margin-bottom: 20px;\n    padding: 20px;\n    \n    border-radius: 16px;\n    overflow: hidden;\n  }\n\n  .bine-stoff-logo {\n    display: block;\n    width: auto;\n    max-width: min(320px, 80%);\n    max-height: 130px;\n    object-fit: contain;\n    will-change: transform;\n    transform: translate3d(0, 0, 0);\n  }\n\n  .bine-stoff-text h2 {\n    margin-top: 0;\n    margin-bottom: 15px;\n    color: #08868a;\n    font-size: clamp(24px, 4vw, 34px);\n    line-height: 1.2;\n  }\n\n  .bine-stoff-text h3 {\n    margin-top: 0;\n    margin-bottom: 10px;\n  }\n\n  .bine-stoff-einleitung {\n    font-size: 1.08em;\n    line-height: 1.65;\n  }\n\n  .bine-stoffarten {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 16px;\n    margin: 25px 0;\n  }\n\n  .bine-stoffart {\n    padding: 20px;\n    background: #f7f7f7;\n    border-top: 4px solid #08868a;\n    border-radius: 10px;\n  }\n\n  .bine-stoffart ul {\n    margin-bottom: 0;\n    padding-left: 20px;\n  }\n\n  .bine-stoffart li {\n    margin-bottom: 5px;\n  }\n\n  .bine-stofflaengen {\n    margin: 25px 0;\n    padding: 22px;\n    background: rgba(8, 134, 138, 0.08);\n    border-radius: 12px;\n  }\n\n  .bine-laengen-auswahl {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin: 16px 0;\n  }\n\n  .bine-laengen-auswahl span {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 90px;\n    padding: 9px 15px;\n    background: #08868a;\n    color: #ffffff;\n    border-radius: 20px;\n    font-weight: 700;\n  }\n\n  .bine-stoff-hinweis {\n    margin-bottom: 0;\n    padding: 14px;\n    background: #ffffff;\n    border-left: 4px solid #08868a;\n    border-radius: 5px;\n  }\n\n  @media screen and (max-width: 650px) {\n    .bine-stoffarten {\n      grid-template-columns: 1fr;\n    }\n\n    .bine-logo-parallax {\n      min-height: 120px;\n    }\n\n    .bine-stoff-logo {\n      max-width: 75%;\n      max-height: 100px;\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .bine-stoff-logo {\n      transform: none !important;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const parallaxElements = document.querySelectorAll(\n    \"[data-bine-parallax] .bine-stoff-logo\"\n  );\n\n  if (\n    !parallaxElements.length ||\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  ) {\n    return;\n  }\n\n  let animationFrameRequested = false;\n\n  function updateParallax() {\n    parallaxElements.forEach(function (logo) {\n      const container = logo.closest(\"[data-bine-parallax]\");\n      const position = container.getBoundingClientRect();\n      const windowHeight = window.innerHeight;\n\n      if (\n        position.bottom \u003e= 0 \u0026\u0026\n        position.top \u003c= windowHeight\n      ) {\n        const containerCenter =\n          position.top + position.height \/ 2;\n\n        const screenCenter = windowHeight \/ 2;\n\n        const offset =\n          (containerCenter - screenCenter) * -0.08;\n\n        const limitedOffset = Math.max(\n          -22,\n          Math.min(22, offset)\n        );\n\n        logo.style.transform =\n          \"translate3d(0,\" + limitedOffset + \"px,0)\";\n      }\n    });\n\n    animationFrameRequested = false;\n  }\n\n  function requestParallaxUpdate() {\n    if (!animationFrameRequested) {\n      window.requestAnimationFrame(updateParallax);\n      animationFrameRequested = true;\n    }\n  }\n\n  window.addEventListener(\n    \"scroll\",\n    requestParallaxUpdate,\n    { passive: true }\n  );\n\n  window.addEventListener(\n    \"resize\",\n    requestParallaxUpdate\n  );\n\n  requestParallaxUpdate();\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif02?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eBaumwolle\u003c\/span\u003e\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif01?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eJersey\u003c\/span\u003e\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n   \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e \u003cp\u003e 100 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Rohgewebe aus 100 % Baumwolle,\u003cbr\u003e hergestellt aus Materialien, die gemäß OEKO-TEX® STANDARD 100 zertifiziert sind. \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 2 cm\u003cbr\u003e\u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 150–153 g\/m² \u003c\/p\u003e \u003chr\u003e \n\u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n\n   \u003ch3\u003eJersey\u003c\/h3\u003e \u003cp\u003e \u003cstrong\u003eZusammensetzung:\u003c\/strong\u003e\u003cbr\u003e 92 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche\u003cbr\u003e 8 % Elasthan \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Maschenwaren aus Baumwollgarnen mit Zusatz von Elastangarnen (EL-Anteil bis zu 8 %).\u003cbr\u003e Ausschließlich hergestellt unter Verwendung von Komponenten und Veredelungsverfahren, die gemäß OEKO-TEX® STANDARD 100 vorzertifiziert sind. \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 3 cm\u003cbr\u003e \u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 200–210 g\/m² \u003c\/p\u003e\n\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    flex: 1;\n    padding: 12px 20px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-gif {\n    width: 32px;\n    height: 32px;\n    object-fit: contain;\n    flex-shrink: 0;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content h3 {\n    margin-top: 0;\n  }\n\n  .stoff-tab-content h4 {\n    margin-top: 20px;\n    margin-bottom: 8px;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tab-button {\n      padding: 11px 8px;\n      gap: 6px;\n    }\n\n    .stoff-tab-gif {\n      width: 28px;\n      height: 28px;\n    }\n\n    .stoff-tab-content {\n      padding: 16px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\"Baumwolle\", \"Jersey\"];\n\n  function normalize(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getCorrectName(value) {\n    const wanted = normalize(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalize(stoffart) === wanted;\n    }) || null;\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalize(element.dataset.stoffButton) === normalize(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalize(correctName);\n\n    container\n      .querySelectorAll(\"[data-stoff-button]\")\n      .forEach(function (button) {\n        const active =\n          !button.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(button.dataset.stoffButton) === wanted;\n\n        button.classList.toggle(\"is-active\", active);\n        button.setAttribute(\n          \"aria-selected\",\n          active ? \"true\" : \"false\"\n        );\n      });\n\n    container\n      .querySelectorAll(\"[data-stoff-content]\")\n      .forEach(function (content) {\n        const active =\n          !content.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(content.dataset.stoffContent) === wanted;\n\n        content.classList.toggle(\"is-active\", active);\n        content.hidden = !active;\n      });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(\n        container,\n        firstButton.dataset.stoffButton\n      );\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section =\n      container.closest(\".shopify-section\") || document;\n\n    const selectedFields = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const field of selectedFields) {\n      const name = getCorrectName(field.value);\n\n      if (name) {\n        return name;\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabs() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart =\n        getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabs();\n      return;\n    }\n\n    const availability = {\n      baumwolle: false,\n      jersey: false\n    };\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const options = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      options.forEach(function (optionValue) {\n        const stoffart =\n          getCorrectName(optionValue);\n\n        if (stoffart) {\n          availability[normalize(stoffart)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available =\n          availability[normalize(stoffart)] === true;\n\n        const button = container.querySelector(\n          '[data-stoff-button=\"' + stoffart + '\"]'\n        );\n\n        const content = container.querySelector(\n          '[data-stoff-content=\"' + stoffart + '\"]'\n        );\n\n        if (button) {\n          button.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabs();\n  }\n\n  function loadProductAvailability() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      syncTabs();\n      return;\n    }\n\n    fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        syncTabs();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button =\n      event.target.closest(\"[data-stoff-button]\");\n\n    if (\n      !button ||\n      button.classList.contains(\"is-unavailable\")\n    ) {\n      return;\n    }\n\n    const container =\n      button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(\n        container,\n        button.dataset.stoffButton\n      );\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches(\n        'input[type=\"radio\"], select'\n      )\n    ) {\n      window.setTimeout(syncTabs, 50);\n    }\n  });\n\n  function initializeStoffTabs() {\n    loadProductAvailability();\n    window.setTimeout(syncTabs, 300);\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeStoffTabs\n    );\n  } else {\n    initializeStoffTabs();\n  }\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"dynamisches-moodbild\" data-moodbild\u003e\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .dynamisches-moodbild {\n    display: none;\n    margin: 25px 0;\n    text-align: center;\n  }\n\n  .dynamisches-moodbild.is-visible {\n    display: block;\n  }\n\n  .moodbild-link {\n    display: block;\n    text-decoration: none;\n    border-radius: 14px;\n    overflow: hidden;\n  }\n\n  .dynamisches-moodbild img {\n    display: block;\n    width: 100%;\n    height: auto;\n    margin: 0 auto;\n    border-radius: 14px;\n    transition: transform 0.25s ease;\n  }\n\n  .moodbild-link:hover img {\n    transform: scale(1.015);\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .dynamisches-moodbild img {\n      transition: none;\n    }\n\n    .moodbild-link:hover img {\n      transform: none;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const MARKER = \"#moodbild\";\n\n  const containers = document.querySelectorAll(\n    \"[data-moodbild]\"\n  );\n\n  if (!containers.length) return;\n\n  function cleanAlt(text) {\n    return String(text || \"\")\n      .replace(\/\\s*#Moodbild\\s*\/gi, \"\")\n      .trim();\n  }\n\n  function getImageUrl(image) {\n    if (image.currentSrc) {\n      return image.currentSrc;\n    }\n\n    const srcset =\n      image.getAttribute(\"srcset\") ||\n      image.getAttribute(\"data-srcset\");\n\n    if (srcset) {\n      const sources = srcset\n        .split(\",\")\n        .map(function (entry) {\n          return entry.trim().split(\/\\s+\/)[0];\n        })\n        .filter(Boolean);\n\n      if (sources.length) {\n        return sources[sources.length - 1];\n      }\n    }\n\n    return (\n      image.getAttribute(\"data-src\") ||\n      image.getAttribute(\"src\") ||\n      \"\"\n    );\n  }\n\n  function insertMoodbild(imageUrl, altText) {\n    if (!imageUrl) return false;\n\n    containers.forEach(function (container) {\n      if (\n        container.dataset.moodbildLoaded === \"true\"\n      ) {\n        return;\n      }\n\n      const link = document.createElement(\"a\");\n\n      link.href = \"https:\/\/www.bines-shop.com\";\n      link.target = \"_blank\";\n      link.rel = \"noopener noreferrer\";\n      link.className = \"moodbild-link\";\n\n      link.setAttribute(\n        \"aria-label\",\n        \"Bines Shop in einem neuen Fenster öffnen\"\n      );\n\n      const image = document.createElement(\"img\");\n\n      image.src = imageUrl;\n\n      image.alt =\n        cleanAlt(altText) ||\n        \"Moodbild zum Produkt\";\n\n      image.loading = \"lazy\";\n      image.decoding = \"async\";\n\n      link.appendChild(image);\n      container.replaceChildren(link);\n\n      container.classList.add(\"is-visible\");\n\n      container.dataset.moodbildLoaded = \"true\";\n    });\n\n    return true;\n  }\n\n  function findMoodbildInGallery() {\n    const images = Array.from(\n      document.querySelectorAll(\"img\")\n    ).filter(function (image) {\n      if (image.closest(\"[data-moodbild]\")) {\n        return false;\n      }\n\n      const searchableText = [\n        image.getAttribute(\"alt\"),\n        image.getAttribute(\"data-alt\"),\n        image.getAttribute(\"aria-label\")\n      ]\n        .filter(Boolean)\n        .join(\" \")\n        .toLowerCase();\n\n      return searchableText.includes(MARKER);\n    });\n\n    if (!images.length) {\n      return false;\n    }\n\n    const moodbild = images.find(function (image) {\n      return getImageUrl(image);\n    });\n\n    if (!moodbild) {\n      return false;\n    }\n\n    const altText =\n      moodbild.getAttribute(\"alt\") ||\n      moodbild.getAttribute(\"data-alt\") ||\n      moodbild.getAttribute(\"aria-label\") ||\n      \"\";\n\n    return insertMoodbild(\n      getImageUrl(moodbild),\n      altText\n    );\n  }\n\n  function findMoodbildInProductData() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return Promise.resolve(false);\n    }\n\n    return fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(function (product) {\n        const media = Array.isArray(product.media)\n          ? product.media\n          : [];\n\n        const moodbild = media.find(function (item) {\n          return String(item.alt || \"\")\n            .toLowerCase()\n            .includes(MARKER);\n        });\n\n        if (!moodbild) {\n          return false;\n        }\n\n        const imageUrl =\n          moodbild.preview_image \u0026\u0026\n          moodbild.preview_image.src\n            ? moodbild.preview_image.src\n            : moodbild.src;\n\n        return insertMoodbild(\n          imageUrl,\n          moodbild.alt\n        );\n      })\n      .catch(function () {\n        return false;\n      });\n  }\n\n  function initializeMoodbild() {\n    if (findMoodbildInGallery()) {\n      return;\n    }\n\n    findMoodbildInProductData()\n      .then(function (found) {\n        if (found) return;\n\n        const observer = new MutationObserver(\n          function () {\n            if (findMoodbildInGallery()) {\n              observer.disconnect();\n            }\n          }\n        );\n\n        observer.observe(\n          document.documentElement,\n          {\n            childList: true,\n            subtree: true,\n            attributes: true,\n            attributeFilter: [\n              \"alt\",\n              \"data-alt\",\n              \"src\",\n              \"data-src\",\n              \"srcset\",\n              \"data-srcset\"\n            ]\n          }\n        );\n\n        window.setTimeout(function () {\n          observer.disconnect();\n        }, 15000);\n      });\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeMoodbild\n    );\n  } else {\n    initializeMoodbild();\n  }\n})();\n\u003c\/script\u003e\n\u003c\/div\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle \/ 1 Meter","offer_id":53327328411915,"sku":"550000185","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 2 Meter","offer_id":53327328444683,"sku":"550000186","price":47.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 3 Meter","offer_id":53327328477451,"sku":"550000187","price":69.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 1 Meter","offer_id":53327328510219,"sku":"550000188","price":27.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 2 Meter","offer_id":53327328542987,"sku":"550000189","price":53.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 3 Meter","offer_id":53327328575755,"sku":"550000190","price":77.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-BlumenzauberMINIrot-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1787876799"},{"product_id":"blumenzauber-mini-turkis-stoff","title":"Blumenzauber MINI türkis – Stoff","description":"\u003c!-- PRODUKTBESCHREIBUNG MIT PARALLAX-LOGO --\u003e\n\n\u003csection class=\"bine-stoff-intro\"\u003e\n\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo-Stoffe-bunt.png?v=1787871207\" alt=\"Bine Brändle Stoffe Logo\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"bine-stoff-text\"\u003e\n\n    \u003ch2\u003eFarbenfrohe Stoffe mit Design von Bine Brändle\u003c\/h2\u003e\n\n    \u003cp class=\"bine-stoff-einleitung\"\u003e\n      Mach dein nächstes Nähprojekt zu etwas ganz Besonderem! Unsere\n      wunderschön bedruckten Stoffe mit einem liebevoll gestalteten Design\n      von \u003cstrong\u003eBine Brändle\u003c\/strong\u003e bringen Farbe, Freude und gute Laune\n      an die Nähmaschine.\n    \u003c\/p\u003e\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"Bine Brändle\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n    \u003cp\u003e\n      Du kannst dein Lieblingsdesign passend zu deinem Projekt als\n      \u003cstrong\u003eBaumwollgewebe\u003c\/strong\u003e oder \u003cstrong\u003eJersey\u003c\/strong\u003e auswählen.\n    \u003c\/p\u003e\n\n    \u003cdiv class=\"bine-stoffarten\"\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Das angenehm glatte und formstabile Baumwollgewebe eignet sich\n          wunderbar für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eHemden und Hosen\u003c\/li\u003e\n          \u003cli\u003eTaschen und Beutel\u003c\/li\u003e\n          \u003cli\u003eKissen und Wohnaccessoires\u003c\/li\u003e\n          \u003cli\u003eDekorationen\u003c\/li\u003e\n          \u003cli\u003ePatchwork-Projekte\u003c\/li\u003e\n          \u003cli\u003eSchultüten und kleine Geschenkideen\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eJersey\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Der weiche und elastische Jersey-Stoff ist besonders angenehm\n          auf der Haut und ideal für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eShirts und Kleider\u003c\/li\u003e\n          \u003cli\u003eHosen und Leggings\u003c\/li\u003e\n          \u003cli\u003eMützen und Halstücher\u003c\/li\u003e\n          \u003cli\u003eKinder- und Babykleidung\u003c\/li\u003e\n          \u003cli\u003eBequeme Lieblingsstücke für Groß und Klein\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"bine-stofflaengen\"\u003e\n      \u003ch3\u003eErhältliche Stofflängen\u003c\/h3\u003e\n\n      \u003cp\u003e\n        Du erhältst den Stoff fertig zugeschnitten in folgenden Längen:\n      \u003c\/p\u003e\n\n      \u003cdiv class=\"bine-laengen-auswahl\"\u003e\n        \u003cspan\u003e1 Meter\u003c\/span\u003e\n        \u003cspan\u003e2 Meter\u003c\/span\u003e\n        \u003cspan\u003e3 Meter\u003c\/span\u003e\n      \u003c\/div\u003e\n\n      \u003cp\u003e\n        Wähle einfach deine gewünschte Stoffart und anschließend die\n        passende Länge aus.\n      \u003c\/p\u003e\n\n      \u003cp class=\"bine-stoff-hinweis\"\u003e\n        \u003cstrong\u003eBitte beachte:\u003c\/strong\u003e Die ausgewählte Länge wird als\n        zusammenhängendes Stoffstück geliefert. Bei der Auswahl von\n        2 Metern erhältst du ein 2 Meter langes Stück, bei 3 Metern ein\n        zusammenhängendes 3-Meter-Stück.\n      \u003c\/p\u003e\n    \u003c\/div\u003e\n\n    \u003cp\u003e\n      Ob als farbenfrohes Kleidungsstück, liebevoll genähtes Geschenk oder\n      besonderer Blickfang für dein Zuhause – mit den Designs von\n      \u003cstrong\u003eBine Brändle\u003c\/strong\u003e wird jedes Nähprojekt herrlich bunt und\n      unverwechselbar.\n    \u003c\/p\u003e\n\n  \u003c\/div\u003e\n\n\u003c\/section\u003e\n\n\u003cstyle\u003e\n  .bine-stoff-intro {\n    margin: 20px 0 35px;\n    overflow: hidden;\n  }\n\n  .bine-logo-parallax {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    min-height: 150px;\n    margin-bottom: 20px;\n    padding: 20px;\n    \n    border-radius: 16px;\n    overflow: hidden;\n  }\n\n  .bine-stoff-logo {\n    display: block;\n    width: auto;\n    max-width: min(320px, 80%);\n    max-height: 130px;\n    object-fit: contain;\n    will-change: transform;\n    transform: translate3d(0, 0, 0);\n  }\n\n  .bine-stoff-text h2 {\n    margin-top: 0;\n    margin-bottom: 15px;\n    color: #08868a;\n    font-size: clamp(24px, 4vw, 34px);\n    line-height: 1.2;\n  }\n\n  .bine-stoff-text h3 {\n    margin-top: 0;\n    margin-bottom: 10px;\n  }\n\n  .bine-stoff-einleitung {\n    font-size: 1.08em;\n    line-height: 1.65;\n  }\n\n  .bine-stoffarten {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 16px;\n    margin: 25px 0;\n  }\n\n  .bine-stoffart {\n    padding: 20px;\n    background: #f7f7f7;\n    border-top: 4px solid #08868a;\n    border-radius: 10px;\n  }\n\n  .bine-stoffart ul {\n    margin-bottom: 0;\n    padding-left: 20px;\n  }\n\n  .bine-stoffart li {\n    margin-bottom: 5px;\n  }\n\n  .bine-stofflaengen {\n    margin: 25px 0;\n    padding: 22px;\n    background: rgba(8, 134, 138, 0.08);\n    border-radius: 12px;\n  }\n\n  .bine-laengen-auswahl {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin: 16px 0;\n  }\n\n  .bine-laengen-auswahl span {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 90px;\n    padding: 9px 15px;\n    background: #08868a;\n    color: #ffffff;\n    border-radius: 20px;\n    font-weight: 700;\n  }\n\n  .bine-stoff-hinweis {\n    margin-bottom: 0;\n    padding: 14px;\n    background: #ffffff;\n    border-left: 4px solid #08868a;\n    border-radius: 5px;\n  }\n\n  @media screen and (max-width: 650px) {\n    .bine-stoffarten {\n      grid-template-columns: 1fr;\n    }\n\n    .bine-logo-parallax {\n      min-height: 120px;\n    }\n\n    .bine-stoff-logo {\n      max-width: 75%;\n      max-height: 100px;\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .bine-stoff-logo {\n      transform: none !important;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const parallaxElements = document.querySelectorAll(\n    \"[data-bine-parallax] .bine-stoff-logo\"\n  );\n\n  if (\n    !parallaxElements.length ||\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  ) {\n    return;\n  }\n\n  let animationFrameRequested = false;\n\n  function updateParallax() {\n    parallaxElements.forEach(function (logo) {\n      const container = logo.closest(\"[data-bine-parallax]\");\n      const position = container.getBoundingClientRect();\n      const windowHeight = window.innerHeight;\n\n      if (\n        position.bottom \u003e= 0 \u0026\u0026\n        position.top \u003c= windowHeight\n      ) {\n        const containerCenter =\n          position.top + position.height \/ 2;\n\n        const screenCenter = windowHeight \/ 2;\n\n        const offset =\n          (containerCenter - screenCenter) * -0.08;\n\n        const limitedOffset = Math.max(\n          -22,\n          Math.min(22, offset)\n        );\n\n        logo.style.transform =\n          \"translate3d(0,\" + limitedOffset + \"px,0)\";\n      }\n    });\n\n    animationFrameRequested = false;\n  }\n\n  function requestParallaxUpdate() {\n    if (!animationFrameRequested) {\n      window.requestAnimationFrame(updateParallax);\n      animationFrameRequested = true;\n    }\n  }\n\n  window.addEventListener(\n    \"scroll\",\n    requestParallaxUpdate,\n    { passive: true }\n  );\n\n  window.addEventListener(\n    \"resize\",\n    requestParallaxUpdate\n  );\n\n  requestParallaxUpdate();\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif02?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eBaumwolle\u003c\/span\u003e\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif01?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eJersey\u003c\/span\u003e\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n   \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e \u003cp\u003e 100 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Rohgewebe aus 100 % Baumwolle,\u003cbr\u003e hergestellt aus Materialien, die gemäß OEKO-TEX® STANDARD 100 zertifiziert sind. \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 2 cm\u003cbr\u003e\u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 150–153 g\/m² \u003c\/p\u003e \u003chr\u003e \n\u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n\n   \u003ch3\u003eJersey\u003c\/h3\u003e \u003cp\u003e \u003cstrong\u003eZusammensetzung:\u003c\/strong\u003e\u003cbr\u003e 92 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche\u003cbr\u003e 8 % Elasthan \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Maschenwaren aus Baumwollgarnen mit Zusatz von Elastangarnen (EL-Anteil bis zu 8 %).\u003cbr\u003e Ausschließlich hergestellt unter Verwendung von Komponenten und Veredelungsverfahren, die gemäß OEKO-TEX® STANDARD 100 vorzertifiziert sind. \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 3 cm\u003cbr\u003e \u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 200–210 g\/m² \u003c\/p\u003e\n\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    flex: 1;\n    padding: 12px 20px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-gif {\n    width: 32px;\n    height: 32px;\n    object-fit: contain;\n    flex-shrink: 0;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content h3 {\n    margin-top: 0;\n  }\n\n  .stoff-tab-content h4 {\n    margin-top: 20px;\n    margin-bottom: 8px;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tab-button {\n      padding: 11px 8px;\n      gap: 6px;\n    }\n\n    .stoff-tab-gif {\n      width: 28px;\n      height: 28px;\n    }\n\n    .stoff-tab-content {\n      padding: 16px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\"Baumwolle\", \"Jersey\"];\n\n  function normalize(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getCorrectName(value) {\n    const wanted = normalize(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalize(stoffart) === wanted;\n    }) || null;\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalize(element.dataset.stoffButton) === normalize(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalize(correctName);\n\n    container\n      .querySelectorAll(\"[data-stoff-button]\")\n      .forEach(function (button) {\n        const active =\n          !button.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(button.dataset.stoffButton) === wanted;\n\n        button.classList.toggle(\"is-active\", active);\n        button.setAttribute(\n          \"aria-selected\",\n          active ? \"true\" : \"false\"\n        );\n      });\n\n    container\n      .querySelectorAll(\"[data-stoff-content]\")\n      .forEach(function (content) {\n        const active =\n          !content.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(content.dataset.stoffContent) === wanted;\n\n        content.classList.toggle(\"is-active\", active);\n        content.hidden = !active;\n      });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(\n        container,\n        firstButton.dataset.stoffButton\n      );\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section =\n      container.closest(\".shopify-section\") || document;\n\n    const selectedFields = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const field of selectedFields) {\n      const name = getCorrectName(field.value);\n\n      if (name) {\n        return name;\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabs() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart =\n        getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabs();\n      return;\n    }\n\n    const availability = {\n      baumwolle: false,\n      jersey: false\n    };\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const options = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      options.forEach(function (optionValue) {\n        const stoffart =\n          getCorrectName(optionValue);\n\n        if (stoffart) {\n          availability[normalize(stoffart)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available =\n          availability[normalize(stoffart)] === true;\n\n        const button = container.querySelector(\n          '[data-stoff-button=\"' + stoffart + '\"]'\n        );\n\n        const content = container.querySelector(\n          '[data-stoff-content=\"' + stoffart + '\"]'\n        );\n\n        if (button) {\n          button.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabs();\n  }\n\n  function loadProductAvailability() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      syncTabs();\n      return;\n    }\n\n    fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        syncTabs();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button =\n      event.target.closest(\"[data-stoff-button]\");\n\n    if (\n      !button ||\n      button.classList.contains(\"is-unavailable\")\n    ) {\n      return;\n    }\n\n    const container =\n      button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(\n        container,\n        button.dataset.stoffButton\n      );\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches(\n        'input[type=\"radio\"], select'\n      )\n    ) {\n      window.setTimeout(syncTabs, 50);\n    }\n  });\n\n  function initializeStoffTabs() {\n    loadProductAvailability();\n    window.setTimeout(syncTabs, 300);\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeStoffTabs\n    );\n  } else {\n    initializeStoffTabs();\n  }\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"dynamisches-moodbild\" data-moodbild\u003e\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .dynamisches-moodbild {\n    display: none;\n    margin: 25px 0;\n    text-align: center;\n  }\n\n  .dynamisches-moodbild.is-visible {\n    display: block;\n  }\n\n  .moodbild-link {\n    display: block;\n    text-decoration: none;\n    border-radius: 14px;\n    overflow: hidden;\n  }\n\n  .dynamisches-moodbild img {\n    display: block;\n    width: 100%;\n    height: auto;\n    margin: 0 auto;\n    border-radius: 14px;\n    transition: transform 0.25s ease;\n  }\n\n  .moodbild-link:hover img {\n    transform: scale(1.015);\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .dynamisches-moodbild img {\n      transition: none;\n    }\n\n    .moodbild-link:hover img {\n      transform: none;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const MARKER = \"#moodbild\";\n\n  const containers = document.querySelectorAll(\n    \"[data-moodbild]\"\n  );\n\n  if (!containers.length) return;\n\n  function cleanAlt(text) {\n    return String(text || \"\")\n      .replace(\/\\s*#Moodbild\\s*\/gi, \"\")\n      .trim();\n  }\n\n  function getImageUrl(image) {\n    if (image.currentSrc) {\n      return image.currentSrc;\n    }\n\n    const srcset =\n      image.getAttribute(\"srcset\") ||\n      image.getAttribute(\"data-srcset\");\n\n    if (srcset) {\n      const sources = srcset\n        .split(\",\")\n        .map(function (entry) {\n          return entry.trim().split(\/\\s+\/)[0];\n        })\n        .filter(Boolean);\n\n      if (sources.length) {\n        return sources[sources.length - 1];\n      }\n    }\n\n    return (\n      image.getAttribute(\"data-src\") ||\n      image.getAttribute(\"src\") ||\n      \"\"\n    );\n  }\n\n  function insertMoodbild(imageUrl, altText) {\n    if (!imageUrl) return false;\n\n    containers.forEach(function (container) {\n      if (\n        container.dataset.moodbildLoaded === \"true\"\n      ) {\n        return;\n      }\n\n      const link = document.createElement(\"a\");\n\n      link.href = \"https:\/\/www.bines-shop.com\";\n      link.target = \"_blank\";\n      link.rel = \"noopener noreferrer\";\n      link.className = \"moodbild-link\";\n\n      link.setAttribute(\n        \"aria-label\",\n        \"Bines Shop in einem neuen Fenster öffnen\"\n      );\n\n      const image = document.createElement(\"img\");\n\n      image.src = imageUrl;\n\n      image.alt =\n        cleanAlt(altText) ||\n        \"Moodbild zum Produkt\";\n\n      image.loading = \"lazy\";\n      image.decoding = \"async\";\n\n      link.appendChild(image);\n      container.replaceChildren(link);\n\n      container.classList.add(\"is-visible\");\n\n      container.dataset.moodbildLoaded = \"true\";\n    });\n\n    return true;\n  }\n\n  function findMoodbildInGallery() {\n    const images = Array.from(\n      document.querySelectorAll(\"img\")\n    ).filter(function (image) {\n      if (image.closest(\"[data-moodbild]\")) {\n        return false;\n      }\n\n      const searchableText = [\n        image.getAttribute(\"alt\"),\n        image.getAttribute(\"data-alt\"),\n        image.getAttribute(\"aria-label\")\n      ]\n        .filter(Boolean)\n        .join(\" \")\n        .toLowerCase();\n\n      return searchableText.includes(MARKER);\n    });\n\n    if (!images.length) {\n      return false;\n    }\n\n    const moodbild = images.find(function (image) {\n      return getImageUrl(image);\n    });\n\n    if (!moodbild) {\n      return false;\n    }\n\n    const altText =\n      moodbild.getAttribute(\"alt\") ||\n      moodbild.getAttribute(\"data-alt\") ||\n      moodbild.getAttribute(\"aria-label\") ||\n      \"\";\n\n    return insertMoodbild(\n      getImageUrl(moodbild),\n      altText\n    );\n  }\n\n  function findMoodbildInProductData() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return Promise.resolve(false);\n    }\n\n    return fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(function (product) {\n        const media = Array.isArray(product.media)\n          ? product.media\n          : [];\n\n        const moodbild = media.find(function (item) {\n          return String(item.alt || \"\")\n            .toLowerCase()\n            .includes(MARKER);\n        });\n\n        if (!moodbild) {\n          return false;\n        }\n\n        const imageUrl =\n          moodbild.preview_image \u0026\u0026\n          moodbild.preview_image.src\n            ? moodbild.preview_image.src\n            : moodbild.src;\n\n        return insertMoodbild(\n          imageUrl,\n          moodbild.alt\n        );\n      })\n      .catch(function () {\n        return false;\n      });\n  }\n\n  function initializeMoodbild() {\n    if (findMoodbildInGallery()) {\n      return;\n    }\n\n    findMoodbildInProductData()\n      .then(function (found) {\n        if (found) return;\n\n        const observer = new MutationObserver(\n          function () {\n            if (findMoodbildInGallery()) {\n              observer.disconnect();\n            }\n          }\n        );\n\n        observer.observe(\n          document.documentElement,\n          {\n            childList: true,\n            subtree: true,\n            attributes: true,\n            attributeFilter: [\n              \"alt\",\n              \"data-alt\",\n              \"src\",\n              \"data-src\",\n              \"srcset\",\n              \"data-srcset\"\n            ]\n          }\n        );\n\n        window.setTimeout(function () {\n          observer.disconnect();\n        }, 15000);\n      });\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeMoodbild\n    );\n  } else {\n    initializeMoodbild();\n  }\n})();\n\u003c\/script\u003e\n\u003c\/div\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle \/ 1 Meter","offer_id":53327330017547,"sku":"550000179","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 2 Meter","offer_id":53327330050315,"sku":"550000180","price":47.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 3 Meter","offer_id":53327330083083,"sku":"550000181","price":69.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 1 Meter","offer_id":53327330115851,"sku":"550000182","price":27.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 2 Meter","offer_id":53327330148619,"sku":"550000183","price":53.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 3 Meter","offer_id":53327330181387,"sku":"550000184","price":77.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-BlumenzauberMINIturkis-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1787876923"},{"product_id":"boho-borduren-blau-2-stoff","title":"Boho-Bordüren blau – Stoff","description":"\u003c!-- PRODUKTBESCHREIBUNG MIT PARALLAX-LOGO --\u003e\n\n\u003csection class=\"bine-stoff-intro\"\u003e\n\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo-Stoffe-bunt.png?v=1787871207\" alt=\"Bine Brändle Stoffe Logo\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"bine-stoff-text\"\u003e\n\n    \u003ch2\u003eFarbenfrohe Stoffe mit Design von Bine Brändle\u003c\/h2\u003e\n\n    \u003cp class=\"bine-stoff-einleitung\"\u003e\n      Mach dein nächstes Nähprojekt zu etwas ganz Besonderem! Unsere\n      wunderschön bedruckten Stoffe mit einem liebevoll gestalteten Design\n      von \u003cstrong\u003eBine Brändle\u003c\/strong\u003e bringen Farbe, Freude und gute Laune\n      an die Nähmaschine.\n    \u003c\/p\u003e\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"Bine Brändle\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n    \u003cp\u003e\n      Du kannst dein Lieblingsdesign passend zu deinem Projekt als\n      \u003cstrong\u003eBaumwollgewebe\u003c\/strong\u003e oder \u003cstrong\u003eJersey\u003c\/strong\u003e auswählen.\n    \u003c\/p\u003e\n\n    \u003cdiv class=\"bine-stoffarten\"\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Das angenehm glatte und formstabile Baumwollgewebe eignet sich\n          wunderbar für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eHemden und Hosen\u003c\/li\u003e\n          \u003cli\u003eTaschen und Beutel\u003c\/li\u003e\n          \u003cli\u003eKissen und Wohnaccessoires\u003c\/li\u003e\n          \u003cli\u003eDekorationen\u003c\/li\u003e\n          \u003cli\u003ePatchwork-Projekte\u003c\/li\u003e\n          \u003cli\u003eSchultüten und kleine Geschenkideen\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eJersey\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Der weiche und elastische Jersey-Stoff ist besonders angenehm\n          auf der Haut und ideal für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eShirts und Kleider\u003c\/li\u003e\n          \u003cli\u003eHosen und Leggings\u003c\/li\u003e\n          \u003cli\u003eMützen und Halstücher\u003c\/li\u003e\n          \u003cli\u003eKinder- und Babykleidung\u003c\/li\u003e\n          \u003cli\u003eBequeme Lieblingsstücke für Groß und Klein\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"bine-stofflaengen\"\u003e\n      \u003ch3\u003eErhältliche Stofflängen\u003c\/h3\u003e\n\n      \u003cp\u003e\n        Du erhältst den Stoff fertig zugeschnitten in folgenden Längen:\n      \u003c\/p\u003e\n\n      \u003cdiv class=\"bine-laengen-auswahl\"\u003e\n        \u003cspan\u003e1 Meter\u003c\/span\u003e\n        \u003cspan\u003e2 Meter\u003c\/span\u003e\n        \u003cspan\u003e3 Meter\u003c\/span\u003e\n      \u003c\/div\u003e\n\n      \u003cp\u003e\n        Wähle einfach deine gewünschte Stoffart und anschließend die\n        passende Länge aus.\n      \u003c\/p\u003e\n\n      \u003cp class=\"bine-stoff-hinweis\"\u003e\n        \u003cstrong\u003eBitte beachte:\u003c\/strong\u003e Die ausgewählte Länge wird als\n        zusammenhängendes Stoffstück geliefert. Bei der Auswahl von\n        2 Metern erhältst du ein 2 Meter langes Stück, bei 3 Metern ein\n        zusammenhängendes 3-Meter-Stück.\n      \u003c\/p\u003e\n    \u003c\/div\u003e\n\n    \u003cp\u003e\n      Ob als farbenfrohes Kleidungsstück, liebevoll genähtes Geschenk oder\n      besonderer Blickfang für dein Zuhause – mit den Designs von\n      \u003cstrong\u003eBine Brändle\u003c\/strong\u003e wird jedes Nähprojekt herrlich bunt und\n      unverwechselbar.\n    \u003c\/p\u003e\n\n  \u003c\/div\u003e\n\n\u003c\/section\u003e\n\n\u003cstyle\u003e\n  .bine-stoff-intro {\n    margin: 20px 0 35px;\n    overflow: hidden;\n  }\n\n  .bine-logo-parallax {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    min-height: 150px;\n    margin-bottom: 20px;\n    padding: 20px;\n    \n    border-radius: 16px;\n    overflow: hidden;\n  }\n\n  .bine-stoff-logo {\n    display: block;\n    width: auto;\n    max-width: min(320px, 80%);\n    max-height: 130px;\n    object-fit: contain;\n    will-change: transform;\n    transform: translate3d(0, 0, 0);\n  }\n\n  .bine-stoff-text h2 {\n    margin-top: 0;\n    margin-bottom: 15px;\n    color: #08868a;\n    font-size: clamp(24px, 4vw, 34px);\n    line-height: 1.2;\n  }\n\n  .bine-stoff-text h3 {\n    margin-top: 0;\n    margin-bottom: 10px;\n  }\n\n  .bine-stoff-einleitung {\n    font-size: 1.08em;\n    line-height: 1.65;\n  }\n\n  .bine-stoffarten {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 16px;\n    margin: 25px 0;\n  }\n\n  .bine-stoffart {\n    padding: 20px;\n    background: #f7f7f7;\n    border-top: 4px solid #08868a;\n    border-radius: 10px;\n  }\n\n  .bine-stoffart ul {\n    margin-bottom: 0;\n    padding-left: 20px;\n  }\n\n  .bine-stoffart li {\n    margin-bottom: 5px;\n  }\n\n  .bine-stofflaengen {\n    margin: 25px 0;\n    padding: 22px;\n    background: rgba(8, 134, 138, 0.08);\n    border-radius: 12px;\n  }\n\n  .bine-laengen-auswahl {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin: 16px 0;\n  }\n\n  .bine-laengen-auswahl span {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 90px;\n    padding: 9px 15px;\n    background: #08868a;\n    color: #ffffff;\n    border-radius: 20px;\n    font-weight: 700;\n  }\n\n  .bine-stoff-hinweis {\n    margin-bottom: 0;\n    padding: 14px;\n    background: #ffffff;\n    border-left: 4px solid #08868a;\n    border-radius: 5px;\n  }\n\n  @media screen and (max-width: 650px) {\n    .bine-stoffarten {\n      grid-template-columns: 1fr;\n    }\n\n    .bine-logo-parallax {\n      min-height: 120px;\n    }\n\n    .bine-stoff-logo {\n      max-width: 75%;\n      max-height: 100px;\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .bine-stoff-logo {\n      transform: none !important;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const parallaxElements = document.querySelectorAll(\n    \"[data-bine-parallax] .bine-stoff-logo\"\n  );\n\n  if (\n    !parallaxElements.length ||\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  ) {\n    return;\n  }\n\n  let animationFrameRequested = false;\n\n  function updateParallax() {\n    parallaxElements.forEach(function (logo) {\n      const container = logo.closest(\"[data-bine-parallax]\");\n      const position = container.getBoundingClientRect();\n      const windowHeight = window.innerHeight;\n\n      if (\n        position.bottom \u003e= 0 \u0026\u0026\n        position.top \u003c= windowHeight\n      ) {\n        const containerCenter =\n          position.top + position.height \/ 2;\n\n        const screenCenter = windowHeight \/ 2;\n\n        const offset =\n          (containerCenter - screenCenter) * -0.08;\n\n        const limitedOffset = Math.max(\n          -22,\n          Math.min(22, offset)\n        );\n\n        logo.style.transform =\n          \"translate3d(0,\" + limitedOffset + \"px,0)\";\n      }\n    });\n\n    animationFrameRequested = false;\n  }\n\n  function requestParallaxUpdate() {\n    if (!animationFrameRequested) {\n      window.requestAnimationFrame(updateParallax);\n      animationFrameRequested = true;\n    }\n  }\n\n  window.addEventListener(\n    \"scroll\",\n    requestParallaxUpdate,\n    { passive: true }\n  );\n\n  window.addEventListener(\n    \"resize\",\n    requestParallaxUpdate\n  );\n\n  requestParallaxUpdate();\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif02?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eBaumwolle\u003c\/span\u003e\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif01?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eJersey\u003c\/span\u003e\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n   \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e \u003cp\u003e 100 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Rohgewebe aus 100 % Baumwolle,\u003cbr\u003e hergestellt aus Materialien, die gemäß OEKO-TEX® STANDARD 100 zertifiziert sind. \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 2 cm\u003cbr\u003e\u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 150–153 g\/m² \u003c\/p\u003e \u003chr\u003e \n\u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n\n   \u003ch3\u003eJersey\u003c\/h3\u003e \u003cp\u003e \u003cstrong\u003eZusammensetzung:\u003c\/strong\u003e\u003cbr\u003e 92 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche\u003cbr\u003e 8 % Elasthan \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Maschenwaren aus Baumwollgarnen mit Zusatz von Elastangarnen (EL-Anteil bis zu 8 %).\u003cbr\u003e Ausschließlich hergestellt unter Verwendung von Komponenten und Veredelungsverfahren, die gemäß OEKO-TEX® STANDARD 100 vorzertifiziert sind. \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 3 cm\u003cbr\u003e \u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 200–210 g\/m² \u003c\/p\u003e\n\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    flex: 1;\n    padding: 12px 20px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-gif {\n    width: 32px;\n    height: 32px;\n    object-fit: contain;\n    flex-shrink: 0;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content h3 {\n    margin-top: 0;\n  }\n\n  .stoff-tab-content h4 {\n    margin-top: 20px;\n    margin-bottom: 8px;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tab-button {\n      padding: 11px 8px;\n      gap: 6px;\n    }\n\n    .stoff-tab-gif {\n      width: 28px;\n      height: 28px;\n    }\n\n    .stoff-tab-content {\n      padding: 16px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\"Baumwolle\", \"Jersey\"];\n\n  function normalize(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getCorrectName(value) {\n    const wanted = normalize(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalize(stoffart) === wanted;\n    }) || null;\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalize(element.dataset.stoffButton) === normalize(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalize(correctName);\n\n    container\n      .querySelectorAll(\"[data-stoff-button]\")\n      .forEach(function (button) {\n        const active =\n          !button.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(button.dataset.stoffButton) === wanted;\n\n        button.classList.toggle(\"is-active\", active);\n        button.setAttribute(\n          \"aria-selected\",\n          active ? \"true\" : \"false\"\n        );\n      });\n\n    container\n      .querySelectorAll(\"[data-stoff-content]\")\n      .forEach(function (content) {\n        const active =\n          !content.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(content.dataset.stoffContent) === wanted;\n\n        content.classList.toggle(\"is-active\", active);\n        content.hidden = !active;\n      });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(\n        container,\n        firstButton.dataset.stoffButton\n      );\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section =\n      container.closest(\".shopify-section\") || document;\n\n    const selectedFields = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const field of selectedFields) {\n      const name = getCorrectName(field.value);\n\n      if (name) {\n        return name;\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabs() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart =\n        getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabs();\n      return;\n    }\n\n    const availability = {\n      baumwolle: false,\n      jersey: false\n    };\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const options = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      options.forEach(function (optionValue) {\n        const stoffart =\n          getCorrectName(optionValue);\n\n        if (stoffart) {\n          availability[normalize(stoffart)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available =\n          availability[normalize(stoffart)] === true;\n\n        const button = container.querySelector(\n          '[data-stoff-button=\"' + stoffart + '\"]'\n        );\n\n        const content = container.querySelector(\n          '[data-stoff-content=\"' + stoffart + '\"]'\n        );\n\n        if (button) {\n          button.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabs();\n  }\n\n  function loadProductAvailability() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      syncTabs();\n      return;\n    }\n\n    fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        syncTabs();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button =\n      event.target.closest(\"[data-stoff-button]\");\n\n    if (\n      !button ||\n      button.classList.contains(\"is-unavailable\")\n    ) {\n      return;\n    }\n\n    const container =\n      button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(\n        container,\n        button.dataset.stoffButton\n      );\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches(\n        'input[type=\"radio\"], select'\n      )\n    ) {\n      window.setTimeout(syncTabs, 50);\n    }\n  });\n\n  function initializeStoffTabs() {\n    loadProductAvailability();\n    window.setTimeout(syncTabs, 300);\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeStoffTabs\n    );\n  } else {\n    initializeStoffTabs();\n  }\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"dynamisches-moodbild\" data-moodbild\u003e\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .dynamisches-moodbild {\n    display: none;\n    margin: 25px 0;\n    text-align: center;\n  }\n\n  .dynamisches-moodbild.is-visible {\n    display: block;\n  }\n\n  .moodbild-link {\n    display: block;\n    text-decoration: none;\n    border-radius: 14px;\n    overflow: hidden;\n  }\n\n  .dynamisches-moodbild img {\n    display: block;\n    width: 100%;\n    height: auto;\n    margin: 0 auto;\n    border-radius: 14px;\n    transition: transform 0.25s ease;\n  }\n\n  .moodbild-link:hover img {\n    transform: scale(1.015);\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .dynamisches-moodbild img {\n      transition: none;\n    }\n\n    .moodbild-link:hover img {\n      transform: none;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const MARKER = \"#moodbild\";\n\n  const containers = document.querySelectorAll(\n    \"[data-moodbild]\"\n  );\n\n  if (!containers.length) return;\n\n  function cleanAlt(text) {\n    return String(text || \"\")\n      .replace(\/\\s*#Moodbild\\s*\/gi, \"\")\n      .trim();\n  }\n\n  function getImageUrl(image) {\n    if (image.currentSrc) {\n      return image.currentSrc;\n    }\n\n    const srcset =\n      image.getAttribute(\"srcset\") ||\n      image.getAttribute(\"data-srcset\");\n\n    if (srcset) {\n      const sources = srcset\n        .split(\",\")\n        .map(function (entry) {\n          return entry.trim().split(\/\\s+\/)[0];\n        })\n        .filter(Boolean);\n\n      if (sources.length) {\n        return sources[sources.length - 1];\n      }\n    }\n\n    return (\n      image.getAttribute(\"data-src\") ||\n      image.getAttribute(\"src\") ||\n      \"\"\n    );\n  }\n\n  function insertMoodbild(imageUrl, altText) {\n    if (!imageUrl) return false;\n\n    containers.forEach(function (container) {\n      if (\n        container.dataset.moodbildLoaded === \"true\"\n      ) {\n        return;\n      }\n\n      const link = document.createElement(\"a\");\n\n      link.href = \"https:\/\/www.bines-shop.com\";\n      link.target = \"_blank\";\n      link.rel = \"noopener noreferrer\";\n      link.className = \"moodbild-link\";\n\n      link.setAttribute(\n        \"aria-label\",\n        \"Bines Shop in einem neuen Fenster öffnen\"\n      );\n\n      const image = document.createElement(\"img\");\n\n      image.src = imageUrl;\n\n      image.alt =\n        cleanAlt(altText) ||\n        \"Moodbild zum Produkt\";\n\n      image.loading = \"lazy\";\n      image.decoding = \"async\";\n\n      link.appendChild(image);\n      container.replaceChildren(link);\n\n      container.classList.add(\"is-visible\");\n\n      container.dataset.moodbildLoaded = \"true\";\n    });\n\n    return true;\n  }\n\n  function findMoodbildInGallery() {\n    const images = Array.from(\n      document.querySelectorAll(\"img\")\n    ).filter(function (image) {\n      if (image.closest(\"[data-moodbild]\")) {\n        return false;\n      }\n\n      const searchableText = [\n        image.getAttribute(\"alt\"),\n        image.getAttribute(\"data-alt\"),\n        image.getAttribute(\"aria-label\")\n      ]\n        .filter(Boolean)\n        .join(\" \")\n        .toLowerCase();\n\n      return searchableText.includes(MARKER);\n    });\n\n    if (!images.length) {\n      return false;\n    }\n\n    const moodbild = images.find(function (image) {\n      return getImageUrl(image);\n    });\n\n    if (!moodbild) {\n      return false;\n    }\n\n    const altText =\n      moodbild.getAttribute(\"alt\") ||\n      moodbild.getAttribute(\"data-alt\") ||\n      moodbild.getAttribute(\"aria-label\") ||\n      \"\";\n\n    return insertMoodbild(\n      getImageUrl(moodbild),\n      altText\n    );\n  }\n\n  function findMoodbildInProductData() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return Promise.resolve(false);\n    }\n\n    return fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(function (product) {\n        const media = Array.isArray(product.media)\n          ? product.media\n          : [];\n\n        const moodbild = media.find(function (item) {\n          return String(item.alt || \"\")\n            .toLowerCase()\n            .includes(MARKER);\n        });\n\n        if (!moodbild) {\n          return false;\n        }\n\n        const imageUrl =\n          moodbild.preview_image \u0026\u0026\n          moodbild.preview_image.src\n            ? moodbild.preview_image.src\n            : moodbild.src;\n\n        return insertMoodbild(\n          imageUrl,\n          moodbild.alt\n        );\n      })\n      .catch(function () {\n        return false;\n      });\n  }\n\n  function initializeMoodbild() {\n    if (findMoodbildInGallery()) {\n      return;\n    }\n\n    findMoodbildInProductData()\n      .then(function (found) {\n        if (found) return;\n\n        const observer = new MutationObserver(\n          function () {\n            if (findMoodbildInGallery()) {\n              observer.disconnect();\n            }\n          }\n        );\n\n        observer.observe(\n          document.documentElement,\n          {\n            childList: true,\n            subtree: true,\n            attributes: true,\n            attributeFilter: [\n              \"alt\",\n              \"data-alt\",\n              \"src\",\n              \"data-src\",\n              \"srcset\",\n              \"data-srcset\"\n            ]\n          }\n        );\n\n        window.setTimeout(function () {\n          observer.disconnect();\n        }, 15000);\n      });\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeMoodbild\n    );\n  } else {\n    initializeMoodbild();\n  }\n})();\n\u003c\/script\u003e\n\u003c\/div\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle \/ 1 Meter","offer_id":53327333097739,"sku":"550000173","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 2 Meter","offer_id":53327333130507,"sku":"550000174","price":47.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 3 Meter","offer_id":53327333163275,"sku":"550000175","price":69.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 1 Meter","offer_id":53327333196043,"sku":"550000176","price":27.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 2 Meter","offer_id":53327333228811,"sku":"550000177","price":53.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 3 Meter","offer_id":53327333261579,"sku":"550000178","price":77.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-Boho-Bordurenblau-Stoff-Baumwolle-Jersey-nahen-bunt-Muster-01.jpg?v=1788121744"},{"product_id":"boho-borduren-orange-stoff","title":"Boho-Bordüren orange – Stoff","description":"\u003c!-- PRODUKTBESCHREIBUNG MIT PARALLAX-LOGO --\u003e\n\n\u003csection class=\"bine-stoff-intro\"\u003e\n\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo-Stoffe-bunt.png?v=1787871207\" alt=\"Bine Brändle Stoffe Logo\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"bine-stoff-text\"\u003e\n\n    \u003ch2\u003eFarbenfrohe Stoffe mit Design von Bine Brändle\u003c\/h2\u003e\n\n    \u003cp class=\"bine-stoff-einleitung\"\u003e\n      Mach dein nächstes Nähprojekt zu etwas ganz Besonderem! Unsere\n      wunderschön bedruckten Stoffe mit einem liebevoll gestalteten Design\n      von \u003cstrong\u003eBine Brändle\u003c\/strong\u003e bringen Farbe, Freude und gute Laune\n      an die Nähmaschine.\n    \u003c\/p\u003e\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"Bine Brändle\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n    \u003cp\u003e\n      Du kannst dein Lieblingsdesign passend zu deinem Projekt als\n      \u003cstrong\u003eBaumwollgewebe\u003c\/strong\u003e oder \u003cstrong\u003eJersey\u003c\/strong\u003e auswählen.\n    \u003c\/p\u003e\n\n    \u003cdiv class=\"bine-stoffarten\"\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Das angenehm glatte und formstabile Baumwollgewebe eignet sich\n          wunderbar für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eHemden und Hosen\u003c\/li\u003e\n          \u003cli\u003eTaschen und Beutel\u003c\/li\u003e\n          \u003cli\u003eKissen und Wohnaccessoires\u003c\/li\u003e\n          \u003cli\u003eDekorationen\u003c\/li\u003e\n          \u003cli\u003ePatchwork-Projekte\u003c\/li\u003e\n          \u003cli\u003eSchultüten und kleine Geschenkideen\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eJersey\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Der weiche und elastische Jersey-Stoff ist besonders angenehm\n          auf der Haut und ideal für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eShirts und Kleider\u003c\/li\u003e\n          \u003cli\u003eHosen und Leggings\u003c\/li\u003e\n          \u003cli\u003eMützen und Halstücher\u003c\/li\u003e\n          \u003cli\u003eKinder- und Babykleidung\u003c\/li\u003e\n          \u003cli\u003eBequeme Lieblingsstücke für Groß und Klein\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"bine-stofflaengen\"\u003e\n      \u003ch3\u003eErhältliche Stofflängen\u003c\/h3\u003e\n\n      \u003cp\u003e\n        Du erhältst den Stoff fertig zugeschnitten in folgenden Längen:\n      \u003c\/p\u003e\n\n      \u003cdiv class=\"bine-laengen-auswahl\"\u003e\n        \u003cspan\u003e1 Meter\u003c\/span\u003e\n        \u003cspan\u003e2 Meter\u003c\/span\u003e\n        \u003cspan\u003e3 Meter\u003c\/span\u003e\n      \u003c\/div\u003e\n\n      \u003cp\u003e\n        Wähle einfach deine gewünschte Stoffart und anschließend die\n        passende Länge aus.\n      \u003c\/p\u003e\n\n      \u003cp class=\"bine-stoff-hinweis\"\u003e\n        \u003cstrong\u003eBitte beachte:\u003c\/strong\u003e Die ausgewählte Länge wird als\n        zusammenhängendes Stoffstück geliefert. Bei der Auswahl von\n        2 Metern erhältst du ein 2 Meter langes Stück, bei 3 Metern ein\n        zusammenhängendes 3-Meter-Stück.\n      \u003c\/p\u003e\n    \u003c\/div\u003e\n\n    \u003cp\u003e\n      Ob als farbenfrohes Kleidungsstück, liebevoll genähtes Geschenk oder\n      besonderer Blickfang für dein Zuhause – mit den Designs von\n      \u003cstrong\u003eBine Brändle\u003c\/strong\u003e wird jedes Nähprojekt herrlich bunt und\n      unverwechselbar.\n    \u003c\/p\u003e\n\n  \u003c\/div\u003e\n\n\u003c\/section\u003e\n\n\u003cstyle\u003e\n  .bine-stoff-intro {\n    margin: 20px 0 35px;\n    overflow: hidden;\n  }\n\n  .bine-logo-parallax {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    min-height: 150px;\n    margin-bottom: 20px;\n    padding: 20px;\n    \n    border-radius: 16px;\n    overflow: hidden;\n  }\n\n  .bine-stoff-logo {\n    display: block;\n    width: auto;\n    max-width: min(320px, 80%);\n    max-height: 130px;\n    object-fit: contain;\n    will-change: transform;\n    transform: translate3d(0, 0, 0);\n  }\n\n  .bine-stoff-text h2 {\n    margin-top: 0;\n    margin-bottom: 15px;\n    color: #08868a;\n    font-size: clamp(24px, 4vw, 34px);\n    line-height: 1.2;\n  }\n\n  .bine-stoff-text h3 {\n    margin-top: 0;\n    margin-bottom: 10px;\n  }\n\n  .bine-stoff-einleitung {\n    font-size: 1.08em;\n    line-height: 1.65;\n  }\n\n  .bine-stoffarten {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 16px;\n    margin: 25px 0;\n  }\n\n  .bine-stoffart {\n    padding: 20px;\n    background: #f7f7f7;\n    border-top: 4px solid #08868a;\n    border-radius: 10px;\n  }\n\n  .bine-stoffart ul {\n    margin-bottom: 0;\n    padding-left: 20px;\n  }\n\n  .bine-stoffart li {\n    margin-bottom: 5px;\n  }\n\n  .bine-stofflaengen {\n    margin: 25px 0;\n    padding: 22px;\n    background: rgba(8, 134, 138, 0.08);\n    border-radius: 12px;\n  }\n\n  .bine-laengen-auswahl {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin: 16px 0;\n  }\n\n  .bine-laengen-auswahl span {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 90px;\n    padding: 9px 15px;\n    background: #08868a;\n    color: #ffffff;\n    border-radius: 20px;\n    font-weight: 700;\n  }\n\n  .bine-stoff-hinweis {\n    margin-bottom: 0;\n    padding: 14px;\n    background: #ffffff;\n    border-left: 4px solid #08868a;\n    border-radius: 5px;\n  }\n\n  @media screen and (max-width: 650px) {\n    .bine-stoffarten {\n      grid-template-columns: 1fr;\n    }\n\n    .bine-logo-parallax {\n      min-height: 120px;\n    }\n\n    .bine-stoff-logo {\n      max-width: 75%;\n      max-height: 100px;\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .bine-stoff-logo {\n      transform: none !important;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const parallaxElements = document.querySelectorAll(\n    \"[data-bine-parallax] .bine-stoff-logo\"\n  );\n\n  if (\n    !parallaxElements.length ||\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  ) {\n    return;\n  }\n\n  let animationFrameRequested = false;\n\n  function updateParallax() {\n    parallaxElements.forEach(function (logo) {\n      const container = logo.closest(\"[data-bine-parallax]\");\n      const position = container.getBoundingClientRect();\n      const windowHeight = window.innerHeight;\n\n      if (\n        position.bottom \u003e= 0 \u0026\u0026\n        position.top \u003c= windowHeight\n      ) {\n        const containerCenter =\n          position.top + position.height \/ 2;\n\n        const screenCenter = windowHeight \/ 2;\n\n        const offset =\n          (containerCenter - screenCenter) * -0.08;\n\n        const limitedOffset = Math.max(\n          -22,\n          Math.min(22, offset)\n        );\n\n        logo.style.transform =\n          \"translate3d(0,\" + limitedOffset + \"px,0)\";\n      }\n    });\n\n    animationFrameRequested = false;\n  }\n\n  function requestParallaxUpdate() {\n    if (!animationFrameRequested) {\n      window.requestAnimationFrame(updateParallax);\n      animationFrameRequested = true;\n    }\n  }\n\n  window.addEventListener(\n    \"scroll\",\n    requestParallaxUpdate,\n    { passive: true }\n  );\n\n  window.addEventListener(\n    \"resize\",\n    requestParallaxUpdate\n  );\n\n  requestParallaxUpdate();\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif02?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eBaumwolle\u003c\/span\u003e\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif01?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eJersey\u003c\/span\u003e\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n   \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e \u003cp\u003e 100 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Rohgewebe aus 100 % Baumwolle,\u003cbr\u003e hergestellt aus Materialien, die gemäß OEKO-TEX® STANDARD 100 zertifiziert sind. \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 2 cm\u003cbr\u003e\u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 150–153 g\/m² \u003c\/p\u003e \u003chr\u003e \n\u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n\n   \u003ch3\u003eJersey\u003c\/h3\u003e \u003cp\u003e \u003cstrong\u003eZusammensetzung:\u003c\/strong\u003e\u003cbr\u003e 92 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche\u003cbr\u003e 8 % Elasthan \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Maschenwaren aus Baumwollgarnen mit Zusatz von Elastangarnen (EL-Anteil bis zu 8 %).\u003cbr\u003e Ausschließlich hergestellt unter Verwendung von Komponenten und Veredelungsverfahren, die gemäß OEKO-TEX® STANDARD 100 vorzertifiziert sind. \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 3 cm\u003cbr\u003e \u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 200–210 g\/m² \u003c\/p\u003e\n\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    flex: 1;\n    padding: 12px 20px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-gif {\n    width: 32px;\n    height: 32px;\n    object-fit: contain;\n    flex-shrink: 0;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content h3 {\n    margin-top: 0;\n  }\n\n  .stoff-tab-content h4 {\n    margin-top: 20px;\n    margin-bottom: 8px;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tab-button {\n      padding: 11px 8px;\n      gap: 6px;\n    }\n\n    .stoff-tab-gif {\n      width: 28px;\n      height: 28px;\n    }\n\n    .stoff-tab-content {\n      padding: 16px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\"Baumwolle\", \"Jersey\"];\n\n  function normalize(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getCorrectName(value) {\n    const wanted = normalize(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalize(stoffart) === wanted;\n    }) || null;\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalize(element.dataset.stoffButton) === normalize(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalize(correctName);\n\n    container\n      .querySelectorAll(\"[data-stoff-button]\")\n      .forEach(function (button) {\n        const active =\n          !button.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(button.dataset.stoffButton) === wanted;\n\n        button.classList.toggle(\"is-active\", active);\n        button.setAttribute(\n          \"aria-selected\",\n          active ? \"true\" : \"false\"\n        );\n      });\n\n    container\n      .querySelectorAll(\"[data-stoff-content]\")\n      .forEach(function (content) {\n        const active =\n          !content.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(content.dataset.stoffContent) === wanted;\n\n        content.classList.toggle(\"is-active\", active);\n        content.hidden = !active;\n      });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(\n        container,\n        firstButton.dataset.stoffButton\n      );\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section =\n      container.closest(\".shopify-section\") || document;\n\n    const selectedFields = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const field of selectedFields) {\n      const name = getCorrectName(field.value);\n\n      if (name) {\n        return name;\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabs() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart =\n        getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabs();\n      return;\n    }\n\n    const availability = {\n      baumwolle: false,\n      jersey: false\n    };\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const options = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      options.forEach(function (optionValue) {\n        const stoffart =\n          getCorrectName(optionValue);\n\n        if (stoffart) {\n          availability[normalize(stoffart)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available =\n          availability[normalize(stoffart)] === true;\n\n        const button = container.querySelector(\n          '[data-stoff-button=\"' + stoffart + '\"]'\n        );\n\n        const content = container.querySelector(\n          '[data-stoff-content=\"' + stoffart + '\"]'\n        );\n\n        if (button) {\n          button.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabs();\n  }\n\n  function loadProductAvailability() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      syncTabs();\n      return;\n    }\n\n    fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        syncTabs();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button =\n      event.target.closest(\"[data-stoff-button]\");\n\n    if (\n      !button ||\n      button.classList.contains(\"is-unavailable\")\n    ) {\n      return;\n    }\n\n    const container =\n      button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(\n        container,\n        button.dataset.stoffButton\n      );\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches(\n        'input[type=\"radio\"], select'\n      )\n    ) {\n      window.setTimeout(syncTabs, 50);\n    }\n  });\n\n  function initializeStoffTabs() {\n    loadProductAvailability();\n    window.setTimeout(syncTabs, 300);\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeStoffTabs\n    );\n  } else {\n    initializeStoffTabs();\n  }\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"dynamisches-moodbild\" data-moodbild\u003e\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .dynamisches-moodbild {\n    display: none;\n    margin: 25px 0;\n    text-align: center;\n  }\n\n  .dynamisches-moodbild.is-visible {\n    display: block;\n  }\n\n  .moodbild-link {\n    display: block;\n    text-decoration: none;\n    border-radius: 14px;\n    overflow: hidden;\n  }\n\n  .dynamisches-moodbild img {\n    display: block;\n    width: 100%;\n    height: auto;\n    margin: 0 auto;\n    border-radius: 14px;\n    transition: transform 0.25s ease;\n  }\n\n  .moodbild-link:hover img {\n    transform: scale(1.015);\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .dynamisches-moodbild img {\n      transition: none;\n    }\n\n    .moodbild-link:hover img {\n      transform: none;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const MARKER = \"#moodbild\";\n\n  const containers = document.querySelectorAll(\n    \"[data-moodbild]\"\n  );\n\n  if (!containers.length) return;\n\n  function cleanAlt(text) {\n    return String(text || \"\")\n      .replace(\/\\s*#Moodbild\\s*\/gi, \"\")\n      .trim();\n  }\n\n  function getImageUrl(image) {\n    if (image.currentSrc) {\n      return image.currentSrc;\n    }\n\n    const srcset =\n      image.getAttribute(\"srcset\") ||\n      image.getAttribute(\"data-srcset\");\n\n    if (srcset) {\n      const sources = srcset\n        .split(\",\")\n        .map(function (entry) {\n          return entry.trim().split(\/\\s+\/)[0];\n        })\n        .filter(Boolean);\n\n      if (sources.length) {\n        return sources[sources.length - 1];\n      }\n    }\n\n    return (\n      image.getAttribute(\"data-src\") ||\n      image.getAttribute(\"src\") ||\n      \"\"\n    );\n  }\n\n  function insertMoodbild(imageUrl, altText) {\n    if (!imageUrl) return false;\n\n    containers.forEach(function (container) {\n      if (\n        container.dataset.moodbildLoaded === \"true\"\n      ) {\n        return;\n      }\n\n      const link = document.createElement(\"a\");\n\n      link.href = \"https:\/\/www.bines-shop.com\";\n      link.target = \"_blank\";\n      link.rel = \"noopener noreferrer\";\n      link.className = \"moodbild-link\";\n\n      link.setAttribute(\n        \"aria-label\",\n        \"Bines Shop in einem neuen Fenster öffnen\"\n      );\n\n      const image = document.createElement(\"img\");\n\n      image.src = imageUrl;\n\n      image.alt =\n        cleanAlt(altText) ||\n        \"Moodbild zum Produkt\";\n\n      image.loading = \"lazy\";\n      image.decoding = \"async\";\n\n      link.appendChild(image);\n      container.replaceChildren(link);\n\n      container.classList.add(\"is-visible\");\n\n      container.dataset.moodbildLoaded = \"true\";\n    });\n\n    return true;\n  }\n\n  function findMoodbildInGallery() {\n    const images = Array.from(\n      document.querySelectorAll(\"img\")\n    ).filter(function (image) {\n      if (image.closest(\"[data-moodbild]\")) {\n        return false;\n      }\n\n      const searchableText = [\n        image.getAttribute(\"alt\"),\n        image.getAttribute(\"data-alt\"),\n        image.getAttribute(\"aria-label\")\n      ]\n        .filter(Boolean)\n        .join(\" \")\n        .toLowerCase();\n\n      return searchableText.includes(MARKER);\n    });\n\n    if (!images.length) {\n      return false;\n    }\n\n    const moodbild = images.find(function (image) {\n      return getImageUrl(image);\n    });\n\n    if (!moodbild) {\n      return false;\n    }\n\n    const altText =\n      moodbild.getAttribute(\"alt\") ||\n      moodbild.getAttribute(\"data-alt\") ||\n      moodbild.getAttribute(\"aria-label\") ||\n      \"\";\n\n    return insertMoodbild(\n      getImageUrl(moodbild),\n      altText\n    );\n  }\n\n  function findMoodbildInProductData() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return Promise.resolve(false);\n    }\n\n    return fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(function (product) {\n        const media = Array.isArray(product.media)\n          ? product.media\n          : [];\n\n        const moodbild = media.find(function (item) {\n          return String(item.alt || \"\")\n            .toLowerCase()\n            .includes(MARKER);\n        });\n\n        if (!moodbild) {\n          return false;\n        }\n\n        const imageUrl =\n          moodbild.preview_image \u0026\u0026\n          moodbild.preview_image.src\n            ? moodbild.preview_image.src\n            : moodbild.src;\n\n        return insertMoodbild(\n          imageUrl,\n          moodbild.alt\n        );\n      })\n      .catch(function () {\n        return false;\n      });\n  }\n\n  function initializeMoodbild() {\n    if (findMoodbildInGallery()) {\n      return;\n    }\n\n    findMoodbildInProductData()\n      .then(function (found) {\n        if (found) return;\n\n        const observer = new MutationObserver(\n          function () {\n            if (findMoodbildInGallery()) {\n              observer.disconnect();\n            }\n          }\n        );\n\n        observer.observe(\n          document.documentElement,\n          {\n            childList: true,\n            subtree: true,\n            attributes: true,\n            attributeFilter: [\n              \"alt\",\n              \"data-alt\",\n              \"src\",\n              \"data-src\",\n              \"srcset\",\n              \"data-srcset\"\n            ]\n          }\n        );\n\n        window.setTimeout(function () {\n          observer.disconnect();\n        }, 15000);\n      });\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeMoodbild\n    );\n  } else {\n    initializeMoodbild();\n  }\n})();\n\u003c\/script\u003e\n\u003c\/div\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle \/ 1 Meter","offer_id":53327335981323,"sku":"550000167","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 2 Meter","offer_id":53327336014091,"sku":"550000168","price":47.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 3 Meter","offer_id":53327336046859,"sku":"550000169","price":69.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 1 Meter","offer_id":53327336079627,"sku":"550000170","price":27.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 2 Meter","offer_id":53327336112395,"sku":"550000171","price":53.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 3 Meter","offer_id":53327336145163,"sku":"550000172","price":77.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-Boho-Bordurenorange-Stoff-Baumwolle-Jersey-nahen-bunt-Muster-2.jpg?v=1788121777"},{"product_id":"flora-blau-stoff","title":"FLORA blau – Stoff","description":"\u003c!-- PRODUKTBESCHREIBUNG MIT PARALLAX-LOGO --\u003e\n\n\u003csection class=\"bine-stoff-intro\"\u003e\n\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo-Stoffe-bunt.png?v=1787871207\" alt=\"Bine Brändle Stoffe Logo\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"bine-stoff-text\"\u003e\n\n    \u003ch2\u003eFarbenfrohe Stoffe mit Design von Bine Brändle\u003c\/h2\u003e\n\n    \u003cp class=\"bine-stoff-einleitung\"\u003e\n      Mach dein nächstes Nähprojekt zu etwas ganz Besonderem! Unsere\n      wunderschön bedruckten Stoffe mit einem liebevoll gestalteten Design\n      von \u003cstrong\u003eBine Brändle\u003c\/strong\u003e bringen Farbe, Freude und gute Laune\n      an die Nähmaschine.\n    \u003c\/p\u003e\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"Bine Brändle\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n    \u003cp\u003e\n      Du kannst dein Lieblingsdesign passend zu deinem Projekt als\n      \u003cstrong\u003eBaumwollgewebe\u003c\/strong\u003e oder \u003cstrong\u003eJersey\u003c\/strong\u003e auswählen.\n    \u003c\/p\u003e\n\n    \u003cdiv class=\"bine-stoffarten\"\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Das angenehm glatte und formstabile Baumwollgewebe eignet sich\n          wunderbar für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eHemden und Hosen\u003c\/li\u003e\n          \u003cli\u003eTaschen und Beutel\u003c\/li\u003e\n          \u003cli\u003eKissen und Wohnaccessoires\u003c\/li\u003e\n          \u003cli\u003eDekorationen\u003c\/li\u003e\n          \u003cli\u003ePatchwork-Projekte\u003c\/li\u003e\n          \u003cli\u003eSchultüten und kleine Geschenkideen\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eJersey\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Der weiche und elastische Jersey-Stoff ist besonders angenehm\n          auf der Haut und ideal für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eShirts und Kleider\u003c\/li\u003e\n          \u003cli\u003eHosen und Leggings\u003c\/li\u003e\n          \u003cli\u003eMützen und Halstücher\u003c\/li\u003e\n          \u003cli\u003eKinder- und Babykleidung\u003c\/li\u003e\n          \u003cli\u003eBequeme Lieblingsstücke für Groß und Klein\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"bine-stofflaengen\"\u003e\n      \u003ch3\u003eErhältliche Stofflängen\u003c\/h3\u003e\n\n      \u003cp\u003e\n        Du erhältst den Stoff fertig zugeschnitten in folgenden Längen:\n      \u003c\/p\u003e\n\n      \u003cdiv class=\"bine-laengen-auswahl\"\u003e\n        \u003cspan\u003e1 Meter\u003c\/span\u003e\n        \u003cspan\u003e2 Meter\u003c\/span\u003e\n        \u003cspan\u003e3 Meter\u003c\/span\u003e\n      \u003c\/div\u003e\n\n      \u003cp\u003e\n        Wähle einfach deine gewünschte Stoffart und anschließend die\n        passende Länge aus.\n      \u003c\/p\u003e\n\n      \u003cp class=\"bine-stoff-hinweis\"\u003e\n        \u003cstrong\u003eBitte beachte:\u003c\/strong\u003e Die ausgewählte Länge wird als\n        zusammenhängendes Stoffstück geliefert. Bei der Auswahl von\n        2 Metern erhältst du ein 2 Meter langes Stück, bei 3 Metern ein\n        zusammenhängendes 3-Meter-Stück.\n      \u003c\/p\u003e\n    \u003c\/div\u003e\n\n    \u003cp\u003e\n      Ob als farbenfrohes Kleidungsstück, liebevoll genähtes Geschenk oder\n      besonderer Blickfang für dein Zuhause – mit den Designs von\n      \u003cstrong\u003eBine Brändle\u003c\/strong\u003e wird jedes Nähprojekt herrlich bunt und\n      unverwechselbar.\n    \u003c\/p\u003e\n\n  \u003c\/div\u003e\n\n\u003c\/section\u003e\n\n\u003cstyle\u003e\n  .bine-stoff-intro {\n    margin: 20px 0 35px;\n    overflow: hidden;\n  }\n\n  .bine-logo-parallax {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    min-height: 150px;\n    margin-bottom: 20px;\n    padding: 20px;\n    \n    border-radius: 16px;\n    overflow: hidden;\n  }\n\n  .bine-stoff-logo {\n    display: block;\n    width: auto;\n    max-width: min(320px, 80%);\n    max-height: 130px;\n    object-fit: contain;\n    will-change: transform;\n    transform: translate3d(0, 0, 0);\n  }\n\n  .bine-stoff-text h2 {\n    margin-top: 0;\n    margin-bottom: 15px;\n    color: #08868a;\n    font-size: clamp(24px, 4vw, 34px);\n    line-height: 1.2;\n  }\n\n  .bine-stoff-text h3 {\n    margin-top: 0;\n    margin-bottom: 10px;\n  }\n\n  .bine-stoff-einleitung {\n    font-size: 1.08em;\n    line-height: 1.65;\n  }\n\n  .bine-stoffarten {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 16px;\n    margin: 25px 0;\n  }\n\n  .bine-stoffart {\n    padding: 20px;\n    background: #f7f7f7;\n    border-top: 4px solid #08868a;\n    border-radius: 10px;\n  }\n\n  .bine-stoffart ul {\n    margin-bottom: 0;\n    padding-left: 20px;\n  }\n\n  .bine-stoffart li {\n    margin-bottom: 5px;\n  }\n\n  .bine-stofflaengen {\n    margin: 25px 0;\n    padding: 22px;\n    background: rgba(8, 134, 138, 0.08);\n    border-radius: 12px;\n  }\n\n  .bine-laengen-auswahl {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin: 16px 0;\n  }\n\n  .bine-laengen-auswahl span {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 90px;\n    padding: 9px 15px;\n    background: #08868a;\n    color: #ffffff;\n    border-radius: 20px;\n    font-weight: 700;\n  }\n\n  .bine-stoff-hinweis {\n    margin-bottom: 0;\n    padding: 14px;\n    background: #ffffff;\n    border-left: 4px solid #08868a;\n    border-radius: 5px;\n  }\n\n  @media screen and (max-width: 650px) {\n    .bine-stoffarten {\n      grid-template-columns: 1fr;\n    }\n\n    .bine-logo-parallax {\n      min-height: 120px;\n    }\n\n    .bine-stoff-logo {\n      max-width: 75%;\n      max-height: 100px;\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .bine-stoff-logo {\n      transform: none !important;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const parallaxElements = document.querySelectorAll(\n    \"[data-bine-parallax] .bine-stoff-logo\"\n  );\n\n  if (\n    !parallaxElements.length ||\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  ) {\n    return;\n  }\n\n  let animationFrameRequested = false;\n\n  function updateParallax() {\n    parallaxElements.forEach(function (logo) {\n      const container = logo.closest(\"[data-bine-parallax]\");\n      const position = container.getBoundingClientRect();\n      const windowHeight = window.innerHeight;\n\n      if (\n        position.bottom \u003e= 0 \u0026\u0026\n        position.top \u003c= windowHeight\n      ) {\n        const containerCenter =\n          position.top + position.height \/ 2;\n\n        const screenCenter = windowHeight \/ 2;\n\n        const offset =\n          (containerCenter - screenCenter) * -0.08;\n\n        const limitedOffset = Math.max(\n          -22,\n          Math.min(22, offset)\n        );\n\n        logo.style.transform =\n          \"translate3d(0,\" + limitedOffset + \"px,0)\";\n      }\n    });\n\n    animationFrameRequested = false;\n  }\n\n  function requestParallaxUpdate() {\n    if (!animationFrameRequested) {\n      window.requestAnimationFrame(updateParallax);\n      animationFrameRequested = true;\n    }\n  }\n\n  window.addEventListener(\n    \"scroll\",\n    requestParallaxUpdate,\n    { passive: true }\n  );\n\n  window.addEventListener(\n    \"resize\",\n    requestParallaxUpdate\n  );\n\n  requestParallaxUpdate();\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif02?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eBaumwolle\u003c\/span\u003e\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif01?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eJersey\u003c\/span\u003e\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n   \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e \u003cp\u003e 100 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Rohgewebe aus 100 % Baumwolle,\u003cbr\u003e hergestellt aus Materialien, die gemäß OEKO-TEX® STANDARD 100 zertifiziert sind. \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 2 cm\u003cbr\u003e\u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 150–153 g\/m² \u003c\/p\u003e \u003chr\u003e \n\u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n\n   \u003ch3\u003eJersey\u003c\/h3\u003e \u003cp\u003e \u003cstrong\u003eZusammensetzung:\u003c\/strong\u003e\u003cbr\u003e 92 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche\u003cbr\u003e 8 % Elasthan \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Maschenwaren aus Baumwollgarnen mit Zusatz von Elastangarnen (EL-Anteil bis zu 8 %).\u003cbr\u003e Ausschließlich hergestellt unter Verwendung von Komponenten und Veredelungsverfahren, die gemäß OEKO-TEX® STANDARD 100 vorzertifiziert sind. \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 3 cm\u003cbr\u003e \u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 200–210 g\/m² \u003c\/p\u003e\n\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    flex: 1;\n    padding: 12px 20px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-gif {\n    width: 32px;\n    height: 32px;\n    object-fit: contain;\n    flex-shrink: 0;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content h3 {\n    margin-top: 0;\n  }\n\n  .stoff-tab-content h4 {\n    margin-top: 20px;\n    margin-bottom: 8px;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tab-button {\n      padding: 11px 8px;\n      gap: 6px;\n    }\n\n    .stoff-tab-gif {\n      width: 28px;\n      height: 28px;\n    }\n\n    .stoff-tab-content {\n      padding: 16px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\"Baumwolle\", \"Jersey\"];\n\n  function normalize(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getCorrectName(value) {\n    const wanted = normalize(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalize(stoffart) === wanted;\n    }) || null;\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalize(element.dataset.stoffButton) === normalize(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalize(correctName);\n\n    container\n      .querySelectorAll(\"[data-stoff-button]\")\n      .forEach(function (button) {\n        const active =\n          !button.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(button.dataset.stoffButton) === wanted;\n\n        button.classList.toggle(\"is-active\", active);\n        button.setAttribute(\n          \"aria-selected\",\n          active ? \"true\" : \"false\"\n        );\n      });\n\n    container\n      .querySelectorAll(\"[data-stoff-content]\")\n      .forEach(function (content) {\n        const active =\n          !content.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(content.dataset.stoffContent) === wanted;\n\n        content.classList.toggle(\"is-active\", active);\n        content.hidden = !active;\n      });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(\n        container,\n        firstButton.dataset.stoffButton\n      );\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section =\n      container.closest(\".shopify-section\") || document;\n\n    const selectedFields = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const field of selectedFields) {\n      const name = getCorrectName(field.value);\n\n      if (name) {\n        return name;\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabs() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart =\n        getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabs();\n      return;\n    }\n\n    const availability = {\n      baumwolle: false,\n      jersey: false\n    };\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const options = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      options.forEach(function (optionValue) {\n        const stoffart =\n          getCorrectName(optionValue);\n\n        if (stoffart) {\n          availability[normalize(stoffart)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available =\n          availability[normalize(stoffart)] === true;\n\n        const button = container.querySelector(\n          '[data-stoff-button=\"' + stoffart + '\"]'\n        );\n\n        const content = container.querySelector(\n          '[data-stoff-content=\"' + stoffart + '\"]'\n        );\n\n        if (button) {\n          button.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabs();\n  }\n\n  function loadProductAvailability() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      syncTabs();\n      return;\n    }\n\n    fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        syncTabs();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button =\n      event.target.closest(\"[data-stoff-button]\");\n\n    if (\n      !button ||\n      button.classList.contains(\"is-unavailable\")\n    ) {\n      return;\n    }\n\n    const container =\n      button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(\n        container,\n        button.dataset.stoffButton\n      );\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches(\n        'input[type=\"radio\"], select'\n      )\n    ) {\n      window.setTimeout(syncTabs, 50);\n    }\n  });\n\n  function initializeStoffTabs() {\n    loadProductAvailability();\n    window.setTimeout(syncTabs, 300);\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeStoffTabs\n    );\n  } else {\n    initializeStoffTabs();\n  }\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"dynamisches-moodbild\" data-moodbild\u003e\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .dynamisches-moodbild {\n    display: none;\n    margin: 25px 0;\n    text-align: center;\n  }\n\n  .dynamisches-moodbild.is-visible {\n    display: block;\n  }\n\n  .moodbild-link {\n    display: block;\n    text-decoration: none;\n    border-radius: 14px;\n    overflow: hidden;\n  }\n\n  .dynamisches-moodbild img {\n    display: block;\n    width: 100%;\n    height: auto;\n    margin: 0 auto;\n    border-radius: 14px;\n    transition: transform 0.25s ease;\n  }\n\n  .moodbild-link:hover img {\n    transform: scale(1.015);\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .dynamisches-moodbild img {\n      transition: none;\n    }\n\n    .moodbild-link:hover img {\n      transform: none;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const MARKER = \"#moodbild\";\n\n  const containers = document.querySelectorAll(\n    \"[data-moodbild]\"\n  );\n\n  if (!containers.length) return;\n\n  function cleanAlt(text) {\n    return String(text || \"\")\n      .replace(\/\\s*#Moodbild\\s*\/gi, \"\")\n      .trim();\n  }\n\n  function getImageUrl(image) {\n    if (image.currentSrc) {\n      return image.currentSrc;\n    }\n\n    const srcset =\n      image.getAttribute(\"srcset\") ||\n      image.getAttribute(\"data-srcset\");\n\n    if (srcset) {\n      const sources = srcset\n        .split(\",\")\n        .map(function (entry) {\n          return entry.trim().split(\/\\s+\/)[0];\n        })\n        .filter(Boolean);\n\n      if (sources.length) {\n        return sources[sources.length - 1];\n      }\n    }\n\n    return (\n      image.getAttribute(\"data-src\") ||\n      image.getAttribute(\"src\") ||\n      \"\"\n    );\n  }\n\n  function insertMoodbild(imageUrl, altText) {\n    if (!imageUrl) return false;\n\n    containers.forEach(function (container) {\n      if (\n        container.dataset.moodbildLoaded === \"true\"\n      ) {\n        return;\n      }\n\n      const link = document.createElement(\"a\");\n\n      link.href = \"https:\/\/www.bines-shop.com\";\n      link.target = \"_blank\";\n      link.rel = \"noopener noreferrer\";\n      link.className = \"moodbild-link\";\n\n      link.setAttribute(\n        \"aria-label\",\n        \"Bines Shop in einem neuen Fenster öffnen\"\n      );\n\n      const image = document.createElement(\"img\");\n\n      image.src = imageUrl;\n\n      image.alt =\n        cleanAlt(altText) ||\n        \"Moodbild zum Produkt\";\n\n      image.loading = \"lazy\";\n      image.decoding = \"async\";\n\n      link.appendChild(image);\n      container.replaceChildren(link);\n\n      container.classList.add(\"is-visible\");\n\n      container.dataset.moodbildLoaded = \"true\";\n    });\n\n    return true;\n  }\n\n  function findMoodbildInGallery() {\n    const images = Array.from(\n      document.querySelectorAll(\"img\")\n    ).filter(function (image) {\n      if (image.closest(\"[data-moodbild]\")) {\n        return false;\n      }\n\n      const searchableText = [\n        image.getAttribute(\"alt\"),\n        image.getAttribute(\"data-alt\"),\n        image.getAttribute(\"aria-label\")\n      ]\n        .filter(Boolean)\n        .join(\" \")\n        .toLowerCase();\n\n      return searchableText.includes(MARKER);\n    });\n\n    if (!images.length) {\n      return false;\n    }\n\n    const moodbild = images.find(function (image) {\n      return getImageUrl(image);\n    });\n\n    if (!moodbild) {\n      return false;\n    }\n\n    const altText =\n      moodbild.getAttribute(\"alt\") ||\n      moodbild.getAttribute(\"data-alt\") ||\n      moodbild.getAttribute(\"aria-label\") ||\n      \"\";\n\n    return insertMoodbild(\n      getImageUrl(moodbild),\n      altText\n    );\n  }\n\n  function findMoodbildInProductData() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return Promise.resolve(false);\n    }\n\n    return fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(function (product) {\n        const media = Array.isArray(product.media)\n          ? product.media\n          : [];\n\n        const moodbild = media.find(function (item) {\n          return String(item.alt || \"\")\n            .toLowerCase()\n            .includes(MARKER);\n        });\n\n        if (!moodbild) {\n          return false;\n        }\n\n        const imageUrl =\n          moodbild.preview_image \u0026\u0026\n          moodbild.preview_image.src\n            ? moodbild.preview_image.src\n            : moodbild.src;\n\n        return insertMoodbild(\n          imageUrl,\n          moodbild.alt\n        );\n      })\n      .catch(function () {\n        return false;\n      });\n  }\n\n  function initializeMoodbild() {\n    if (findMoodbildInGallery()) {\n      return;\n    }\n\n    findMoodbildInProductData()\n      .then(function (found) {\n        if (found) return;\n\n        const observer = new MutationObserver(\n          function () {\n            if (findMoodbildInGallery()) {\n              observer.disconnect();\n            }\n          }\n        );\n\n        observer.observe(\n          document.documentElement,\n          {\n            childList: true,\n            subtree: true,\n            attributes: true,\n            attributeFilter: [\n              \"alt\",\n              \"data-alt\",\n              \"src\",\n              \"data-src\",\n              \"srcset\",\n              \"data-srcset\"\n            ]\n          }\n        );\n\n        window.setTimeout(function () {\n          observer.disconnect();\n        }, 15000);\n      });\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeMoodbild\n    );\n  } else {\n    initializeMoodbild();\n  }\n})();\n\u003c\/script\u003e\n\u003c\/div\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle \/ 1 Meter","offer_id":53327339979019,"sku":"550000161","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 2 Meter","offer_id":53327340011787,"sku":"550000162","price":47.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 3 Meter","offer_id":53327340044555,"sku":"550000163","price":69.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 1 Meter","offer_id":53327340077323,"sku":"550000164","price":27.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 2 Meter","offer_id":53327340110091,"sku":"550000165","price":53.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 3 Meter","offer_id":53327340142859,"sku":"550000166","price":77.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-FLORAblau-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1787877379"},{"product_id":"flora-hellgrun-stoff","title":"FLORA hellgrün – Stoff","description":"\u003c!-- PRODUKTBESCHREIBUNG MIT PARALLAX-LOGO --\u003e\n\n\u003csection class=\"bine-stoff-intro\"\u003e\n\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo-Stoffe-bunt.png?v=1787871207\" alt=\"Bine Brändle Stoffe Logo\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"bine-stoff-text\"\u003e\n\n    \u003ch2\u003eFarbenfrohe Stoffe mit Design von Bine Brändle\u003c\/h2\u003e\n\n    \u003cp class=\"bine-stoff-einleitung\"\u003e\n      Mach dein nächstes Nähprojekt zu etwas ganz Besonderem! Unsere\n      wunderschön bedruckten Stoffe mit einem liebevoll gestalteten Design\n      von \u003cstrong\u003eBine Brändle\u003c\/strong\u003e bringen Farbe, Freude und gute Laune\n      an die Nähmaschine.\n    \u003c\/p\u003e\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"Bine Brändle\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n    \u003cp\u003e\n      Du kannst dein Lieblingsdesign passend zu deinem Projekt als\n      \u003cstrong\u003eBaumwollgewebe\u003c\/strong\u003e oder \u003cstrong\u003eJersey\u003c\/strong\u003e auswählen.\n    \u003c\/p\u003e\n\n    \u003cdiv class=\"bine-stoffarten\"\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Das angenehm glatte und formstabile Baumwollgewebe eignet sich\n          wunderbar für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eHemden und Hosen\u003c\/li\u003e\n          \u003cli\u003eTaschen und Beutel\u003c\/li\u003e\n          \u003cli\u003eKissen und Wohnaccessoires\u003c\/li\u003e\n          \u003cli\u003eDekorationen\u003c\/li\u003e\n          \u003cli\u003ePatchwork-Projekte\u003c\/li\u003e\n          \u003cli\u003eSchultüten und kleine Geschenkideen\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eJersey\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Der weiche und elastische Jersey-Stoff ist besonders angenehm\n          auf der Haut und ideal für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eShirts und Kleider\u003c\/li\u003e\n          \u003cli\u003eHosen und Leggings\u003c\/li\u003e\n          \u003cli\u003eMützen und Halstücher\u003c\/li\u003e\n          \u003cli\u003eKinder- und Babykleidung\u003c\/li\u003e\n          \u003cli\u003eBequeme Lieblingsstücke für Groß und Klein\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"bine-stofflaengen\"\u003e\n      \u003ch3\u003eErhältliche Stofflängen\u003c\/h3\u003e\n\n      \u003cp\u003e\n        Du erhältst den Stoff fertig zugeschnitten in folgenden Längen:\n      \u003c\/p\u003e\n\n      \u003cdiv class=\"bine-laengen-auswahl\"\u003e\n        \u003cspan\u003e1 Meter\u003c\/span\u003e\n        \u003cspan\u003e2 Meter\u003c\/span\u003e\n        \u003cspan\u003e3 Meter\u003c\/span\u003e\n      \u003c\/div\u003e\n\n      \u003cp\u003e\n        Wähle einfach deine gewünschte Stoffart und anschließend die\n        passende Länge aus.\n      \u003c\/p\u003e\n\n      \u003cp class=\"bine-stoff-hinweis\"\u003e\n        \u003cstrong\u003eBitte beachte:\u003c\/strong\u003e Die ausgewählte Länge wird als\n        zusammenhängendes Stoffstück geliefert. Bei der Auswahl von\n        2 Metern erhältst du ein 2 Meter langes Stück, bei 3 Metern ein\n        zusammenhängendes 3-Meter-Stück.\n      \u003c\/p\u003e\n    \u003c\/div\u003e\n\n    \u003cp\u003e\n      Ob als farbenfrohes Kleidungsstück, liebevoll genähtes Geschenk oder\n      besonderer Blickfang für dein Zuhause – mit den Designs von\n      \u003cstrong\u003eBine Brändle\u003c\/strong\u003e wird jedes Nähprojekt herrlich bunt und\n      unverwechselbar.\n    \u003c\/p\u003e\n\n  \u003c\/div\u003e\n\n\u003c\/section\u003e\n\n\u003cstyle\u003e\n  .bine-stoff-intro {\n    margin: 20px 0 35px;\n    overflow: hidden;\n  }\n\n  .bine-logo-parallax {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    min-height: 150px;\n    margin-bottom: 20px;\n    padding: 20px;\n    \n    border-radius: 16px;\n    overflow: hidden;\n  }\n\n  .bine-stoff-logo {\n    display: block;\n    width: auto;\n    max-width: min(320px, 80%);\n    max-height: 130px;\n    object-fit: contain;\n    will-change: transform;\n    transform: translate3d(0, 0, 0);\n  }\n\n  .bine-stoff-text h2 {\n    margin-top: 0;\n    margin-bottom: 15px;\n    color: #08868a;\n    font-size: clamp(24px, 4vw, 34px);\n    line-height: 1.2;\n  }\n\n  .bine-stoff-text h3 {\n    margin-top: 0;\n    margin-bottom: 10px;\n  }\n\n  .bine-stoff-einleitung {\n    font-size: 1.08em;\n    line-height: 1.65;\n  }\n\n  .bine-stoffarten {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 16px;\n    margin: 25px 0;\n  }\n\n  .bine-stoffart {\n    padding: 20px;\n    background: #f7f7f7;\n    border-top: 4px solid #08868a;\n    border-radius: 10px;\n  }\n\n  .bine-stoffart ul {\n    margin-bottom: 0;\n    padding-left: 20px;\n  }\n\n  .bine-stoffart li {\n    margin-bottom: 5px;\n  }\n\n  .bine-stofflaengen {\n    margin: 25px 0;\n    padding: 22px;\n    background: rgba(8, 134, 138, 0.08);\n    border-radius: 12px;\n  }\n\n  .bine-laengen-auswahl {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin: 16px 0;\n  }\n\n  .bine-laengen-auswahl span {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 90px;\n    padding: 9px 15px;\n    background: #08868a;\n    color: #ffffff;\n    border-radius: 20px;\n    font-weight: 700;\n  }\n\n  .bine-stoff-hinweis {\n    margin-bottom: 0;\n    padding: 14px;\n    background: #ffffff;\n    border-left: 4px solid #08868a;\n    border-radius: 5px;\n  }\n\n  @media screen and (max-width: 650px) {\n    .bine-stoffarten {\n      grid-template-columns: 1fr;\n    }\n\n    .bine-logo-parallax {\n      min-height: 120px;\n    }\n\n    .bine-stoff-logo {\n      max-width: 75%;\n      max-height: 100px;\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .bine-stoff-logo {\n      transform: none !important;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const parallaxElements = document.querySelectorAll(\n    \"[data-bine-parallax] .bine-stoff-logo\"\n  );\n\n  if (\n    !parallaxElements.length ||\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  ) {\n    return;\n  }\n\n  let animationFrameRequested = false;\n\n  function updateParallax() {\n    parallaxElements.forEach(function (logo) {\n      const container = logo.closest(\"[data-bine-parallax]\");\n      const position = container.getBoundingClientRect();\n      const windowHeight = window.innerHeight;\n\n      if (\n        position.bottom \u003e= 0 \u0026\u0026\n        position.top \u003c= windowHeight\n      ) {\n        const containerCenter =\n          position.top + position.height \/ 2;\n\n        const screenCenter = windowHeight \/ 2;\n\n        const offset =\n          (containerCenter - screenCenter) * -0.08;\n\n        const limitedOffset = Math.max(\n          -22,\n          Math.min(22, offset)\n        );\n\n        logo.style.transform =\n          \"translate3d(0,\" + limitedOffset + \"px,0)\";\n      }\n    });\n\n    animationFrameRequested = false;\n  }\n\n  function requestParallaxUpdate() {\n    if (!animationFrameRequested) {\n      window.requestAnimationFrame(updateParallax);\n      animationFrameRequested = true;\n    }\n  }\n\n  window.addEventListener(\n    \"scroll\",\n    requestParallaxUpdate,\n    { passive: true }\n  );\n\n  window.addEventListener(\n    \"resize\",\n    requestParallaxUpdate\n  );\n\n  requestParallaxUpdate();\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif02?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eBaumwolle\u003c\/span\u003e\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif01?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eJersey\u003c\/span\u003e\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n   \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e \u003cp\u003e 100 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Rohgewebe aus 100 % Baumwolle,\u003cbr\u003e hergestellt aus Materialien, die gemäß OEKO-TEX® STANDARD 100 zertifiziert sind. \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 2 cm\u003cbr\u003e\u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 150–153 g\/m² \u003c\/p\u003e \u003chr\u003e \n\u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n\n   \u003ch3\u003eJersey\u003c\/h3\u003e \u003cp\u003e \u003cstrong\u003eZusammensetzung:\u003c\/strong\u003e\u003cbr\u003e 92 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche\u003cbr\u003e 8 % Elasthan \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Maschenwaren aus Baumwollgarnen mit Zusatz von Elastangarnen (EL-Anteil bis zu 8 %).\u003cbr\u003e Ausschließlich hergestellt unter Verwendung von Komponenten und Veredelungsverfahren, die gemäß OEKO-TEX® STANDARD 100 vorzertifiziert sind. \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 3 cm\u003cbr\u003e \u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 200–210 g\/m² \u003c\/p\u003e\n\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    flex: 1;\n    padding: 12px 20px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-gif {\n    width: 32px;\n    height: 32px;\n    object-fit: contain;\n    flex-shrink: 0;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content h3 {\n    margin-top: 0;\n  }\n\n  .stoff-tab-content h4 {\n    margin-top: 20px;\n    margin-bottom: 8px;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tab-button {\n      padding: 11px 8px;\n      gap: 6px;\n    }\n\n    .stoff-tab-gif {\n      width: 28px;\n      height: 28px;\n    }\n\n    .stoff-tab-content {\n      padding: 16px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\"Baumwolle\", \"Jersey\"];\n\n  function normalize(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getCorrectName(value) {\n    const wanted = normalize(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalize(stoffart) === wanted;\n    }) || null;\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalize(element.dataset.stoffButton) === normalize(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalize(correctName);\n\n    container\n      .querySelectorAll(\"[data-stoff-button]\")\n      .forEach(function (button) {\n        const active =\n          !button.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(button.dataset.stoffButton) === wanted;\n\n        button.classList.toggle(\"is-active\", active);\n        button.setAttribute(\n          \"aria-selected\",\n          active ? \"true\" : \"false\"\n        );\n      });\n\n    container\n      .querySelectorAll(\"[data-stoff-content]\")\n      .forEach(function (content) {\n        const active =\n          !content.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(content.dataset.stoffContent) === wanted;\n\n        content.classList.toggle(\"is-active\", active);\n        content.hidden = !active;\n      });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(\n        container,\n        firstButton.dataset.stoffButton\n      );\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section =\n      container.closest(\".shopify-section\") || document;\n\n    const selectedFields = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const field of selectedFields) {\n      const name = getCorrectName(field.value);\n\n      if (name) {\n        return name;\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabs() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart =\n        getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabs();\n      return;\n    }\n\n    const availability = {\n      baumwolle: false,\n      jersey: false\n    };\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const options = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      options.forEach(function (optionValue) {\n        const stoffart =\n          getCorrectName(optionValue);\n\n        if (stoffart) {\n          availability[normalize(stoffart)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available =\n          availability[normalize(stoffart)] === true;\n\n        const button = container.querySelector(\n          '[data-stoff-button=\"' + stoffart + '\"]'\n        );\n\n        const content = container.querySelector(\n          '[data-stoff-content=\"' + stoffart + '\"]'\n        );\n\n        if (button) {\n          button.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabs();\n  }\n\n  function loadProductAvailability() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      syncTabs();\n      return;\n    }\n\n    fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        syncTabs();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button =\n      event.target.closest(\"[data-stoff-button]\");\n\n    if (\n      !button ||\n      button.classList.contains(\"is-unavailable\")\n    ) {\n      return;\n    }\n\n    const container =\n      button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(\n        container,\n        button.dataset.stoffButton\n      );\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches(\n        'input[type=\"radio\"], select'\n      )\n    ) {\n      window.setTimeout(syncTabs, 50);\n    }\n  });\n\n  function initializeStoffTabs() {\n    loadProductAvailability();\n    window.setTimeout(syncTabs, 300);\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeStoffTabs\n    );\n  } else {\n    initializeStoffTabs();\n  }\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"dynamisches-moodbild\" data-moodbild\u003e\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .dynamisches-moodbild {\n    display: none;\n    margin: 25px 0;\n    text-align: center;\n  }\n\n  .dynamisches-moodbild.is-visible {\n    display: block;\n  }\n\n  .moodbild-link {\n    display: block;\n    text-decoration: none;\n    border-radius: 14px;\n    overflow: hidden;\n  }\n\n  .dynamisches-moodbild img {\n    display: block;\n    width: 100%;\n    height: auto;\n    margin: 0 auto;\n    border-radius: 14px;\n    transition: transform 0.25s ease;\n  }\n\n  .moodbild-link:hover img {\n    transform: scale(1.015);\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .dynamisches-moodbild img {\n      transition: none;\n    }\n\n    .moodbild-link:hover img {\n      transform: none;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const MARKER = \"#moodbild\";\n\n  const containers = document.querySelectorAll(\n    \"[data-moodbild]\"\n  );\n\n  if (!containers.length) return;\n\n  function cleanAlt(text) {\n    return String(text || \"\")\n      .replace(\/\\s*#Moodbild\\s*\/gi, \"\")\n      .trim();\n  }\n\n  function getImageUrl(image) {\n    if (image.currentSrc) {\n      return image.currentSrc;\n    }\n\n    const srcset =\n      image.getAttribute(\"srcset\") ||\n      image.getAttribute(\"data-srcset\");\n\n    if (srcset) {\n      const sources = srcset\n        .split(\",\")\n        .map(function (entry) {\n          return entry.trim().split(\/\\s+\/)[0];\n        })\n        .filter(Boolean);\n\n      if (sources.length) {\n        return sources[sources.length - 1];\n      }\n    }\n\n    return (\n      image.getAttribute(\"data-src\") ||\n      image.getAttribute(\"src\") ||\n      \"\"\n    );\n  }\n\n  function insertMoodbild(imageUrl, altText) {\n    if (!imageUrl) return false;\n\n    containers.forEach(function (container) {\n      if (\n        container.dataset.moodbildLoaded === \"true\"\n      ) {\n        return;\n      }\n\n      const link = document.createElement(\"a\");\n\n      link.href = \"https:\/\/www.bines-shop.com\";\n      link.target = \"_blank\";\n      link.rel = \"noopener noreferrer\";\n      link.className = \"moodbild-link\";\n\n      link.setAttribute(\n        \"aria-label\",\n        \"Bines Shop in einem neuen Fenster öffnen\"\n      );\n\n      const image = document.createElement(\"img\");\n\n      image.src = imageUrl;\n\n      image.alt =\n        cleanAlt(altText) ||\n        \"Moodbild zum Produkt\";\n\n      image.loading = \"lazy\";\n      image.decoding = \"async\";\n\n      link.appendChild(image);\n      container.replaceChildren(link);\n\n      container.classList.add(\"is-visible\");\n\n      container.dataset.moodbildLoaded = \"true\";\n    });\n\n    return true;\n  }\n\n  function findMoodbildInGallery() {\n    const images = Array.from(\n      document.querySelectorAll(\"img\")\n    ).filter(function (image) {\n      if (image.closest(\"[data-moodbild]\")) {\n        return false;\n      }\n\n      const searchableText = [\n        image.getAttribute(\"alt\"),\n        image.getAttribute(\"data-alt\"),\n        image.getAttribute(\"aria-label\")\n      ]\n        .filter(Boolean)\n        .join(\" \")\n        .toLowerCase();\n\n      return searchableText.includes(MARKER);\n    });\n\n    if (!images.length) {\n      return false;\n    }\n\n    const moodbild = images.find(function (image) {\n      return getImageUrl(image);\n    });\n\n    if (!moodbild) {\n      return false;\n    }\n\n    const altText =\n      moodbild.getAttribute(\"alt\") ||\n      moodbild.getAttribute(\"data-alt\") ||\n      moodbild.getAttribute(\"aria-label\") ||\n      \"\";\n\n    return insertMoodbild(\n      getImageUrl(moodbild),\n      altText\n    );\n  }\n\n  function findMoodbildInProductData() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return Promise.resolve(false);\n    }\n\n    return fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(function (product) {\n        const media = Array.isArray(product.media)\n          ? product.media\n          : [];\n\n        const moodbild = media.find(function (item) {\n          return String(item.alt || \"\")\n            .toLowerCase()\n            .includes(MARKER);\n        });\n\n        if (!moodbild) {\n          return false;\n        }\n\n        const imageUrl =\n          moodbild.preview_image \u0026\u0026\n          moodbild.preview_image.src\n            ? moodbild.preview_image.src\n            : moodbild.src;\n\n        return insertMoodbild(\n          imageUrl,\n          moodbild.alt\n        );\n      })\n      .catch(function () {\n        return false;\n      });\n  }\n\n  function initializeMoodbild() {\n    if (findMoodbildInGallery()) {\n      return;\n    }\n\n    findMoodbildInProductData()\n      .then(function (found) {\n        if (found) return;\n\n        const observer = new MutationObserver(\n          function () {\n            if (findMoodbildInGallery()) {\n              observer.disconnect();\n            }\n          }\n        );\n\n        observer.observe(\n          document.documentElement,\n          {\n            childList: true,\n            subtree: true,\n            attributes: true,\n            attributeFilter: [\n              \"alt\",\n              \"data-alt\",\n              \"src\",\n              \"data-src\",\n              \"srcset\",\n              \"data-srcset\"\n            ]\n          }\n        );\n\n        window.setTimeout(function () {\n          observer.disconnect();\n        }, 15000);\n      });\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeMoodbild\n    );\n  } else {\n    initializeMoodbild();\n  }\n})();\n\u003c\/script\u003e\n\u003c\/div\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle \/ 1 Meter","offer_id":53327344107787,"sku":"550000155","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 2 Meter","offer_id":53327344140555,"sku":"550000156","price":47.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 3 Meter","offer_id":53327344173323,"sku":"550000157","price":69.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 1 Meter","offer_id":53327344206091,"sku":"550000158","price":27.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 2 Meter","offer_id":53327344238859,"sku":"550000159","price":53.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 3 Meter","offer_id":53327344271627,"sku":"550000160","price":77.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-FLORAhellgrun-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1787877512"},{"product_id":"flora-orange-stoff","title":"FLORA Orange – Stoff","description":"\u003c!-- PRODUKTBESCHREIBUNG MIT PARALLAX-LOGO --\u003e\n\n\u003csection class=\"bine-stoff-intro\"\u003e\n\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Logo-Stoffe-bunt.png?v=1787871207\" alt=\"Bine Brändle Stoffe Logo\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"bine-stoff-text\"\u003e\n\n    \u003ch2\u003eFarbenfrohe Stoffe mit Design von Bine Brändle\u003c\/h2\u003e\n\n    \u003cp class=\"bine-stoff-einleitung\"\u003e\n      Mach dein nächstes Nähprojekt zu etwas ganz Besonderem! Unsere\n      wunderschön bedruckten Stoffe mit einem liebevoll gestalteten Design\n      von \u003cstrong\u003eBine Brändle\u003c\/strong\u003e bringen Farbe, Freude und gute Laune\n      an die Nähmaschine.\n    \u003c\/p\u003e\n  \u003cdiv class=\"bine-logo-parallax\" data-bine-parallax\u003e\n    \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Oko-TEX100.jpg?v=1750455570\" alt=\"Bine Brändle\" class=\"bine-stoff-logo\"\u003e\n  \u003c\/div\u003e\n    \u003cp\u003e\n      Du kannst dein Lieblingsdesign passend zu deinem Projekt als\n      \u003cstrong\u003eBaumwollgewebe\u003c\/strong\u003e oder \u003cstrong\u003eJersey\u003c\/strong\u003e auswählen.\n    \u003c\/p\u003e\n\n    \u003cdiv class=\"bine-stoffarten\"\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Das angenehm glatte und formstabile Baumwollgewebe eignet sich\n          wunderbar für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eHemden und Hosen\u003c\/li\u003e\n          \u003cli\u003eTaschen und Beutel\u003c\/li\u003e\n          \u003cli\u003eKissen und Wohnaccessoires\u003c\/li\u003e\n          \u003cli\u003eDekorationen\u003c\/li\u003e\n          \u003cli\u003ePatchwork-Projekte\u003c\/li\u003e\n          \u003cli\u003eSchultüten und kleine Geschenkideen\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n      \u003cdiv class=\"bine-stoffart\"\u003e\n        \u003ch3\u003eJersey\u003c\/h3\u003e\n\n        \u003cp\u003e\n          Der weiche und elastische Jersey-Stoff ist besonders angenehm\n          auf der Haut und ideal für:\n        \u003c\/p\u003e\n\n        \u003cul\u003e\n          \u003cli\u003eShirts und Kleider\u003c\/li\u003e\n          \u003cli\u003eHosen und Leggings\u003c\/li\u003e\n          \u003cli\u003eMützen und Halstücher\u003c\/li\u003e\n          \u003cli\u003eKinder- und Babykleidung\u003c\/li\u003e\n          \u003cli\u003eBequeme Lieblingsstücke für Groß und Klein\u003c\/li\u003e\n        \u003c\/ul\u003e\n      \u003c\/div\u003e\n\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"bine-stofflaengen\"\u003e\n      \u003ch3\u003eErhältliche Stofflängen\u003c\/h3\u003e\n\n      \u003cp\u003e\n        Du erhältst den Stoff fertig zugeschnitten in folgenden Längen:\n      \u003c\/p\u003e\n\n      \u003cdiv class=\"bine-laengen-auswahl\"\u003e\n        \u003cspan\u003e1 Meter\u003c\/span\u003e\n        \u003cspan\u003e2 Meter\u003c\/span\u003e\n        \u003cspan\u003e3 Meter\u003c\/span\u003e\n      \u003c\/div\u003e\n\n      \u003cp\u003e\n        Wähle einfach deine gewünschte Stoffart und anschließend die\n        passende Länge aus.\n      \u003c\/p\u003e\n\n      \u003cp class=\"bine-stoff-hinweis\"\u003e\n        \u003cstrong\u003eBitte beachte:\u003c\/strong\u003e Die ausgewählte Länge wird als\n        zusammenhängendes Stoffstück geliefert. Bei der Auswahl von\n        2 Metern erhältst du ein 2 Meter langes Stück, bei 3 Metern ein\n        zusammenhängendes 3-Meter-Stück.\n      \u003c\/p\u003e\n    \u003c\/div\u003e\n\n    \u003cp\u003e\n      Ob als farbenfrohes Kleidungsstück, liebevoll genähtes Geschenk oder\n      besonderer Blickfang für dein Zuhause – mit den Designs von\n      \u003cstrong\u003eBine Brändle\u003c\/strong\u003e wird jedes Nähprojekt herrlich bunt und\n      unverwechselbar.\n    \u003c\/p\u003e\n\n  \u003c\/div\u003e\n\n\u003c\/section\u003e\n\n\u003cstyle\u003e\n  .bine-stoff-intro {\n    margin: 20px 0 35px;\n    overflow: hidden;\n  }\n\n  .bine-logo-parallax {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    min-height: 150px;\n    margin-bottom: 20px;\n    padding: 20px;\n    \n    border-radius: 16px;\n    overflow: hidden;\n  }\n\n  .bine-stoff-logo {\n    display: block;\n    width: auto;\n    max-width: min(320px, 80%);\n    max-height: 130px;\n    object-fit: contain;\n    will-change: transform;\n    transform: translate3d(0, 0, 0);\n  }\n\n  .bine-stoff-text h2 {\n    margin-top: 0;\n    margin-bottom: 15px;\n    color: #08868a;\n    font-size: clamp(24px, 4vw, 34px);\n    line-height: 1.2;\n  }\n\n  .bine-stoff-text h3 {\n    margin-top: 0;\n    margin-bottom: 10px;\n  }\n\n  .bine-stoff-einleitung {\n    font-size: 1.08em;\n    line-height: 1.65;\n  }\n\n  .bine-stoffarten {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n    gap: 16px;\n    margin: 25px 0;\n  }\n\n  .bine-stoffart {\n    padding: 20px;\n    background: #f7f7f7;\n    border-top: 4px solid #08868a;\n    border-radius: 10px;\n  }\n\n  .bine-stoffart ul {\n    margin-bottom: 0;\n    padding-left: 20px;\n  }\n\n  .bine-stoffart li {\n    margin-bottom: 5px;\n  }\n\n  .bine-stofflaengen {\n    margin: 25px 0;\n    padding: 22px;\n    background: rgba(8, 134, 138, 0.08);\n    border-radius: 12px;\n  }\n\n  .bine-laengen-auswahl {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    margin: 16px 0;\n  }\n\n  .bine-laengen-auswahl span {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 90px;\n    padding: 9px 15px;\n    background: #08868a;\n    color: #ffffff;\n    border-radius: 20px;\n    font-weight: 700;\n  }\n\n  .bine-stoff-hinweis {\n    margin-bottom: 0;\n    padding: 14px;\n    background: #ffffff;\n    border-left: 4px solid #08868a;\n    border-radius: 5px;\n  }\n\n  @media screen and (max-width: 650px) {\n    .bine-stoffarten {\n      grid-template-columns: 1fr;\n    }\n\n    .bine-logo-parallax {\n      min-height: 120px;\n    }\n\n    .bine-stoff-logo {\n      max-width: 75%;\n      max-height: 100px;\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .bine-stoff-logo {\n      transform: none !important;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const parallaxElements = document.querySelectorAll(\n    \"[data-bine-parallax] .bine-stoff-logo\"\n  );\n\n  if (\n    !parallaxElements.length ||\n    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  ) {\n    return;\n  }\n\n  let animationFrameRequested = false;\n\n  function updateParallax() {\n    parallaxElements.forEach(function (logo) {\n      const container = logo.closest(\"[data-bine-parallax]\");\n      const position = container.getBoundingClientRect();\n      const windowHeight = window.innerHeight;\n\n      if (\n        position.bottom \u003e= 0 \u0026\u0026\n        position.top \u003c= windowHeight\n      ) {\n        const containerCenter =\n          position.top + position.height \/ 2;\n\n        const screenCenter = windowHeight \/ 2;\n\n        const offset =\n          (containerCenter - screenCenter) * -0.08;\n\n        const limitedOffset = Math.max(\n          -22,\n          Math.min(22, offset)\n        );\n\n        logo.style.transform =\n          \"translate3d(0,\" + limitedOffset + \"px,0)\";\n      }\n    });\n\n    animationFrameRequested = false;\n  }\n\n  function requestParallaxUpdate() {\n    if (!animationFrameRequested) {\n      window.requestAnimationFrame(updateParallax);\n      animationFrameRequested = true;\n    }\n  }\n\n  window.addEventListener(\n    \"scroll\",\n    requestParallaxUpdate,\n    { passive: true }\n  );\n\n  window.addEventListener(\n    \"resize\",\n    requestParallaxUpdate\n  );\n\n  requestParallaxUpdate();\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"stoff-tabs\" data-stoff-tabs\u003e\n\n  \u003cdiv class=\"stoff-tabs__buttons\" role=\"tablist\"\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Baumwolle\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif02?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eBaumwolle\u003c\/span\u003e\n    \u003c\/button\u003e\n\n    \u003cbutton type=\"button\" class=\"stoff-tab-button\" data-stoff-button=\"Jersey\" role=\"tab\" aria-selected=\"false\"\u003e\n\n      \u003cimg src=\"https:\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Button-Gif01?v=1787787739\" alt=\"\" class=\"stoff-tab-gif\"\u003e\n\n      \u003cspan\u003eJersey\u003c\/span\u003e\n    \u003c\/button\u003e\n\n  \u003c\/div\u003e\n\n  \u003c!-- BAUMWOLLE --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Baumwolle\" role=\"tabpanel\" hidden\u003e\n\n   \u003ch3\u003eBaumwollgewebe\u003c\/h3\u003e \u003cp\u003e 100 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Rohgewebe aus 100 % Baumwolle,\u003cbr\u003e hergestellt aus Materialien, die gemäß OEKO-TEX® STANDARD 100 zertifiziert sind. \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 2 cm\u003cbr\u003e\u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 150–153 g\/m² \u003c\/p\u003e \u003chr\u003e \n\u003c\/div\u003e\n\n  \u003c!-- JERSEY --\u003e\n\n  \u003cdiv class=\"stoff-tab-content\" data-stoff-content=\"Jersey\" role=\"tabpanel\" hidden\u003e\n\n\n   \u003ch3\u003eJersey\u003c\/h3\u003e \u003cp\u003e \u003cstrong\u003eZusammensetzung:\u003c\/strong\u003e\u003cbr\u003e 92 % Baumwolle (gekämmtes Garn)\u003cbr\u003e Schöne, glatte Oberfläche\u003cbr\u003e 8 % Elasthan \u003c\/p\u003e \u003cp\u003e \u003cstrong\u003eMaterialzusammensetzung\u003c\/strong\u003e\u003cbr\u003e Maschenwaren aus Baumwollgarnen mit Zusatz von Elastangarnen (EL-Anteil bis zu 8 %).\u003cbr\u003e Ausschließlich hergestellt unter Verwendung von Komponenten und Veredelungsverfahren, die gemäß OEKO-TEX® STANDARD 100 vorzertifiziert sind. \u003c\/p\u003e \u003cp\u003e\u003cstrong\u003eStoffbreite:\u003c\/strong\u003e 160 cm ± 3 cm\u003cbr\u003e \u003cstrong\u003eGrammgewicht:\u003c\/strong\u003e 200–210 g\/m² \u003c\/p\u003e\n\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .stoff-tabs {\n    margin: 25px 0;\n  }\n\n  .stoff-tabs__buttons {\n    display: flex;\n    gap: 6px;\n    border-bottom: 1px solid #d8d8d8;\n  }\n\n  .stoff-tab-button {\n    appearance: none;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    flex: 1;\n    padding: 12px 20px;\n    border: 1px solid #d8d8d8;\n    border-bottom: 0;\n    border-radius: 7px 7px 0 0;\n    background: #f5f5f5;\n    color: inherit;\n    font: inherit;\n    font-weight: 600;\n    cursor: pointer;\n  }\n\n  .stoff-tab-gif {\n    width: 32px;\n    height: 32px;\n    object-fit: contain;\n    flex-shrink: 0;\n  }\n\n  .stoff-tab-button:hover {\n    background: #e9e9e9;\n  }\n\n  .stoff-tab-button.is-active {\n    background: #08868a;\n    border-color: #08868a;\n    color: #ffffff;\n  }\n\n  .stoff-tab-button.is-unavailable {\n    display: none !important;\n  }\n\n  .stoff-tab-content {\n    padding: 20px;\n    border: 1px solid #d8d8d8;\n    border-top: 0;\n  }\n\n  .stoff-tab-content h3 {\n    margin-top: 0;\n  }\n\n  .stoff-tab-content h4 {\n    margin-top: 20px;\n    margin-bottom: 8px;\n  }\n\n  .stoff-tab-content[hidden],\n  .stoff-tab-content.is-unavailable {\n    display: none !important;\n  }\n\n  @media screen and (max-width: 600px) {\n    .stoff-tab-button {\n      padding: 11px 8px;\n      gap: 6px;\n    }\n\n    .stoff-tab-gif {\n      width: 28px;\n      height: 28px;\n    }\n\n    .stoff-tab-content {\n      padding: 16px;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const STOFFARTEN = [\"Baumwolle\", \"Jersey\"];\n\n  function normalize(value) {\n    return String(value || \"\").trim().toLowerCase();\n  }\n\n  function getCorrectName(value) {\n    const wanted = normalize(value);\n\n    return STOFFARTEN.find(function (stoffart) {\n      return normalize(stoffart) === wanted;\n    }) || null;\n  }\n\n  function getTabs() {\n    return document.querySelectorAll(\"[data-stoff-tabs]\");\n  }\n\n  function isTabAvailable(container, stoffart) {\n    const button = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (element) {\n      return normalize(element.dataset.stoffButton) === normalize(stoffart);\n    });\n\n    return button \u0026\u0026 !button.classList.contains(\"is-unavailable\");\n  }\n\n  function openStoffTab(container, stoffart) {\n    const correctName = getCorrectName(stoffart);\n\n    if (!correctName || !isTabAvailable(container, correctName)) {\n      return;\n    }\n\n    const wanted = normalize(correctName);\n\n    container\n      .querySelectorAll(\"[data-stoff-button]\")\n      .forEach(function (button) {\n        const active =\n          !button.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(button.dataset.stoffButton) === wanted;\n\n        button.classList.toggle(\"is-active\", active);\n        button.setAttribute(\n          \"aria-selected\",\n          active ? \"true\" : \"false\"\n        );\n      });\n\n    container\n      .querySelectorAll(\"[data-stoff-content]\")\n      .forEach(function (content) {\n        const active =\n          !content.classList.contains(\"is-unavailable\") \u0026\u0026\n          normalize(content.dataset.stoffContent) === wanted;\n\n        content.classList.toggle(\"is-active\", active);\n        content.hidden = !active;\n      });\n  }\n\n  function openFirstAvailableTab(container) {\n    const firstButton = Array.from(\n      container.querySelectorAll(\"[data-stoff-button]\")\n    ).find(function (button) {\n      return !button.classList.contains(\"is-unavailable\");\n    });\n\n    if (firstButton) {\n      openStoffTab(\n        container,\n        firstButton.dataset.stoffButton\n      );\n    }\n  }\n\n  function getSelectedStoffart(container) {\n    const section =\n      container.closest(\".shopify-section\") || document;\n\n    const selectedFields = section.querySelectorAll(\n      'input[type=\"radio\"]:checked, select'\n    );\n\n    for (const field of selectedFields) {\n      const name = getCorrectName(field.value);\n\n      if (name) {\n        return name;\n      }\n    }\n\n    return null;\n  }\n\n  function syncTabs() {\n    getTabs().forEach(function (container) {\n      const selectedStoffart =\n        getSelectedStoffart(container);\n\n      if (\n        selectedStoffart \u0026\u0026\n        isTabAvailable(container, selectedStoffart)\n      ) {\n        openStoffTab(container, selectedStoffart);\n      } else {\n        openFirstAvailableTab(container);\n      }\n    });\n  }\n\n  function updateAvailability(product) {\n    if (!product || !Array.isArray(product.variants)) {\n      syncTabs();\n      return;\n    }\n\n    const availability = {\n      baumwolle: false,\n      jersey: false\n    };\n\n    product.variants.forEach(function (variant) {\n      if (!variant.available) {\n        return;\n      }\n\n      const options = variant.options || [\n        variant.option1,\n        variant.option2,\n        variant.option3\n      ];\n\n      options.forEach(function (optionValue) {\n        const stoffart =\n          getCorrectName(optionValue);\n\n        if (stoffart) {\n          availability[normalize(stoffart)] = true;\n        }\n      });\n    });\n\n    getTabs().forEach(function (container) {\n      STOFFARTEN.forEach(function (stoffart) {\n        const available =\n          availability[normalize(stoffart)] === true;\n\n        const button = container.querySelector(\n          '[data-stoff-button=\"' + stoffart + '\"]'\n        );\n\n        const content = container.querySelector(\n          '[data-stoff-content=\"' + stoffart + '\"]'\n        );\n\n        if (button) {\n          button.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          button.hidden = !available;\n        }\n\n        if (content) {\n          content.classList.toggle(\n            \"is-unavailable\",\n            !available\n          );\n\n          if (!available) {\n            content.hidden = true;\n          }\n        }\n      });\n\n      const activeButton = container.querySelector(\n        \"[data-stoff-button].is-active:not(.is-unavailable)\"\n      );\n\n      if (!activeButton) {\n        openFirstAvailableTab(container);\n      }\n    });\n\n    syncTabs();\n  }\n\n  function loadProductAvailability() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      syncTabs();\n      return;\n    }\n\n    fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(updateAvailability)\n      .catch(function () {\n        syncTabs();\n      });\n  }\n\n  document.addEventListener(\"click\", function (event) {\n    const button =\n      event.target.closest(\"[data-stoff-button]\");\n\n    if (\n      !button ||\n      button.classList.contains(\"is-unavailable\")\n    ) {\n      return;\n    }\n\n    const container =\n      button.closest(\"[data-stoff-tabs]\");\n\n    if (container) {\n      openStoffTab(\n        container,\n        button.dataset.stoffButton\n      );\n    }\n  });\n\n  document.addEventListener(\"change\", function (event) {\n    if (\n      event.target.matches(\n        'input[type=\"radio\"], select'\n      )\n    ) {\n      window.setTimeout(syncTabs, 50);\n    }\n  });\n\n  function initializeStoffTabs() {\n    loadProductAvailability();\n    window.setTimeout(syncTabs, 300);\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeStoffTabs\n    );\n  } else {\n    initializeStoffTabs();\n  }\n})();\n\u003c\/script\u003e\n\u003cdiv class=\"dynamisches-moodbild\" data-moodbild\u003e\u003c\/div\u003e\n\n\u003cstyle\u003e\n  .dynamisches-moodbild {\n    display: none;\n    margin: 25px 0;\n    text-align: center;\n  }\n\n  .dynamisches-moodbild.is-visible {\n    display: block;\n  }\n\n  .moodbild-link {\n    display: block;\n    text-decoration: none;\n    border-radius: 14px;\n    overflow: hidden;\n  }\n\n  .dynamisches-moodbild img {\n    display: block;\n    width: 100%;\n    height: auto;\n    margin: 0 auto;\n    border-radius: 14px;\n    transition: transform 0.25s ease;\n  }\n\n  .moodbild-link:hover img {\n    transform: scale(1.015);\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .dynamisches-moodbild img {\n      transition: none;\n    }\n\n    .moodbild-link:hover img {\n      transform: none;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function () {\n  const MARKER = \"#moodbild\";\n\n  const containers = document.querySelectorAll(\n    \"[data-moodbild]\"\n  );\n\n  if (!containers.length) return;\n\n  function cleanAlt(text) {\n    return String(text || \"\")\n      .replace(\/\\s*#Moodbild\\s*\/gi, \"\")\n      .trim();\n  }\n\n  function getImageUrl(image) {\n    if (image.currentSrc) {\n      return image.currentSrc;\n    }\n\n    const srcset =\n      image.getAttribute(\"srcset\") ||\n      image.getAttribute(\"data-srcset\");\n\n    if (srcset) {\n      const sources = srcset\n        .split(\",\")\n        .map(function (entry) {\n          return entry.trim().split(\/\\s+\/)[0];\n        })\n        .filter(Boolean);\n\n      if (sources.length) {\n        return sources[sources.length - 1];\n      }\n    }\n\n    return (\n      image.getAttribute(\"data-src\") ||\n      image.getAttribute(\"src\") ||\n      \"\"\n    );\n  }\n\n  function insertMoodbild(imageUrl, altText) {\n    if (!imageUrl) return false;\n\n    containers.forEach(function (container) {\n      if (\n        container.dataset.moodbildLoaded === \"true\"\n      ) {\n        return;\n      }\n\n      const link = document.createElement(\"a\");\n\n      link.href = \"https:\/\/www.bines-shop.com\";\n      link.target = \"_blank\";\n      link.rel = \"noopener noreferrer\";\n      link.className = \"moodbild-link\";\n\n      link.setAttribute(\n        \"aria-label\",\n        \"Bines Shop in einem neuen Fenster öffnen\"\n      );\n\n      const image = document.createElement(\"img\");\n\n      image.src = imageUrl;\n\n      image.alt =\n        cleanAlt(altText) ||\n        \"Moodbild zum Produkt\";\n\n      image.loading = \"lazy\";\n      image.decoding = \"async\";\n\n      link.appendChild(image);\n      container.replaceChildren(link);\n\n      container.classList.add(\"is-visible\");\n\n      container.dataset.moodbildLoaded = \"true\";\n    });\n\n    return true;\n  }\n\n  function findMoodbildInGallery() {\n    const images = Array.from(\n      document.querySelectorAll(\"img\")\n    ).filter(function (image) {\n      if (image.closest(\"[data-moodbild]\")) {\n        return false;\n      }\n\n      const searchableText = [\n        image.getAttribute(\"alt\"),\n        image.getAttribute(\"data-alt\"),\n        image.getAttribute(\"aria-label\")\n      ]\n        .filter(Boolean)\n        .join(\" \")\n        .toLowerCase();\n\n      return searchableText.includes(MARKER);\n    });\n\n    if (!images.length) {\n      return false;\n    }\n\n    const moodbild = images.find(function (image) {\n      return getImageUrl(image);\n    });\n\n    if (!moodbild) {\n      return false;\n    }\n\n    const altText =\n      moodbild.getAttribute(\"alt\") ||\n      moodbild.getAttribute(\"data-alt\") ||\n      moodbild.getAttribute(\"aria-label\") ||\n      \"\";\n\n    return insertMoodbild(\n      getImageUrl(moodbild),\n      altText\n    );\n  }\n\n  function findMoodbildInProductData() {\n    const productPath =\n      window.location.pathname.replace(\/\\\/$\/, \"\");\n\n    if (!productPath.includes(\"\/products\/\")) {\n      return Promise.resolve(false);\n    }\n\n    return fetch(productPath + \".js\")\n      .then(function (response) {\n        if (!response.ok) {\n          throw new Error(\n            \"Produktdaten konnten nicht geladen werden.\"\n          );\n        }\n\n        return response.json();\n      })\n      .then(function (product) {\n        const media = Array.isArray(product.media)\n          ? product.media\n          : [];\n\n        const moodbild = media.find(function (item) {\n          return String(item.alt || \"\")\n            .toLowerCase()\n            .includes(MARKER);\n        });\n\n        if (!moodbild) {\n          return false;\n        }\n\n        const imageUrl =\n          moodbild.preview_image \u0026\u0026\n          moodbild.preview_image.src\n            ? moodbild.preview_image.src\n            : moodbild.src;\n\n        return insertMoodbild(\n          imageUrl,\n          moodbild.alt\n        );\n      })\n      .catch(function () {\n        return false;\n      });\n  }\n\n  function initializeMoodbild() {\n    if (findMoodbildInGallery()) {\n      return;\n    }\n\n    findMoodbildInProductData()\n      .then(function (found) {\n        if (found) return;\n\n        const observer = new MutationObserver(\n          function () {\n            if (findMoodbildInGallery()) {\n              observer.disconnect();\n            }\n          }\n        );\n\n        observer.observe(\n          document.documentElement,\n          {\n            childList: true,\n            subtree: true,\n            attributes: true,\n            attributeFilter: [\n              \"alt\",\n              \"data-alt\",\n              \"src\",\n              \"data-src\",\n              \"srcset\",\n              \"data-srcset\"\n            ]\n          }\n        );\n\n        window.setTimeout(function () {\n          observer.disconnect();\n        }, 15000);\n      });\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\n      \"DOMContentLoaded\",\n      initializeMoodbild\n    );\n  } else {\n    initializeMoodbild();\n  }\n})();\n\u003c\/script\u003e\n\u003c\/div\u003e","brand":"DESDA Verlag GmbH","offers":[{"title":"Baumwolle \/ 1 Meter","offer_id":53327347220747,"sku":"550000149","price":24.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 2 Meter","offer_id":53327347253515,"sku":"550000150","price":47.95,"currency_code":"EUR","in_stock":true},{"title":"Baumwolle \/ 3 Meter","offer_id":53327347286283,"sku":"550000151","price":69.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 1 Meter","offer_id":53327347319051,"sku":"550000152","price":27.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 2 Meter","offer_id":53327347351819,"sku":"550000153","price":53.95,"currency_code":"EUR","in_stock":true},{"title":"Jersey \/ 3 Meter","offer_id":53327347384587,"sku":"550000154","price":77.95,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/files\/Aufmacher-FLORAOrange-Stoff-Baumwolle-Jersey-nahen-bunt-Muster.jpg?v=1787877693"}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0042\/4758\/4881\/collections\/Header_Desda-Allgemein_2f90f602-7b51-431b-b3ad-4b6fec208236.jpg?v=1774967827","url":"https:\/\/desdo.shop\/collections\/%f0%9f%91%89-nur-noch-wenige-%f0%9f%94%a5.oembed","provider":"DESDA Verlag GmbH","version":"1.0","type":"link"}