PageSourceSearch

https://studio.yogaeasy.de/calendar/agenda

html yogaeasy.de collected 2026-10-03 19:45:07 UTC 82,595 bytes, 2,225 lines download raw bytes

1<!DOCTYPE html>
2<html lang="de" class="h-full">
3  <head>
4    <meta name="csrf-param" content="authenticity_token" />
5<meta name="csrf-token" content="VCE6AhuxMCp8vpq6NLrY6D2G39mxNtt-TWn3VCrapU8E3C1HEtA9c_Tj4cnfFMlGhEqY3N7cAAMlwLnEx_ePbA" />
6    <meta charset="utf-8">
7    <meta name="viewport" content="width=device-width, initial-scale=1.0">
8
9    <link rel="preconnect" href="https://alpha.uscreencdn.com">
10    <link rel="preconnect">
11    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;display=swap">
12
13    <meta name="view-transition" content="same-origin">
14
15    
15<script src="https://assets-gke.uscreencdn.com/vite/assets/web-components-BnSq9iDb.js" crossorigin="anonymous" type="module"></script>
15<link rel="modulepreload" href="https://assets-gke.uscreencdn.com/vite/assets/preload-helper-s3Hva0Xc.js" as="script" crossorigin="anonymous">
16    
16<script src="https://assets-gke.uscreencdn.com/vite/assets/storefront_hotwire-BZ9GwY5K.js" crossorigin="anonymous" type="module"></script>
16<link rel="modulepreload" href="https://assets-gke.uscreencdn.com/vite/assets/preload-helper-s3Hva0Xc.js" as="script" crossorigin="anonymous">
17<link rel="modulepreload" href="https://assets-gke.uscreencdn.com/vite/assets/excludeTurboTrainBroadcastReactEvents-Ch9QDwMS.js" as="script" crossorigin="anonymous">
18<link rel="modulepreload" href="https://assets-gke.uscreencdn.com/vite/assets/VideoTracker-DKtiOlZs.js" as="script" crossorigin="anonymous">
19<link rel="modulepreload" href="https://assets-gke.uscreencdn.com/vite/assets/turbo.es2017-esm-CbrezRZI.js" as="script" crossorigin="anonymous">
20<link rel="modulepreload" href="https://assets-gke.uscreencdn.com/vite/assets/_commonjsHelpers-BosuxZz1.js" as="script" crossorigin="anonymous"><link rel="stylesheet" href="https://assets-gke.uscreencdn.com/vite/assets/storefront_hotwire-DtHu4oK3.css" media="screen" />
21    <link rel="stylesheet" href="https://assets-gke.uscreencdn.com/vite/assets/tailwind-CeQmPIAS.css" media="all" />
22    <link rel="stylesheet" href="https://assets-gke.uscreencdn.com/vite/assets/storefront_common-D_5IJA0x.css" media="all" />
23
24      
24<script>
25        window.hotwireCatalog = true
26      </script>
26
27    
27<script>
28      window.apiUrl = "https://api.uscreencdn.com"
29      window.apiOrigin = "yogaeasygermansite"
30    </script>
30
31
32    
32<script>
33//<![CDATA[
34window.gon={};gon.global={"uppy_companion_url":"/uppy","assets_cdn_url":"https://alpha.uscreencdn.com","mixpanel_token":"44e8d04b49529b306bd4a1c79afcc56e"};gon.feature_flags=["themeless","themeless_catalog_visual_fixes","ai_video_recommendations","ai_storefront_related_videos","ai_storefront_related_video_collections","show_captcha","show_captcha_v3","network_spam_detection","calendar_collection_scheduling","live_events_feed","checkout_donations","allow_invite_people","hotwire_catalog","passwordless_sign_up","rudolph_subscription_plans","content_moderation","audiences","email_topics","order_bumps","external_checkout","custom_google_cast_receiver","hubspot_hand_raise","refer_to_uscreen","billing_sales_tax_on_fees","community_notifications_v1","new_video_get_access_page","real_time_apple_notifications","real_time_roku_notifications","real_time_amazon_notifications","real_time_android_notifications","developer_apps_ui","users_mcp_permissions","apps_update_requests","account_creation_required","banner_with_live_stream_overage","paypal_beta","client_emails_footer","force_shortened_checkout","stripe_subscriptions","new_subscription_flow","pause_subscriptions","dm_attachments_in_app","stripe_tax_collection","ai_video_recommendations_waitlist","mobile_featured_category_thumbnail_style","prompt_app_updates","videos_bulk_all_matching","webhook_auto_disable","launch_dashboard_growth_ab_test"];gon.settings={"primary_color":"#009090","commenting":true,"my_library":true,"stats_id":"c43v645rpolt89529v2pesifn","analytics_env_id":"production","chapter_style":"sidebar","store_title":"yogaeasy.de","continue_watching":false,"mark_as_watched":true,"live_event_feed_link":"webcal://studio.yogaeasy.de/live_event_feed.ics","locale":"de","autoplay":true,"uplayer":false,"show_notifications_center":false,"time_format":null,"local_timezone":null};gon.currency="EUR";gon.locale="de";gon.country="US";gon.stripe="pk_live_DImPqz7QOOyx70XCA9DSifxb";gon.recaptcha_v3_pub_key="6LenX-EZAAAAAAITxgGIXKB3S2OVKsM5r5tg0wKL";gon.store_id=114449;gon.writing_direction="ltr";
35//]]>
36</script>
36
37
38    
38<script>
39      var sentryDsn = 'https://[email protected]/4509910289350656'
40      var sentryStorefrontActive = true
41      var sentryStorefrontSampleRate = 1.0
42      var railsEnv = 'production'
43      var analyticsHost = 'https://stats.uscreen.io'
44      var uscreenStoreId = 114449
45      var isUscreenUserImpersonated = false
46      var analyticsConfig = null
47      var storeId = 114449
48    </script>
48
49
50      <link
51  rel="icon"
52  href="https://alpha.uscreencdn.com/images/favicontypes/114649/favicon.1764767796.png"
53>
54<link
55  rel="apple-touch-icon"
56  sizes="180x180"
57  href="https://alpha.uscreencdn.com/images/favicontypes/114649/favicon.1764767796.png"
58>
59
60
61    
61<script
62  type="module"
63  crossorigin
64  src="https://cdn.jsdelivr.net/npm/@uscreentv/[email protected]/+esm"
65></script>
65
66
67      <title>yogaeasy.de</title>
68<meta name="description" content="">
69<!-- Twitter Card data -->
70<meta name="twitter:card" content="summary" />
71<meta name="twitter:title" content="yogaeasy.de" />
72<meta name="twitter:description" content="" />
73<meta name="twitter:image" content="https://alpha.uscreencdn.com/450xnull/images/logotypes/114649/logo_rgb.1775111741.png?auto=webp" />
74<!-- Open Graph data -->
75<meta property="og:title" content="yogaeasy.de" >
76<meta property="og:type" content="website" />
77<meta property="og:url" content="https://studio.yogaeasy.de/calendar/agenda" >
78<meta property="og:image" content="https://alpha.uscreencdn.com/1110xnull/images/logotypes/114649/logo_rgb.1775111741.png?auto=webp" >
79<meta property="og:image:width" content="740" >
80<meta property="og:image:height" content="420" >
81<meta property="og:description" content="" >
82<meta property="og:site_name" content="yogaeasy.de" />
83<link rel="canonical" href="https://studio.yogaeasy.de/calendar/agenda" />
84<script type = 'application/ld+json' >
85  {
86    "@context": "https://schema.org",
87    "@type": "Organization",
88    "url": "https://studio.yogaeasy.de",
89    "@id": "https://studio.yogaeasy.de/#organization",
90    "name": "yogaeasy.de",
91    "logo": "https://alpha.uscreencdn.com/450xnull/images/logotypes/114649/logo_rgb.1775111741.png?auto=webp"
92  }
93</script>
93
94
95
96        <!-- Google Analytics -->
97
98<!-- End Google Analytics -->
99
100  <!-- Google Tag Manager -->
101  
101<script>
vendor: 348 bytes, lines 101-106
101
102    (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
103    new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
104    j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
105    'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
106    })(window,document,'script','dataLayer','
106GTM-5VXV6X
vendor: 6 bytes, lines 106-107
106');
107  
107</script>
107
108<!-- End Google Tag Manager -->
109
110
111
112    <style>
113    :root {
114        --primary: #009090;
115        --primary-rgb: 0, 144, 144;
116        --contrast-text-color: #FFFFFF;
117        --contrast-to-primary-color: #fff;
118    }
119</style>
120<style id="custom-theme-styles" media="all">
121    body.light {
122        --main-color-h: 0;
123        --main-color-s: 100;
124        --main-color-l: 100;
125        --ds-bg-main: hsl(0, 100%, 100%);
126
127        --ds-bg-default: hsl(var(--main-color-h), calc(var(--main-color-s) * 1%), 96%);
128        --ds-bg-inset: hsl(var(--main-color-h), calc(var(--main-color-s) * 1%), 91%);
129        --ds-bg-overlay: hsl(var(--main-color-h), calc(var(--main-color-s) * 1%), 98%);
130        --ds-bg-subtle: hsl(var(--main-color-h), calc(var(--main-color-s) * 1%), 91%);
131
132        --ds-border-default: hsl(var(--main-color-h), calc(var(--main-color-s) * 1%), 85%);
133        --ds-fg-default: hsl(var(--main-color-h), calc(var(--main-color-s) * 1%), 8%);
134        --ds-fg-muted: hsl(var(--main-color-h), calc(var(--main-color-s) * 1%), 25%);
135        --ds-fg-subtle: hsl(var(--main-color-h), calc(var(--main-color-s) * 1%), 50%);
136    }
137
138    body.dark {
139        --main-color-h: 0;
140        --main-color-s: 100;
141        --main-color-l: 0;
142        --ds-bg-main: hsl(0, 100%, 0%);
143
144        --ds-bg-default: hsl(var(--main-color-h), calc(var(--main-color-s) * 1%), 10%);
145        --ds-bg-overlay: hsl(var(--main-color-h), calc(var(--main-color-s) * 1%), 2%);
146        --ds-bg-inset: hsl(var(--main-color-h), calc(var(--main-color-s) * 1%), 6%);
147        --ds-bg-subtle: hsl(var(--main-color-h), calc(var(--main-color-s) * 1%), 15%);
148
149        --ds-border-default: hsl(var(--main-color-h), calc(var(--main-color-s) * 1%), 20%);
150        --ds-fg-default: hsl(var(--main-color-h), calc(var(--main-color-s) * 1%), 98%);
151        --ds-fg-muted: hsl(var(--main-color-h), calc(var(--main-color-s) * 1%), 70%);
152        --ds-fg-subtle: hsl(var(--main-color-h), clamp(7%, calc(calc(var(--main-color-s) - 10) * 1%), 100%), 60%);
153    }
154
155    body.dark,
156    body.light {
157        --main-color-h: 60;
158        --main-color-s: 12;
159        --main-color-l: 97;
160        --ds-bg-main: hsl(60, 12%, 97%);
161    }
162</style>
163
164    
165<link rel="preconnect" href="https://s3.us-east-1.amazonaws.com">
166<style>
167  .agenda-item-card:hover .agenda-item-button {
168    background-color: #009090;
169  }
170  .light-forced, :root {
171    --gray-700: #555547;
172  }
173  body.light,
174  html,
175  body {
176    --ds-fg-default: #555547 !important;
177  }
178  #website-links a {
179    font-weight: 500;
180  }
181  #upsell_form ds-button {
182    --primary-color: #009090 !important;
183  }
184  a.category.bg-black {
185    background: #009090;
186  }
187    @font-face {
188      font-family: 'SourceSansPro';
189      src: url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansproregular.1772012777.eot');
190      src: url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansproregular.1772012777.eot') format('embedded-opentype'),
191            url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansproregular.1772012770.woff2') format('woff2'),
192            url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansproregular.1772012783.woff') format('woff'),
193            url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansproregular.1772012893.ttf') format('truetype'),
194            url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansproregular.1772012790.svg#SourceSansPro') format('svg');
195      font-weight: normal;
196      font-style: normal;
197      font-display: swap;
198    }
199    @font-face {
200      font-family: 'SourceSansPro';
201      src: url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansprosemibold.1772012932.eot');
202      src: url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansprosemibold.1772012932.eot') format('embedded-opentype'),
203            url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansprosemibold.1772012925.woff2') format('woff2'),
204            url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansprosemibold.1772012945.woff') format('woff'),
205            url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansprosemibold.1772012950.ttf') format('truetype'),
206            url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansprosemibold.1772012939.svg#SourceSansPro') format('svg');
207      font-weight: 500;
208      font-style: normal;
209      font-display: swap;
210    }
211    @font-face {
212      font-family: 'SourceSansPro';
213      src: url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansprobold.1772113704.eot');
214      src: url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansprobold.1772113704.eot') format('embedded-opentype'),
215            url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansprobold.1772113691.woff2') format('woff2'),
216            url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansprobold.1772113714.woff') format('woff'),
217            url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansprobold.1772113736.ttf') format('truetype'),
218            url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansprobold.1772113723.svg#SourceSansPro') format('svg');
219      font-weight: 600;
220      font-style: normal;
221      font-display: swap;
222    }
223    @font-face {
224      font-family: 'SourceSansPro';
225      src: url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansproitalic.1772012981.eot');
226      src: url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansproitalic.1772012981.eot') format('embedded-opentype'),
227            url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansproitalic.1772012975.woff2') format('woff2'),
228            url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansproitalic.1772012986.woff') format('woff'),
229            url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansproitalic.1772012999.ttf') format('truetype'),
230            url('https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fsourcesansproitalic.1772012991.svg#SourceSansPro') format('svg');
231      font-weight: normal;
232      font-style: italic;
233      font-display: swap;
234    }
235
236
237h1, h2, h3, h4, h5,
238.cbt-tabs--subtitle,
239.cbt-related-title,
240.category-title,
241.heading_font_family,
242.theme\:title {
243  font-family: 'SourceSansPro', sans-serif !important;
244  color: #0e5c63 !important
245}
246.category-title {
247  font-weight: 600;
248}
249.comments-title,
250.cbt-related-title {
251  color: #555547 !important
252}
253
254body, html,
255.card-title .card-duration,
256.cbt-tabs--content,
257.body_font_family {
258  font-family: 'SourceSansPro', sans-serif;
259}
260
261
262.theme\:navigation a,
263.button-content,
264.s-button {
265  font-family: 'SourceSansPro', sans-serif;
266}
267
268.light .category-see-all a {
269  color: #555547 !important;
270  font-weight: 400;
271}
272</style>
273
274<!-- Feature: YogaEasy checkout full-page coupon gate (Tag: checkout, coupon, multi-offer) -->
275<style>
276  html.ye-coupon-gate-open,
277  body.ye-coupon-gate-open {
278    overflow: hidden !important;
279  }
280
281  #ye-coupon-gate {
282    position: fixed;
283    inset: 0;
284    display: block;
285    overflow: auto;
286    background: #ffffff;
287    z-index: 2147483000;
288  }
289
290  #ye-coupon-gate[hidden] {
291    display: none;
292  }
293
294  .ye-coupon-gate__dialog {
295    width: 100%;
296    min-height: 100%;
297    background: #ffffff;
298  }
299
300  .ye-coupon-gate__inner {
301    width: min(1220px, 100%);
302    margin: 0 auto;
303    padding: clamp(14px, 2.8vh, 28px) clamp(14px, 3vw, 34px) clamp(20px, 4vh, 40px);
304    text-align: center;
305  }
306
307  .ye-coupon-gate__logo {
308    display: block;
309    width: min(290px, 72vw);
310    margin: 0 auto 12px;
311  }
312
313  .ye-coupon-gate__title {
314    margin: 0;
315    color: #005666;
316    font-size: clamp(1.9rem, 3.3vw, 3.7rem);
317    line-height: 1.08;
318    font-family: Georgia, "Times New Roman", serif;
319    font-weight: 700;
320    letter-spacing: -0.02em;
321  }
322
323  .ye-coupon-gate__copy {
324    margin: 8px auto 14px;
325    color: #3c3f42;
326    font-size: clamp(0.95rem, 1.22vw, 1.42rem);
327    line-height: 1.34;
328    max-width: 900px;
329    font-family: "Avenir Next", Avenir, "Segoe UI", Arial, sans-serif;
330  }
331
332  .ye-coupon-gate__hero {
333    display: block;
334    width: 100%;
335    max-width: 100%;
336    max-height: min(29vh, 340px);
337    aspect-ratio: 2.4 / 1;
338    object-fit: cover;
339    margin: 0 auto;
340    border-radius: 52% 52% 0 0 / 15% 15% 0 0;
341  }
342
343  .ye-coupon-gate__form-wrap {
344    padding: clamp(12px, 2.4vh, 24px) 0 clamp(10px, 1.8vh, 20px);
345  }
346
347  .ye-coupon-gate__form-title {
348    margin: 0 0 10px;
349    color: #005666;
350    font-size: clamp(1.3rem, 2.45vw, 2.1rem);
351    line-height: 1.2;
352    font-family: Georgia, "Times New Roman", serif;
353    font-weight: 700;
354  }
355
356  .ye-coupon-gate__field {
357    width: min(560px, 100%);
358    margin: 0 auto;
359  }
360
361  .ye-coupon-gate__input {
362    width: 100%;
363    border: 1px solid #cfd3d5;
364    background: #ffffff;
365    color: #2f3a3d;
366    border-radius: 0;
367    font-size: 1.05rem;
368    line-height: 1.3;
369    padding: 14px 16px;
370    outline: none;
371    font-family: "Avenir Next", Avenir, "Segoe UI", Arial, sans-serif;
372  }
373
374  .ye-coupon-gate__input:focus {
375    border-color: #1b7685;
376    box-shadow: 0 0 0 3px rgba(27, 118, 133, 0.2);
377  }
378
379  .ye-coupon-gate__submit {
380    width: min(560px, 100%);
381    margin: 10px auto 0;
382    border: none;
383    border-radius: 999px;
384    background: #f7d9d2;
385    color: #ffffff;
386    font-size: 1.08rem;
387    font-weight: 700;
388    letter-spacing: 0.01em;
389    padding: 13px 18px;
390    cursor: pointer;
391    font-family: "Avenir Next", Avenir, "Segoe UI", Arial, sans-serif;
392    transition: transform 0.16s ease, box-shadow 0.2s ease, filter 0.2s ease;
393  }
394
395  .ye-coupon-gate__submit:hover:not([disabled]) {
396    transform: translateY(-1px);
397    box-shadow: 0 10px 22px rgba(0, 86, 102, 0.18);
398    filter: brightness(1.02);
399  }
400
401  .ye-coupon-gate__submit[disabled] {
402    cursor: not-allowed;
403    opacity: 0.7;
404    box-shadow: none;
405    transform: none;
406  }
407
408  .ye-coupon-gate__error {
409    min-height: 22px;
410    margin: 8px auto 0;
411    color: #c13f4b;
412    font-size: 0.95rem;
413    line-height: 1.3;
414    font-family: "Avenir Next", Avenir, "Segoe UI", Arial, sans-serif;
415  }
416
417  @media (max-width: 900px) {
418    .ye-coupon-gate__inner {
419      padding-top: 16px;
420    }
421
422    .ye-coupon-gate__logo {
423      width: min(250px, 74vw);
424      margin-bottom: 10px;
425    }
426
427    .ye-coupon-gate__copy {
428      margin-bottom: 12px;
429    }
430
431    .ye-coupon-gate__hero {
432      max-height: min(24vh, 220px);
433      border-radius: 34% 34% 0 0 / 11% 11% 0 0;
434    }
435  }
436
437  @media (max-width: 640px) {
438    .ye-coupon-gate__title {
439      line-height: 1.08;
440    }
441
442    .ye-coupon-gate__copy {
443      max-width: 96vw;
444      font-size: 0.98rem;
445    }
446
447    .ye-coupon-gate__form-wrap {
448      padding-top: 12px;
449    }
450
451    .ye-coupon-gate__form-title {
452      margin-bottom: 8px;
453    }
454
455    .ye-coupon-gate__hero {
456      max-height: 170px;
457    }
458  }
459</style>
460
461<script>
462  (() => {
463    const FLAG = '__YE_COUPON_GATE__';
464    if (window[FLAG]) return;
465    window[FLAG] = true;
466
467    const OFFER_IDS = new Set(['224153', '224840', '224842', '224843']);
468    const COUPON_PARAM = 'd';
469    const COUPON_SUCCESS_SELECTOR = '#coupon-code';
470    const OVERLAY_ID = 'ye-coupon-gate';
471    const LOCK_CLASS = 'ye-coupon-gate-open';
472    const HERO_IMAGE_URL = 'https://unode1.s3.amazonaws.com/assets%2F%2Fassets%2F15576%2F%2Fcampaign_de.1770400233.jpg';
473    const LOGO_IMAGE_URL = 'https://alpha.uscreencdn.com/images/logotypes/114649/logo_rgb.1775111741.png';
474
475    let overlayEl = null;
476    let formEl = null;
477    let inputEl = null;
478    let submitEl = null;
479    let errorEl = null;
480    let focusables = [];
481    let observer = null;
482    let watchInterval = null;
483    let pendingStatusTimer = null;
484    let isLocked = false;
485
486    const isTargetCheckout = (url) => (
487      url.pathname === '/checkout/new' &&
488      OFFER_IDS.has(url.searchParams.get('o') || '')
489    );
490
491    const normalizeCoupon = (value) => String(value || '')
492      .trim()
493      .replace(/\s+/g, '')
494      .toUpperCase();
495
496    const getCouponFromUrl = () => {
497      const url = new URL(window.location.href);
498      return normalizeCoupon(url.searchParams.get(COUPON_PARAM));
499    };
500
501    const clearPendingStatusTimer = () => {
502      if (pendingStatusTimer) {
503        window.clearTimeout(pendingStatusTimer);
504        pendingStatusTimer = null;
505      }
506    };
507
508    const couponAppliedSuccessfully = () => {
509      const couponEl = document.querySelector(COUPON_SUCCESS_SELECTOR);
510      if (!couponEl) return false;
511      const pageCoupon = normalizeCoupon(couponEl.textContent || '');
512      if (!pageCoupon) return false;
513
514      const urlCoupon = getCouponFromUrl();
515      if (!urlCoupon) return true;
516      return pageCoupon.includes(urlCoupon) || urlCoupon.includes(pageCoupon);
517    };
518
519    const stopWatchingCouponApply = () => {
520      if (observer) {
521        observer.disconnect();
522        observer = null;
523      }
524      if (watchInterval) {
525        window.clearInterval(watchInterval);
526        watchInterval = null;
527      }
528      clearPendingStatusTimer();
529    };
530
531    const startWatchingCouponApply = () => {
532      if (observer || watchInterval) return;
533      if (!document.body) return;
534
535      observer = new MutationObserver(() => {
536        evaluateRoute();
537      });
538      observer.observe(document.body, {
539        childList: true,
540        subtree: true,
541        characterData: true
542      });
543
544      watchInterval = window.setInterval(() => {
545        evaluateRoute();
546      }, 500);
547    };
548
549    const schedulePendingCouponStatus = () => {
550      clearPendingStatusTimer();
551      const couponFromUrl = getCouponFromUrl();
552      if (!couponFromUrl) {
553        if (errorEl) errorEl.textContent = '';
554        return;
555      }
556
557      pendingStatusTimer = window.setTimeout(() => {
558        if (!isLocked) return;
559        if (couponAppliedSuccessfully()) return;
560        const liveInputCoupon = normalizeCoupon(inputEl?.value || '');
561        if (liveInputCoupon && liveInputCoupon !== couponFromUrl) return;
562        if (errorEl) {
563          errorEl.textContent = 'Coupon code not found. Bitte prüfe den Code und versuche es erneut.';
564        }
565        if (submitEl) {
566          submitEl.disabled = false;
567          submitEl.textContent = 'Gutschein einlösen';
568        }
569        if (inputEl) {
570          inputEl.disabled = false;
571          inputEl.focus();
572          inputEl.select();
573        }
574      }, 2800);
575    };
576
577    const setLocked = (locked) => {
578      const wasLocked = isLocked;
579      isLocked = locked;
580      document.documentElement.classList.toggle(LOCK_CLASS, locked);
581      document.body?.classList.toggle(LOCK_CLASS, locked);
582      const existingOverlay = overlayEl && overlayEl.isConnected
583        ? overlayEl
584        : document.getElementById(OVERLAY_ID);
585      const overlay = locked ? ensureOverlay() : existingOverlay;
586      if (!overlay) return;
587      overlay.hidden = !locked;
588      if (locked && !wasLocked) {
589        const couponFromUrl = getCouponFromUrl();
590        if (couponFromUrl && inputEl && !normalizeCoupon(inputEl.value)) {
591          inputEl.value = couponFromUrl;
592        }
593        schedulePendingCouponStatus();
594        setTimeout(() => inputEl?.focus(), 70);
595      } else if (!locked && wasLocked) {
596        clearPendingStatusTimer();
597        if (errorEl) errorEl.textContent = '';
598        if (submitEl) {
599          submitEl.disabled = false;
600          submitEl.textContent = 'Gutschein einlösen';
601        }
602        if (inputEl) inputEl.disabled = false;
603      }
604    };
605
606    const ensureOverlay = () => {
607      if (overlayEl && overlayEl.isConnected) return overlayEl;
608      if (!document.body) return null;
609
610      overlayEl = document.getElementById(OVERLAY_ID);
611      if (!overlayEl) {
612        overlayEl = document.createElement('div');
613        overlayEl.id = OVERLAY_ID;
614        overlayEl.hidden = true;
615        overlayEl.innerHTML = `
616          <div class="ye-coupon-gate__dialog" role="dialog" aria-modal="true" aria-labelledby="ye-coupon-gate-title">
617            <div class="ye-coupon-gate__inner">
618              <img class="ye-coupon-gate__logo" src="${LOGO_IMAGE_URL}" alt="yogaeasy logo" loading="eager" decoding="async" />
619              <h2 id="ye-coupon-gate-title" class="ye-coupon-gate__title">Dein Online-Yogastudio</h2>
620              <p class="ye-coupon-gate__copy">
621                Übe Yoga mit den besten Lehrer:innen für mehr Balance, Stärke und innere Ruhe.
622                Yoga für jeden Körper. Jederzeit.
623              </p>
624              <div class="ye-coupon-gate__form-wrap">
625                <h3 class="ye-coupon-gate__form-title">Gib hier deinen Gutscheincode ein</h3>
626                <form novalidate>
627                  <div class="ye-coupon-gate__field">
628                    <input
629                      class="ye-coupon-gate__input"
630                      type="text"
631                      name="coupon"
632                      autocomplete="off"
633                      autocapitalize="characters"
634                      spellcheck="false"
635                      placeholder="Dein persönlicher Gutscheincode"
636                      required
637                    />
638                  </div>
639                  <button class="ye-coupon-gate__submit" type="submit">Gutschein einlösen</button>
640                  <p class="ye-coupon-gate__error" role="alert" aria-live="assertive"></p>
641                </form>
642              </div>
643              <img class="ye-coupon-gate__hero" src="${HERO_IMAGE_URL}" alt="" loading="eager" decoding="async" />
644            </div>
645          </div>
646        `;
647        document.body.appendChild(overlayEl);
648      }
649
650      if (!overlayEl.dataset.bound) {
651        overlayEl.dataset.bound = 'true';
652        formEl = overlayEl.querySelector('form');
653        inputEl = overlayEl.querySelector('.ye-coupon-gate__input');
654        submitEl = overlayEl.querySelector('.ye-coupon-gate__submit');
655        errorEl = overlayEl.querySelector('.ye-coupon-gate__error');
656        focusables = [inputEl, submitEl].filter(Boolean);
657
658        formEl?.addEventListener('submit', (event) => {
659          event.preventDefault();
660
661          const rawCoupon = inputEl?.value || '';
662          const coupon = normalizeCoupon(rawCoupon);
663
664          if (!coupon) {
665            if (errorEl) errorEl.textContent = 'Bitte gib einen gültigen Gutscheincode ein.';
666            inputEl?.focus();
667            return;
668          }
669
670          if (errorEl) errorEl.textContent = '';
671          clearPendingStatusTimer();
672          if (submitEl) {
673            submitEl.disabled = true;
674            submitEl.textContent = 'Wird angewendet...';
675          }
676          if (inputEl) inputEl.disabled = true;
677
678          const url = new URL(window.location.href);
679          url.searchParams.set(COUPON_PARAM, coupon);
680          window.location.replace(url.toString());
681        });
682
683        inputEl?.addEventListener('input', () => {
684          if (errorEl) errorEl.textContent = '';
685          if (submitEl) {
686            submitEl.disabled = false;
687            submitEl.textContent = 'Gutschein einlösen';
688          }
689          clearPendingStatusTimer();
690        });
691
692        overlayEl.addEventListener('keydown', (event) => {
693          if (event.key !== 'Tab' || focusables.length < 2) return;
694          const first = focusables[0];
695          const last = focusables[focusables.length - 1];
696          if (event.shiftKey && document.activeElement === first) {
697            event.preventDefault();
698            last.focus();
699          } else if (!event.shiftKey && document.activeElement === last) {
700            event.preventDefault();
701            first.focus();
702          }
703        });
704      }
705
706      formEl = overlayEl.querySelector('form');
707      inputEl = overlayEl.querySelector('.ye-coupon-gate__input');
708      submitEl = overlayEl.querySelector('.ye-coupon-gate__submit');
709      errorEl = overlayEl.querySelector('.ye-coupon-gate__error');
710      return overlayEl;
711    };
712
713    const evaluateRoute = () => {
714      const url = new URL(window.location.href);
715      if (!isTargetCheckout(url)) {
716        setLocked(false);
717        stopWatchingCouponApply();
718        return;
719      }
720
721      if (couponAppliedSuccessfully()) {
722        setLocked(false);
723        stopWatchingCouponApply();
724      } else {
725        setLocked(true);
726        startWatchingCouponApply();
727      }
728    };
729
730    const init = () => {
731      evaluateRoute();
732    };
733
734    if (document.readyState === 'loading') {
735      document.addEventListener('DOMContentLoaded', init, { once: true });
736    } else {
737      init();
738    }
739
740    document.addEventListener('turbo:load', evaluateRoute);
741    document.addEventListener('turbo:frame-load', evaluateRoute);
742    window.addEventListener('popstate', evaluateRoute);
743  })();
744</script>
744
745<!--
746  Uscreen Support Snippet
747  Description: Content card metadata enhancement - adds author and tag information to Swiper content cards and playlist page cards with dynamic API fetching and Turbo navigation support
748  Added by: matt w
749  Date: 2026-04-29
750-->
751<script>
752(function () {
753var processed = new WeakSet();
754var timer = null;
755var tagCache = {};
756var permalinkCache = {};
757function fetchPermalink(contentId) {
758if (permalinkCache[contentId] !== undefined) return Promise.resolve(permalinkCache[contentId]);
759return fetch('/api/contents?id=' + contentId, {
760        credentials: 'same-origin',
761        headers: { 'Accept': 'application/json' }
762})
763.then(function (res) { return res.ok ? res.json() : null; })
764.then(function (json) {
765var item = Array.isArray(json) && json.length > 0 ? json[0] : null;
766var permalink = item && item.url ? item.url.replace('/programs/', '').split(/[?#]/)[0] : null;
767          permalinkCache[contentId] = permalink;
768return permalink;
769})
770.catch(function () {
771          permalinkCache[contentId] = null;
772return null;
773});
774}
775function fetchTags(permalink) {
776if (tagCache[permalink] !== undefined) return Promise.resolve(tagCache[permalink]);
777return fetch('/api/contents/' + encodeURIComponent(permalink), {
778        credentials: 'same-origin',
779        headers: { 'Accept': 'application/json' }
780})
781.then(function (res) { return res.ok ? res.json() : null; })
782.then(function (json) {
783var result = {
784            tags: json && Array.isArray(json.tags) ? json.tags : [],
785            author: json && json.author ? json.author.title : null
786};
787          tagCache[permalink] = result;
788return result;
789})
790.catch(function () {
791          tagCache[permalink] = { tags: [], author: null };
792return tagCache[permalink];
793});
794}
795function processCards() {
796var slides = document.querySelectorAll('swiper-slide[data-custom="content-card"]');
797      slides.forEach(function (slide) {
798if (processed.has(slide)) return;
799if (slide.querySelector('.us-card-info')) return;
800        processed.add(slide);
801var titleLink = slide.querySelector('.card-title');
802if (!titleLink) return;
803var wrapper = document.createElement('div');
804        wrapper.className = 'us-card-info';
805var author = slide.getAttribute('data-author-title-0');
806if (author) {
807var authorEl = document.createElement('p');
808          authorEl.className = 'us-custom-author';
809          authorEl.textContent = author;
810          wrapper.appendChild(authorEl);
811}
812        slide.insertBefore(wrapper, titleLink);
813        wrapper.appendChild(titleLink);
814var link = slide.querySelector('a[href^="/programs/"]');
815if (link) {
816var permalink = link.getAttribute('href').replace('/programs/', '').split(/[?#]/)[0];
817          fetchTags(permalink).then(function (result) {
818if (result.tags.length > 0 && !wrapper.querySelector('.us-custom-meta')) {
819var metaEl = document.createElement('p');
820              metaEl.className = 'us-custom-meta';
821              metaEl.textContent = result.tags.join('  |  ');
822              wrapper.appendChild(metaEl);
823}
824});
825}
826});
827}
828function processPlaylistCards() {
829var cards = document.querySelectorAll('div[data-test="playlist-card"]');
830      cards.forEach(function (card) {
831if (processed.has(card)) return;
832if (card.querySelector('.us-playlist-author')) return;
833        processed.add(card);
834var infoRow = card.querySelector('.flex.justify-between');
835if (!infoRow) return;
836// Pull the dropdown out of the flex row and pin it to the card corner
837var dropdown = infoRow.querySelector('ds-dropdown');
838if (dropdown) {
839          card.style.position = 'relative';
840          dropdown.style.position = 'absolute';
841          dropdown.style.top = '8px';
842          dropdown.style.right = '8px';
843          card.appendChild(dropdown);
844}
845var contentId = card.getAttribute('data-content-id');
846if (!contentId) return;
847        fetchPermalink(contentId).then(function (permalink) {
848if (!permalink) return;
849          fetchTags(permalink).then(function (result) {
850if (result.author && !card.querySelector('.us-playlist-author')) {
851var authorEl = document.createElement('p');
852              authorEl.className = 'us-playlist-author';
853              authorEl.textContent = result.author;
854              card.insertBefore(authorEl, infoRow);
855}
856if (result.tags.length > 0 && !card.querySelector('.us-playlist-meta')) {
857var metaEl = document.createElement('p');
858              metaEl.className = 'us-playlist-meta';
859              metaEl.textContent = result.tags.join('  |  ');
860              card.appendChild(metaEl);
861}
862});
863});
864});
865}
866function run() {
867      clearTimeout(timer);
868      timer = setTimeout(function () {
869        processCards();
870        processPlaylistCards();
871}, 150);
872}
873var observer = new MutationObserver(run);
874function init() {
875      observer.disconnect();
876      observer.observe(document.body, { childList: true, subtree: true });
877      processCards();
878      processPlaylistCards();
879}
880if (document.body) init();
881else document.addEventListener('DOMContentLoaded', init);
882    document.addEventListener('turbo:load', init);
883    document.addEventListener('turbo:frame-load', run);
884})();
885</script>
885
886<style>
887swiper-slide[data-custom="content-card"] {
888border-radius: 12px !important;
889overflow: hidden;
890background: #ffffff;
891box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
892}
893div[data-test="playlist-card"] {
894border-radius: 12px !important;
895background: #ffffff;
896box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
897padding: 0 0 10px 0;
898}
899.us-card-info {
900padding: 10px 12px 14px;
901background: #ffffff;
902}
903.us-custom-author {
904font-size: 0.85rem;
905color: #6b7280;
906margin: 0 0 2px 0;
907line-height: 1.4;
908white-space: nowrap;
909overflow: hidden;
910text-overflow: ellipsis;
911}
912.us-card-info .card-title {
913font-weight: 600 !important;
914display: block;
915padding: 0 !important;
916margin: 0 !important;
917}
918.us-custom-meta {
919font-size: 0.8rem;
920color: #9ca3af;
921margin: 6px 0 0 0;
922line-height: 1.4;
923white-space: nowrap;
924overflow: hidden;
925text-overflow: ellipsis;
926}
927.us-playlist-author {
928font-size: 0.85rem;
929color: #6b7280;
930margin: 4px 12px 2px;
931line-height: 1.4;
932white-space: nowrap;
933overflow: hidden;
934text-overflow: ellipsis;
935}
936div[data-test="playlist-card"] .card-title {
937font-weight: 600 !important;
938display: block;
939padding: 0 !important;
940margin: 0 !important;
941width: 100%;
942}
943div[data-test="playlist-card"] .flex.justify-between {
944display: block !important;
945padding: 0 12px;
946}
947.us-playlist-meta {
948font-size: 0.8rem;
949color: #9ca3af;
950margin: 4px 12px 0;
951line-height: 1.4;
952white-space: nowrap;
953overflow: hidden;
954text-overflow: ellipsis;
955}
956</style>
957<!-- end uscreen support snippet -->
958<style>
959  .uscr-program-tags {
960  margin-top: 6px;
961  margin-bottom: 0;
962  font-size: 1rem;
963  font-weight: 600;
964  color: #0e5c63;
965  opacity: 0.9;
966  line-height: 1.4;
967  }
968</style>
969<script>
970(function () {
971  'use strict';
972
973  var TAG_CACHE = {};
974  var TAG_EL_ID = 'uscr-program-tag-line';
975  var WRAPPER_ID = 'uscr-program-title-wrapper';
976  var TITLE_SELECTOR = '[data-area="program-title"], [data-area="collection-title"]';
977  var processedTitles = new WeakSet();
978  var timer = null;
979
980  function getPermalink() {
981    var match = window.location.pathname.match(/^\/programs\/([^/?#]+)/);
982    return match ? match[1] : null;
983  }
984
985  function fetchTags(permalink) {
986    if (TAG_CACHE[permalink] !== undefined) {
987      return Promise.resolve(TAG_CACHE[permalink]);
988    }
989    return fetch('/api/contents/' + encodeURIComponent(permalink), {
990      credentials: 'same-origin',
991      headers: { 'Accept': 'application/json' }
992    })
993      .then(function (res) { return res.ok ? res.json() : null; })
994      .then(function (json) {
995        var tags = json && Array.isArray(json.tags) ? json.tags : [];
996        TAG_CACHE[permalink] = tags;
997        return tags;
998      })
999      .catch(function () {
1000        TAG_CACHE[permalink] = [];
1001        return [];
1002      });
1003  }
1004
1005  function processTitle() {
1006    var titleEl = document.querySelector(TITLE_SELECTOR);
1007    if (!titleEl) return;
1008    if (processedTitles.has(titleEl)) return;
1009    if (titleEl.parentNode && titleEl.parentNode.id === WRAPPER_ID) {
1010      processedTitles.add(titleEl);
1011      return;
1012    }
1013
1014    processedTitles.add(titleEl);
1015
1016    var permalink = getPermalink();
1017    if (!permalink) return;
1018
1019    // Only the program/collection-detail layout has the title as a flex child
1020    // next to the action bar. The collection landing page does NOT, so we skip
1021    // the wrapper there.
1022    var needsWrapping = titleEl.getAttribute('data-area') === 'program-title';
1023    var wrapper = null;
1024
1025    if (needsWrapping) {
1026      wrapper = document.createElement('div');
1027      wrapper.id = WRAPPER_ID;
1028      wrapper.className = 'flex-1 max-w-full lg:max-w-lg xl:max-w-2xl';
1029      titleEl.parentNode.insertBefore(wrapper, titleEl);
1030      wrapper.appendChild(titleEl);
1031    }
1032
1033    fetchTags(permalink).then(function (tags) {
1034      if (!tags || tags.length === 0) return;
1035      if (document.getElementById(TAG_EL_ID)) return;
1036      if (getPermalink() !== permalink) return;
1037      if (!document.body.contains(titleEl)) return;
1038
1039      var p = document.createElement('p');
1040      p.id = TAG_EL_ID;
1041      p.className = 'uscr-program-tags';
1042      p.textContent = tags.join('   |   ');
1043
1044      if (wrapper) {
1045        wrapper.appendChild(p);
1046      } else {
1047        titleEl.insertAdjacentElement('afterend', p);
1048      }
1049    });
1050  }
1051
1052  function run() {
1053    clearTimeout(timer);
1054    timer = setTimeout(processTitle, 100);
1055  }
1056
1057  var observer = new MutationObserver(run);
1058
1059  function init() {
1060    observer.disconnect();
1061    observer.observe(document.body, { childList: true, subtree: true });
1062    processTitle();
1063  }
1064
1065  if (document.body) init();
1066  else document.addEventListener('DOMContentLoaded', init);
1067  document.addEventListener('turbo:load', init);
1068  document.addEventListener('turbo:frame-load', run);
1069})();
1070</script>
1070
1071<!-- end uscreen support snippet -->
1072
1073<script src="https://5b8b666b9e33410b917df182e00403a0.js.ubembed.com" async></script>
1073
1074
1075<!-- Disable autoplay -->
1076<script>
1077   window.localStorage.setItem('AUTOPLAY', false)
1078</script>
1078
1079
1080
1081<script>
1082/*
1083########################################
1084# Replace the Filter placeholder terms below
1085########################################
1086*/
1087const newPlaceholderAuthor = "Teacher: All";
1088
1089/*
1090########################################
1091# Not recommended to change the code below
1092########################################
1093*/
1094
1095const updatePlaceholdersInterval = setInterval(() => {
1096    const authorSelect = document.getElementById('author_id');
1097    const categorySelect = document.getElementById('category_id');
1098
1099    let authorUpdated = false;
1100    let categoryUpdated = false;
1101
1102    if (authorSelect && authorSelect.placeholder !== newPlaceholderAuthor) {
1103        authorSelect.placeholder = newPlaceholderAuthor;
1104    }
1105    if (authorSelect && authorSelect.placeholder === newPlaceholderAuthor) {
1106        authorUpdated = true;
1107    }
1108
1109    if (categorySelect && categorySelect.placeholder !== newPlaceholderCategory) {
1110        categorySelect.placeholder = newPlaceholderCategory;
1111    }
1112    if (categorySelect && categorySelect.placeholder === newPlaceholderCategory) {
1113        categoryUpdated = true;
1114    }
1115
1116    if (authorUpdated && categoryUpdated) {
1117        clearInterval(updatePlaceholdersInterval);
1118    }
1119}, 500);
1120</script>
1120
1121  <!--
1122    Uscreen Support Snippet
1123    Description: Collection cards (.content-item) — boxed card + per-video author + tags
1124    Added by: matt w
1125    Date: 2026-06-01
1126  -->
1127  <style>
1128    .content-item {
1129      background: #ffffff;
1130      border-radius: 12px;
1131      overflow: hidden;
1132      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
1133    }
1134    .content-item .content-item-video img {
1135      border-radius: 0 !important;
1136    }
1137    .content-item .content-item-title,
1138    .content-item .us-ci-author,
1139    .content-item .content-item-description,
1140    .content-item .us-ci-tags {
1141      padding-left: 12px;
1142      padding-right: 12px;
1143    }
1144    .content-item .content-item-title {
1145      margin-top: 10px !important;
1146      font-weight: 600 !important;
1147    }
1148    .content-item .us-ci-author {
1149      margin: 2px 0 0;
1150      font-size: 0.85rem;
1151      line-height: 1.4;
1152      color: #6b7280;
1153      white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
1154    }
1155    .content-item .content-item-description {
1156      margin-top: 4px;
1157    }
1158    .content-item .us-ci-tags {
1159      margin: 6px 0 0;
1160      padding-bottom: 14px;
1161      font-size: 0.8rem;
1162      line-height: 1.4;
1163      color: #9ca3af;
1164      white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
1165    }
1166  </style>
1167  
1167<script>
1168  (function () {
1169    'use strict';
1170
1171    var META_CACHE = {};
1172    var processed = new WeakSet();
1173    var timer = null;
1174
1175    function fetchMeta(permalink) {
1176      if (META_CACHE[permalink]) return META_CACHE[permalink];
1177      META_CACHE[permalink] = fetch('/api/contents/' + encodeURIComponent(permalink), {
1178        credentials: 'same-origin',
1179        headers: { 'Accept': 'application/json' }
1180      })
1181        .then(function (res) { return res.ok ? res.json() : null; })
1182        .then(function (json) {
1183          return {
1184            author: json && json.author && json.author.title ? json.author.title : null,
1185            tags: json && Array.isArray(json.tags) ? json.tags : []
1186          };
1187        })
1188        .catch(function () { return { author: null, tags: [] }; });
1189      return META_CACHE[permalink];
1190    }
1191
1192    function permalinkFor(card) {
1193      // Variant A — no whole-collection access: card opens a modal, permalink in a data attr
1194      var modal = card.getAttribute('data-playlist-modal-subject-permalink-param');
1195      if (modal) return modal;
1196      // Variant B — has access: permalink is the ?permalink= query param on the href
1197      try {
1198        var pl = new URL(card.href).searchParams.get('permalink');
1199        if (pl) return pl;
1200      } catch (e) {}
1201      // Last resort: the program slug in the path
1202      var m = (card.getAttribute('href') || '').match(/\/programs\/([^/?#]+)/);
1203      return m ? m[1] : null;
1204    }
1205
1206    function processCards() {
1207      document.querySelectorAll('.content-item').forEach(function (card) {
1208        if (processed.has(card)) return;
1209        processed.add(card);
1210
1211        var permalink = permalinkFor(card);
1212        if (!permalink) return;
1213
1214        fetchMeta(permalink).then(function (meta) {
1215          if (!card.isConnected) return;
1216          var title = card.querySelector('.content-item-title');
1217          var desc = card.querySelector('.content-item-description');
1218
1219          if (meta.author && title && !card.querySelector('.us-ci-author')) {
1220            var a = document.createElement('p');
1221            a.className = 'us-ci-author';
1222            a.textContent = meta.author;
1223            title.insertAdjacentElement('afterend', a);
1224          }
1225
1226          if (meta.tags.length && !card.querySelector('.us-ci-tags')) {
1227            var t = document.createElement('p');
1228            t.className = 'us-ci-tags';
1229            t.textContent = meta.tags.join('   |   ');
1230            (desc || title).insertAdjacentElement('afterend', t);
1231          }
1232        });
1233      });
1234    }
1235
1236    function run() { clearTimeout(timer); timer = setTimeout(processCards, 150); }
1237    var observer = new MutationObserver(run);
1238    function init() {
1239      observer.disconnect();
1240      observer.observe(document.body, { childList: true, subtree: true });
1241      processCards();
1242    }
1243    if (document.body) init();
1244    else document.addEventListener('DOMContentLoaded', init);
1245    document.addEventListener('turbo:load', init);
1246    document.addEventListener('turbo:frame-load', run);
1247  })();
1248  </script>
1248
1249  <!-- end uscreen support snippet -->
1250
1251
1252
1253<!-- Feature: YogaEasy calendar dynamic banner sync (Tag: calendar, page-builder, Turbo) -->
1254<style>
1255  /* Page Builder's global :root defaults must not turn the calendar blue. */
1256  .calendar-page {
1257    --primary: #009090;
1258    --primary-rgb: 0, 144, 144;
1259    --uscreen-primary: #009090;
1260  }
1261
1262  #ye-calendar-banner {
1263    width: 100%;
1264    position: relative;
1265    z-index: 1;
1266  }
1267
1268  #ye-calendar-banner [data-editor="section"] {
1269    width: 100%;
1270  }
1271
1272
1273</style>
1274<script>
1275  (() => {
1276    const FLAG = "__YE_CALENDAR_BANNER_SYNC__";
1277    if (window[FLAG]) return;
1278    window[FLAG] = true;
1279
1280    const SOURCE_PATH = "/pages/calendar-banner";
1281    const TARGET_PATH_RE = /^\/calendar\/agenda\/?$/;
1282    const TARGET_SELECTOR = ".calendar-page";
1283    const WRAPPER_ID = "ye-calendar-banner";
1284    const STYLE_MARKER = "data-ye-calendar-banner-source-style";
1285
1286    let observer = null;
1287    let debounceTimer = null;
1288    let pendingBannerFetch = null;
1289    let routeRunId = 0;
1290    let resolvedTarget = null;
1291    const stylesheetCache = new Map();
1292
1293    const isTargetRoute = () => TARGET_PATH_RE.test(window.location.pathname);
1294
1295    const removeBanner = () => {
1296      const existing = document.getElementById(WRAPPER_ID);
1297      if (existing) existing.remove();
1298      resolvedTarget = null;
1299    };
1300
1301    const disconnectObserver = () => {
1302      if (observer) {
1303        observer.disconnect();
1304        observer = null;
1305      }
1306      if (debounceTimer) {
1307        window.clearTimeout(debounceTimer);
1308        debounceTimer = null;
1309      }
1310    };
1311
1312    const scopeStyles = (css) => {
1313      const sheet = new CSSStyleSheet();
1314      sheet.replaceSync(css);
1315      const serialize = (rules) => Array.from(rules).map((rule) => {
1316        if (rule.type === CSSRule.STYLE_RULE) {
1317          // The storefront has an !important heading-font override. Within
1318          // this banner, the Page Builder's selected font takes precedence.
1319          if (/\.(heading|body)_font_family/.test(rule.selectorText)) {
1320            const family = rule.style.getPropertyValue("font-family");
1321            if (family) rule.style.setProperty("font-family", `${family}, sans-serif`, "important");
1322          }
1323          return `#${WRAPPER_ID} :is(${rule.selectorText}) { ${rule.style.cssText} }`;
1324        }
1325        if (rule.type === CSSRule.MEDIA_RULE || rule.type === CSSRule.SUPPORTS_RULE) {
1326          const group = rule.type === CSSRule.MEDIA_RULE ? "media" : "supports";
1327          return `@${group} ${rule.conditionText} { ${serialize(rule.cssRules)} }`;
1328        }
1329        return "";
1330      }).join("\n");
1331      return serialize(sheet.cssRules);
1332    };
1333
1334    const sourceStyles = async (sourceDoc) => {
1335      const links = Array.from(sourceDoc.querySelectorAll('link[rel="stylesheet"]'));
1336      const builder = links.find((link) => /\/page_builder-[^/]+\.css/.test(link.href));
1337      let css = "";
1338      if (builder) {
1339        if (!stylesheetCache.has(builder.href)) {
1340          stylesheetCache.set(builder.href, fetch(builder.href).then((response) => {
1341            if (!response.ok) throw new Error(`Banner stylesheet returned ${response.status}`);
1342            return response.text();
1343          }).catch((error) => {
1344            stylesheetCache.delete(builder.href);
1345            throw error;
1346          }));
1347        }
1348        css = await stylesheetCache.get(builder.href);
1349      }
1350      sourceDoc.querySelectorAll(
1351        "style#card_border_radius, style#button_border_radius, style#heading_font_family, style#body_font_family",
1352      ).forEach((style) => { css += `\n${style.textContent}`; });
1353      // Page Builder normally asks WebFont to load these configured families.
1354      // Read the data without executing the source page's scripts.
1355      const families = new Set();
1356      sourceDoc.querySelectorAll("script:not([src])").forEach((script) => {
1357        for (const match of script.textContent.matchAll(/window\.(?:body|heading)_font_family\s*=\s*['"]([^'"]+)['"]/g)) {
1358          families.add(match[1]);
1359        }
1360      });
1361      const fonts = links.filter((link) => new URL(link.href).hostname === "fonts.googleapis.com")
1362        .map((link) => link.href);
1363      if (families.size) {
1364        fonts.push(`https://fonts.googleapis.com/css?family=${encodeURIComponent(Array.from(families).join("|"))}&display=swap`);
1365      }
1366      return {
1367        css: scopeStyles(css),
1368        fonts,
1369      };
1370    };
1371
1372    const promoteLazyImages = (root) => {
1373      root.querySelectorAll("a[href]").forEach((link) => {
1374        // Preserve the editor's URL (including query/hash) and new-tab choice.
1375        if (!link.target || link.target === "_self") link.target = "_top";
1376        link.setAttribute("data-turbo-frame", "_top");
1377      });
1378      root.querySelectorAll("[data-srcset], img[data-src]").forEach((image) => {
1379        for (const name of ["srcset", "src"]) {
1380          const value = image.getAttribute(`data-${name}`);
1381          if (value) {
1382            image.setAttribute(name, value);
1383            image.removeAttribute(`data-${name}`);
1384          }
1385        }
1386      });
1387      root.querySelectorAll(".lazy-loaded_preview, [data-controller~='lazy-loaded']").forEach((image) => {
1388        image.classList.remove("lazy-loaded_preview", "lazy-loaded_loaded");
1389        const controllers = (image.getAttribute("data-controller") || "")
1390          .split(/\s+/).filter((name) => name && name !== "lazy-loaded");
1391        if (controllers.length) image.setAttribute("data-controller", controllers.join(" "));
1392        else image.removeAttribute("data-controller");
1393      });
1394    };
1395
1396    const extractBannerHtml = async (html) => {
1397      const sourceDoc = new DOMParser().parseFromString(html, "text/html");
1398      // Native per-page Settings > General > Head Code Snippet > Active is
1399      // the calendar-copy switch. It does not unpublish the source page.
1400      const enabled = Array.from(sourceDoc.scripts).some((script) => script.textContent.includes(FLAG));
1401      if (!enabled) return null;
1402
1403      const section =
1404        sourceDoc.querySelector("body > [data-editor='section']") ||
1405        sourceDoc.querySelector("main [data-editor='section']") ||
1406        sourceDoc.querySelector("[data-editor='section']");
1407
1408      if (!section) return null;
1409
1410      const clone = section.cloneNode(true);
1411      clone.querySelectorAll("script").forEach((script) => script.remove());
1412      return { html: clone.outerHTML, ...await sourceStyles(sourceDoc) };
1413    };
1414
1415    const fetchBannerHtml = () => {
1416      if (pendingBannerFetch) return pendingBannerFetch;
1417
1418      pendingBannerFetch = fetch(SOURCE_PATH, {
1419        credentials: "same-origin",
1420        cache: "no-store",
1421        headers: { Accept: "text/html" },
1422      })
1423        .then((response) => {
1424          if (!response.ok) {
1425            throw new Error(`Banner page returned ${response.status}`);
1426          }
1427          return response.text();
1428        })
1429        .then(extractBannerHtml)
1430        .finally(() => {
1431          pendingBannerFetch = null;
1432        });
1433
1434      return pendingBannerFetch;
1435    };
1436
1437    const injectBanner = async () => {
1438      const runId = ++routeRunId;
1439
1440      if (!isTargetRoute()) {
1441        removeBanner();
1442        disconnectObserver();
1443        return;
1444      }
1445
1446      const target = document.querySelector(TARGET_SELECTOR);
1447      if (!target) return;
1448
1449      const existing = document.getElementById(WRAPPER_ID);
1450      if (existing && existing.nextElementSibling === target) {
1451        return;
1452      }
1453      if (!existing && resolvedTarget === target) return;
1454      if (existing) existing.remove();
1455
1456      try {
1457        const bannerHtml = await fetchBannerHtml();
1458        if (runId !== routeRunId || !isTargetRoute()) return;
1459        resolvedTarget = target;
1460        if (!bannerHtml) return;
1461
1462        const currentTarget = document.querySelector(TARGET_SELECTOR);
1463        if (!currentTarget) return;
1464
1465        const wrapper = document.createElement("div");
1466        wrapper.id = WRAPPER_ID;
1467        wrapper.setAttribute("data-ye-calendar-banner", "");
1468        const styles = document.createElement("style");
1469        styles.setAttribute(STYLE_MARKER, "");
1470        styles.textContent = bannerHtml.css;
1471        wrapper.appendChild(styles);
1472        bannerHtml.fonts.forEach((href) => {
1473          const font = document.createElement("link");
1474          font.rel = "stylesheet";
1475          font.href = href;
1476          wrapper.appendChild(font);
1477        });
1478        wrapper.insertAdjacentHTML("beforeend", bannerHtml.html);
1479        promoteLazyImages(wrapper);
1480        currentTarget.insertAdjacentElement("beforebegin", wrapper);
1481      } catch (error) {
1482        console.warn("[YogaEasy] Calendar banner injection failed", error);
1483      }
1484    };
1485
1486    const scheduleInject = () => {
1487      if (debounceTimer) window.clearTimeout(debounceTimer);
1488      debounceTimer = window.setTimeout(injectBanner, 80);
1489    };
1490
1491    const ensureObserver = () => {
1492      if (observer || !document.body) return;
1493      observer = new MutationObserver(scheduleInject);
1494      observer.observe(document.body, { childList: true, subtree: true });
1495    };
1496
1497    const boot = () => {
1498      if (isTargetRoute()) {
1499        ensureObserver();
1500        scheduleInject();
1501      } else {
1502        removeBanner();
1503        disconnectObserver();
1504      }
1505    };
1506
1507    if (document.readyState === "loading") {
1508      document.addEventListener("DOMContentLoaded", boot, { once: true });
1509    } else {
1510      boot();
1511    }
1512
1513    document.addEventListener("turbo:load", boot);
1514    document.addEventListener("turbo:render", boot);
1515    document.addEventListener("turbo:frame-load", boot);
1516    document.addEventListener("turbo:before-cache", () => {
1517      ++routeRunId;
1518      disconnectObserver();
1519      removeBanner();
1520    });
1521    window.addEventListener("popstate", boot);
1522  })();
1523</script>
1523
1524
1525<!--
1526    Feature: YogaEasy content card metadata enhancement (Tag: catalog, card-style)
1527    Purpose: Show level/style (from API `tags`) as a meta line under each catalog card.
1528  -->
1529<script>
1530  (function () {
1531    var processed = new WeakSet();
1532    var timer = null;
1533    var tagCache = {};
1534
1535    function fetchTags(permalink) {
1536      if (tagCache[permalink] !== undefined) {
1537        return Promise.resolve(tagCache[permalink]);
1538      }
1539      return fetch("/api/contents/" + encodeURIComponent(permalink), {
1540        credentials: "same-origin",
1541        headers: { Accept: "application/json" },
1542      })
1543        .then(function (res) {
1544          return res.ok ? res.json() : null;
1545        })
1546        .then(function (json) {
1547          var tags = json && Array.isArray(json.tags) ? json.tags : [];
1548          tagCache[permalink] = tags;
1549          return tags;
1550        })
1551        .catch(function () {
1552          tagCache[permalink] = [];
1553          return [];
1554        });
1555    }
1556
1557    function processCards() {
1558      var slides = document.querySelectorAll(
1559        'swiper-slide[data-custom="content-card"]',
1560      );
1561
1562      slides.forEach(function (slide) {
1563        if (processed.has(slide)) return;
1564        if (slide.querySelector(".us-card-info")) return;
1565        processed.add(slide);
1566
1567        var titleLink = slide.querySelector(".card-title");
1568        if (!titleLink) return;
1569
1570        var wrapper = document.createElement("div");
1571        wrapper.className = "us-card-info";
1572        slide.insertBefore(wrapper, titleLink);
1573        wrapper.appendChild(titleLink);
1574
1575        var link = slide.querySelector('a[href^="/programs/"]');
1576        if (!link) return;
1577
1578        var permalink = link
1579          .getAttribute("href")
1580          .replace("/programs/", "")
1581          .split(/[?#]/)[0];
1582
1583        fetchTags(permalink).then(function (tags) {
1584          if (!tags.length || wrapper.querySelector(".us-custom-meta")) return;
1585          var metaEl = document.createElement("p");
1586          metaEl.className = "us-custom-meta";
1587          metaEl.textContent = tags.join("  |  ");
1588          wrapper.appendChild(metaEl);
1589        });
1590      });
1591    }
1592
1593    function run() {
1594      clearTimeout(timer);
1595      timer = setTimeout(processCards, 150);
1596    }
1597    var observer = new MutationObserver(run);
1598    function init() {
1599      observer.disconnect();
1600      observer.observe(document.body, { childList: true, subtree: true });
1601      processCards();
1602    }
1603    if (document.body) init();
1604    else document.addEventListener("DOMContentLoaded", init);
1605    document.addEventListener("turbo:load", init);
1606    document.addEventListener("turbo:frame-load", run);
1607  })();
1608</script>
1608
1609
1610<style>
1611  swiper-slide[data-custom="content-card"] {
1612    border-radius: 12px !important;
1613    overflow: hidden;
1614    background: #ffffff;
1615    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
1616  }
1617
1618  .us-card-info {
1619    padding: 10px 12px 14px;
1620    background: #ffffff;
1621  }
1622
1623  .us-card-info .card-title {
1624    display: block;
1625    padding: 0 !important;
1626    margin: 0 !important;
1627    font-weight: 600 !important;
1628  }
1629
1630  .us-custom-meta {
1631    margin: 6px 0 0;
1632    font-size: 0.8rem;
1633    line-height: 1.4;
1634    color: #9ca3af;
1635    white-space: nowrap;
1636    overflow: hidden;
1637    text-overflow: ellipsis;
1638  }
1639</style>
1640
1641<script>
1642  /*
1643  ########################################
1644  # Replace the Filter placeholder terms below
1645  ########################################
1646  */
1647  const newPlaceholderAuthor = "Lehrer:innen: Alle";
1648  /*
1649  ########################################
1650  # Not recommended to change the code below
1651  ########################################
1652  */
1653
1654  (function () {
1655    let attempts = 0;
1656    const maxAttempts = 60; // 60 * 500ms = 30s total
1657
1658    const interval = setInterval(() => {
1659      attempts += 1;
1660
1661      const authorSelect = document.getElementById("author_id");
1662      if (authorSelect && authorSelect.placeholder !== newPlaceholderAuthor) {
1663        authorSelect.placeholder = newPlaceholderAuthor;
1664      }
1665
1666      if (
1667        (authorSelect && authorSelect.placeholder === newPlaceholderAuthor) ||
1668        attempts >= maxAttempts
1669      ) {
1670        clearInterval(interval);
1671      }
1672    }, 500);
1673  })();
1674</script>
1674
1675
1676<!-- Disable autoplay -->
1677<script>
1678  window.localStorage.setItem("AUTOPLAY", false);
1679</script>
1679
1680
1681<script
1682  src="https://5b8b666b9e33410b917df182e00403a0.js.ubembed.com"
1683  async
1684></script>
1684
1685<!--
1686  Feature: YogaEasy content card metadata enhancement
1687  Ported from: studio.yogaeasy.com
1688  Purpose: Add author/tag metadata and boxed card treatment on Swiper content cards.
1689-->
1690<script>
1691  (function () {
1692    var processed = new WeakSet();
1693    var timer = null;
1694    var tagCache = {};
1695
1696    function fetchTags(permalink) {
1697      if (tagCache[permalink] !== undefined) {
1698        return Promise.resolve(tagCache[permalink]);
1699      }
1700      return fetch("/api/contents/" + encodeURIComponent(permalink), {
1701        credentials: "same-origin",
1702        headers: { Accept: "application/json" },
1703      })
1704        .then(function (res) {
1705          return res.ok ? res.json() : null;
1706        })
1707        .then(function (json) {
1708          var tags = json && Array.isArray(json.tags) ? json.tags : [];
1709          tagCache[permalink] = tags;
1710          return tags;
1711        })
1712        .catch(function () {
1713          tagCache[permalink] = [];
1714          return [];
1715        });
1716    }
1717
1718    function processCards() {
1719      var slides = document.querySelectorAll(
1720        'swiper-slide[data-custom="content-card"]',
1721      );
1722      slides.forEach(function (slide) {
1723        if (processed.has(slide)) return;
1724        processed.add(sli
1724de);
1725
1726        var titleLink = slide.querySelector(".card-title");
1727        if (!titleLink) return;
1728
1729        // Get or create the wrapper
1730        var wrapper = slide.querySelector(".us-card-info");
1731        if (!wrapper) {
1732          wrapper = document.createElement("div");
1733          wrapper.className = "us-card-info";
1734          slide.insertBefore(wrapper, titleLink);
1735          wrapper.appendChild(titleLink);
1736        }
1737
1738        // Add author if present and not already rendered
1739        if (!wrapper.querySelector(".us-custom-author")) {
1740          var author = slide.getAttribute("data-author-title-0");
1741          if (author) {
1742            var authorEl = document.createElement("p");
1743            authorEl.className = "us-custom-author";
1744            authorEl.textContent = author;
1745            wrapper.insertBefore(authorEl, wrapper.firstChild);
1746          }
1747        }
1748
1749        // Fetch and add tags if not already rendered
1750        if (!wrapper.querySelector(".us-custom-meta")) {
1751          var link = slide.querySelector('a[href^="/programs/"]');
1752          if (!link) return;
1753          var permalink = link
1754            .getAttribute("href")
1755            .replace("/programs/", "")
1756            .split(/[?#]/)[0];
1757          fetchTags(permalink).then(function (tags) {
1758            if (!tags.length || wrapper.querySelector(".us-custom-meta"))
1759              return;
1760            var metaEl = document.createElement("p");
1761            metaEl.className = "us-custom-meta";
1762            metaEl.textContent = tags.join("  |  ");
1763            wrapper.appendChild(metaEl);
1764          });
1765        }
1766      });
1767    }
1768
1769    function run() {
1770      clearTimeout(timer);
1771      timer = setTimeout(processCards, 150);
1772    }
1773
1774    var observer = new MutationObserver(run);
1775
1776    function init() {
1777      observer.disconnect();
1778      observer.observe(document.body, { childList: true, subtree: true });
1779      processCards();
1780    }
1781
1782    if (document.body) {
1783      init();
1784    } else {
1785      document.addEventListener("DOMContentLoaded", init);
1786    }
1787    document.addEventListener("turbo:load", init);
1788    document.addEventListener("turbo:frame-load", run);
1789  })();
1790</script>
1790
1791
1792<style>
1793  swiper-slide[data-custom="content-card"] {
1794    border-radius: 12px !important;
1795    overflow: hidden;
1796    background: #ffffff;
1797    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
1798  }
1799  .us-card-info {
1800    padding: 10px 12px 14px;
1801    background: #ffffff;
1802  }
1803  .us-custom-author {
1804    margin: 0 0 2px;
1805    font-size: 0.85rem;
1806    line-height: 1.4;
1807    color: #6b7280;
1808    white-space: nowrap;
1809    overflow: hidden;
1810    text-overflow: ellipsis;
1811  }
1812  .us-card-info .card-title {
1813    display: block;
1814    padding: 0 !important;
1815    margin: 0 !important;
1816    font-weight: 600 !important;
1817  }
1818  .us-custom-meta {
1819    margin: 6px 0 0;
1820    font-size: 0.8rem;
1821    line-height: 1.4;
1822    color: #9ca3af;
1823    white-space: nowrap;
1824    overflow: hidden;
1825    text-overflow: ellipsis;
1826  }
1827</style>
1828<!--
1829    Uscreen Support Snippet
1830    Description: Collection cards (.content-item) — boxed card + per-video author + tags
1831    Added by: matt w
1832    Date: 2026-06-01
1833  -->
1834<style>
1835  .content-item {
1836    background: #ffffff;
1837    border-radius: 12px;
1838    overflow: hidden;
1839    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
1840  }
1841  /* thumbnail flush to the top; the card's rounding clips its corners */
1842  .content-item .content-item-video img {
1843    border-radius: 0 !important;
1844  }
1845  /* pad the text inset from the card edges (image stays full-bleed) */
1846  .content-item .content-item-title,
1847  .content-item .us-ci-author,
1848  .content-item .content-item-description,
1849  .content-item .us-ci-tags {
1850    padding-left: 12px;
1851    padding-right: 12px;
1852  }
1853  .content-item .content-item-title {
1854    margin-top: 10px !important;
1855    font-weight: 600 !important;
1856  }
1857  .content-item .us-ci-author {
1858    margin: 2px 0 0;
1859    font-size: 0.85rem;
1860    line-height: 1.4;
1861    color: #6b7280;
1862    white-space: nowrap;
1863    overflow: hidden;
1864    text-overflow: ellipsis;
1865  }
1866  .content-item .content-item-description {
1867    margin-top: 4px;
1868  }
1869  .content-item .us-ci-tags {
1870    margin: 6px 0 0;
1871    padding-bottom: 14px;
1872    font-size: 0.8rem;
1873    line-height: 1.4;
1874    color: #9ca3af;
1875    white-space: nowrap;
1876    overflow: hidden;
1877    text-overflow: ellipsis;
1878  }
1879</style>
1880<script>
1881  (function () {
1882    "use strict";
1883
1884    var META_CACHE = {};
1885    var processed = new WeakSet();
1886    var timer = null;
1887
1888    function fetchMeta(permalink) {
1889      if (META_CACHE[permalink]) return META_CACHE[permalink];
1890      META_CACHE[permalink] = fetch(
1891        "/api/contents/" + encodeURIComponent(permalink),
1892        {
1893          credentials: "same-origin",
1894          headers: { Accept: "application/json" },
1895        },
1896      )
1897        .then(function (res) {
1898          return res.ok ? res.json() : null;
1899        })
1900        .then(function (json) {
1901          return {
1902            author:
1903              json && json.author && json.author.title
1904                ? json.author.title
1905                : null,
1906            tags: json && Array.isArray(json.tags) ? json.tags : [],
1907          };
1908        })
1909        .catch(function () {
1910          return { author: null, tags: [] };
1911        });
1912      return META_CACHE[permalink];
1913    }
1914
1915    function permalinkFor(card) {
1916      try {
1917        var pl = new URL(card.href).searchParams.get("permalink");
1918        if (pl) return pl;
1919      } catch (e) {}
1920      var m = (card.getAttribute("href") || "").match(/\/programs\/([^/?#]+)/);
1921      return m ? m[1] : null;
1922    }
1923
1924    function processCards() {
1925      document
1926        .querySelectorAll('a.content-item[href^="/programs/"]')
1927        .forEach(function (card) {
1928          if (processed.has(card)) return;
1929          processed.add(card);
1930
1931          var permalink = permalinkFor(card);
1932          if (!permalink) return;
1933
1934          fetchMeta(permalink).then(function (meta) {
1935            if (!card.isConnected) return;
1936            var title = card.querySelector(".content-item-title");
1937            var desc = card.querySelector(".content-item-description");
1938
1939            // Author — directly below the title
1940            if (meta.author && title && !card.querySelector(".us-ci-author")) {
1941              var a = document.createElement("p");
1942              a.className = "us-ci-author";
1943              a.textContent = meta.author;
1944              title.insertAdjacentElement("afterend", a);
1945            }
1946
1947            // Tags — at the bottom of the card
1948            if (meta.tags.length && !card.querySelector(".us-ci-tags")) {
1949              var t = document.createElement("p");
1950              t.className = "us-ci-tags";
1951              t.textContent = meta.tags.join("   |   ");
1952              (desc || title).insertAdjacentElement("afterend", t);
1953            }
1954          });
1955        });
1956    }
1957
1958    function run() {
1959      clearTimeout(timer);
1960      timer = setTimeout(processCards, 150);
1961    }
1962    var observer = new MutationObserver(run);
1963    function init() {
1964      observer.disconnect();
1965      observer.observe(document.body, { childList: true, subtree: true });
1966      processCards();
1967    }
1968    if (document.body) init();
1969    else document.addEventListener("DOMContentLoaded", init);
1970    document.addEventListener("turbo:load", init);
1971    document.addEventListener("turbo:frame-load", run);
1972  })();
1973</script>
1973
1974<!-- end uscreen support snippet -->
1975
1976  </head>
1977  <body
1978    dir="ltr"
1979    class="light themeless group/body min-h-screen flex flex-col catalog-constrained"
1980  >
1981    
1982    <turbo-frame id="website_modal"></turbo-frame>    <div id="flash_messages">
1983</div>
1984
1985      <div class="sticky top-0 z-[100]">
1986
1987
1988
1989        
1990 
1991        
1992        <header id="website-header"
1993  class="sticky top-0 z-[100] flex bg-ds-main h-20 border-b border-ds-default"
1994  data-controller="themeless--header"
1995>
1996  <div class="container flex justify-between items-center my-auto">
1997    <div class="flex justify-start w-[8.75rem] flex-none">
1998      <a class="max-w-36 me-auto text-sm md:text-base text-ds-default" id="store-logo" data-type="main_logo" href="/">
1999          <img alt="yogaeasy.de" src="https://alpha.uscreencdn.com/images/logotypes/114649/logo_rgb.1775111741.png" />
2000</a>    </div>
2001
2002    <nav id="website-links" class="h-full min-w-0 mx-auto hidden md:flex justify-center -mb-px" data-themeless--header-target="navigation">
2003      <ul class="h-full flex justify-start space-x-4 invisible" data-themeless--header-target="navigationLinks">
2004
2005          <li class="relative group/item">
2006            <a class="flex items-center min-w-max overflow-y-hidden text-center px-4 pt-8 pb-7 h-full border-b border-b-4 hover:text-primary font-medium border-transparent text-ds-default" target="_self" data-type="home" href="/"><span class="flex items-center"><span>Videos</span></span></a>
2007          </li>
2008
2009          <li class="relative group/item">
2010            <a class="flex items-center min-w-max overflow-y-hidden text-center px-4 pt-8 pb-7 h-full border-b border-b-4 hover:text-primary font-medium border-transparent text-ds-default" target="_self" href="https://studio.yogaeasy.de/categories/programme"><span class="flex items-center"><span>Programme</span></span></a>
2011          </li>
2012
2013          <li class="relative group/item">
2014            <a class="flex items-center min-w-max overflow-y-hidden text-center px-4 pt-8 pb-7 h-full border-b border-b-4 hover:text-primary font-medium border-primary text-primary" target="_self" data-type="calendar" href="/calendar"><span class="flex items-center"><span>Kalender</span></span></a>
2015          </li>
2016
2017          <li class="relative group/item">
2018            <a class="flex items-center min-w-max overflow-y-hidden text-center px-4 pt-8 pb-7 h-full border-b border-b-4 hover:text-primary font-medium border-transparent text-ds-default" target="_self" href="/pages/yogalehrer"><span class="flex items-center"><span>Lehrer:innen</span></span></a>
2019          </li>
2020
2021          <li class="relative group/item">
2022            <a class="flex items-center min-w-max overflow-y-hidden text-center px-4 pt-8 pb-7 h-full border-b border-b-4 hover:text-primary font-medium border-transparent text-ds-default" target="_blank" href="https://www.yogaeasy.de/magazin"><span class="flex items-center"><span>Magazin</span></span></a>
2023          </li>
2024
2025          <li class="relative group/item">
2026            <a class="flex items-center min-w-max overflow-y-hidden text-center px-4 pt-8 pb-7 h-full border-b border-b-4 hover:text-primary font-medium border-transparent text-ds-default" target="_self" href="https://studio.yogaeasy.de/categories/academy"><span class="flex items-center"><span>Academy</span></span></a>
2027          </li>
2028
2029          <li class="relative group/item">
2030            <a class="flex items-center min-w-max overflow-y-hidden text-center px-4 pt-8 pb-7 h-full border-b border-b-4 hover:text-primary font-medium border-transparent text-ds-default" target="_blank" href="https://shop.yogaeasy.com/?_gl=1*gi6jl5*_gcl_aw*R0NMLjE3NzQ0MzcyMzguQ2owS0NRandqNDdPQmhDbUFSSXNBRjV3VUVHbFBROENjQkJwWFJKTTgwMkkxOEFMNWU5T2hpbTBDdmZKMzJjNWNFRmJPQ193MHNyb3JmWWFBb3dkRUFMd193Y0I.*_gcl_au*MTQ5NjU1OTgyMi4xNzczMjE3NTc3"><span class="flex items-center"><span>Shop</span></span></a>
2031          </li>
2032      </ul>
2033    </nav>
2034
2035    <div class="flex gap-x-2 items-center justify-end relative flex-none">
2036
2037
2038      <ds-button style="--font-size: var(--font-size-4-5)" class="hidden lg:block" size="big" variant="regular" href="/sign_in">Anmelden</ds-button>
2039          <ds-button style="--font-size: var(--font-size-4-5)" size="big" variant="primary" href="/join">Beitreten</ds-button>
2040
2041    <div class="relative flex items-center">
2042      <input type="checkbox" id="menu-toggle" class="peer hidden" data-themeless--header-target="toggle" data-action="change->themeless--header#onMenuToggle">
2043      <label for="menu-toggle" class="overlay hidden peer-checked:block fixed inset-0 "></label>
2044      <label for="menu-toggle" class="cursor-pointer flex items-center justify-center p-2 rounded bg-ds-subtle hover:opacity-80 lg:hidden w-12"
2045        data-themeless--header-target="toggleBurger"
2046      >
2047        <ds-icon class="text-ds-default block p-1" name="burger" size="24"></ds-icon>
2048        <ds-icon class="text-ds-default hidden p-1" name="cross" size="24"></ds-icon>
2049      </label>
2050
2051      <div id="main-menu" class="invisible" data-themeless--header-target="menu">
2052        <div class="overflow-hidden relative box-border h-full flex w-full">
2053          <nav class="h-auto max-h-full w-full min-h-0 overflow-y-auto px-4 py-2 border-ds-default" data-themeless--header-target="menuNavigation">
2054            <ul class="flex flex-col space-y-1">
2055                <li class="text-base md:text-sm font-normal leading-tight md:leading-none">
2056                  <a class="block py-2 h-10 md:h-8 hover:underline" href="/sign_in">Anmelden</a>
2057                </li>
2058                <li class="text-base md:text-sm font-normal leading-tight md:leading-none">
2059                  <a class="block py-2 min-h-10 md:min-h-8 hover:underline underline-offset-2" target="_self" data-type="home" href="/"><span class="flex items-center justify-items-stretch"><span>Videos</span></span></a>
2060                </li>
2061                <li class="text-base md:text-sm font-normal leading-tight md:leading-none">
2062                  <a class="block py-2 min-h-10 md:min-h-8 hover:underline underline-offset-2" target="_self" href="https://studio.yogaeasy.de/categories/programme"><span class="flex items-center justify-items-stretch"><span>Programme</span></span></a>
2063                </li>
2064                <li class="text-base md:text-sm font-normal leading-tight md:leading-none">
2065                  <a class="block py-2 min-h-10 md:min-h-8 hover:underline underline-offset-2 underline" target="_self" data-type="calendar" href="/calendar"><span class="flex items-center justify-items-stretch"><span>Kalender</span></span></a>
2066                </li>
2067                <li class="text-base md:text-sm font-normal leading-tight md:leading-none">
2068                  <a class="block py-2 min-h-10 md:min-h-8 hover:underline underline-offset-2" target="_self" href="/pages/yogalehrer"><span class="flex items-center justify-items-stretch"><span>Lehrer:innen</span></span></a>
2069                </li>
2070                <li class="text-base md:text-sm font-normal leading-tight md:leading-none">
2071                  <a class="block py-2 min-h-10 md:min-h-8 hover:underline underline-offset-2" target="_blank" href="https://www.yogaeasy.de/magazin"><span class="flex items-center justify-items-stretch"><span>Magazin</span></span></a>
2072                </li>
2073                <li class="text-base md:text-sm font-normal leading-tight md:leading-none">
2074                  <a class="block py-2 min-h-10 md:min-h-8 hover:underline underline-offset-2" target="_self" href="https://studio.yogaeasy.de/categories/academy"><span class="flex items-center justify-items-stretch"><span>Academy</span></span></a>
2075                </li>
2076                <li class="text-base md:text-sm font-normal leading-tight md:leading-none">
2077                  <a class="block py-2 min-h-10 md:min-h-8 hover:underline underline-offset-2" target="_blank" href="https://shop.yogaeasy.com/?_gl=1*gi6jl5*_gcl_aw*R0NMLjE3NzQ0MzcyMzguQ2owS0NRandqNDdPQmhDbUFSSXNBRjV3VUVHbFBROENjQkJwWFJKTTgwMkkxOEFMNWU5T2hpbTBDdmZKMzJjNWNFRmJPQ193MHNyb3JmWWFBb3dkRUFMd193Y0I.*_gcl_au*MTQ5NjU1OTgyMi4xNzczMjE3NTc3"><span class="flex items-center justify-items-stretch"><span>Shop</span></span></a>
2078                </li>
2079              <ds-viewport data-action="change->themeless--header#onScrollAnchorChange">
2080                <div class="w-full h-[1px]"></div>
2081              </ds-viewport>
2082            </ul>
2083          </nav>
2084          <div aria-hidden="true" class="absolute top-0 left-0 right-0 bottom-0 pointer-events-none opacity-1 translate-y-0 aria-hidden:translate-y-10 aria-hidden:opacity-0 transition-all" style="box-shadow: 0px -60px 30px -32px var(--ds-bg-overlay) inset;" data-themeless--header-target="menuNavigationScrollShadow"></div>
2085        </div>
2086        <div class="p-4 border-ds-default border-t bg-ds-overlay text-xs font-normal">
2087          <div class="flex flex-wrap gap-y-2">
2088              <a class="block w-full mb-2 text-primary hover:underline underline-offset-2" data-turbo="true" data-turbo-frame="website_modal" href="/about/apps">App holen -&gt;</a>
2089              <a class="text-ds-muted text-sm md:text-[11px]" href="/terms">
2090                <span class="hover:underline underline-offset-2">AGB</span>
2091</a>              <span class="flex items-center font-medium mx-1 last:hidden">∙</span>
2092              <a class="text-ds-muted text-sm md:text-[11px]" href="/privacy">
2093                <span class="hover:underline underline-offset-2">Datenschutz</span>
2094</a>              <span class="flex items-center font-medium mx-1 last:hidden">∙</span>
2095              <a class="text-ds-muted text-sm md:text-[11px]" href="/pages/impressum">
2096                <span class="hover:underline underline-offset-2">Impressum</span>
2097</a>
2097              <span class="flex items-center font-medium mx-1 last:hidden">∙</span>
2098              <a class="text-ds-muted text-sm md:text-[11px]" href="https://yogaeasy.jobs.personio.de/?language=de">
2099                <span class="hover:underline underline-offset-2">Jobs</span>
2100</a>              <span class="flex items-center font-medium mx-1 last:hidden">∙</span>
2101              <a class="text-ds-muted text-sm md:text-[11px]" href="https://portal.yogaeasy.de/cancel">
2102                <span class="hover:underline underline-offset-2">Verträge hier kündigen</span>
2103</a>              <span class="flex items-center font-medium mx-1 last:hidden">∙</span>
2104              <a class="text-ds-muted text-sm md:text-[11px]" href="/pages/faq">
2105                <span class="hover:underline underline-offset-2">Fragen und Antworten</span>
2106</a>              <span class="flex items-center font-medium mx-1 last:hidden">∙</span>
2107              <a class="text-ds-muted text-sm md:text-[11px]" href="https://studio.yogaeasy.de/pages/ye-newsletter-anmeldung">
2108                <span class="hover:underline underline-offset-2">Newsletter-Anmeldung</span>
2109</a>              <span class="flex items-center font-medium mx-1 last:hidden">∙</span>
2110              <a class="text-ds-muted text-sm md:text-[11px]" href="https://apps.apple.com/de/app/yogaeasy-online-yoga-studio/id692200180">
2111                <span class="hover:underline underline-offset-2">YogaEasy iOS App</span>
2112</a>              <span class="flex items-center font-medium mx-1 last:hidden">∙</span>
2113              <a class="text-ds-muted text-sm md:text-[11px]" href="https://play.google.com/store/apps/details?id=de.yogaeasy.videoapp&amp;hl=de&amp;pli=1">
2114                <span class="hover:underline underline-offset-2">YogaEasy Android App</span>
2115</a>              <span class="flex items-center font-medium mx-1 last:hidden">∙</span>
2116          </div>
2117        </div>
2118      </div>
2119    </div>
2120  </div>
2121  </div>
2122</header>
2123
2124
2125      </div>
2126
2127      
2128
2129      <main class="flex-1 bg-ds-default relative" data-controller="catalog-scroll-position">
2130          
2130<script>
2131    window.hotwireCatalog = true
2132  </script>
2132
2133
2134<script>
2135  window.apiUrl = "https://api.uscreencdn.com"
2136  window.apiOrigin = "yogaeasygermansite"
2137
2138  if (!window.CatalogAnalytics) {
2139    // Allows to call CatalogAnalytics from any other script or page without having to wait for the DOM to be loaded
2140    window.CatalogAnalytics = (...args) => {
2141      (window.CatalogAnalytics.q = window.CatalogAnalytics.q || []).push(args);
2142    };
2143  }
2144</script>
2144
2145
2146  <div id="flash_messages">
2147</div>
2148
2149
2150    
2151
2152
2153
2154      
2155<div class="storefront-widgets-container flex flex-col gap-4 sm:gap-3 fixed z-10 bottom-4 right-4 sm:bottom-auto sm:mt-10 sm:right-0 sm:mr-5">
2156
2157
2158</div>
2159
2160
2161      <turbo-frame data-turbo-action="replace" id="calendar" src="/calendar/agenda?action=show&amp;controller=storefront%2Fcalendar%2Fagenda"></turbo-frame>
2162
2163  
2164
2165
2166      </main>
2167
2168      <footer class="px-[3%] py-8 lg:py-6 w-full bg-ds-main border-t border-ds-default bg-ds-default text-ds-muted text-ds-base-regular">
2169  <div class="container flex flex-col space-y-4 justify-center">
2170    <div class="flex flex-wrap justify-center items-center space-y-2 space-x-2 text-center md:text-left rtl:md:text-right">
2171        <p class="mt-2 flex justify-center space-x-2 w-full md:w-fit text-center text-sm font-normal leading-5 text-ds-muted">
2172          <span id="store-title">© 2026 yogaeasy.de</span>
2173          <span class="ms-2 text-sm font-normal leading-5 text-ds-muted hidden md:inline-block">∙</span>
2174        </p>
2175          <p class="text-sm font-normal leading-5 text-ds-muted">
2176            <a class="hover:underline underline-offset-2" target="_self" href="/terms">AGB</a>
2177          </p>
2178          <p class="text-sm font-normal leading-5 text-ds-muted last-of-type:hidden">∙</p>
2179          <p class="text-sm font-normal leading-5 text-ds-muted">
2180            <a class="hover:underline underline-offset-2" target="_self" href="/privacy">Datenschutz</a>
2181          </p>
2182          <p class="text-sm font-normal leading-5 text-ds-muted last-of-type:hidden">∙</p>
2183          <p class="text-sm font-normal leading-5 text-ds-muted">
2184            <a class="hover:underline underline-offset-2" target="_self" href="/pages/impressum">Impressum</a>
2185          </p>
2186          <p class="text-sm font-normal leading-5 text-ds-muted last-of-type:hidden">∙</p>
2187          <p class="text-sm font-normal leading-5 text-ds-muted">
2188            <a class="hover:underline underline-offset-2" target="_blank" href="https://yogaeasy.jobs.personio.de/?language=de">Jobs</a>
2189          </p>
2190          <p class="text-sm font-normal leading-5 text-ds-muted last-of-type:hidden">∙</p>
2191          <p class="text-sm font-normal leading-5 text-ds-muted">
2192            <a class="hover:underline underline-offset-2" target="_blank" href="https://portal.yogaeasy.de/cancel">Verträge hier kündigen</a>
2193          </p>
2194          <p class="text-sm font-normal leading-5 text-ds-muted last-of-type:hidden">∙</p>
2195          <p class="text-sm font-normal leading-5 text-ds-muted">
2196            <a class="hover:underline underline-offset-2" target="_self" href="/pages/faq">Fragen und Antworten</a>
2197          </p>
2198          <p class="text-sm font-normal leading-5 text-ds-muted last-of-type:hidden">∙</p>
2199          <p class="text-sm font-normal leading-5 text-ds-muted">
2200            <a class="hover:underline underline-offset-2" target="_self" href="https://studio.yogaeasy.de/pages/ye-newsletter-anmeldung">Newsletter-Anmeldung</a>
2201          </p>
2202          <p class="text-sm font-normal leading-5 text-ds-muted last-of-type:hidden">∙</p>
2203          <p class="text-sm font-normal leading-5 text-ds-muted">
2204            <a class="hover:underline underline-offset-2" target="_blank" href="https://apps.apple.com/de/app/yogaeasy-online-yoga-studio/id692200180">YogaEasy iOS App</a>
2205          </p>
2206          <p class="text-sm font-normal leading-5 text-ds-muted last-of-type:hidden">∙</p>
2207          <p class="text-sm font-normal leading-5 text-ds-muted">
2208            <a class="hover:underline underline-offset-2" target="_blank" href="https://play.google.com/store/apps/details?id=de.yogaeasy.videoapp&amp;hl=de&amp;pli=1">YogaEasy Android App</a>
2209          </p>
2210          <p class="text-sm font-normal leading-5 text-ds-muted last-of-type:hidden">∙</p>
2211          <div class="mt-4 md:!ms-4 w-full md:w-fit text-center text-primary hover:underline underline-offset-2 leading-5">
2212            <a class="text-sm text-primary font-medium" data-turbo="true" data-turbo-frame="website_modal" href="/about/apps">App holen -&gt;</a>
2213          </div>
2214      </div>
2215
2216  </div>
2217  <div class="flex mt-0 md:mt-4 items-center justify-center">
2218    
2219  </div>
2220</footer>
2221
2222
2223
2224  </body>
2225</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.