PageSourceSearch

https://medicrise.cz/app.js

js medicrise.cz collected 2026-10-08 21:08:15 UTC 209,012 bytes, 2,750 lines download raw bytes

1const ASSETS = {
2  logo: "/assets/medicrise-logo.svg",
3  hero: "https://static.wixstatic.com/media/dd6e74_3ceeb2ec9bb3417881d2906c4dd65b7af000.jpg/v1/fill/w_980%2Ch_653%2Cal_c%2Cq_85%2Cusm_0.66_1.00_0.01%2Cenc_avif%2Cquality_auto/dd6e74_3ceeb2ec9bb3417881d2906c4dd65b7af000.jpg",
4  seminar: "/assets/illustrations/Seminar-pana-3.svg",
5  doctors: "/assets/illustrations/Doctors-pana.svg",
6  discussion: "/assets/illustrations/Group discussion-pana-2.svg",
7  subscribe: "/assets/illustrations/Subscriber-pana-2.svg",
8  partners: "/assets/illustrations/Creative team-pana.svg",
9};
10
11const courses = {
12  nespokojeny: {
13    title: "Komunikace s nespokojeným pacientem",
14    lead: "Rozvíjejte své dovednosti potřebné pro zvládání interakcí s pacienty, kteří vyjadřují nespokojenost a stížnosti na poskytovanou péči.",
15    protocol: "BLAST",
16    intro: [
17      "V rámci poskytování zdravotní péče se zdravotníci často setkávají s nespokojenými pacienty, kteří mohou vyjadřovat frustraci, obavy nebo nedorozumění týkající se poskytované péče. Efektivní komunikace je klíčová pro zachování důvěry, respektu a kvality zdravotních služeb.",
18      "Kurz se zaměřuje na komunikační protokol BLAST (Believe – Listen – Apologize – Satisfy – Thank) jako strukturovaný nástroj pro zvládání interakcí s nespokojenými pacienty. Praktická cvičení a simulace pomáhají účastníkům převést techniky přímo do praxe.",
19    ],
20    features: [
21      [
22        "💡",
23        "Tipy a triky",
24        "Konkrétní kroky protokolu BLAST a jejich využití při komunikaci s nespokojenými pacienty.",
25      ],
26      [
27        "💬",
28        "Networking a sdílení",
29        "Sdílení zkušeností a strategií v interaktivních diskuzích a skupinových cvičeních.",
30      ],
31      [
32        "🧭",
33        "Efektivní techniky",
34        "Uznávání perspektivy, aktivní naslouchání, identifikace potřeb a řešení problémů.",
35      ],
36      [
37        "🎭",
38        "Praktický nácvik",
39        "Simulované situace v bezpečném prostředí a okamžitá praktická aplikace.",
40      ],
41    ],
42    guide: "Průvodce komunikací „BLAST“",
43    guideSlug: "blast-pruvodce",
44    guideTitle: "BLAST: pět kroků pro rozhovor s nespokojeným pacientem",
45    guideDescription:
46      "Praktický čtyřstránkový průvodce s konkrétními větami pro uznání perspektivy, naslouchání, omluvu, hledání řešení a bezpečné uzavření rozhovoru.",
47    guideCover: "/assets/email/lead-magnets/blast-1.png",
48  },
49  agresivni: {
50    title: "Komunikace s agresivním pacientem",
51    lead: "Získejte dovednost zvládání krizových situací v kontaktu s rozčílenými či agresivními pacienty.",
52    protocol: "Verbální deeskalace",
53    intro: [
54      "Během poskytování zdravotní péče se nezřídka setkáváme s pacienty, kteří jsou rozčílení, neklidní či verbálně agresivní. Tyto situace kladou vysoké nároky na zdravotnický personál.",
55      "Cílem kurzu je seznámit účastníky s komunikačními technikami pro zklidnění rozčíleného či neklidného pacienta, základy verbální deeskalace a prevencí eskalace napětí.",
56    ],
57    features: [
58      [
59        "💡",
60        "Tipy a triky",
61        "Rozpoznání potenciálně konfliktních situací a aktivní prevence.",
62      ],
63      [
64        "💬",
65        "Networking a sdílení",
66        "Vzájemné sdílení zkušeností prostřednictvím vedených diskuzí.",
67      ],
68      [
69        "🧭",
70        "Efektivní techniky",
71        "Jednoduché techniky pro verbální zklidnění pacienta.",
72      ],
73      [
74        "🎭",
75        "Praktický nácvik",
76        "Procvičení nově nabytých technik v simulovaných situacích.",
77      ],
78    ],
79    guide: "Desatero deeskalace",
80    guideSlug: "desatero-deeskalace",
81    guideTitle: "Desatero deeskalace: opora pro vyhrocené situace",
82    guideDescription:
83      "Deset srozumitelných principů pro bezpečí, zklidnění napětí, nastavení hranic a návrat ke konstruktivní spolupráci.",
84    guideCover: "/assets/email/lead-magnets/desatero-1.png",
85  },
86  "zavazne-zpravy": {
87    title: "Sdělování závažných zpráv",
88    lead: "Posilněte svou dovednost jasného a empatického předávání náročných informací pacientům a jejich rodinám.",
89    protocol: "SPIKES",
90    intro: [
91      "Během klinické praxe se zdravotníci často ocitají v situacích, kdy musí pacientům sdělovat závažné informace týkající se jejich zdravotního stavu a poskytované péče.",
92      "Kurz představuje protokol SPIKES (Setting – Perception – Invitation – Knowledge – Emotions – Strategy) a jeho použití při strukturovaném, empatickém sdělování náročných informací.",
93    ],
94    features: [
95      [
96        "💡",
97        "Tipy a triky",
98        "Efektivní a empatické sdělování závažných informací pomocí SPIKES.",
99      ],
100      [
101        "💬",
102        "Networking a sdílení",
103        "Prostor pro podporu, diskuzi a výměnu zkušeností.",
104      ],
105      [
106        "🧭",
107        "Efektivní techniky",
108        "Praktické nástroje pro práci s emočními reakcemi pacientů.",
109      ],
110      [
111        "🎭",
112        "Praktický nácvik",
113        "Simulace a interaktivní cvičení s možností zpětné vazby.",
114      ],
115    ],
116    guide: null,
117  },
118  asertivni: {
119    title: "Asertivní komunikace",
120    lead: "Naučte se s respektem vyjadřovat své potřeby, stanovovat hranice a zvládat náročné komunikační situace.",
121    protocol: "DESC",
122    intro: [
123      "Asertivní komunikace umožňuje zdravotníkům chránit vlastní hranice a současně udržet profesionální, respektující vztah s pacientem i kolegy.",
124      "Kurz pracuje s protokolem DESC a konkrétními technikami, které pomáhají strukturovat sdělení, popsat situaci a formulovat potřeby i důsledky bez zbytečné eskalace.",
125    ],
126    features: [
127      [
128        "💡",
129        "Tipy a triky",
130        "Komunikační protokol DESC a jeho aplikace v praktických situacích.",
131      ],
132      [
133        "✋",
134        "Stanovování hranic",
135        "Asertivní a respektující vyjadřování potřeb a jasných hranic.",
136      ],
137      [
138        "🧭",
139        "Efektivní techniky",
140        "Nástroje pro situace, kdy jednání pacienta narušuje poskytování péče.",
141      ],
142      [
143        "🎭",
144        "Praktický nácvik",
145        "Simulace a sdílení zkušeností s ostatními účastníky kurzu.",
146      ],
147    ],
148    guide: null,
149  },
150  "mbti-pacienti": {
151    title: "Typologie MBTI® – Komunikace s pacienty",
152    lead: "Naučte se efektivně komunikovat s pacienty díky porozumění odlišným osobnostním preferencím.",
153    protocol: "MBTI®",
154    intro: [
155      "Workshop je zaměřen na rozvoj dovedností efektivní komunikace prostřednictvím porozumění osobnostním preferencím pacientů. Účastníci se seznámí se základními dimenzemi MBTI® a s tím, jak se mohou promítat do komunikačních potřeb.",
156      "Důraz je kladen na schopnost přizpůsobovat vlastní komunikační styl konkrétnímu pacientovi a na praktickou aplikaci v simulovaných situacích.",
157    ],
158    features: [
159      [
160        "🧩",
161        "Základní principy MBTI®",
162        "Porozumění základním principům typologie a její aplikaci v praxi.",
163      ],
164      [
165        "↔️",
166        "Reagování na odlišnosti",
167        "Lepší naplňování potřeb pacientů s ohledem na jejich preference.",
168      ],
169      [
170        "🔎",
171        "Rozpoznání preferencí",
172        "Citlivější vnímání rozdílů v tom, jak lidé přijímají a zpracovávají informace.",
173      ],
174      [
175        "💬",
176        "Komunikační techniky",
177        "Praktické přizpůsobování stylu komunikace konkrétním pacientům.",
178      ],
179    ],
180    guide: "Průvodce komunikací dle typologie MBTI®",
181    guideSlug: "mbti-komunikace-pruvodce",
182    guideTitle: "MBTI® v komunikaci s pacienty",
183    guideDescription:
184      "Praktický průvodce komunikací s pacienty s různými potřebami a způsoby rozhodování.",
185    guideCover: "/assets/email/lead-magnets/mbti-1.png",
186  },
187  "mbti-tym": {
188    title: "Typologie MBTI® – Týmová spolupráce",
189    lead: "Využijte MBTI® jako společný jazyk pro porozumění rozdílům, komunikaci a spolupráci v týmu.",
190    protocol: "MBTI®",
191    intro: [
192      "Workshop využívá typologii MBTI® k lepšímu porozumění tomu, proč různí lidé preferují odlišné způsoby komunikace, rozhodování, plánování nebo spolupráce.",
193      "Účastníci získají bezpečný rámec pro pojmenování rozdílů a hledání způsobů, jak z nich v týmové práci udělat výhodu.",
194    ],
195    features: [
196      [
197        "🧩",
198        "Společný jazyk",
199        "Srozumitelný rámec pro popis odlišných preferencí.",
200      ],
201      [
202        "💬",
203        "Komunikační styl",
204        "Lepší přizpůsobení komunikace různým členům týmu.",
205      ],
206      [
207        "🤝",
208        "Silnější spolupráce",
209        "Využití rozdílů jako zdroje, nikoli překážky.",
210      ],
211      [
212        "🎭",
213        "Praktický nácvik",
214        "Cvičení a situace navázané na každodenní týmovou praxi.",
215      ],
216    ],
217    guide: null,
218  },
219};
220
221const coursePages = {
222  "komunikace-s-nespokojenym-pacientem.html": "nespokojeny",
223  "komunikace-s-agresivnim-pacientem.html": "agresivni",
224  "sdelovani-zavaznych-zprav.html": "zavazne-zpravy",
225  "asertivni-komunikace.html": "asertivni",
226  "typologie-mbti-komunikace-s-pacienty.html": "mbti-pacienti",
227  "typologie-mbti-tymova-spoluprace.html": "mbti-tym",
228};
229
230const CONF_ASSETS = {
231  hotel:
232    "https://static.wixstatic.com/media/dd6e74_1b101117d53f4e458925f50d12adaa9b~mv2.jpeg/v1/fill/w_1100,h_730,al_c,q_85,enc_avif,quality_auto/dd6e74_1b101117d53f4e458925f50d12adaa9b~mv2.jpeg",
233  catering:
234    "https://static.wixstatic.com/media/dd6e74_1a472764fb264979b1b8bdd337221022~mv2.jpg/v1/fill/w_1100,h_730,al_c,q_85,enc_avif,quality_auto/file-3.jpg",
235  afterparty:
236    "https://static.wixstatic.com/media/dd6e74_102222a5b59a43db9f0b59de1ba4ed66~mv2.png/v1/fill/w_700,h_700,al_c,q_85,enc_avif,quality_auto/Kopie%20output-onlinepngtools.png",
237};
238
239const conferenceYears = {
240  2026: {
241    edition: "5. ročník",
242    topic: "Zdravé zdravotnictví 2026",
243    date: "27. 11. 2026",
244    venue: "Hermitage Hotel Praha",
245    status: "Připravujeme",
246    intro:
247      "Pátý ročník konference připravujeme. Opět chceme propojit praktické komunikační dovednosti, inspirativní přednášky, interaktivní workshopy a lidi, kteří chtějí posouvat zdravotnictví k větší lidskosti.",
248    program: null,
249  },
250  2025: {
251    edition: "4. ročník",
252    topic: "Nespokojený pacient, vyčerpaný zdravotník: Existuje cesta ven?",
253    date: "1. 11. 2025",
254    venue: "Hermitage Hotel Praha",
255    status: "Proběhlo",
256    intro:
257      "Čtvrtý ročník otevřel téma péče o pacienty i o samotné zdravotníky a poprvé zařadil také panelovou diskuzi.",
258    program: [
259      { time: "8:00–9:00", label: "Registrace", kind: "service" },
260      { time: "9:00–9:30", label: "Úvod a zahájení", kind: "service" },
261      {
262        time: "9:30–10:30",
263        label: "Úvodní přednáška",
264        sessions: [
265          {
266            type: "Přednáška",
267            speaker: "Jana Valachovičová",
268            title:
269              "Za péčí o pacienty stojí ti, o které často nikdo nepečuje. Pojďme to změnit.",
270            img: "https://static.wixstatic.com/media/dd6e74_f325535599ec4e57814a83087864aa5e~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/valachovicova-modified.png",
271          },
272        ],
273      },
274      { time: "10:30–11:00", label: "Coffee break", kind: "break" },
275      {
276        time: "11:00–12:30",
277        label: "Vzdělávací blok",
278        sessions: [
279          {
280            type: "Přednáška",
281            speaker: "Petra Vavrošová",
282            title:
283              "Naslouchám i tam, kde jiní mlčí – úskalí a radosti práce nemocniční ombudsmanky",
284            img: "https://static.wixstatic.com/media/dd6e74_c18e85280b5940489ba6c9b3d46b6865~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Vavro%C5%A1ov%C3%A1.png",
285          },
286          {
287            type: "Workshop",
288            speaker: "Václav Strnadel",
289            title: "Respektující komunikace (nejen) s pacientem",
290            img: "https://static.wixstatic.com/media/dd6e74_f2b44b37c37742e7a5a567a13f8db734~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Strnadel.png",
291          },
292          {
293            type: "Workshop",
294            speaker: "Barbora Mechúrová",
295            title:
296              "Asertivní komunikace: Jak si nastavit zdravé hranice ve vztahu k pacientům i kolegům",
297            img: "https://static.wixstatic.com/media/dd6e74_2ef74daba3ad4513b4053d1dff085a72~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Mech%C3%BArov%C3%A1.png",
298          },
299          {
300            type: "Workshop",
301            speaker: "Michal Dubec",
302            title: "Zvládání manipulací a výmluv při spolupráci v týmu",
303            img: "https://static.wixstatic.com/media/dd6e74_940a7f71ae2c4693b4eae202c2b7d1fc~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Dubec.png",
304          },
305        ],
306      },
307      { time: "12:30–14:00", label: "Oběd", kind: "break" },
308      {
309        time: "14:00–15:30",
310        label: "Vzdělávací blok",
311        sessions: [
312          {
313            type: "Panelová diskuze",
314            speakers: [
315              { name: "Petra Vavrošová", img: "/assets/speakers/petra-vavrosova.webp" },
316              { name: "Václav Strnadel", img: "/assets/speakers/vaclav-strnadel.webp" },
317              { name: "JUDr. Barbora Steinlauf MA, Ph.D.", img: "/assets/speakers/barbora-steinlauf.webp" },
318              { name: "Anežka Dašková", img: "/assets/speakers/anezka-daskova.webp" },
319            ],
320            title:
321              "Cestou necestou aneb když pacient prochází systémem. Umíme číst v mapě?",
322          },
323          {
324            type: "Workshop",
325            speaker: "Lucie Hornová",
326            title: "Dialogická komunikace v týmu zdravotníků",
327            img: "https://static.wixstatic.com/media/dd6e74_d4a8c2efe4c849d1a16954e17c46e642~mv2.png/v1/fill/w_320,h_160,al_c,q_85,enc_avif,quality_auto/Hornov%C3%A1.png",
328          },
329          {
330            type: "Workshop",
331            speaker: "Ondřej Mechúr",
332            title: "Jak osobnostní preference ovlivňují komunikaci s pacienty?",
333            img: "https://static.wixstatic.com/media/dd6e74_7c085bffc99441d5b1b05b815f8f3bec~mv2.png/v1/fill/w_320,h_160,al_c,q_85,enc_avif,quality_auto/Mech%C3%BAr.png",
334          },
335          {
336            type: "Workshop",
337            speaker: "Michal Dubec",
338            title:
339              "Práce se vzájemnými očekáváními a pravidly při spolupráci v týmu",
340            img: "https://static.wixstatic.com/media/dd6e74_ca5a4ce132c74916a4e655cbf3e52f61~mv2.png/v1/fill/w_320,h_160,al_c,q_85,enc_avif,quality_auto/Dubec2.png",
341          },
342        ],
343      },
344      { time: "15:30–16:00", label: "Coffee break", kind: "break" },
345      {
346        time: "16:00–17:30",
347        label: "Vzdělávací blok",
348        sessions: [
349          {
350            type: "Přednáška",
351            speaker: "Lenka Kaška",
352            title:
353              "Budoucnost zdravotnické dokumentace v době eHealth a jak to změní komunikaci s pacientem",
354            img: "https://static.wixstatic.com/media/dd6e74_66d6029b901a4765b1b788c8da8db0bb~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Ka%C5%A1ka.png",
355          },
356          {
357            type: "Workshop",
358            speaker: "Marika Svatošová",
359            title: "Work-life balance aneb Jak se ze všeho nezbláznit?",
360            img: "https://static.wixstatic.com/media/dd6e74_778bc0a820cd4149b35e0dbb044c18f9~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Svato%C5%A1ov%C3%A1.png",
361          },
362          {
363            type: "Workshop",
364            speaker: "Dobroslava Stránská",
365            title: "Zpětná vazba v práci zdravotníků",
366            img: "https://static.wixstatic.com/media/dd6e74_cf41b7d69d4b4b2696101e7b2176fef4~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Str%C3%A1nsk%C3%A1.png",
367          },
368          {
369            type: "Workshop",
370            speaker: "Jan Běhounek",
371            title: "Komunikace s agresivním pacientem",
372            img: "https://static.wixstatic.com/media/dd6e74_bf7fe23f94ac4b7a98fe23a95df4997c~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/B%C4%9Bhounek.png",
373          },
374        ],
375      },
376      { time: "17:30–18:00", label: "Slavnostní ukončení", kind: "service" },
377    ],
378  },
379  2024: {
380    edition: "3. ročník",
381    topic: "Náročný pacient: Jak s ním komunikovat (a existuje vůbec)?",
382    date: "23. 11. 2024",
383    venue: "Hermitage Hotel Praha",
384    status: "Proběhlo",
385    intro:
386      "Třetí ročník se zaměřil na náročné komunikační situace a praktickou práci s pacientem i týmem.",
387    program: [
388      { time: "8:00–9:00", label: "Registrace", kind: "service" },
389      { time: "9:00–9:30", label: "Úvod a zahájení", kind: "service" },
390      {
391        time: "9:30–10:30",
392        label: "Úvodní přednáška",
393        sessions: [
394          {
395            type: "Přednáška",
396            speaker: "MUDr. Lucie Hrdličková",
397            title: "Náročný pacient: Jak s ním komunikovat (a existuje vůbec)?",
398            img: "https://static.wixstatic.com/media/dd6e74_8db8edaf81cc482780de772b95ab07cc~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Hrdlickova.png",
399          },
400        ],
401      },
402      { time: "10:30–11:00", label: "Coffee break", kind: "break" },
403      {
404        time: "11:00–12:30",
405        label: "Vzdělávací blok",
406        sessions: [
407          {
408            type: "Přednáška",
409            speaker: "MUDr. Dita Šamánková",
410            title: "Jak sladit vnitřní svět pacienta a zdravotníka?",
411            img: "https://static.wixstatic.com/media/dd6e74_f6cbff41e7bc4472b78d7c82e97f5931~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Samankova.png",
412          },
413          {
414            type: "Workshop",
415            speaker: "MUDr. Petr Pavlíček",
416            title: "Komunikace náročných zpráv v onkologické praxi",
417            img: "https://static.wixstatic.com/media/dd6e74_cbcf5afc62cf479c8bee41347d576eda~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Pavlicek.png",
418          },
419          {
420            type: "Workshop",
421            speaker: "MUDr. Marika Svatošová",
422            title: "Motivace pacienta a jak s ní pracovat",
423            img: "https://static.wixstatic.com/media/dd6e74_f298788292be41d4a9c4e9072c66d0d4~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Marika.png",
424          },
425          {
426            type: "Workshop",
427            speaker: "Mgr. Michal Dubec, Ph.D.",
428            title: "Za zdraví pacienta není odpovědný lékař",
429            img: "https://static.wixstatic.com/media/dd6e74_940a7f71ae2c4693b4eae202c2b7d1fc~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Dubec.png",
430          },
431        ],
432      },
433      { time: "12:30–14:00", label: "Oběd", kind: "break" },
434      {
435        time: "14:00–15:30",
436        label: "Vzdělávací blok",
437        sessions: [
438          {
439            type: "Přednáška",
440            speakers: [
441              { name: "Mgr. Petra Adámková", img: "/assets/speakers/petra-adamkova.webp" },
442              { name: "Bc. Michaela Tůmová", img: "/assets/speakers/michaela-tumova.webp" },
443            ],
444            title:
445              "Cesta tam a zase zpátky aneb cesta systémem zdravotní péče očima pacienta",
446          },
447          {
448            type: "Workshop",
449            speaker: "MUDr. Karolína Ježková",
450            title:
451              "Komunikace jako jeden ze základních kamenů kvalitní zdravotní péče – srovnání ČR/Německo",
452            img: "https://static.wixstatic.com/media/dd6e74_4232fdf1c8dc441db2bdef0e378a144d~mv2.png/v1/fill/w_320,h_160,al_c,q_85,enc_avif,quality_auto/Jezkova.png",
453          },
454          {
455            type: "Workshop",
456            speaker: "MUDr. Barbora Mechúrová",
457            title: "Komunikace s nespokojeným pacientem",
458            img: "https://static.wixstatic.com/media/dd6e74_073a2c586b414e24aa536ad70b5053ae~mv2.png/v1/fill/w_320,h_160,al_c,q_85,enc_avif,quality_auto/Mechurova.png",
459          },
460          {
461            type: "Workshop",
462            speaker: "Mgr. Michal Dubec, Ph.D.",
463            title: "Za zdraví pacienta není odpovědný lékař",
464            img: "https://static.wixstatic.com/media/dd6e74_ca5a4ce132c74916a4e655cbf3e52f61~mv2.png/v1/fill/w_320,h_160,al_c,q_85,enc_avif,quality_auto/Dubec2.png",
465          },
466        ],
467      },
468      { time: "15:30–16:00", label: "Coffee break", kind: "break" },
469      {
470        time: "16:00–17:30",
471        label: "Vzdělávací blok",
472        sessions: [
473          {
474            type: "Přednáška",
475            speaker: "JUDr. Barbora Steinlauf, MA, Ph.D.",
476            title:
477              "Pacient s Alzheimerovou nemocí – příklady nevhodné komunikace a špatného zacházení pohledem advokáta",
478            img: "https://static.wixstatic.com/media/dd6e74_e38fdc78d1a34fd19dc5ff6d2c8120ab~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Steinlauf.png",
479          },
480          {
481            type: "Workshop",
482            speaker: "Mgr. Jan Běhounek",
483            title: "Komunikace s agresivním pacientem",
484            img: "https://static.wixstatic.com/media/dd6e74_f8cef6955c4b4174820e8eb5d1ac2f61~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Behounek.png",
485          },
486          {
487            type: "Workshop",
488            speaker: "Ing. Bc. Dobroslava Stránská",
489            title: "Zpětná vazba v práci zdravotníků",
490            img: "https://static.wixstatic.com/media/dd6e74_cf41b7d69d4b4b2696101e7b2176fef4~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Stranska.png",
491          },
492          {
493            type: "Workshop",
494            speaker: "Tereza Robinson",
495            title: "Respektující komunikace a psychohygiena",
496            img: "https://static.wixstatic.com/media/dd6e74_a82a098afc5b4d67a6a1175725671d05~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Robinson.png",
497          },
498        ],
499      },
500      { time: "17:30–18:00", label: "Slavnostní ukončení", kind: "service" },
501    ],
502  },
503  2023: {
504    edition: "2. ročník",
505    topic: "Pacient jako partner v léčebném procesu",
506    date: "2. 12. 2023",
507    venue: "Hermitage Hotel Praha",
508    status: "Proběhlo",
509    intro:
510      "Druhý ročník rozvíjel partnerský přístup k pacientům, motivaci, všímavou komunikaci a práci s náročnými situacemi.",
511    program: [
512      { time: "8:00–9:00", label: "Registrace", kind: "service" },
513      { time: "9:00–9:30", label: "Úvod a zahájení", kind: "service" },
514      {
515        time: "9:30–10:30",
516        label: "Úvodní přednáška",
517        sessions: [
518          {
519            type: "Přednáška",
520            speaker: "MUDr. Marika Svatošová",
521            title: "Pacient jako partner v léčebném procesu",
522            img: "https://static.wixstatic.com/media/dd6e74_f298788292be41d4a9c4e9072c66d0d4~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Marika.png",
523          },
524        ],
525      },
526      { time: "10:30–11:00", label: "Coffee break", kind: "break" },
527      {
528        time: "11:00–12:30",
529        label: "Vzdělávací blok",
530        sessions: [
531          {
532            type: "Přednáška",
533            speaker: "MUDr. Peter Pöthe",
534            title: "Jak být s dětmi ve vztahu",
535            img: "https://static.wixstatic.com/media/dd6e74_ea5bdf6d0b104bc3ba8ea8bb989b322d~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Poethe.png",
536          },
537          {
538            type: "Workshop",
539            speaker: "prof. Ing. Mgr. et Mgr. Peter Egýd Tavel, Ph.D.",
540            title: "Asertivní komunikace",
541            img: "https://static.wixstatic.com/media/dd6e74_1836d8ecbcc44b00b990469fdce2ee15~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Tavel.png",
542          },
543          {
544            type: "Workshop",
545            speaker: "MUDr. Vladimír Kmoch",
546            title: "Jak motivovat pacienty ke změně",
547            img: "https://static.wixstatic.com/media/dd6e74_7b4ec8ee8af94372867c1d137824318d~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Kmoch.png",
548          },
549          {
550            type: "Workshop",
551            speaker: "Bc. Jana Valachovičová",
552            title: "Zdravotník, pacient a předsudky v komunikaci",
553            img: "https://static.wixstatic.com/media/dd6e74_20c4bbc432ae4f14b489486a2cad2b1a~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Valachovicova.png",
554          },
555        ],
556      },
557      { time: "12:30–14:00", label: "Oběd", kind: "break" },
558      {
559        time: "14:00–15:30",
560        label: "Vzdělávací blok",
561        sessions: [
562          {
563            type: "Přednáška",
564            speaker: "Mgr. et Mgr. Martin Fojtíček",
565            title: "Zplnomocňující jazyk – jazyk naděje",
566            img: "https://static.wixstatic.com/media/dd6e74_ac90a45ccaaf4fc7b529e3f6c8bc26dd~mv2.png/v1/fill/w_320,h_160,al_c,q_85,enc_avif,quality_auto/Fojticek.png",
567          },
568          {
569            type: "Workshop",
570            speaker: "Bc. Jana Valachovičová",
571            title: "Zdravotník, pacient a předsudky v komunikaci",
572            img: "https://static.wixstatic.com/media/dd6e74_dcab2396aa474ee98b5621c20a380e04~mv2.png/v1/fill/w_320,h_160,al_c,q_85,enc_avif,quality_auto/Valachovicova.png",
573          },
574          {
575            type: "Workshop",
576            speaker: "MUDr. Barbora Mechúrová",
577            title: "Komunikace s neklidným pacientem",
578            img: "https://static.wixstatic.com/media/dd6e74_27e1f1a356ea4d8891be95472fe8e254~mv2.png/v1/fill/w_320,h_160,al_c,q_85,enc_avif,quality_auto/Mechurova.png",
579          },
580          {
581            type: "Workshop",
582            speakers: [
583              { name: "Ing. Jan Hrdlička", img: "/assets/speakers/jan-hrdlicka.webp" },
584              { name: "Mgr. Jitka Kosíková", img: "/assets/speakers/jitka-kosikova.webp" },
585            ],
586            title: "Praktický nácvik komunikace ve virtuální realitě",
587          },
588        ],
589      },
590      { time: "15:30–16:00", label: "Coffee break", kind: "break" },
591      {
592        time: "16:00–17:30",
593        label: "Vzdělávací blok",
594        sessions: [
595          {
596            type: "Přednáška",
597            speaker: "PhDr. Markéta Celerová, Ph.D.",
598            title: "Krizová intervence v nemocniční péči",
599            img: "https://static.wixstatic.com/media/dd6e74_173c3aeb118c41ffb0be4cad80124faf~mv2.png/v1/fill/w_320,h_160,al_c,q_85,enc_avif,quality_auto/Celerova.png",
600          },
601          {
602            type: "Workshop",
603            speaker: "Mgr. Martin Janeček, Ph.D.",
604            title:
605              "Všímavá komunikace aneb Jak využít mindfulness během rozhovoru s pacienty",
606            img: "https://static.wixstatic.com/media/dd6e74_f3d166d5cdee42dfad0b3e59b5ecfacf~mv2.png/v1/fill/w_320,h_160,al_c,q_85,enc_avif,quality_auto/Janecek.png",
607          },
608          {
609            type: "Workshop",
610            speaker: "MUDr. Lucie Hrdličková",
611            title: "Sdělování závažné zprávy – jak na to",
612            img: "https://static.wixstatic.com/media/dd6e74_206d026d09b04daf97e0300652843145~mv2.png/v1/fill/w_320,h_160,al_c,q_85,enc_avif,quality_auto/Hrdlickova.png",
613          },
614          {
615            type: "Workshop",
616            speakers: [
617              { name: "Ing. Jan Hrdlička", img: "/assets/speakers/jan-hrdlicka.webp" },
618              { name: "Mgr. Jitka Kosíková", img: "/assets/speakers/jitka-kosikova.webp" },
619            ],
620            title: "Praktický nácvik komunikace ve virtuální realitě",
621          },
622        ],
623      },
624      { time: "17:30–18:00", label: "Slavnostní ukončení", kind: "service" },
625    ],
626  },
627  2022: {
628    edition: "1. ročník",
629    topic: "Komunikace ve zdravotnictví",
630    date: "24. 9. 2022",
631    venue: "Hermitage Hotel Praha",
632    status: "Proběhlo",
633    intro:
634      "První ročník položil základ konference: dialog, motivace, nenásilná komunikace a praktické workshopy pro zdravotníky.",
635    program: [
636      { time: "8:00–9:00", label: "Registrace", kind: "service" },
637      { time: "9:00–9:30", label: "Úvod a zahájení", kind: "service" },
638      {
639        time: "9:30–10:30",
640        label: "Úvodní přednáška",
641        sessions: [
642          {
643            type: "Přednáška",
644            speaker: "Mgr. Lucie Hornová, Ph.D.",
645            title: "Dialogická komunikace v týmu zdravotníků",
646            img: "https://static.wixstatic.com/media/dd6e74_468e988a77314acab62c0d3b1d648985~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Lucie%20Hornova.png",
647          },
648        ],
649      },
650      { time: "10:30–11:00", label: "Coffee break", kind: "break" },
651      {
652        time: "11:00–12:30",
653        label: "Vzdělávací blok",
654        sessions: [
655          {
656            type: "Workshop",
657            speaker: "MUDr. Marika Svatošová",
658            title: "Emočně inteligentní komunikace",
659            img: "https://static.wixstatic.com/media/dd6e74_b3ddfd557f2b4ca2b076a47dc39131a9~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Marika.png",
660          },
661          {
662            type: "Workshop",
663            speaker: "Mgr. Petr Sucháček",
664            title: "Nenásilná komunikace pro pohodu v ordinaci i doma",
665            img: "https://static.wixstatic.com/media/dd6e74_c76fb840017a4dfb8f56f136b1fc7a28~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Petr%20Suchacek.png",
666          },
667          {
668            type: "Workshop",
669            speaker: "MUDr. Ondřej Žiak",
670            title: "Dialóg a odozva v pomáhajúcej profesii",
671            img: "https://static.wixstatic.com/media/dd6e74_d7dced05d3844d669ede7ba72b9f45fa~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Ondrej%20Ziak.png",
672          },
673        ],
674      },
675      { time: "12:30–14:00", label: "Oběd", kind: "break" },
676      {
677        time: "14:00–15:30",
678        label: "Vzdělávací blok",
679        sessions: [
680          {
681            type: "Přednáška",
682            speaker: "Mgr. Michal Dubec",
683            title: "Vztahová rovina komunikace při práci s pacienty",
684            img: "https://static.wixstatic.com/media/dd6e74_46e372a3978549329e597e3b3fef5c90~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Michal%20Dubec.png",
685          },
686          {
687            type: "Workshop",
688            speaker: "MUDr. Marika Svatošová",
689            title: "Emočně inteligentní komunikace",
690            img: "https://static.wixstatic.com/media/dd6e74_b3ddfd557f2b4ca2b076a47dc39131a9~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Marika.png",
691          },
692          {
693            type: "Workshop",
694            speaker: "Mgr. Petr Sucháček",
695            title: "Nenásilná komunikace pro pohodu v ordinaci i doma",
696            img: "https://static.wixstatic.com/media/dd6e74_c76fb840017a4dfb8f56f136b1fc7a28~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Petr%20Suchacek.png",
697          },
698          {
699            type: "Workshop",
700            speaker: "Mgr. Katarína Pabištová",
701            title: "V dialogu se sebou a se světem",
702            img: "https://static.wixstatic.com/media/dd6e74_7984caae8c2c4d0c9bb723786b7e2c0f~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Katarina.png",
703          },
704        ],
705      },
706      { time: "15:30–16:00", label: "Coffee break", kind: "break" },
707      {
708        time: "16:00–17:30",
709        label: "Vzdělávací blok",
710        sessions: [
711          {
712            type: "Přednáška",
713            speaker: "PhDr. Martin Dlabal, Ph.D.",
714            title: "Motivační rozhovory ve zdravotnictví",
715            img: "https://static.wixstatic.com/media/dd6e74_4e70fca5879246a9a6ec602dc73288e2~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Martin%20Dlabal.png",
716          },
717          {
718            type: "Workshop",
719            speaker: "Mgr. Michal Dubec",
720            title: "Vedení pacientů k odpovědnosti prostřednictvím komunikace",
721            img: "https://static.wixstatic.com/media/dd6e74_46e372a3978549329e597e3b3fef5c90~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Michal%20Dubec.png",
722          },
723          {
724            type: "Workshop",
725            speaker: "MUDr. Ondřej Žiak",
726            title: "Dialóg a odozva v pomáhajúcej profesii",
727            img: "https://static.wixstatic.com/media/dd6e74_d7dced05d3844d669ede7ba72b9f45fa~mv2.png/v1/fill/w_160,h_160,al_c,q_85,enc_avif,quality_auto/Ondrej%20Ziak.png",
728          },
729        ],
730      },
731      { time: "17:30–18:00", label: "Slavnostní ukončení", kind: "service" },
732    ],
733  },
734};
735
736const current =
737  location.pathname.split("/").filter(Boolean).pop() || "index.html";
738const decodedCurrent = (() => {
739  try {
740    return decodeURIComponent(current);
741  } catch {
742    return current;
743  }
744})();
745const isAdminRoute = location.pathname === "/admin" || current === "admin.html";
746const isRegistrationRoute =
747  current === "registrace-na-konferenci" ||
748  current === "registrace-na-konferenci.html" ||
749  current === "euc-vstupy";
750const isRegistrationPortalRoute =
751  current === "moje-registrace" || current === "moje-registrace.html";
752const isProgrammePortalRoute =
753  current === "sestavit-program" || current === "sestavit-program.html";
754const isFeedbackRoute =
755  current === "zpetna-vazba" || current === "zpetna-vazba.html";
756const isPrivacyRoute = current === "gdpr" || current === "gdpr.html";
757const isTermsRoute =
758  current === "obchodni-podminky" || current === "obchodni-podminky.html";
759const isEmailPreferencesRoute =
760  current === "email-preference" || current === "email-preference.html";
761const isDistributionKitRoute =
762  current === "pozvanka-pro-tym" || current === "pozvanka-pro-tym.html";
763const hostedMarketingFormMatch = location.pathname.match(
764  /^\/formular\/([a-z0-9]+(?:-[a-z0-9]+)*)\/?$/,
765);
766const NEWSLETTER_FORM_SLUG = "medicrise-newsletter";
767const AUDIT_DOWNLOAD_FORM_SLUG = "audit-komunikace";
768const AUDIT_CONSULTATION_FORM_SLUG = "audit-komunikace-konzultace";
769const isAuditLandingRoute = [
770  "audit-komunikace-bara",
771  "audit-komunikace-bara.html",
772  "audit-komunikace-bára",
773  "audit-komunikace-bára.html",
774].includes(decodedCurrent);
775const isAuditContentRoute = [
776  "audit-komunikace-bara-video-a-audit-ke-stazeni",
777  "audit-komunikace-bara-video-a-audit-ke-stazeni.html",
778  "audit-komunikace-bára-video-a-audit-ke-stažení",
779  "audit-komunikace-bára-video-a-audit-ke-stažení.html",
780].includes(decodedCurrent);
781const conferenceYearMatch = current.match(
782  /^konference-(2022|2023|2024|2025|2026)\.html$/,
783);
784const courseMenu = Object.entries(coursePages)
785  .map(([href, key]) => `<a href="${href}">${courses[key].title}</a>`)
786  .join("");
787function header() {
788  return `<header class="site-header"><div class="container nav"><a href="index.html"><img class="logo" src="${ASSETS.logo}" alt="MedicRise"></a><nav class="nav-links"><a href="index.html">Domů</a><a href="o-nas.html">O nás</a><div class="menu-group"><a href="vzdelavani.html">Vzdělávání</a><div class="dropdown">${courseMenu}<a href="online-kurzy.html">On-line kurzy</a></div></div><div class="menu-group"><a href="konference.html">Konference</a><div class="dropdown"><a href="konference.html">O konferenci</a><a href="konference-2026.html">Zdravé zdravotnictví 2026</a><a href="konference-2025.html">Zdravé zdravotnictví 2025</a><a href="konference-2024.html">Zdravé zdravotnictví 2024</a><a href="konference-2023.html">Zdravé zdravotnictví 2023</a><a href="konference-2022.html">Zdravé zdravotnictví 2022</a></div></div><a href="partneri.html">Partneři</a><a class="nav-portal-mobile" href="/portal">Můj portál</a></nav><div class="nav-actions"><a class="site-portal-link" href="/portal">Můj portál</a><a class="btn btn-primary" href="vzdelavani.html">Pro organizace</a><button class="mobile-toggle" aria-label="Menu">☰</button></div></div></header>`;
789}
790
791async function newsletterApi(body, idempotencyKey = "") {
792  const response = await fetch(`${CMS_SUPABASE_URL}/functions/v1/marketing-form-submit`, {
793    method: "POST",
794    headers: {
795      apikey: CMS_SUPABASE_KEY,
796      "content-type": "application/json",
797      ...(idempotencyKey ? { "x-idempotency-key": idempotencyKey } : {}),
798    },
799    body: JSON.stringify(body),
800  });
801  const data = await response.json().catch(() => ({}));
802  if (!response.ok) throw new Error(data.error || "Přihlášení se nepodařilo.");
803  return data;
804}
805
806async function initNewsletterSignup() {
807  const form = document.querySelector("[data-newsletter-form]");
808  if (!form) return;
809  const email = form.elements.email;
810  const consent = form.elements.consent;
811  const button = form.querySelector('button[type="submit"]');
812  const status = form.querySelector("[data-newsletter-status]");
813  let definition;
814  try {
815    const result = await newsletterApi({ action: "definition", slug: NEWSLETTER_FORM_SLUG });
816    definition = result.form;
817    email.disabled = false;
818    consent.disabled = false;
819    button.disabled = false;
820    status.textContent = "Přihlášení je chráněné proti duplicitám a spamu.";
821  } catch (error) {
822    status.textContent = "Přihlášení k novinkám je nyní dočasně nedostupné.";
823    return;
824  }
825  form.addEventListener("submit", async (event) => {
826    event.preventDefault();
827    if (!form.reportValidity()) return;
828    button.disabled = true;
829    button.textContent = "Ukládáme…";
830    status.textContent = "Bezpečně ukládáme váš souhlas…";
831    const idempotencyKey = `${crypto.randomUUID()}:${Date.now()}`;
832    try {
833      const result = await newsletterApi({
834        form_id: definition.id,
835        public_token: definition.public_token,
836        email: email.value,
837        payload: {},
838        consent: consent.checked,
839        website: form.elements.website.value,
840        page_url: location.href,
841        referrer: document.referrer,
842      }, idempotencyKey);
843      form.innerHTML = `<div class="footer-newsletter-success"><strong>${escapeHtml(result.success_title || "Děkujeme za přihlášení.")}</strong><span>${escapeHtml(result.success_message || "Vaše volba byla uložena.")}</span></div>`;
844    } catch (error) {
845      button.disabled = false;
846      button.textContent = "Přihlásit";
847      status.textContent = error.message;
848    }
849  });
850}
851
852function hostedMarketingFormPage() {
853  return `<main class="registration-page marketing-public-form-page"><section class="registration-hero"><div class="container"><span class="kicker">MedicRise · bezpečný formulář</span><h1>Načítáme formulář…</h1><p>Vaše údaje zpracujeme pouze pro uvedený účel.</p></div></section><section class="section"><div class="container portal-shell" id="hosted-marketing-form-root"><div class="portal-loading">Načítáme aktuální podobu formuláře…</div></div></section></main>`;
854}
855
856function hostedMarketingField(field) {
857  const key=escapeHtml(field.key),label=`${escapeHtml(field.label)}${field.required?" *":""}`,required=field.required?" required":"";
858  if(field.type==="consent"||field.custom_field_type==="boolean")return `<label class="registration-consent"><input type="checkbox" name="${key}"${required}><span>${label}</span></label>`;
859  if(["select","multi_select"].includes(field.custom_field_type)){
860    const multiple=field.custom_field_type==="multi_select"?" multiple":"";
861    return `<label>${label}<select name="${key}"${multiple}${required}>${field.required?"":'<option value="">Nevybráno</option>'}${(Array.isArray(field.options)?field.options:[]).map((option)=>`<option value="${escapeHtml(option)}">${escapeHtml(option)}</option>`).join("")}</select></label>`;
862  }
863  const type=field.type==="custom"?({number:"number",date:"date"}[field.custom_field_type]||"text"):field.type;
864  const autocomplete={first_name:"given-name",last_name:"family-name",email:"email",phone:"tel",company:"organization",job_title:"organization-title"}[field.key]||"off";
865  const maxLength=type==="number"||type==="date"?"":' maxlength="2000"';
866  return `<label>${label}<input name="${key}" type="${type}" autocomplete="${autocomplete}"${maxLength}${required}></label>`;
867}
868
869function safePublicDownloadUrl(value) {
870  try { const url=new URL(String(value||"")); return ["https:","http:"].includes(url.protocol)?url.href:""; }
871  catch { return ""; }
872}
873
874async function recordLeadMagnetDownload(formId, publicToken, submissionId) {
875  const response=await fetch(`${CMS_SUPABASE_URL}/rest/v1/rpc/record_marketing_lead_magnet_download`,{
876    method:"POST",
877    headers:{apikey:CMS_SUPABASE_KEY,"content-type":"application/json"},
878    body:JSON.stringify({p_form_id:formId,p_public_token:publicToken,p_submission_id:submissionId}),
879  });
880  const data=await response.json().catch(()=>({}));
881  if(!response.ok||data.accepted!==true)throw new Error("Stažení se nepodařilo bezpečně potvrdit. Zkuste to prosím znovu.");
882  return data;
883}
884
885async function initHostedMarketingForm(slug) {
886  const root=document.getElementById("hosted-marketing-form-root");
887  try{
888    const result=await newsletterApi({action:"definition",slug}),definition=result.form,fields=Array.isArray(definition?.fields)?definition.fields:[];
889    if(!fields.some((field)=>field.key==="email")||!fields.some((field)=>field.key==="marketing_consent"))throw new Error("Formulář nemá platnou strukturu.");
890    document.querySelector(".registration-hero h1").textContent=definition.title;
891    document.querySelector(".registration-hero p").textContent=definition.description||"Vaše údaje zpracujeme pouze pro uvedený účel.";
892    root.innerHTML=`<form class="portal-card hosted-marketing-form" id="hosted-marketing-form"><header><div><span class="kicker">MedicRise</span><h2>${escapeHtml(definition.title)}</h2><p>${escapeHtml(definition.description||"")}</p></div></header><div class="hosted-marketing-form-grid">${fields.map(hostedMarketingField).join("")}</div><label class="footer-newsletter-honeypot" aria-hidden="true">Web<input name="website" tabindex="-1" autocomplete="off"></label><p class="hosted-marketing-form-legal">Odesláním potvrzujete uvedený souhlas. Podrobnosti najdete v <
892a href="/gdpr" target="_blank" rel="noopener">zásadách ochrany osobních údajů</a>.</p><button class="btn btn-primary" type="submit">Odeslat</button><p class="form-status" aria-live="polite"></p></form>`;
893    const form=document.getElementById("hosted-marketing-form"),button=form.querySelector('button[type="submit"]'),status=form.querySelector(".form-status");
894    form.addEventListener("submit",async(event)=>{
895      event.preventDefault();if(!form.reportValidity())return;
896      button.disabled=true;button.textContent="Bezpečně ukládáme…";status.textContent="Odesíláme údaje…";
897      const data=new FormData(form),payload={};
898      for(const field of fields){
899        if(["email","marketing_consent"].includes(field.key))continue;
900        if(field.custom_field_type==="boolean")payload[field.key]=data.has(field.key);
901        else if(field.custom_field_type==="multi_select"){const values=data.getAll(field.key).map(String).filter(Boolean);if(values.length)payload[field.key]=values;}
902        else {const raw=String(data.get(field.key)||"").trim();if(raw!=="")payload[field.key]=field.custom_field_type==="number"?Number(raw):raw;}
903      }
904      try{
905        const submitted=await newsletterApi({form_id:definition.id,public_token:definition.public_token,email:String(data.get("email")||""),payload,consent:data.has("marketing_consent"),website:String(data.get("website")||""),page_url:location.href,referrer:document.referrer},`${crypto.randomUUID()}:${Date.now()}`);
906        const downloadUrl=safePublicDownloadUrl(submitted.download?.url);
907        root.innerHTML=`<div class="portal-error hosted-marketing-form-success"><span class="hosted-marketing-form-check">✓</span><h2>${escapeHtml(submitted.success_title||"Děkujeme.")}</h2><p>${escapeHtml(submitted.success_message||"Vaše údaje jsme bezpečně přijali.")}</p>${downloadUrl?`<button class="btn btn-primary" type="button" data-lead-magnet-download>Stáhnout ${escapeHtml(submitted.download?.filename||"materiál")}</button><small class="form-status" data-lead-magnet-download-status></small>`:""}<a class="btn btn-secondary" href="/">Zpět na MedicRise</a></div>`;
908        root.querySelector("[data-lead-magnet-download]")?.addEventListener("click",async(event)=>{
909          const downloadButton=event.currentTarget,downloadStatus=root.querySelector("[data-lead-magnet-download-status]");
910          downloadButton.disabled=true;downloadButton.textContent="Připravuji stažení…";
911          try{
912            await recordLeadMagnetDownload(definition.id,definition.public_token,submitted.submission_id);
913            window.location.assign(downloadUrl);
914          }catch(downloadError){downloadButton.disabled=false;downloadButton.textContent=`Stáhnout ${submitted.download?.filename||"materiál"}`;downloadStatus.textContent=downloadError.message;}
915        });
916      }catch(error){button.disabled=false;button.textContent="Odeslat";status.textContent=error.message;}
917    });
918  }catch(error){root.innerHTML=`<div class="portal-error"><span class="kicker">Formulář není dostupný</span><h2>Odkaz se nepodařilo otevřít</h2><p>${escapeHtml(error.message)}</p><a class="btn btn-secondary" href="/">Zpět na MedicRise</a></div>`;}
919}
920
921function emailPreferencePage() {
922  return `<main class="email-preference-page"><section class="email-preference-shell"><article class="email-preference-card" id="email-preference-card" aria-live="polite"><img src="${ASSETS.logo}" alt="MedicRise"><span class="kicker">Vaše soukromí</span><h1>Nastavení e-mailové komunikace</h1><p class="email-preference-lead">Bezpečně načítáme vaše aktuální nastavení…</p><div class="email-preference-loading" aria-label="Načítání"><i></i><i></i><i></i></div></article></section></main>`;
923}
924
925async function emailPreferenceApi(body) {
926  const response = await fetch(`${CMS_SUPABASE_URL}/functions/v1/marketing-preferences`, {
927    method: "POST",
928    headers: { apikey: CMS_SUPABASE_KEY, "content-type": "application/json" },
929    body: JSON.stringify(body),
930  });
931  const data = await response.json().catch(() => ({}));
932  if (!response.ok) throw new Error(data.error || "Nastavení se nepodařilo 
932načíst.");
933  return data;
934}
935
936async function initEmailPreferences() {
937  const card=document.getElementById("email-preference-card"),params=new URL(location.href).searchParams,token=params.get("token")||"";
938  const showError=(message)=>{card.innerHTML=`<img src="${ASSETS.logo}" alt="MedicRise"><span class="kicker">Odkaz není dostupný</span><h1>Nastavení se nepodařilo otevřít</h1><p class="email-preference-lead">${escapeHtml(message)}</p><a class="btn btn-secondary" href="/">Zpět na MedicRise</a>`;};
939  if(params.get("preview")==="test"){
940    const unsubscribe=params.get("action")==="unsubscribe";
941    card.innerHTML=`<img src="${ASSETS.logo}" alt="MedicRise"><span class="kicker">Bezpečný test</span><h1>${unsubscribe?"Odhlášení z odběru":"Nastavení e-mailové komunikace"}</h1><p class="email-preference-lead">Tento odkaz pochází z testovacího e-mailu. Ověřili jste, že odkaz a cílová stránka fungují; žádný skutečný kontakt ani jeho souhlasy se při testu nezmění.</p><div class="email-preference-confirmed">✓ Testovací odkaz je funkční.</div><a class="btn btn-secondary" href="/">Zpět na MedicRise</a>`;
942    return;
943  }
944  if(!/^[0-9a-f]{64}$/i.test(token)) return showError("Odkaz není platný nebo už vypršel. Použijte prosím odkaz z posledního e-mailu MedicRise.");
945  try{
946    const data=await emailPreferenceApi({action:"get",token});
947    card.innerHTML=`<img src="${ASSETS.logo}" alt="MedicRise"><span class="kicker">Vaše soukromí</span><h1>${escapeHtml(data.preference_title)}</h1><p class="email-preference-lead">${escapeHtml(data.preference_copy)}</p><div class="email-preference-identity"><span>Komunikace pro</span><strong>${escapeHtml(data.first_name?`${data.first_name} · ${data.email_hint}`:data.email_hint)}</strong></div><div class="email-preference-choice"><div><span>Novinky, pozvánky a vzdělávací obsah</span><small>${data.marketing_enabled?"Aktuálně je odběr aktivní.":"Odběr je vypnutý a kontakt je na suppression listu."}</small></div><span class="email-preference-state ${data.marketing_enabled?"active":"off"}">${data.marketing_enabled?"Zapnuto":"Vypnuto"}</span></div>${data.marketing_enabled?'<button class="btn btn-secondary email-preference-unsubscribe" type="button">Odhlásit marketingové e-maily</button>':'<div class="email-preference-confirmed">Vaše volba je uložená. Pro nové přihlášení bude později potřeba nový potvrzený souhlas.</div>'}<p class="email-preference-transactional">Odhlášení se netýká nezbytných transakčních zpráv, například potvrzení registrace, faktur a organizačních informací k objednané službě.</p><small class="email-preference-legal">${escapeHtml(data.company_name)} · ${escapeHtml(data.company_address)} · IČ ${escapeHtml(data.company_id)}</small>`;
948    card.querySelector(".email-preference-unsubscribe")?.addEventListener("click",async(event)=>{
949      if(!confirm("Opravdu chcete vypnout marketingové e-maily MedicRise?"))return;
950      const button=event.currentTarget;button.disabled=true;button.textContent="Ukládáme…";
951      try{const result=await emailPreferenceApi({action:"unsubscribe",token,reason:"preference_center"});card.innerHTML=`<img src="${ASSETS.logo}" alt="MedicRise"><span class="kicker">Volba uložena</span><h1>${escapeHtml(result.title)}</h1><p class="email-preference-lead">${escapeHtml(result.message)}</p><div class="email-preference-confirmed">✓ Marketingové e-maily jsou vypnuté.</div><a class="btn btn-secondary" href="/">Zpět na MedicRise</a>`;}catch(error){button.disabled=false;button.textContent="Odhlásit marketingové e-maily";alert(error.message);}
952    });
953  }catch(error){showError(error.message);}
954}
955function socialIcon(kind) {
956  const brandIcons = {
957    facebook: {
958      viewBox: "0 0 320 512",
959      path: "M279.14 288l14.22-92.66h-88.91v-60.13c0-25.35 12.42-50.06 52.24-50.06H297V6.26S260.43 0 225.36 0c-73.22 0-121.08 44.38-121.08 124.72v70.62H22.89V288h81.39v224h100.17V288z",
960    },
961    instagram: {
962      viewBox: "0 0 448 512",
963      path: "M224.1 141c-63.6 0-114.9 51.3-114.9 114.9s51.3 114.9 114.9 114.9S339 319.5 339 255.9 287.7 141 224.1 141zm0 189.6c-41.1 0-74.7-33.5-74.7-74.7s33.5-74.7 74.7-74.7 74.7 33.5 74.7 74.7-33.6 74.7-74.7 74.7zm146.4-194.3c0 14.9-12 26.8-26.8 26.8-14.9 0-26.8-12-26.8-26.8s12-26.8 26.8-26.8 26.8 12 26.8 26.8zm76.1 27.2c-1.7-35.9-9.9-67.7-36.2-93.9-26.2-26.2-58-34.4-93.9-36.2-37-2.1-147.9-2.1-184.9 0-35.8 1.7-67.6 9.9-93.9 36.1s-34.4 58-36.2 93.9c-2.1 37-2.1 147.9 0 184.9 1.7 35.9 9.9 67.7 36.2 93.9s58 34.4 93.9 36.2c37 2.1 147.9 2.1 184.9 0 35.9-1.7 67.7-9.9 93.9-36.2 26.2-26.2 34.4-58 36.2-93.9 2.1-37 2.1-147.8 0-184.8zM398.8 388c-7.8 19.6-22.9 34.7-42.6 42.6-29.5 11.7-99.5 9-132.1 9s-102.7 2.6-132.1-9c-19.6-7.8-34.7-22.9-42.6-42.6-11.7-29.5-9-99.5-9-132.1s-2.6-102.7 9-132.1c7.8-19.6 22.9-34.7 42.6-42.6 29.5-11.7 99.5-9 132.1-9s102.7-2.6 132.1 9c19.6 7.8 34.7 22.9 42.6 42.6 11.7 29.5 9 99.5 9 132.1s2.7 102.7-9 132.1z",
964    },
965    linkedin: {
966      viewBox: "0 0 448 512",
967      path: "M100.28 448H7.4V148.9h92.88zm-46.49-340.7C24.09 107.3 0 83.2 0 53.6A53.6 53.6 0 01107.2 53.6c0 29.7-24.1 53.7-53.6 53.7zM447.9 448h-92.68V302.4c0-34.7-.7-79.2-48.29-79.2-48.29 0-55.69 37.7-55.69 76.7V448h-92.78V148.9h89.08v40.8h1.3c12.4-23.5 42.69-48.3 87.88-48.3 94 0 111.28 61.9 111.28 142.3V448z",
968    },
969  };
970  const icon = brandIcons[kind];
971  return `<svg class="social-icon social-icon-${kind}" viewBox="${icon.viewBox}" aria-hidden="true" focusable="false" preserveAspectRatio="xMidYMid meet"><path d="${icon.path}"/></svg>`;
972}
973
974function footer() {
975  return `<footer><div class="container"><div class="footer-grid"><div><img class="footer-logo" src="${ASSETS.logo}" alt="MedicRise"><p style="margin-top:18px;max-width:410px">Tvoříme zdravotnictví, ve kterém spolu zdravotníci a pacienti komunikují s respektem, empatií a větší jistotou.</p><div class="footer-socials" aria-label="Sociální sítě MedicRise"><a href="https://www.facebook.com/MedicRise/" target="_blank" rel="noopener" aria-label="MedicRise na Facebooku">${socialIcon("facebook")}</a><a href="https://www.instagram.com/medicrise/" target="_blank" rel="noopener" aria-label="MedicRise na Instagramu">${socialIcon("instagram")}</a><a href="https://www.linkedin.com/company/76107861/" target="_blank" rel="noopener" aria-label="MedicRise na LinkedIn">${socialIcon("linkedin")}</a></div></div><div class="footer-col"><h3>MedicRise</h3><a href="o-nas.html">O nás</a><a href="vzdelavani.html">Vzdělávání</a><a href="konference.html">Konference</a><a href="partneri.html">Partneři</a><a href="/audit-komunikace-bara">Audit komunikace</a><a href="/gdpr">Ochrana osobních údajů</a><a href="/obchodni-podminky">Obchodní podmínky</a><a class="footer-admin-link" href="/admin">Administrace</a></div><div class="footer-col"><h3>NapiÅ¡te nám</h3><p>MedicRise z. s.</p><p>Å kolská 660/3, Nové Město<br>110 00 Praha 1</p><p>IČ: 14176921</p><a href="mailto:[email protected]">
975[email protected]</a></div><section class="footer-newsletter" aria-labelledby="footer-newsletter-title"><h3 id="footer-newsletter-title">Novinky MedicRise</h3><p>Pozvánky a praktický vzdělávací obsah přímo do e-mailu.</p><form data-newsletter-form><label class="sr-only" for="footer-newsletter-email">E-mail</label><div><input id="footer-newsletter-email" name="email" type="email" autocomplete="email" placeholder="[email protected]" maxlength="320" required disabled><button type="submit" disabled>Přihlásit</button></div><label class="footer-newsletter-consent"><input name="consent" type="checkbox" required disabled><span>Souhlasím se zasíláním novinek. Souhlas lze kdykoli odvolat; více v <a href="/gdpr">ochraně osobních údajů</a>.</span></label><label class="footer-newsletter-honeypot" aria-hidden="true">Web<input name="website" tabindex="-1" autocomplete="off"></label><small data-newsletter-status aria-live="polite">Načítáme bezpečný formulář…</small></form></section></div><div class="footer-bottom"><span>© 2022–2026 MedicRise z. s.</span><span>Číslo účtu: 2102117954/2010 · Nejsme plátci DPH.</span></div></div></footer>`;
976}
977
978function home() {
979  return `<main><section class="hero"><div class="container hero-grid"><div class="hero-copy"><span class="kicker">Komunikace, která mění zdravotnictví</span><h1>Podílejme se společně na tvorbě zdravého zdravotnictví.</h1><p>Jsme nezisková vzdělávací organizace zaměřená na rozvoj komunikačních a měkkých dovedností zdravotníků. Prakticky, interaktivně a s respektem k realitě zdravotnického provozu.</p><div class="hero-actions"><a class="btn btn-primary" href="vzdelavani.html">Prozkoumat vzdělávání →</a><a class="btn btn-secondary" href="o-nas.html">Poznat MedicRise</a></div></div><div class="hero-visual"><div class="hero-photo"><img src="${ASSETS.hero}" alt="Workshop MedicRise"></div><div class="hero-badge"><strong>180 min</strong><span>prakticky orientovaný formát naÅ¡ich hlavních prezenčních kurzů</span></div></div></div></section><section class="trust-strip"><div class="container trust-grid"><div class="trust-item"><div class="trust-icon">🩺</div><div><strong>Pro zdravotnictví</strong><span>Obsah vychází z reálných situací z praxe.</span></div></div><div class="trust-item"><div class="trust-icon">🎭</div><div><strong>Praktický nácvik</strong><span>Simulace, diskuze a okamžité použití.</span></div></div><div class="trust-item"><div class="trust-icon">🧠</div><div><strong>Ověřené rámce</strong><span>BLAST, SPIKES, DESC a další.</span></div></div><div class="trust-item"><div class="trust-icon">🤝</div><div><strong>Pro celé týmy</strong><span>Kurzy, on-line vzdělávání i konference.</span></div></div></div></section><section class="section"><div class="container"><div class="section-head"><div><span class="kicker">Co děláme</span><h2>Vzdělávání, které nekončí u prezentace.</h2></div><p>Prostřednictvím interaktivních vzdělávacích kurzů přinášíme ověřené informace, osvědčené metody a praktické zkuÅ¡enosti využitelné v každodenní medicínské praxi.</p></div><div class="cards"><a class="card" href="vzdelavani.html"><div class="card-icon">🎓</div><h3>Kurzy a workshopy</h3><p>Prezenční vzdělávání zaměřené na komunikaci, týmovou spolupráci a další měkké dovednosti.</p><span class="card-link">Zobrazit témata</span></a><a class="card" href="online-kurzy.html"><div class="card-icon">💻</div><h3>On-line kurzy</h3><p>Self-guided vzdělávání dostupné kdykoli a odkudkoli, vhodné i pro větší organizace.</p><span class="card-link">On-line vzdělávání</span></a><a class="card card-accent" href="konference.html"><div class="card-icon">🎤</div><h3>Konference</h3><p>Zdravé zdravotnictví propojuje odborné přednášky, praktické workshopy a komunitu lidí se stejnou vizí.</p><span class="card-link">Zjistit více</span></a></div></div></section><section class="section section-soft"><div class="container split"><div class="split-media"><img src="${ASSETS.discussion}" alt="Skupinová komunikace"></div><div class="split-copy"><span class="kicker">NaÅ¡e filozofie</span><h2>Systematicky. Lidsky. Použitelně.</h2><p>V MedicRise si zakládáme na vzdělávání, které reflektuje potřeby zaměstnanců i celého týmu. Znalosti a dovednosti předáváme způsobem, který lze okamžitě využít v praxi.</p><div class="bullets"><div class="bullet"><span class="check">✓</span><div><strong>Interaktivní forma</strong><p>Účastníci nejsou pasivními posluchači.</p></div></div><div class="bullet"><span class="check">✓</span><div><strong>Kontinuita vzdělávání</strong><p>Umíme navázat dalšími kurzy, on-line vzděláváním i materiály.</p></div></div><div class="bullet"><span class="check">✓</span><div><strong>Obsah na míru</strong><p>Program lze přizpůsobit potřebám konkrétního zdravotnického zařízení.</p></div></div></div><div><a class="btn btn-primary" href="o-nas.html">Více o nás</a></div></div></div></section><section class="section"><div class="container"><div class="section-head"><div><span class="kicker">Nejžádanější témata</span><h2>Situace, které zdravotníci řeší každý den.</h2></div><a class="btn btn-secondary" href="vzdelavani.html">VÅ¡echna témata</a></div><div class="cards"><a class="card" href="komunikace-s-nespokojenym-pacientem.html"><div class="card-icon">💬</div><h3>Komunikace s nespokojeným pacientem</h3><p>Strukturovaný protokol BLAST, aktivní naslouchání a řeÅ¡ení problémů.</p><span class="card-link">Detail kurzu</span></a><a class="card" href="komunikace-s-agresivnim-pacientem.html"><div class="card-icon">🛡️</div><h3>Komunikace s agresivním pacientem</h3><p>Verbální deeskalace a prevence eskalace napětí v náročných situacích.</p><span class="card-link">Detail kurzu</span></a><a class="card" href="asertivni-komunikace.html"><div class="card-icon">✋</div><h3>Asertivní komunikace</h3><p>Respektující vyjádření potřeb, jasné hranice a struktura DESC pro náročné rozhovory.</p><span class="card-link">Detail kurzu</span></a></div></div></section><section class="section home-conference-section" data-conference-card-year="2026"><div class="container home-conference"><div class="home-conference-copy"><span class="kicker">Konference Zdravé zdravotnictví 2026</span><h2 data-edition-name>Boj o přežití: Dá se ve zdravotnictví zůstat člověkem?</h2><p data-edition-intro>Pátý ročník konference propojí praktické komunikační dovednosti, inspirativní přednášky, interaktivní workshopy a lidi, kteří chtějí posouvat zdravotnictví k větší lidskosti.</p><div class="conference-meta"><span>📅 <span data-edition-date>27. 11. 2026</span></span><span>📍 <span data-edition-venue>Hermitage Hotel Praha</span></span></div><div class="hero-actions"><a class="btn btn-primary" href="konference-2026.html">Prohlédnout ročník 2026 →</a><a class="btn btn-secondary" href="/registrace-na-konferenci">Registrovat se</a></div></div><div class="home-conference-art"><img src="${ASSETS.seminar}" alt="Konference Zdravé zdravotnictví 2026"><strong>2026</strong></div></div></section><section class="section section-soft"><div class="container split"><div class="split-copy"><span class="kicker">Audit komunikace</span><h2>Odhalte slepá místa dřív, než se promění ve stížnosti.</h2><p>Bezplatný audit komunikace pomůže vedoucím pracovníkům rychle zmapovat oblasti, které mohou ovlivňovat spokojenost pacientů i personálu.</p><div class="hero-actions"><a class="btn btn-primary" href="/audit-komunikace-bara">Stáhnout audit zdarma</a></div></div><div class="page-hero-art"><img src="${ASSETS.doctors}" alt="Zdravotnický tým"></div></div></section><section class="section"><div class="container"><div class="cta"><div><span class="kicker cta-kicker">Pro zdravotnická zařízení</span><h2>Chcete vzdělávání postavit podle potřeb vaÅ¡eho týmu?</h2><p>NapiÅ¡te nám. Společně vybereme vhodné téma, formát a podobu vzdělávání pro vaÅ¡e zaměstnance.</p></div><div class="cta-side"><span class="cta-side-eyebrow">JednoduÅ¡e a nezávazně</span><strong>Pojďme to posunout dál.</strong><span class="cta-side-note">Stačí krátká zpráva. Společně vybereme vhodný další krok podle vaÅ¡ich potřeb.</span><div class="cta-actions"><a class="btn btn-primary" href="mailto:[email protected]">Napsat MedicRise</a><a class="btn btn-secondary" href="vzdelavani.html">Prohlédnout nabídku</a></div></div></div></div></section></main>`;
980}
981
982const renderHomeWithLegacyCtas = home;
983home = function homeWithConferenceCtas() {
984  return renderHomeWithLegacyCtas().replace(
985    '<div class="hero-actions"><a class="btn btn-primary" href="vzdelavani.html">Prozkoumat vzdělávání →</a><a class="btn btn-secondary" href="o-nas.html">Poznat MedicRise</a></div>',
986    '<div class="hero-actions"><a class="btn btn-primary" href="konference-2026.html">Konference 2026 →</a><a class="btn btn-secondary" href="vzdelavani.html">Prozkoumat vzdělávání</a></div>',
987  );
988};
989
990const teamMembers = [
991  ["MUDr. Marika Svatošová", "/assets/team/marika-svatosova.webp"],
992  ["MUDr. Karolína Dutková", "/assets/team/karolina-dutkova.webp"],
993  ["MUDr. Jan Pavlok", "/assets/team/jan-pavlok.webp"],
994  ["MUDr. Dominika Kocmanová", "/assets/team/dominika-kocmanova.webp"],
995  ["Mgr. Veronika Kocmanová", "/assets/team/veronika-kocmanova.webp"],
996  ["Mgr. Eva Batůšková", "/assets/team/eva-batuskova.webp"],
997];
998
999function about() {
1000  const compactTeam = teamMembers.map(([name, image]) => `<article class="team-person-small"><img src="${image}" alt="${name}" loading="lazy" decoding="async"><strong>${name}</strong></article>`).join("");
1001  return `<main><section class="page-hero"><div class="container page-hero-grid"><div><span class="kicker">O MedicRise</span><h1>Tvoříme zdravé zdravotnictví.</h1><p>Jsme otevřená vzdělávací organizace, která podporuje zdravotníky v růstu a osvojování měkkých dovedností. Chceme zdravotnictví, v němž poskytovatelé péče komunikují empaticky a symetricky, provázejí pacienty na cestě k uzdravení a zažívají pocit smyslu a naplnění ze své práce.</p></div><div class="page-hero-art"><img src="${ASSETS.doctors}" alt="MedicRise"></div></div></section><section class="section"><div class="container split"><div class="split-media"><img src="${ASSETS.hero}" alt="MedicRise v praxi"></div><div class="split-copy"><span class="kicker">NaÅ¡e filozofie</span><h2>Systematické vzdělávání s jasným dopadem do praxe.</h2><p>Zakládáme si na vzdělávání, které reflektuje potřeby zaměstnanců i členů týmu. Prostřednictvím interaktivních a prakticky orientovaných kurzů předáváme znalosti a dovednosti okamžitě využitelné v každodenní praxi.</p><p>Klíčový důraz klademe na koncepci a kontinuitu vzdělávání s jasně definovanými cíli a výstupy.</p></div></div></section><section class="section section-soft"><div class="container"><div class="section-head"><div><span class="kicker">Čemu se věnujeme</span><h2>Více cest k jednomu cíli.</h2></div></div><div class="feature-grid"><div class="feature"><div class="card-icon">🎓</div><h3>Kurzy a workshopy</h3><p>Interaktivní Å¡kolení s moderními metodami a simulacemi reálných situací.</p></div><div class="feature"><div class="card-icon">📘</div><h3>Edukační materiály</h3><p>Stručné a přehledné manuály s návody pro každodenní komunikaci.</p></div><div class="feature"><div class="card-icon">💻</div><h3>On-line kurzy</h3><p>Flexibilní forma vzdělávání dostupná odkudkoli a kdykoli.</p></div><div class="feature"><div class="card-icon">🎤</div><h3>Konference</h3><p>Celodenní vzdělávací akce propojující přednášky, workshopy a komunitu.</p></div></div></div></section><section class="section"><div class="container"><div class="section-head"><div><span class="kicker">Náš tým</span><h2>Lidé za MedicRise.</h2></div><p>Spojuje nás zkuÅ¡enost ze zdravotnictví a chuÅ¥ měnit způsob, jak spolu zdravotníci, pacienti i týmy komunikují.</p></div><div class="team-leads"><article class="team-lead-card"><img src="/assets/team/barbora-mechurova.webp" alt="MUDr. Barbora Mechúrová" loading="lazy" decoding="async"><div><span class="kicker">Leader projektu · lektorka</span><h3>MUDr. Barbora Mechúrová</h3><a href="mailto:[email protected]">
1001[email protected]</a></div></article><article class="team-lead-card"><img src="/assets/team/ondrej-mechur.webp" alt="Ondřej Mechúr" loading="lazy" decoding="async"><div><span class="kicker">Management · lektor</span><h3>Ondřej Mechúr</h3><a href="mailto:[email protected]">[email protected]</a></div></article></div><div class="team-compact-grid">${compactTeam}</div></div></section></main>`;
1002}
1003
1004function layout(content) {
1005  document.body.innerHTML = header() + content + footer();
1006  document.querySelector(".mobile-toggle").onclick = () =>
1007    document.querySelector(".site-header").classList.toggle("mobile-open");
1008  document.querySelectorAll(".menu-group>a").forEach((a) =>
1009    a.addEventListener("click", (e) => {
1010      if (innerWidth <= 980) {
1011        e.preventDefault();
1012        a.parentElement.classList.toggle("open");
1013      }
1014    }),
1015  );
1016  initNewsletterSignup();
1017  initOutreachAttribution();
1018}
1019
1020function courseGuideSection(course) {
1021  if (!course.guideSlug) return "";
1022  return `<section class="section course-guide-section"><div class="container"><div class="course-guide-card"><div class="course-guide-preview"><img src="${course.guideCover}" alt="Náhled materiálu ${escapeHtml(course.guide)}"><span>PDF · zdarma</span></div><div class="course-guide-copy"><span class="kicker">Praktický materiál ke stažení</span><h2>${escapeHtml(course.guideTitle)}</h2><p>${escapeHtml(course.guideDescription)}</p><ul><li>okamžité stažení po odeslání formuláře</li><li>navazující praktické tipy do e-mailu</li><li>odběr lze kdykoli jednoduÅ¡e ukončit</li></ul></div><form class="course-guide-form" data-course-guide-form data-form-slug="${course.guideSlug}"><div class="course-guide-form-head"><strong>Kam vám máme průvodce poslat?</strong><span>Vyplnění zabere méně než minutu.</span></div><div class="course-guide-fields"><label>Jméno<input name="first_name" autocomplete="given-name" required maxlength="120" disabled></label><label>Příjmení<input name="last_name" autocomplete="family-name" required maxlength="120" disabled></label><label class="span-2">E-mail<input name="email" type="email" autocomplete="email" required maxlength="320" placeholder="[email protected]" disabled></label></div><label class="course-guide-consent"><input name="marketing_consent" type="checkbox" required disabled><span>Souhlasím se zasláním průvodce a navazujících praktických e-mailů MedicRise. Souhlas mohu kdykoli odvolat.</span></label><label class="footer-newsletter-honeypot" aria-hidden="true">Web<input name="website" tabindex="-1" autocomplete="off"></label><button class="btn btn-primary" type="submit" disabled>Načítáme formulář…</button><small data-course-guide-status aria-live="polite">Připravujeme bezpečné stažení.</small></form></div></div></section>`;
1023}
1024
1025async function initCourseGuideForm() {
1026  const form = document.querySelector("[data-course-guide-form]");
1027  if (!form) return;
1028  const button = form.querySelector('button[type="submit"]');
1029  const status = form.querySelector("[data-course-guide-status]");
1030  let definition;
1031  try {
1032    const result = await newsletterApi({ action: "definition", slug: form.dataset.formSlug });
1033    definition = result.form;
1034    for (const control of form.querySelectorAll("input,button")) control.disabled = false;
1035    button.textContent = "Stáhnout průvodce zdarma";
1036    status.textContent = "Materiál se zpřístupní ihned a současně dorazí do e-mailu.";
1037  } catch (error) {
1038    button.textContent = "Formulář je dočasně nedostupný";
1039    status.textContent = error.message;
1040    return;
1041  }
1042  form.addEventListener("submit", async (event) => {
1043    event.preventDefault();
1044    if (!form.reportValidity()) return;
1045    const data = new FormData(form);
1046    button.disabled = true;
1047    button.textContent = "Připravuji průvodce…";
1048    status.textContent = "Bezpečně ukládáme vaše údaje a souhlas.";
1049    try {
1050      const submitted = await newsletterApi({
1051        form_id: definition.id,
1052        public_token: definition.public_token,
1053        email: String(data.get("email") || ""),
1054        payload: {
1055          first_name: String(data.get("first_name") || "").trim(),
1056          last_name: String(data.get("last_name") || "").trim(),
1057        },
1058        consent: data.has("marketing_consent"),
1059        website: String(data.get("website") || ""),
1060        page_url: location.href,
1061        referrer: document.referrer,
1062      }, `${crypto.randomUUID()}:${Date.now()}`);
1063      const downloadUrl = safePublicDownloadUrl(submitted.download?.url);
1064      form.innerHTML = `<div class="course-guide-success"><span>
1064✓</span><div><strong>${escapeHtml(submitted.success_title || "Průvodce je připravený.")}</strong><p>${escapeHtml(submitted.success_message || "Odkaz posíláme také do vašeho e-mailu.")}</p></div>${downloadUrl ? `<button class="btn btn-primary" type="button" data-course-guide-download>Stáhnout PDF</button>` : ""}<small data-course-guide-download-status aria-live="polite"></small></div>`;
1065      form.querySelector("[data-course-guide-download]")?.addEventListener("click", async (clickEvent) => {
1066        const downloadButton = clickEvent.currentTarget;
1067        const downloadStatus = form.querySelector("[data-course-guide-download-status]");
1068        downloadButton.disabled = true;
1069        downloadButton.textContent = "Otevírám PDF…";
1070        try {
1071          await recordLeadMagnetDownload(definition.id, definition.public_token, submitted.submission_id);
1072          window.location.assign(downloadUrl);
1073        } catch (downloadError) {
1074          downloadButton.disabled = false;
1075          downloadButton.textContent = "Stáhnout PDF";
1076          downloadStatus.textContent = downloadError.message;
1077        }
1078      });
1079    } catch (error) {
1080      button.disabled = false;
1081      button.textContent = "Stáhnout průvodce zdarma";
1082      status.textContent = error.message;
1083    }
1084  });
1085}
1086
1087
1088function coursePage(c) {
1089  return `<main><section class="page-hero"><div class="container page-hero-grid"><div><span class="kicker">Prezenční kurz</span><h1>${c.title}</h1><p>${c.lead}</p><div class="meta-row"><span class="pill">⏳ 180 minut</span><span class="pill">🩺 VÅ¡echny zdravotnické profese</span><span class="pill">🎭 Praktické simulace</span></div><div class="hero-actions" style="margin-top:26px"><a class="btn btn-primary" href="mailto:[email protected]?subject=Zájem o kurz: ${encodeURIComponent(c.title)}">Mám zájem o kurz</a></div></div><div class="page-hero-art"><img src="${ASSETS.seminar}" alt="Vzdělávací kurz"></div></div></section><section class="section"><div class="container course-overview"><div class="rich-copy"><span class="kicker">Základní informace</span><h2 style="margin:14px 0 24px">Komunikace, která obstojí v reálné praxi.</h2>${c.intro.map((p) => `<p>${p}</p>`).join("")}</div><aside class="info-panel"><h3>Rychlý přehled</h3><div class="info-list"><div><span>Délka</span><strong>180 minut</strong></div><div><span>Forma</span><strong>Interaktivní</strong></div><div><span>Hlavní rámec</span><strong>${c.protocol}</strong></div><div><span>Cílová skupina</span><strong>Zdravotníci</strong></div></div><a class="btn btn-primary" style="width:100%;margin-top:22px" href="mailto:[email protected]">Poptat kurz</a></aside></div></section><section class="section section-soft"><div class="container"><div class="section-head"><div><span class="kicker">Co získáte</span><h2>Konkrétní dovednosti. Ne jen teorii.</h2></div></div><div class="feature-grid">${c.features.map(([i, t, d]) => `<div class="feature"><div class="card-icon">${i}</div><h3>${t}</h3><p>${d}</p></div>`).join("")}</div></div></section>${courseGuideSection(c)}<section class="section"><div class="container"><div class="cta"><div><span class="kicker cta-kicker">Kurz pro týmy</span><h2>Chcete téma otevřít ve vaÅ¡em zdravotnickém zařízení?</h2><p>Poptejte prezenční vzdělávací kurz pro sebe a své kolegy. Obsah můžeme přizpůsobit konkrétním potřebám vaÅ¡eho týmu.</p></div><div class="cta-side"><span class="cta-side-eyebrow">JednoduÅ¡e a nezávazně</span><strong>Pojďme to posunout dál.</strong><span class="cta-side-note">Stačí krátká zpráva. Společně vybereme vhodný další krok podle vaÅ¡ich potřeb.</span><div class="cta-actions"><a class="btn btn-primary" href="mailto:[email protected]?subject=Zájem o kurz: ${encodeURIComponent(c.title)}">Mám zájem o kurz</a></div></div></div></div></section></main>`;
1090}
1091
1092function education() {
1093  return `<main><section class="page-hero"><div class="container page-hero-grid"><div><span class="kicker">Vzdělávání MedicRise</span><h1>Praktické dovednosti pro každodenní zdravotnickou praxi.</h1><p>Kurzy stavíme na interakci, simulacích reálných situací, sdílení zkušeností a nástrojích, které lze použít hned při další směně.</p></div><div class="page-hero-art"><img src="${ASSETS.discussion}" alt="Vzdělávání"></div></div></section><section class="section"><div class="container"><div class="section-head"><div><span class="kicker">Kurzy a workshopy</span><h2>Vyberte téma pro svůj tým.</h2></div><p>Všechny hlavní prezenční kurzy mají doporučenou časovou dotaci 180 minut a jsou určeny zdravotníkům napříč profesemi.</p></div><div class="cards">${Object.entries(
1094    coursePages,
1095  )
1096    .map(
1097      ([href, key]) =>
1098        `<a class="card" href="${href}"><div class="card-icon">${key.includes("mbti") ? "🧩" : key === "tym" ? "🤝" : "💬"}</div><h3>${courses[key].title}</h3><p>${courses[key].lead}</p><span class="card-link">Detail kurzu</span></a>`,
1099    )
1100    .join(
1101      "",
1102    )}</div></div></section><section class="section section-soft"><div class="container split"><div class="split-copy"><span class="kicker">Pro organizace</span><h2>Jeden kurz, celý vzdělávací den nebo dlouhodobá spolupráce.</h2><p>Podle velikosti a potřeb vašeho zařízení můžeme realizovat samostatný kurz, dva kurzy během jednoho dne, kombinovat prezenční a on-line vzdělávání nebo sestavit větší program.</p><div class="bullets"><div class="bullet"><span class="check">✓</span><div><strong>Prezenční kurz</strong><p>Doporučená délka 180 minut, interaktivní formát a materiály.</p></div></div><div class="bullet"><span class="check">✓</span><div><strong>On-line kurzy</strong><p>Flexibilní self-guided forma pro jednotlivce i větší skupiny.
1102</p></div></div><div class="bullet"><span class="check">✓</span><div><strong>Konference na míru</strong><p>Komplexní vzdělávací den pro desítky až stovky zaměstnanců.</p></div></div></div></div><div class="split-media" style="padding:40px;display:grid;place-items:center"><img src="${ASSETS.seminar}" alt="Seminář"></div></div></section></main>`;
1103}
1104
1105
1106function online() {
1107  return `<main class="course-store"><section class="page-hero"><div class="container page-hero-grid"><div><span class="kicker">On-line kurzy</span><h1>Vzdělávání, ke kterému se můžete kdykoli vrátit.</h1><p>Studujte vlastním tempem, sledujte svůj pokrok a vraťte se kdykoliv přes osobní portál MedicRise.</p><div class="hero-actions"><a class="btn btn-primary" href="/moje-kurzy">Moje kurzy →</a></div></div><div class="page-hero-art"><img src="${ASSETS.subscribe}" alt="Online vzdělávání"></div></div></section><section class="section"><div class="container"><div class="section-head"><div><span class="kicker">Aktuální nabídka</span><h2>Vyberte si kurz.</h2></div><p>Po úhradě faktury se přístup automaticky objeví v části Moje kurzy.</p></div><div id="course-store-grid" class="course-store-grid"><div class="course-store-loading">Načítám aktuální kurzy…</div></div></div></section><div id="course-order-modal"></div></main>`;
1108}
1109async function courseOrderApi(body, idempotencyKey = "") {
1110  const headers = {
1111    apikey: CMS_SUPABASE_KEY,
1112    "Content-Type": "application/json",
1113  };
1114  if (idempotencyKey) headers["x-idempotency-key"] = idempotencyKey;
1115  const response = await fetch(
1116      `${CMS_SUPABASE_URL}/functions/v1/course-order`,
1117      {
1118        method: "POST",
1119        headers,
1120        body: JSON.stringify(body),
1121      },
1122    ),
1123    data = await response.json().catch(() => ({}));
1124  if (!response.ok)
1125    throw new Error(data.error || "Požadavek se nepodařilo zpracovat.");
1126  return data;
1127}
1128function courseMoney(value, currency = "CZK") {
1129  return new Intl.NumberFormat("cs-CZ", {
1130    style: "currency",
1131    currency,
1132    maximumFractionDigits: 0,
1133  }).format(Number(value || 0));
1134}
1135function courseOrderForm(course) {
1136  return `<div class="course-order-backdrop"><form class="course-order-dialog" id="course-order-form"><header><div><span class="kicker">Objednávka kurzu</span><h2>${escapeHtml(course.title)}</h2></div><button type="button" data-close-course-order aria-label="Zavřít">×</button></header><input type="hidden" name="course_id" value="${course.id}"><input class="hp-field" name="website" tabindex="-1" autocomplete="off"><div class="course-order-grid"><label>Jméno *<input name="first_name" required></label><label>Příjmení *<input name="last_name" required></label><label>E-mail *<input name="email" type="email" required></label><label>Telefon<input name="phone" type="tel"></label></div><div class="billing-choice"><label><input type="radio" name="billing_type" value="person" checked> Soukromá osoba</label><label><input type="radio" name="billing_type" value="company"> Firma / organizace</label></div><div class="course-order-grid"><label class="span-2">Fakturační jméno / název *<input name="billing_name" required></label><label data-course-company hidden>IČO<input name="ico" inputmode="numeric" maxlength="8"></label><label data-course-company hidden>DIČ<input name="dic"></label><label class="span-2">Ulice a číslo *<input name="street" required></label><label>Město *<input name="city" required></label><label>PSČ *<input name="postal_code" required></label></div><div class="course-discount-box"><label>Slevový kód<div><input name="discount_code" autocomplete="off"><button type="button" data-validate-course-code>Použít kód</button></div></label><small data-course-discount-status></small></div><div class="course-order-total"><span>K úhradě</span><strong data-course-total>${courseMoney(course.price, course.currency)}</strong></div><label class="registration-consent"><input type="checkbox" required> Souhlasím se zpracováním údajů nezbytných pro vyřízení objednávky.</label><button class="btn btn-primary course-order-submit">Objednat kurz</button><p class="form-status" data-course-order-status></p></form></div>`;
1137}
1138function bindCourseOrder(course) {
1139  const modal = document.querySelector("#course-order-modal");
1140  modal.innerHTML = courseOrderForm(course);
1141  document.body.classList.add("course-order-open");
1142  const form = document.querySelector("#course-order-form"),
1143    status = form.querySelector("[data-course-order-status]"),
1144    total = form.querySelector("[data-course-total]");
1145  let discount = null;
1146  const orderIdempotencyKey = crypto.randomUUID();
1147  form.querySelector("[data-close-course-order]").onclick = () => {
1148    modal.innerHTML = "";
1149    document.body.classList.remove("course-order-open");
1150  };
1151  form
1152    .querySelectorAll('[name="billing_type"]')
1153    .forEach(
1154      (r) =>
1155        (r.onchange = () =>
1156          form
1157            .querySelectorAll("[data-course-company]")
1158            .forEach((x) => (x.hidden = r.value !== "company"))),
1159    );
1160  form.querySelector("[data-validate-course-code]").onclick = async () => {
1161    const code = form.elements.discount_code.value.trim(),
1162      message = form.querySelector("[data-course-discount-status]");
1163    message.textContent = "Ověřuji…";
1164    try {
1165      discount = await courseOrderApi({
1166        action: "validate-discount",
1167        course_id: course.id,
1168        code,
1169      });
1170      const amount =
1171        discount.discount_type === "percent"
1172          ? Math.round((Number(course.price) * discount.discount_value) / 100)
1173          : Math.min(Number(course.price), discount.discount_value);
1174      total.textContent = courseMoney(
1175        Math.max(0, Number(course.price) - amount),
1176        course.currency,
1177      );
1178      message.textContent = "Kód byl použit.";
1179    } catch (err) {
1180      discount = null;
1181      total.textContent = courseMoney(course.price, course.currency);
1182      message.textContent = err.message;
1183    }
1184  };
1185  form.onsubmit = async (e) => {
1186    e.preventDefault();
1187    const button = form.querySelector(".course-order-submit"),
1188      payload = Object.fromEntries(new FormData(form));
1189    button.disabled = true;
1190    button.textContent = "Vytvářím objednávku…";
1191    status.textContent = "";
1192    try {
1193      const result = await courseOrderApi(
1194        { action: "create-order", ...payload },
1195        orderIdempotencyKey,
1196      );
1197      form.innerHTML = `<div class="course-order-success"><span>
1197✓</span><h2>Objednávka je hotová.</h2><p>Číslo objednávky: <strong>${escapeHtml(result.order_number)}</strong></p>${result.access_active ? "<p>Přístup je aktivní. Přihlaste se v části Moje kurzy.</p>" : `<div><span>Částka</span><strong>${courseMoney(result.final_price)}</strong></div><p>Faktura ${escapeHtml(result.invoice_number || "")} je evidována v systému. Po spárování platby se kurz automaticky aktivuje.</p>`}<a class="btn btn-primary" href="/moje-kurzy">Přejít do Moje kurzy</a></div>`;
1198    } catch (err) {
1199      status.textContent = err.message;
1200      button.disabled = false;
1201      button.textContent = "Objednat kurz";
1202    }
1203  };
1204}
1205function showCourseStoreDetail(course) {
1206  const modal = document.querySelector("#course-order-modal"),
1207    access =
1208      course.access_mode === "duration"
1209        ? `${course.access_days || 90} dní`
1210        : course.access_mode === "fixed_date" && course.access_until
1211          ? `do ${new Intl.DateTimeFormat("cs-CZ").format(new Date(course.access_until))}`
1212          : "bez omezení";
1213  modal.innerHTML = `<div class="course-order-backdrop"><section class="course-order-dialog course-detail-dialog"><header><div><span class="kicker">Detail on-line kurzu</span><h2>${escapeHtml(course.title)}</h2></div><button type="button" data-close-course-order aria-label="Zavřít">×</button></header>${course.thumbnail_path ? `<img class="course-detail-cover" src="${CMS_SUPABASE_URL}/storage/v1/object/public/course-thumbnails/${escapeHtml(course.thumbnail_path)}" alt="">` : ""}<p class="course-detail-lead">${escapeHtml(course.short_description || "")}</p><div class="course-detail-body">${escapeHtml(course.description || "Kurz můžete absolvovat vlastním tempem v osobním studijním portálu MedicRise.")}</div><div class="course-detail-facts"><span><b>⏱</b> Studujte vlastním tempem</span><span><b>🔐</b> Přístup ${escapeHtml(access)}</span><span><b>📈</b> Ukládání průběhu</span><span><b>🎓</b> Certifikát dle nastavení kurzu</span></div><div class="course-order-total"><span>Cena kurzu</span><strong>${course.is_free ? "Zdarma" : courseMoney(course.price, course.currency)}</strong></div><button class="btn btn-primary course-order-submit" data-detail-buy>${course.is_free ? "Získat přístup" : "Objednat kurz"}</button></section></div>`;
1214  document.body.classList.add("course-order-open");
1215  modal.querySelector("[data-close-course-order]").onclick = () => {
1216    modal.innerHTML = "";
1217    document.body.classList.remove("course-order-open");
1218  };
1219  modal.querySelector("[data-detail-buy]").onclick = () =>
1220    bindCourseOrder(course);
1221}
1222async function initOnlineStore() {
1223  const grid = document.querySelector("#course-store-grid");
1224  if (!grid) return;
1225  try {
1226    const data = await courseOrderApi({ action: "catalog" }),
1227      courses = data.courses || [];
1228    grid.innerHTML =
1229      courses
1230        .map(
1231          (c) =>
1232            `<article class="course-store-card"><div class="course-store-cover">${c.thumbnail_path ? `<img src="${CMS_SUPABASE_URL}/storage/v1/object/public/course-thumbnails/${escapeHtml(c.thumbnail_path)}" alt="">` : "<span>🎓</span>"}</div><div><span class="kicker">On-line kurz</span><h3>${escapeHtml(c.title)}</h3><p>${escapeHtml(c.short_description || "")}</p><div class="course-store-price">${c.compare_at_price ? `<s>${courseMoney(c.compare_at_price, c.currency)}</s>` : ""}<strong>${c.is_free ? "Zdarma" : courseMoney(c.price, c.currency)}</strong></div><div class="course-store-actions"><button class="btn btn-secondary" data-course-detail="${c.id}">Detail kurzu</button><button class="btn btn-primary" data-buy-course="${c.id}">${c.is_free ? "Získat přístup" : "Objednat kurz"}</button></div></div></article>`,
1233        )
1234        .join("") ||
1235      '<div class="course-store-empty"><span>🎓</span><h3>Nové kurzy právě připravujeme.</h3><p>Jakmile některý kurz zveřejníme k prodeji, objeví se tady.</p></div>';
1236    grid
1237      .querySelectorAll("[data-buy-course]")
1238      .forEach(
1239        (button) =>
1240          (button.onclick = () =>
1241            bindCourseOrder(
1242              courses.find((c) => c.id === button.dataset.buyCourse),
1243            )),
1244      );
1245    grid
1246      .querySelectorAll("[data-course-detail]")
1247      .forEach(
1248        (button) =>
1249          (button.onclick = () =>
1250            showCourseStoreDetail(
1251              courses.find((c) => c.id === button.dataset.courseDetail),
1252            )),
1253      );
1254  } catch (err) {
1255    grid.innerHTML = `<div class="course-store-empty"><h3>
1255Nabídku se nepodařilo načíst.</h3><p>${escapeHtml(err.message)}</p></div>`;
1256  }
1257}
1258
1259function programTypeOrder(type) {
1260  return {
1261    Přednáška: 0,
1262    "Panelová diskuze": 1,
1263    Workshop: 2,
1264  }[type] ?? 3;
1265}
1266
1267function compareProgramSessions(a, b) {
1268  return (
1269    programTypeOrder(a.item_type || a.type) -
1270      programTypeOrder(b.item_type || b.type) ||
1271    Number(a.sort_order || 0) - Number(b.sort_order || 0) ||
1272    String(a.title || "").localeCompare(String(b.title || ""), "cs")
1273  );
1274}
1275
1276function publicSpeakerName(speaker = {}) {
1277  if (speaker.first_name || speaker.last_name)
1278    return [speaker.first_name, speaker.last_name].filter(Boolean).join(" ");
1279  return String(speaker.name || "")
1280    .replace(
1281      /^(?:(?:prof\.|doc\.|MUDr\.|MDDr\.|JUDr\.|Mgr\.|Ing\.|Bc\.|PhDr\.|RNDr\.|PaedDr\.|ThLic\.|et)\s+)+/i,
1282      "",
1283    )
1284    .replace(
1285      /(?:,?\s+(?:MA|MBA|MHA|MPH|Ph\.D\.|PhD|DiS\.))+(?:\s*,?\s*)?$/i,
1286      "",
1287    )
1288    .trim();
1289}
1290
1291function programCard(s) {
1292  const typeClass =
1293    s.type === "Panelová diskuze"
1294      ? "panel"
1295      : s.type === "Přednáška"
1296        ? "lecture"
1297        : "workshop";
1298  const speakers = Array.isArray(s.speakers)
1299    ? s.speakers
1300    : s.speaker
1301      ? [{ name: s.speaker, img: s.img || "" }]
1302      : [];
1303  const speakerList = speakers.length
1304    ? `<div class="program-speaker-list">${speakers
1305        .map((speaker) => {
1306          const name = escapeHtml(publicSpeakerName(speaker));
1307          return `<div class="program-speaker">${speaker.img ? `<img class="program-avatar" src="${escapeHtml(speaker.img)}" alt="${name}" loading="lazy">` : `<span class="program-avatar program-avatar-empty" aria-hidden="true">${name.slice(0, 1) || "•"}</span>`}<span>${name}</span></div>`;
1308        })
1309        .join("")}</div>`
1310    : "";
1311  return `<article class="program-card"><div class="program-card-top"><span class="program-type ${typeClass}">${escapeHtml(s.type || "Příspěvek")}</span></div><h4>${escapeHtml(s.title || "")}</h4>${speakerList}</article>`;
1312}
1313
1314function programTimeline(program) {
1315  if (!program) {
1316    return `<div class="program-coming"><span class="kicker">Program 2026</span><h3>Program právě připravujeme.</h3><p>Jména lektorů, témata a přesný harmonogram zveřejníme postupně. Stránka je připravená na stejný programový formát jako archivní ročníky.</p><div class="program-preview-times"><span>Ráno · registrace a zahájení</span><span>Dopoledne · přednášky a workshopy</span><span>Odpoledne · vzdělávací bloky a networking</span></div></div>`;
1317  }
1318  return `<div class="program-timeline">${program.map((block) => `<section class="program-row ${block.kind || ""}"><div class="program-time"><strong>${block.time}</strong></div><div class="program-content">${block.sessions ? `<div class="program-grid">${[...block.sessions].sort(compareProgramSessions).map(programCard).join("")}</div>` : `<div class="program-service"><strong>${block.label}</strong></div>`}</div></section>`).join("")}</div>`;
1319}
1320
1321function conferenceHub() {
1322  const archive = ["2026", "2025", "2024", "2023", "2022"];
1323  return `<main><section class="conference-hero"><div class="container conference-hero-grid"><div><span class="kicker">Konference MedicRise</span><h1>Zdravé zdravotnictví.</h1><p>Každoroční setkání zdravotníků, lektorů a lidí, kteří chtějí komunikaci, spolupráci a péči posouvat k větší lidskosti. Jeden den plný přednášek, praktických workshopů a setkávání napříč profesemi.</p><div class="hero-actions"><a class="btn btn-primary" href="konference-2026.html">Ročník 2026 →</a><a class="btn btn-secondary" href="#rocniky">Prohlédnout archiv</a></div></div><div class="conference-hero-card"><span>2022 → 2026</span><strong>5 ročníků.<br>Jedna společná vize.</strong><p>Praktické vzdělávání, které propojuje zkušenost, odborné know-how a reálné situace ze zdravotnictví.</p></div></div></section><section class="section"><div class="container"><div class="section-head"><div><span class="kicker">Jak konference funguje</span><h2>Odbornost, praxe a networking v jednom dni.</h2></div><p>Každý ročník kombinuje společné přednášky s paralelními workshopy. Účastníci si tak mohou den poskládat podle témat, která jsou pro ně nejrelevantnější.</p></div><div class="feature-grid"><div class="feature"><div class="card-icon">🎤</div><h3>Přednášky</h3><p>Společná témata, která otevírají širší kontext zdravotnictví.</p></div><div class="feature"><div class="card-icon">🎭</div><h3>Workshopy</h3><p>Menší skupiny a praktická práce s konkrétními situacemi.</p></div><div class="feature"><div class="card-icon">💬</div><h3>Diskuze</h3><p>Prostor pro různé perspektivy, otázky a zkušenosti z praxe.</p></div><div class="feature"><div class="card-icon">🤝</div><h3>Networking</h3><p>Neformální setkávání zdravotníků napříč profesemi a organizacemi.</p></div></div></div></section><section class="section section-soft" id="rocniky"><div class="container"><div class="section-head"><div><span class="kicker">Jednotlivé ročníky</span><h2>Pět let Zdravého zdravotnictví.</h2></div></div><div class="year-grid">${archive
1324    .map((y) => {
1325      const d = conferenceYears[y];
1326      return `<a class="year-card ${y === "2026" ? "current" : ""}" data-conference-card-year="${y}" href="konference-${y}.html"><div class="year-card-top"><span data-edition-label>${d.edition}</span><span class="year-status" data-edition-status>${d.status}</span></div><strong class="year-number">${y}</strong><h3 data-edition-name>${d.topic}</h3><p><span data-edition-date>${d.date}</span> · <span data-edition-venue>${d.venue}</span></p><span class="card-link">Detail ročníku</span></a>`;
1327    })
1328    .join("")}</div></div></section></main>`;
1329}
1330
1331function conferenceYear(year) {
1332  const d = conferenceYears[year] || conferenceYears["2026"];
1333  const isCurrent = year === "2026";
1334  return `<main><section class="conference-year-hero" data-conference-edition-year="${year}"><div class="container"><div class="year-nav">${["2026", "2025", "2024", "2023", "2022"].map((y) => `<a class="${y === year ? "active" : ""}" href="konference-${y}.html">${y}</a>`).join("")}</div><div class="conference-year-grid"><div><span class="kicker"><span data-edition-label>${d.edition}</span> · <span data-edition-status>${d.status}</span></span><h1 data-edition-name>${d.topic}</h1><p data-edition-intro>${d.intro}</p><div class="conference-meta"><span>📅 <span data-edition-date>${d.date}</span></span><span>📍 <span data-edition-venue>${d.venue}</span></span>${year !== "2026" ? "<span>✓ Archiv ročníku</span>" : '<span class="conference-accreditation">
1334✓ Akreditováno ČLK · 6 kreditů</span><span class="conference-accreditation">✓ Garance ČAS</span>'}</div>${isCurrent ? '<div class="hero-actions"><a class="btn btn-primary" href="/registrace-na-konferenci">Registrace na konferenci →</a><a class="btn btn-secondary" href="mailto:[email protected]?subject=Konference MedicRise 2026">Mám dotaz</a></div>' : ""}</div><div class="conference-year-art"><img src="${ASSETS.seminar}" alt="Konference Zdravé zdravotnictví ${year}"><strong>${year}</strong></div></div></div></section><section class="section program-section"><div class="container"><div class="section-head"><div><span class="kicker">Program konference</span><h2>Jeden den. Jasný harmonogram.</h2></div><p>Každý blok jasně ukazuje čas, typ příspěvku, téma a lektora. Paralelní příspěvky jsou seskupené v jednom časovém bloku.</p></div><div id="published-program-${year}">${programTimeline(d.program)}</div></div></section>${isCurrent ? conference2026Experience() : conferenceArchiveOutro(year)}${conferenceGallerySection(year)}<section class="section conference-partners-section"><div class="container"><div class="section-head"><div><span class="kicker">Společně tvoříme změnu</span><h2>Partneři konference</h2></div><p>Děkujeme organizacím, které pomáhají rozvíjet zdravější komunikaci a spolupráci ve zdravotnictví.</p></div><div class="conference-partner-levels" id="conference-partners-${year}"><p class="conference-partners-empty">Partnerská sestava se připravuje.</p></div></div></section></main>`;
1335}
1336
1337function conferenceGallerySection(year) {
1338  return `<section class="section conference-gallery-section" data-conference-gallery="${year}" hidden><div class="container"><div class="conference-gallery-heading"><div><span class="kicker">Atmosféra ročníku</span><h2>Fotogalerie ${year}</h2></div><p data-gallery-count></p></div><div class="conference-gallery-grid" data-gallery-grid></div><div class="conference-gallery-more" data-gallery-more hidden><button class="btn btn-secondary" type="button">Zobrazit další fotografie</button></div></div><div class="conference-lightbox" data-gallery-lightbox hidden role="dialog" aria-modal="true" aria-label="Fotogalerie konference"><button class="conference-lightbox-close" type="button" aria-label="Zavřít galerii">×</button><button class="conference-lightbox-arrow prev" type="button" aria-label="Předchozí fotografie">←</button><figure><img alt=""><figcaption></figcaption></figure><button class="conference-lightbox-arrow next" type="button" aria-label="Další fotografie">→</button><span class="conference-lightbox-position" aria-live="polite"></span></div></section>`;
1339}
1340
1341function conferenceEditionStatus(edition) {
1342  if (edition.status === "archived") return "Proběhlo";
1343  if (edition.status === "active") return "Aktuální ročník";
1344  return "Připravujeme";
1345}
1346
1347function conferenceEditionDate(value) {
1348  if (!value) return "Termín připravujeme";
1349  const [year, month, day] = String(value).split("-").map(Number);
1350  return `${day}. ${month}. ${year}`;
1351}
1352
1353function fillConferenceEdition(root, edition) {
1354  const values = {
1355    "[data-edition-label]": edition.edition_label || `${edition.year}. ročník`,
1356    "[data-edition-status]": conferenceEditionStatus(edition),
1357    "[data-edition-name]": edition.name,
1358    "[data-edition-intro]": edition.intro || "",
1359    "[data-edition-date]": conferenceEditionDate(edition.event_date),
1360    "[data-edition-venue]": edition.venue || "Místo připravujeme",
1361  };
1362  Object.entries(values).forEach(([selector, value]) => {
1363    root.querySelectorAll(selector).forEach((element) => {
1364      element.textContent = value;
1365    });
1366  });
1367}
1368
1369async function hydrateConferenceEditions(year = null) {
1370  try {
1371    const filter = year ? `year=eq.${encodeURIComponent(year)}&` : "";
1372    const editions = await fetch(
1373      `${CMS_SUPABASE_URL}/rest/v1/conference_editions?${filter}select=year,name,edition_label,intro,event_date,venue,status&order=year.desc`,
1374      { headers: { apikey: CMS_SUPABASE_KEY } },
1375    ).then((response) =>
1376      response.ok ? response.json() : Promise.reject(response),
1377    );
1378    editions.forEach((edition) => {
1379      document
1380        .querySelectorAll(`[data-conference-card-year="${edition.year}"], [data-conference-edition-year="${edition.year}"]`)
1381        .forEach((root) =>
1381 fillConferenceEdition(root, edition));
1382    });
1383  } catch (error) {
1384    console.warn("Conference edition details are temporarily unavailable.", error);
1385  }
1386}
1387
1388const CMS_SUPABASE_URL = "https://hmxfyykksjbzgungkhlj.supabase.co";
1389const CMS_SUPABASE_KEY = "sb_publishable_dlI-aI6JlDTNYRF59D-Bkw_9HiE8ZSu";
1390
1391function escapeHtml(value = "") {
1392  return String(value).replace(
1393    /[&<>"']/g,
1394    (ch) =>
1395      ({
1396        "&": "&amp;",
1397        "<": "&lt;",
1398        ">": "&gt;",
1399        '"': "&quot;",
1400        "'": "&#039;",
1401      })[ch],
1402  );
1403}
1404
1405function formatCmsTime(value) {
1406  return value ? String(value).slice(0, 5) : "";
1407}
1408function publicProgramFromSnapshot(snapshot) {
1409  const items = snapshot?.items || [];
1410  if (!items.length) return null;
1411  const groups = [];
1412  items.forEach((item) => {
1413    const key = `${formatCmsTime(item.start_time)}–${formatCmsTime(item.end_time)}`;
1414    let group = groups.find((g) => g.time === key);
1415    if (!group) {
1416      group = { time: key, items: [] };
1417      groups.push(group);
1418    }
1419    group.items.push(item);
1420  });
1421  return `<div class="program-timeline">${groups
1422    .map((group) => {
1423      const service = group.items.length === 1 && group.items[0].is_service;
1424      if (service)
1425        return `<section class="program-row service"><div class="program-time"><strong>${escapeHtml(group.time)}</strong></div><div class="program-content"><div class="program-service"><strong>${escapeHtml(group.items[0].title)}</strong></div></div></section>`;
1426      return `<section class="program-row"><div class="program-time"><strong>${escapeHtml(group.time)}</strong></div><div class="program-content"><div class="program-grid">${group.items
1427        .sort(compareProgramSessions)
1428        .map((item) =>
1429          programCard({
1430            type: item.item_type,
1431            title: item.title,
1432            speakers: (item.speakers || []).map((speaker) => ({
1433              first_name: speaker.first_name,
1434              last_name: speaker.last_name,
1435              img: speaker.photo_url || "",
1436            })),
1437          }),
1438        )
1439        .join("")}</div></div></section>`;
1440    })
1441    .join("")}</div>`;
1442}
1443
1444async function hydratePublishedProgram(year) {
1445  try {
1446    const headers = { apikey: CMS_SUPABASE_KEY };
1447    const editions = await fetch(
1448      `${CMS_SUPABASE_URL}/rest/v1/conference_editions?year=eq.${year}&select=id`,
1449      { headers },
1450    ).then((r) => (r.ok ? r.json() : Promise.reject(r)));
1451    if (!editions[0]) return;
1452    const pubs = await fetch(
1453      `${CMS_SUPABASE_URL}/rest/v1/program_publications?edition_id=eq.${editions[0].id}&is_current=eq.true&select=snapshot&order=published_at.desc&limit=1`,
1454      { headers },
1455    ).then((r) => (r.ok ? r.json() : Promise.reject(r)));
1456    const html = publicProgramFromSnapshot(pubs[0]?.snapshot);
1457    const target = document.getElementById(`published-program-${year}`);
1458    if (target && html) target.innerHTML = html;
1459  } catch (error) {
1460    console.warn("Published program is temporarily unavailable.", error);
1461  }
1462}
1463
1464async function hydrateConferencePartners(year) {
1465  try {
1466    const headers = { apikey: CMS_SUPABASE_KEY },
1467      editions = await fetch(
1468        `${CMS_SUPABASE_URL}/rest/v1/conference_editions?year=eq.${year}&select=id`,
1469        { headers },
1470      ).then((response) =>
1471        response.ok ? response.json() : Promise.reject(response),
1472      );
1473    if (!editions[0]) return;
1474    const partners = await fetch(
1475      `${CMS_SUPABASE_URL}/rest/v1/conference_partners?edition_id=eq.${editions[0].id}&status=eq.confirmed&logo_url=not.is.null&select=name,partner_level,display_level,logo_url&order=display_level.asc,name.asc`,
1476      { headers },
1477    ).then((response) =>
1478      response.ok ? response.json() : Promise.reject(response),
1479    );
1480    const target = document.getElementById(`conference-partners-${year}`);
1481    if (!target || !partners.length) return;
1482    target.innerHTML = [1, 2, 3]
1483      .map((level) => {
1484        const group = partners.filter(
1485          (partner) => Number(partner.display_level || 3) === level,
1486        );
1487        return group.length
1488          ? `<div class="conference-partner-tier tier-${level}">${group.map((partner) => `<figure data-partner-name="${escapeHtml(partner.name)}
1488"><img src="${escapeHtml(partner.logo_url)}" alt="${escapeHtml(partner.name)}"></figure>`).join("")}</div>`
1489          : "";
1490      })
1491      .join("");
1492  } catch (error) {
1493    console.warn("Conference partners are temporarily unavailable.", error);
1494  }
1495}
1496
1497function conferenceGalleryImageUrl(path) {
1498  const safePath = String(path || "")
1499    .split("/")
1500    .map(encodeURIComponent)
1501    .join("/");
1502  return `${CMS_SUPABASE_URL}/storage/v1/object/public/conference-gallery/${safePath}`;
1503}
1504
1505async function hydrateConferenceGallery(year) {
1506  const section = document.querySelector(`[data-conference-gallery="${year}"]`);
1507  if (!section) return;
1508  try {
1509    const headers = { apikey: CMS_SUPABASE_KEY };
1510    const editions = await fetch(
1511      `${CMS_SUPABASE_URL}/rest/v1/conference_editions?year=eq.${year}&select=id`,
1512      { headers },
1513    ).then((response) =>
1514      response.ok ? response.json() : Promise.reject(response),
1515    );
1516    if (!editions[0]) return;
1517    const photos = await fetch(
1518      `${CMS_SUPABASE_URL}/rest/v1/conference_gallery_photos?edition_id=eq.${editions[0].id}&is_published=eq.true&select=id,storage_path,thumbnail_path,preview_path,width,height,caption,alt_text,is_cover,sort_order&order=is_cover.desc,sort_order.asc,created_at.asc`,
1519      { headers },
1520    ).then((response) =>
1521      response.ok ? response.json() : Promise.reject(response),
1522    );
1523    if (!photos.length) return;
1524
1525    const grid = section.querySelector("[data-gallery-grid]");
1526    const more = section.querySelector("[data-gallery-more]");
1527    const count = section.querySelector("[data-gallery-count]");
1528    const lightbox = section.querySelector("[data-gallery-lightbox]");
1529    const lightboxImage = lightbox.querySelector("img");
1530    const lightboxCaption = lightbox.querySelector("figcaption");
1531    const lightboxPosition = lightbox.querySelector(
1532      ".conference-lightbox-position",
1533    );
1534    let rendered = 0;
1535    let activeIndex = 0;
1536
1537    const photoAlt = (photo) =>
1538      photo.alt_text ||
1539      photo.caption ||
1540      `Fotografie z konference Zdravé zdravotnictví ${year}`;
1541    const renderBatch = () => {
1542      const next = photos.slice(rendered, rendered + 24);
1543      grid.insertAdjacentHTML(
1544        "beforeend",
1545        next
1546          .map((photo, offset) => {
1547            const index = rendered + offset;
1548            const alt = escapeHtml(photoAlt(photo));
1549            const thumbnail = conferenceGalleryImageUrl(photo.thumbnail_path || photo.preview_path || photo.storage_path);
1550            const preview = conferenceGalleryImageUrl(photo.preview_path || photo.thumbnail_path || photo.storage_path);
1551            return `<button class="conference-gallery-photo${photo.is_cover ? " is-cover" : ""}" type="button" data-gallery-index="${index}" aria-label="Otevřít fotografii ${index + 1} z ${photos.length}"><img src="${thumbnail}" srcset="${thumbnail} 480w, ${preview} 1080w" sizes="(max-width: 680px) 50vw, (max-width: 1050px) 33vw, 25vw" width="1920" height="1280" loading="lazy" decoding="async" alt="${alt}"></button>`;
1552          })
1553          .join(""),
1554      );
1555      rendered += next.length;
1556      more.hidden = rendered >= photos.length;
1557    };
1558    const showPhoto = (index) => {
1559      activeIndex = (index + photos.length) % photos.length;
1560      const photo = photos[activeIndex];
1561      lightboxImage.src = conferenceGalleryImageUrl(photo.storage_path);
1562      lightboxImage.alt = photoAlt(photo);
1563      lightboxCaption.textContent = photo.caption || "";
1564      lightboxCaption.hidden = !photo.caption;
1565      lightboxPosition.textContent = `${activeIndex + 1} / ${photos.length}`;
1566    };
1567    const onKey = (event) => {
1568      if (event.key === "Escape") closeLightbox();
1569      if (event.key === "ArrowLeft") showPhoto(activeIndex - 1);
1570      if (event.key === "ArrowRight") showPhoto(activeIndex + 1);
1571    };
1572    const openLightbox = (index) => {
1573      showPhoto(index);
1574      lightbox.hidden = false;
1575      document.body.classList.add("conference-lightbox-open");
1576      document.addEventListener("keydown", onKey);
1577      lightbox.querySelector(".conference-lightbox-close").focus();
1578    };
1579    function closeLightbox() {
1580      lightbox.hidden = true;
1581      lightboxImage.removeAttribute("src");
1582      document.body.classList.remove("conference-lightbox-open");
1583      document.removeEventListener("keydown", onKey);
1584    }
1585
1586    count.textContent = `${photos.length} ${photos.length === 1 ? "fotografie" : photos.length < 5 ? "fotografie" : "fotografií"}`;
1587    grid.addEventListener("click", (event) => {
1588      const button = event.target.closest("[data-gallery-index]");
1589      if (button) openLightbox(Number(button.dataset.galleryIndex));
1590    });
1591    more.querySelector("button").addEventListener("click", renderBatch);
1592    lightbox
1593      .querySelector(".conference-lightbox-close")
1594      .addEventListener("click", closeLightbox);
1595    lightbox
1596      .querySelector(".conference-lightbox-arrow.prev")
1597      .addEventListener("click", () => showPhoto(activeIndex - 1));
1598    lightbox
1599      .querySelector(".conference-lightbox-arrow.next")
1600      .addEventListener("click", () => showPhoto(activeIndex + 1));
1601    lightbox.addEventListener("click", (event) => {
1602      if (event.target === lightbox) closeLightbox();
1603    });
1604
1605    section.hidden = false;
1606    renderBatch();
1607  } catch (error) {
1608    console.warn("Conference gallery is temporarily unavailable.", error);
1609  }
1610}
1611
1612async function hydrateConferencePricePeriods(year) {
1613  const target = document.getElementById(`conference-price-periods-${year}`);
1614  if (!target) return;
1615  try {
1616    const response = await fetch(
1617      `${CMS_SUPABASE_URL}/rest/v1/rpc/conference_public_price_periods`,
1618      {
1619        method: "POST",
1620        headers: {
1621          apikey: CMS_SUPABASE_KEY,
1622          "content-type": "application/json",
1623        },
1624        body: JSON.stringify({ p_year: Number(year) }),
1625      },
1626    );
1627    if (!response.ok) throw response;
1628    const periods = await response.json();
1629    if (!periods.length) {
1630      target.innerHTML = '<p class="conference-price-empty">Cenová pásma právě připravujeme.</p>';
1631      return;
1632    }
1633    target.innerHTML = periods
1634      .map(
1635        (period) =>
1636          `<article class="conference-price-period${period.is_current ? " is-current" : ""}"><div class="conference-price-period-meta">${period.is_current ? '<span class="conference-price-current">Aktuální cena</span>' : ""}<small>${escapeHtml(conferenceEditionDate(period.valid_from))} – ${escapeHtml(conferenceEditionDate(period.valid_to))}</small></div><strong>${escapeHtml(money(period.amount_czk))}</strong></article>`,
1637      )
1638      .join("");
1639  } catch (error) {
1640    target.innerHTML = '<p class="conference-price-empty">Cenová pásma se nyní nepodařilo načíst.</p>';
1641    console.warn("Conference price periods are temporarily unavailable.", error);
1642  }
1643}
1644
1645function registrationPage() {
1646  return `<main class="registration-page"><section class="registration-hero"><div class="container registration-hero-grid"><div><span class="kicker">Zdravé zdravotnictví 2026</span><h1>Registrace na konferenci</h1><p>Vyplňte registrační údaje. Aktuální cena se načítá automaticky podle data a případný slevový kód se projeví ještě před odesláním.</p></div><aside class="registration-price-card"><span>Aktuální cena</span><strong id="registration-price">Načítám…</strong><small id="registration-price-note">Ověřuji aktuální cenové období.</small></aside></div></section><section class="section registration-section"><div class="container registration-layout"><form id="conference-registration-form" class="registration-form"><input class="hp-field" tabindex="-1" autocomplete="off" name="company_website"><div class="registration-block"><div class="registration-block-head"><span>01</span><div><h2>Základní osobní údaje</h2><p>Údaje účastníka konference.</p></div></div><div class="registration-grid four"><label>Titul před jménem<input name="title_before" placeholder="MUDr."></label><label>Jméno *<input name="first_name" required></label><label>Příjmení *<input name="last_name" required></label><label>Titul za jménem<input name="title_after" placeholder="Ph.D."></label><label>Datum narození *<input name="birth_date" type="date" required></label><label>E-mail *<input name="email" type="email" autocomplete="email" required></label><label>Telefon *<input name="phone" type="tel" autocomplete="tel" required></label></div></div><div class="registration-block"><div class="registration-block-head"><span>02</span><div><h2>Profese a pracoviště</h2><p>Informace pro organizaci konference a případnou evidenci vzdělávání.</p></div></div><div class="registration-grid"><label>Profese<select name="profession"><option value="">Vyberte profesi</option><option>Lékař/ka</option><option>Všeobecná sestra</option><option>Praktická sestra</option><option>Zdravotnický záchranář/ka</option><option>Farmaceut/ka</option><option>Psycholog/žka</option><option>Management zdravotnictví</option><option>Student/ka</option><option>Jiná</option></select></label><label>Pracoviště<input name="workplace" placeholder="Např. FN Motol"></label><label>Evidenční číslo ČLK<input name="clk_number" placeholder="Pouze pokud je relevantní"></label></div></div><div class="registration-block"><div class="registration-block-head"><span>03</span><div><h2>Fakturační údaje</h2><p>
1646Doklad může být vystaven na vás nebo na organizaci.</p></div></div><div class="billing-choice"><label><input type="radio" name="billing_type" value="person" checked> Soukromá osoba</label><label><input type="radio" name="billing_type" value="company"> Firma / organizace</label></div><div class="registration-grid" id="billing-fields"><label class="span-2">Jméno a příjmení / název organizace *<input name="billing_name" required></label><label class="company-only" hidden>IČO *<div class="input-with-action"><input name="ico" inputmode="numeric" maxlength="8" pattern="[0-9]{8}" autocomplete="off"><button type="button" id="ares-lookup">Načíst z ARES</button></div></label><label class="company-only" hidden>DIČ<input name="dic"></label><label class="span-2">Ulice a číslo popisné *<input name="street" required autocomplete="street-address"></label><label>Město *<input name="city" required autocomplete="address-level2"></label><label>PSČ *<input name="postal_code" required inputmode="numeric" autocomplete="postal-code"></label></div><p class="form-hint company-only" hidden>Údaje načítáme z veřejného registru ARES. DIČ lze případně doplnit ručně.</p><p id="ares-status" class="form-status" role="status" aria-live="polite"></p></div><div class="registration-block"><div class="registration-block-head"><span>04</span><div><h2>Dieta a poznámka</h2><p>Pomozte nám připravit občerstvení podle vašich potřeb.</p></div></div><div class="diet-grid"><label><input type="checkbox" name="diets" value="Vegetariánská"> Vegetariánská</label><label><input type="checkbox" name="diets" value="Veganská"> Veganská</label><label><input type="checkbox" name="diets" value="Bezlepková"> Bezlepková</label><label><input type="checkbox" name="diets" value="Bezlaktózová"> Bezlaktózová</label></div><div class="registration-grid"><label>Jiná dieta / alergie<input name="diet_other"></label><label class="span-2">Poznámka<textarea name="note" rows="3"></textarea></label></div></div><div class="registration-block discount-block"><div class="registration-block-head"><span>05</span><div><h2>Slevový kód</h2><p>Pokud jej máte, ověříme ho před dokončením registrace.</p></div></div><div class="discount-entry"><input name="discount_code" placeholder="Např. PARTNER20" autocomplete="off"><button type="button" id="apply-discount">Použít kód</button></div><p id="discount-status" class="form-status compact" role="status" aria-live="polite"></p><div class="price-summary"><div><span>Základní cena</span><strong id="summary-base">—</strong></div><div id="summary-discount-row" hidden><span>Sleva</span><strong id="summary-discount">—</strong></div><div class="total"><span>K úhradě</span><strong id="summary-total">—</strong></div></div></div><label class="registration-consent"><input type="checkbox" name="consent" required> Souhlasím se zpracováním údajů nezbytných pro organizaci konference a beru na vědomí podmínky registrace.</label><button class="btn btn-primary registration-submit" id="registration-submit" type="submit" disabled>Dokončit registraci</button><p id="registration-status" class="form-status" role="status" aria-live="polite"></p></form><aside class="registration-side"><div><span class="kicker">Po registraci</span><h3>Co se stane po odeslání?</h3><ol><li><strong>1.</strong><span>Pokud je volné místo, vystavíme fakturu a nabídneme vám ji ihned ke stažení.</span></li><li><strong>2.</strong><span>Prosíme o její úhradu do data splatnosti. Vaše účast je definitivně potvrzená až po přijetí platby.</span></li><li><strong>3.</strong><span>Potvrzení registrace a navazující organizační informace vám automaticky pošleme e-mailem.</span></li></ol><p class="registration-side-note">Pokud bude kapacita právě naplněná, zařadíme vás na čekací listinu a fakturu vystavíme až po uvolnění místa.</p></div></aside></div></section></main>`;
1647}
1648
1649// Current registration form with separately evidenced contractual, privacy,
1650// dietary-data and optional marketing choices.
1651registrationPage = function registrationPage2026() {
1652  return `<main class="registration-page">
1653    <section class="registration-hero"><div class="container registration-hero-grid"><div><span class="kicker">Zdravé zdravotnictví 2026</span><h1>Registrace na konferenci</h1><p>27. listopadu 2026 · Hermitage Hotel Praha, Svobodova 1. Cena se automaticky načte podle aktuálního období.</p></div><aside class="registration-price-card"><span>Aktuální cena</span><strong id="registration-price">Načítám…</strong><small id="registration-price-note">Ověřuji aktuální cenové období.</small></aside></div></section>
1654    <section class="section registration-section"><div class="container registration-layout">
1655      <form id="conference-registration-form" class="registration-form">
1656        <input class="hp-field" tabindex="-1" autocomplete="off" name="company_website">
1657        <div class="registration-block"><div class="registration-block-head"><span>01</span><div><h2>Základní osobní údaje</h2><p>Údaje účastníka uvedené také na certifikátu.</p></div></div><div class="registration-grid four"><label>Titul před jménem<input name="title_before" placeholder="MUDr."></label><label>Jméno *<input name="first_name" required></label><label>Příjmení *<input name="last_name" required></label><label>Titul za jménem<input name="title_after" placeholder="Ph.D."></label><label>Datum narození *<input name="birth_date" type="date" required></label><label>E-mail *<input name="email" type="email" autocomplete="email" required></label><label>Telefon *<input name="phone" type="tel" autocomplete="tel" required></label></div></div>
1658        <div class="registration-block"><div class="registration-block-head"><span>02</span><div><h2>Profese a pracoviště</h2><p>Informace pro evidenci vzdělávání.</p></div></div><div class="registration-grid"><label>Profese<select name="profession"><option value="">Vyberte profesi</option><option>Lékař/ka</option><option>Všeobecná sestra</option><option>Praktická sestra</option><option>Zdravotnický záchranář/ka</option><option>Farmaceut/ka</option><option>Psycholog/žka</option><option>Management zdravotnictví</option><option>Student/ka</option><option>Jiná</option></select></label><label>Pracoviště<input name="workplace" placeholder="Např. FN Motol"></label><label>Evidenční číslo ČLK<input name="clk_number" placeholder="Pouze pokud je relevantní"></label></div></div>
1659        <div class="registration-block"><div class="registration-block-head"><span>03</span><div><h2>Fakturační údaje</h2><p>
1659Doklad může být vystaven na vás nebo organizaci.</p></div></div><div class="billing-choice"><label><input type="radio" name="billing_type" value="person" checked> Soukromá osoba</label><label><input type="radio" name="billing_type" value="company"> Firma / organizace</label></div><div class="registration-grid" id="billing-fields"><label class="span-2">Jméno a příjmení / název organizace *<input name="billing_name" required></label><label class="company-only" hidden>IČO *<div class="input-with-action"><input name="ico" inputmode="numeric" maxlength="8" pattern="[0-9]{8}" autocomplete="off"><button type="button" id="ares-lookup">Načíst z ARES</button></div></label><label class="company-only" hidden>DIČ<input name="dic"></label><label class="span-2">Ulice a číslo popisné *<input name="street" required autocomplete="street-address"></label><label>Město *<input name="city" required autocomplete="address-level2"></label><label>PSČ *<input name="postal_code" required inputmode="numeric" autocomplete="postal-code"></label></div><p class="form-hint company-only" hidden>Údaje načítáme z veřejného registru ARES. DIČ lze doplnit ručně.</p><p id="ares-status" class="form-status" role="status" aria-live="polite"></p></div>
1660        <div class="registration-block"><div class="registration-block-head"><span>04</span><div><h2>Dieta a poznámka</h2><p>Volitelné informace pro občerstvení a bezpečný průběh akce.</p></div></div><div class="diet-grid"><label><input type="checkbox" name="diets" value="Vegetariánská"> Vegetariánská</label><label><input type="checkbox" name="diets" value="Veganská"> Veganská</label><label><input type="checkbox" name="diets" value="Bezlepková"> Bezlepková</label><label><input type="checkbox" name="diets" value="Bezlaktózová"> Bezlaktózová</label></div><div class="registration-grid"><label>Jiná dieta / alergie<input name="diet_other"></label><label class="span-2">Poznámka<textarea name="note" rows="3"></textarea></label></div><label class="registration-consent dietary" id="dietary-consent-row" hidden><input type="checkbox" name="dietary_consent"> Výslovně souhlasím se zpracováním uvedené diety, alergie či zdravotního omezení výhradně pro zajištění občerstvení a organizaci akce.</label></div>
1661        <div class="registration-block discount-block"><div class="registration-block-head"><span>05</span><div><h2>Slevový kód</h2><p>Pokud jej máte, ověříme ho před dokončením registrace.</p></div></div><div class="discount-entry"><input name="discount_code" placeholder="Např. PARTNER20" autocomplete="off"><button type="button" id="apply-discount">Použít kód</button></div><p id="discount-status" class="form-status compact" role="status" aria-live="polite"></p><div class="price-summary"><div><span>Základní cena</span><strong id="summary-base">—</strong></div><div id="summary-discount-row" hidden><span>Sleva</span><strong id="summary-discount">—</strong></div><div class="total"><span>K úhradě</span><strong id="summary-total">—</strong></div></div></div>
1662        <div class="registration-legal-consents"><label class="registration-consent"><input type="checkbox" name="terms_accepted" required> Souhlasím s <a href="/obchodni-podminky" target="_blank" rel="noopener">obchodními podmínkami MedicRise</a>.</label><label class="registration-consent"><input type="checkbox" name="privacy_acknowledged" required> Potvrzuji, že jsem se seznámil/a s <a href="/gdpr" target="_blank" rel="noopener">informacemi o zpracování osobních údajů</a>.</label><label class="registration-consent optional"><input type="checkbox" name="marketing_consent"> Chci dostávat novinky MedicRise a pozvánky na další vzdělávací akce. Dobrovolný souhlas lze kdykoli odvolat.</label></div>
1663        <button class="btn btn-primary registration-submit" id="registration-submit" type="submit" disabled>Dokončit registraci</button><p id="registration-status" class="form-status" role="status" aria-live="polite"></p>
1664      </form>
1665      <aside class="registration-side"><div><span class="kicker">Po registraci</span><h3>Co se stane po odeslání?</h3><ol><li><strong>1.</strong><span>Při volné kapacitě ihned vystavíme fakturu.</span></li><li><strong>2.</strong><span>Účast definitivně potvrdí přijetí platby převodem.</span></li><li><strong>3.</strong><span>Dostanete bezpečný odkaz ke své registraci a faktuře.</span></li></ol><p class="registration-side-note">Při naplnění míst vás zařadíme na čekací listinu. Fakturu vystavíme až po potvrzení místa.</p></div></aside>
1666    </div></section>
1667  </main>`;
1668};
1669
1670const registrationPageWithUnifiedNurseProfession = registrationPage;
1671registrationPage = function registrationPageWithBaraFeedback() {
1672  return registrationPageWithUnifiedNurseProfession()
1673    .replace(
1674      "<option>Všeobecná sestra</option><option>Praktická sestra</option>",
1675      "<option>Zdravotní sestra</option>",
1676    );
1677};
1678
1679async function registrationApi(body, idempotencyKey = "") {
1680  const headers = {
1681    apikey: CMS_SUPABASE_KEY,
1682    "content-type": "application/json",
1683  };
1684  if (idempotencyKey) headers["x-idempotency-key"] = idempotencyKey;
1685  const r = await fetch(
1686    `${CMS_SUPABASE_URL}/functions/v1/conference-registration`,
1687    {
1688      method: "POST",
1689      headers,
1690      body: JSON.stringify(body),
1691    },
1692  );
1693  const data = await r.json().catch(() => ({}));
1694  if (!r.ok) throw new Error(data.error || "Požadavek se nezdařil.");
1695  return data;
1696}
1697
1698const OUTREACH_SOURCE_KEY = "medicrise_outreach_source_v1";
1699const outreachSourceFromPage = () => {
1700  const incoming = new URL(location.href).searchParams.get("mr_source") || "";
1701  const valid = /^mrh_[a-f0-9]{32}$/i.test(incoming) ? incoming : "";
1702  try {
1703    if (valid) sessionStorage.setItem(OUTREACH_SOURCE_KEY, valid);
1704    return valid || sessionStorage.getItem(OUTREACH_SOURCE_KEY) || "";
1705  } catch { return valid; }
1706};
1707async function initOutreachAttribution() {
1708  const sourceToken = outreachSourceFromPage();
1709  if (!sourceToken) return;
1710  document.querySelectorAll('a[href]').forEach((link) => {
1711    try {
1712      const url = new URL(link.href, location.origin);
1713      if (url.origin !== location.origin) return;
1714      if (!/(konference-2026|registrace-na-konferenci|pozvanka-pro-tym)/.test(url.pathname)) return;
1715      url.searchParams.set("mr_source", sourceToken);
1716      link.href = `${url.pathname}${url.search}${url.hash}`;
1717    } catch {}
1718  });
1719  const eventType = isRegistrationRoute ? "registration_view" : "landing_view";
1720  try {
1721    const result=await registrationApi({ action: "outreach_visit", source_token: sourceToken, event_type: eventType });
1722    if(isDistributionKitRoute&&result?.distribution_body){
1723      const title=document.querySelector(".distribution-copy-card h2"),copy=document.getElementById("distribution-copy");
1724      const params=new URL(location.href).searchParams;
1725      const faculty=params.get("audience")==="faculty",euc=(params.get("partner")||"").toLowerCase()==="euc";
1726      if(title&&result.distribution_title)title.textContent=result.distribution_title;
1727      if(copy)setDistributionCopy(copy,decorateDistributionCopy(String(result.distribution_body).replace("https://medicrise.cz/konference-2026",`https://medicrise.cz/konference-2026?mr_source=${sourceToken}`),{faculty,euc}));
1728    }
1729  } catch {}
1730}
1731
1732function decorateDistributionCopy(value,{faculty=false,euc=false}={}) {
1733  let text=String(value||"")
1734    .replace(/\r\n?/g,"\n")
1735    .replace(/\*\*/g,"")
1736    .replace(/[🩺💡📅📍🎓👉🎁🔑][ \t]*/gu,"");
1737  if(faculty){
1738    text=text.replace("\n\nmedicína a zdravotnické obory", "\n\n🩺 **Na praxi se nepřipravujeme jen odbornými znalostmi.**\n\nMedicína a zdravotnické obory");
1739  }else{
1740    text=text.replace("\n\nco dělat, když už člověk jede na doraz?", "\n\n🩺 **Každodenní praxe přináší těžké otázky:**\n\nCo dělat, když už člověk jede na doraz?");
1741  }
1742  text=text
1743    .replace("Právě tyto otázky otevře 5. ročník konference Zdravé zdravotnictví", "Právě tyto otázky otevře **5. ročník konference Zdravé zdravotnictví**")
1744    .replace("„Boj o přežití: Dá se ve zdravotnictví zůstat člověkem?“", "**„Boj o přežití: Dá se ve zdravotnictví zůstat člověkem?“*
1744*")
1745    .replace("Na jaká témata se můžete těšit?", "💡 **Na jaká témata se můžete těšit?**")
1746    .replace("Pátek 27. listopadu 2026", "Kdy: pátek 27. listopadu 2026")
1747    .replace("Kdy: pátek 27. listopadu 2026", "📅 **Kdy:** pátek 27. listopadu 2026")
1748    .replace("Kde: Hermitage Hotel Praha", "📍 **Kde:** Hermitage Hotel Praha")
1749    .replace(/^Hermitage Hotel Praha$/m, "📍 **Kde:** Hermitage Hotel Praha")
1750    .replace("Akreditace ČLK (6 kreditů) · garance ČAS", "Akreditace: ČLK (6 kreditů) · garance ČAS")
1751    .replace("Akreditace: ČLK (6 kreditů) · garance ČAS", "🎓 **Akreditace:** ČLK (6 kreditů) · garance ČAS")
1752    .replace("Program a registrace:", "👉 **Program a registrace:**")
1753    .replace(/\nTým MedicRise$/, "\n**Tým MedicRise**");
1754  if(euc){
1755    text=text
1756      .replace("Zaměstnanci EUC mohou při registraci využít 15% slevu z účastnického poplatku.", "🎁 **Výhoda pro zaměstnance EUC:** při registraci můžete využít 15% slevu z účastnického poplatku.")
1757      .replace("Slevový kód: EUC15", "🔑 **Slevový kód: EUC15**");
1758  }
1759  return text;
1760}
1761function distributionInlineHtml(value) {
1762  return escapeHtml(String(value||"")).replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>");
1763}
1764function distributionCopyHtml(value) {
1765  const blocks=String(value||"").trim().split(/\n{2,}/),html=[];
1766  for(const block of blocks){
1767    const lines=block.split("\n").filter(Boolean);
1768    if(lines.length&&lines.every((line)=>line.startsWith("• "))){
1769      html.push(`<ul>${lines.map((line)=>`<li>${distributionInlineHtml(line.slice(2))}</li>`).join("")}</ul>`);
1770    }else{
1771      html.push(`<p>${lines.map(distributionInlineHtml).join("<br>")}</p>`);
1772    }
1773  }
1774  return html.join("");
1775}
1776function distributionPlainText(value) {
1777  return String(value||"").replace(/\*\*/g,"");
1778}
1779function setDistributionCopy(node,value) {
1780  if(!node)return;
1781  node.dataset.source=String(value||"");
1782  node.innerHTML=distributionCopyHtml(value);
1783}
1784
1785const DISTRIBUTION_EMPLOYEE_COPY=`Vážené kolegyně, vážení kolegové, milí zdravotníci,
1786
1787co dělat, když už člověk jede na doraz? Jak se s pacientem domluvit místo toho, abychom se navzájem přesvědčovali? A co pomáhá týmu, aby dokázal fungovat s respektem a bezpečně i ve chvíli, kdy je práce příliš?
1788
1789Právě tyto otázky otevře 5. ročník konference Zdravé zdravotnictví s letošním tématem:
1790
1791„Boj o přežití: Dá se ve zdravotnictví zůstat člověkem?“
1792
1793Konference propojuje lidi napříč zdravotnickými profesemi, kteří nechtějí zůstat jen u konstatování, že je systém náročný. Společně budeme hledat konkrétní cesty, jak lépe komunikovat, spolupracovat, zvládat zátěž a uchovat si v každodenní praxi lidskost.
1794
1795Nečeká Vás den plný obecné teorie. Program kombinuje inspirativní přednášky s praktickými workshopy, na kterých si vyzkoušíte postupy využitelné při další směně, poradě nebo rozhovoru s pacientem.
1796
1797Na jaká témata se můžete těšit?
1798
1799• Jak básníci přicházejí o motivaci
1800• Když je toho moc: Mindfulness jako první pomoc pro zdravotníky
1801• Od přesvědčování k dohodě aneb Jak vyjednávat s pacientem
1802• Prakticky o respektu, psychologickém bezpečí a podpoře v týmu
1803• Co dělat, když už jedu na doraz
1804• Homo emoticus: Můžou s námi být v ordinaci i emoce?
1805• Hodnoty jako kompas: jak najít cestu, když nás práce ani život netěší
1806
1807To vše v atmosféře sdílení, praktického nácviku a vzájemné podpory. Účast je otevřená všem zdravotnickým profesím.
1808
1809Pátek 27. listopadu 2026
1810Hermitage Hotel Praha
1811Akreditace ČLK (6 kreditů) · garance ČAS
1812
1813Pokud hledáte nejen inspiraci, ale také konkrétní dovednosti pro každodenní praxi, budeme rádi, když u toho budete s námi.
1814
1815Program a registrace:
1816https://medicrise.cz/konference-2026
1817
1818Těšíme se na setkání!
1819
1820Tým MedicRise`;
1821const DISTRIBUTION_FACULTY_COPY=`Vážené studentky, vážení studenti,
1822
1823medicína a zdravotnické obory nejsou jen o tom, co všechno víme. V praxi velmi rychle zjistíme, že stejně důležité je umět mluvit s pacientem, domluvit se v týmu, pracovat s emocemi a poznat chvíli, kdy už sami jedeme na doraz.
1824
1825Jak hledat dohodu s pacientem, který nechce přijmout naše doporučení? Jak se ozvat v týmu a přitom udržet bezpečné vztahy? Co dělat, když z práce nebo studia mizí motivace? A mohou s námi být v ordinaci i emoce?
1826
1827Právě tyto otázky otevře 5. ročník konference Zdravé zdravotnictví s letošním tématem:
1828
1829„Boj o přežití: Dá se ve zdravotnictví zůstat člověkem?“
1830
1831Nečeká Vás jen poslouchání obecné teorie. Program propojuje odborné přednášky s interaktivními workshopy, na kterých si můžete vyzkoušet konkrétní postupy využitelné v budoucí klinické praxi.
1832
1833Na jaká témata se můžete těšit?
1834
1835• Od přesvědčování k dohodě aneb Jak vyjednávat s pacientem
1836• Když je toho moc: Mindfulness jako první pomoc pro zdravotníky
1837• Prakticky o respektu, psychologickém bezpečí a podpoře v týmu
1838• Umíme naslouchat aneb Co nás ovlivňuje při prvním kontaktu s pacientem?
1839• Co dělat, když už jedu na doraz
1840• Homo emoticus: Můžou s námi být v ordinaci i emoce?
1841• Hodnoty jako kompas: jak najít cestu, když nás práce ani život netěší
1842
1843Kromě programu získáte možnost potkat zdravotníky i studenty napříč profesemi, sdílet zkušenosti a mluvit otevřeně o tom, co každodenní práce ve zdravotnictví skutečně přináší.
1844
1845Pátek 27. listopadu 2026
1846Hermitage Hotel Praha
1847Akreditace ČLK (6 kreditů) · garance ČAS
1848
1849Pokud chcete být lépe připraveni i na situace, které se z učebnic naučit nedají, budeme rádi, když u toho budete s námi.
1850
1851Program a registrace:
1852https://medicrise.cz/konference-2026
1853
1854Těšíme se na setkání!
1855
1856Tým MedicRise`;
1857const DISTRIBUTION_EUC_COPY=DISTRIBUTION_EMPLOYEE_COPY.replace(
1858  "Pokud hledáte nejen inspiraci, ale také konkrétní dovednosti pro každodenní praxi, budeme rádi, když u toho budete s námi.",
1859  "Zaměstnanci EUC mohou při registraci využít 15% slevu z účastnického poplatku.\n\nSlevový kód: EUC15\n\nPokud hledáte nejen inspiraci, ale také konkrétní dovednosti pro každodenní praxi, budeme rádi, když u toho budete s námi."
1860);
1861function distributionKitPage() {
1862  const params = new URL(location.href).searchParams;
1863  const euc = params.get("partner")?.toLocaleLowerCase("cs-CZ") === "euc";
1864  const audience = params.get("audience") === "faculty" ? "faculty" : "hospital";
1865  const faculty = audience === "faculty";
1866  const subject = euc
1867    ? "15% sleva pro zaměstnance EUC: Zdravé zdravotnictví 2026"
1868    : faculty
1869    ? "Pozvánka pro studenty: Zdravé zdravotnictví 2026"
1870    : "Pozvánka pro zaměstnance: Zdravé zdravotnictví 2026";
1871  const copy = decorateDistributionCopy(euc ? DISTRIBUTION_EUC_COPY : faculty ? DISTRIBUTION_FACULTY_COPY : DISTRIBUTION_EMPLOYEE_COPY,{faculty,euc});
1872  return `<main class="distribution-kit"><section class="page-hero"><div class="container page-hero-grid"><div><span class="kicker">${euc?"Speciálně pro EUC":"Zdravé zdravotnictví 2026"}</span><h1>Pozvánka připravená k&nbsp;rozeslání.</h1><p>Bez příloh a bez úprav. Zkopírujte hotový text do interního e-mailu, newsletteru nebo intranetu.</p></div><div class="page-hero-art"><img src="${ASSETS.seminar}" alt="Konference Zdravé zdravotnictví"></div></div></section><section class="section"><div class="container"><div class="distribution-kit-grid">
1872<article class="admin-panel distribution-copy-card"><span class="kicker">${euc?"Pro zaměstnance EUC":faculty?"Pro studenty":"Pro zaměstnance"}</span><h2>${subject}</h2><div id="distribution-copy" data-source="${escapeHtml(copy)}">${distributionCopyHtml(copy)}</div><div class="hero-actions"><button class="btn btn-primary" id="copy-distribution-text">Zkopírovat formátovaný text</button><a class="btn btn-secondary" href="/konference-2026">Zobrazit konferenci</a></div><p id="distribution-copy-status" class="form-status" aria-live="polite"></p></article><aside class="distribution-kit-side"><strong>${euc?"EUC15":"27. 11. 2026"}</strong><span>${euc?"15% sleva pro zaměstnance EUC":"Hermitage Hotel Praha"}</span><p>${euc?"Kód stačí zadat při registraci. Platí pro zaměstnance EUC.":"5. ročník · praktické workshopy · přednášky · networking"}</p><a class="btn btn-primary" href="/registrace-na-konferenci">Registrovat se →</a><button class="btn btn-secondary" id="confirm-distribution-share">Pozvánku jsme rozeslali</button><small id="distribution-share-status" aria-live="polite">Potvrzením pomůžete MedicRise vyhodnotit, které instituce pozvánku skutečně předaly dál.</small></aside></div><section class="distribution-assets" aria-labelledby="distribution-assets-title"><div class="distribution-assets-heading"><div><span class="kicker">Podklady pro sdílení</span><h2 id="distribution-assets-title">Grafiky ke stažení</h2></div><p>Vyberte si formát podle místa, kde chcete pozvánku sdílet. Každou grafiku si můžete nejdříve prohlédnout a potom stáhnout v plném rozlišení.</p></div><div class="distribution-assets-grid"><article class="distribution-asset-card"><a class="distribution-asset-preview" href="/assets/conference-2026/email-banner.png" target="_blank" rel="noopener" aria-label="Otevřít e-mailový banner v plné velikosti"><img src="/assets/conference-2026/email-banner.png" width="1875" height="625" loading="lazy" decoding="async" alt="E-mailový banner konference Zdravé zdravotnictví 2026"></a><div class="distribution-asset-copy"><span class="distribution-asset-tag">E-mail · newsletter · intranet</span><h3>E-mailový banner</h3><p>Široký formát vhodný do hlavičky interního e-mailu, newsletteru nebo článku na intranetu.</p><small>PNG · 1875 × 625 px</small><div class="hero-actions"><a class="btn btn-primary" href="/assets/conference-2026/email-banner.png" download="MedicRise-Konference-2026-email-banner.png">Stáhnout PNG</a><a class="btn btn-secondary" href="/assets/conference-2026/email-banner.png" target="_blank" rel="noopener">Otevřít náhled</a></div></div></article><article class="distribution-asset-card"><a class="distribution-asset-preview" href="/assets/conference-2026/social-feed.png" target="_blank" rel="noopener" aria-label="Otevřít grafiku pro Facebook a 
1872LinkedIn v plné velikosti"><img src="/assets/conference-2026/social-feed.png" width="2048" height="1072" loading="lazy" decoding="async" alt="Grafika konference Zdravé zdravotnictví 2026 pro Facebook a LinkedIn"></a><div class="distribution-asset-copy"><span class="distribution-asset-tag">Facebook · LinkedIn</span><h3>Grafika pro sociální sítě</h3><p>Formát připravený pro příspěvek na Facebooku, LinkedInu nebo pro větší plochu na intranetu.</p><small>PNG · 2048 × 1072 px</small><div class="hero-actions"><a class="btn btn-primary" href="/assets/conference-2026/social-feed.png" download="MedicRise-Konference-2026-Facebook-LinkedIn.png">Stáhnout PNG</a><a class="btn btn-secondary" href="/assets/conference-2026/social-feed.png" target="_blank" rel="noopener">Otevřít náhled</a></div></div></article></div></section></div></section></main>`;
1873}
1874function initDistributionKit() {
1875  const copyButton = document.getElementById("copy-distribution-text"),copy = document.getElementById("distribution-copy"),status = document.getElementById("distribution-copy-status");
1876  const sourceToken=outreachSourceFromPage();
1877  if(copy&&sourceToken)setDistributionCopy(copy,String(copy.dataset.source||"").replace("https://medicrise.cz/konference-2026",`https://medicrise.cz/konference-2026?mr_source=${sourceToken}`));
1878  copyButton?.addEventListener("click", async () => {
1879    try {
1880      const source=String(copy.dataset.source||""),plain=distributionPlainText(source);
1881      if(navigator.clipboard?.write&&window.ClipboardItem){
1882        const html=`<div style="font-family:Arial,Helvetica,sans-serif;font-size:16px;line-height:1.6;color:#263238">${distributionCopyHtml(source)}</div>`;
1883        await navigator.clipboard.write([new ClipboardItem({"text/html":new Blob([html],{type:"text/html"}),"text/plain":new Blob([plain],{type:"text/plain"})})]);
1884      }else await navigator.clipboard.writeText(plain);
1885      copyButton.textContent = "✓ Zkopírováno i s formátováním";
1886      status.textContent = "Text můžete vložit přímo do interního e-mailu nebo intranetu. Pokud editor neumí formátování, použije čistou textovou verzi.";
1887    }
1888    catch { status.textContent = "Označte text a zkopírujte jej klávesami Ctrl/Cmd + C."; }
1889  });
1890  document.getElementById("confirm-distribution-share")?.addEventListener("click", async (event) => {
1891    const button=event.currentTarget,sourceToken=outreachSourceFromPage(),shareStatus=document.getElementById("distribution-share-status");
1892    if(!sourceToken){shareStatus.textContent="Potvrzení není dostupné mimo osobní distribuční odkaz.";return;}
1893    button.disabled=true;
1894    try { await registrationApi({action:"outreach_visit",source_token:sourceToken,event_type:"share_confirmed"}); button.textContent="✓ Rozeslání potvrzeno"; shareStatus.textContent="Děkujeme za sdílení pozvánky."; }
1895    catch { button.disabled=false; shareStatus.textContent="Potvrzení se nepodařilo uložit. Zkuste to prosím znovu."; }
1896  });
1897}
1898
1899async function conferenceProgramApi(body) {
1900  const r = await fetch(
1901    `${CMS_SUPABASE_URL}/functions/v1/conference-program-operations`,
1902    {
1903      method: "POST",
1904      headers: { apikey: CMS_SUPABASE_KEY, "content-type": "application/json" },
1905      body: JSON.stringify(body),
1906    },
1907  );
1908  const data = await r.json().catch(() => ({}));
1909  if (!r.ok) throw new Error(data.error || "Program se nepodařilo načíst.");
1910  return data;
1911}
1912
1913function money(value) {
1914  return `${Number(value || 0).toLocaleString("cs-CZ", { maximumFractionDigits: 0 })} Kč`;
1915}
1916async function initRegistration() {
1917  const form = document.getElementById("conference-registration-form");
1918  if (!form) return;
1919  const registrationParams = new URL(location.href).searchParams;
1920  const EUC_COMPLIMENTARY_ACCESS_TOKEN = "mrc_J7oln8gNC_aO4_8Qads5tnYRDN3BuRE9xvTb5HCI-9k";
1921  const complimentaryAccessToken = registrationParams.get("access") ||
1922    (current === "euc-vstupy" ? EUC_COMPLIMENTARY_ACCESS_TOKEN : "");
1923  const complimentaryInviteToken = registrationParams.get("invite") || "";
1924  const complimentary = complimentaryAccessToken.startsWith("mrc_");
1925  const status = document.getElementById("registration-status");
1926  const discountStatus = document.getElementById("discount-status");
1927  const submit = document.getElementById("registration-submit");
1928  let edition = null;
1929  let quote = null;
1930  let aresPayload = null;
1931  let appliedDiscount = "";
1932  let legalVersions = {};
1933  let complimentaryOffer = null;
1934  const registrationIdempotencyKey = crypto.randomUUID();
1935  form.elements.birth_date.max = new Date().toISOString().slice(0, 10);
1936  try {
1937    const legal = await registrationApi({ action: "legal" });
1938    legalVersions = Object.fromEntries(
1939      (legal.documents || []).map((document) => [
1940        document.document_key,
1941        document.version,
1942      ]),
1943    );
1944  } catch (error) {
1945    status.textContent =
1946      "Aktuální právní dokumenty se nepodařilo ověřit. Obnovte prosím stránku.";
1947  }
1948  const syncDietaryConsent = () => {
1949    const hasDiet =
1950      form.querySelectorAll('input[name="diets"]:checked').length > 0 ||
1951      form.elements.diet_other.value.trim().length > 0;
1952    const row = document.getElementById("dietary-consent-row");
1953    row.hidden = !hasDiet;
1954    form.elements.dietary_consent.required = hasDiet;
1955    if (!hasDiet) form.elements.dietary_consent.checked = false;
1956  };
1957  form
1958    .querySelectorAll('input[name="diets"]')
1959    .forEach((input) => input.addEventListener("change", syncDietaryConsent));
1960  form.elements.diet_other.addEventListener("input", syncDietaryConsent);
1961  const normalizeCode = () =>
1962    form.elements.discount_code.value.trim().toUpperCase();
1963  const setQuote = (q, code = "") => {
1964    quote = q;
1965    appliedDiscount = code;
1966    document.getElementById("registration-price").textContent = money(
1967      q.final_price_czk,
1968    );
1969    document.getElementById("registration-price-note").textContent =
1970      q.discount_amount_czk > 0
1971        ? "Cena po uplatnění slevového kódu."
1972        : "Cena podle aktuálního období.";
1973    document.getElementById("summary-base").textContent = money(
1974      q.base_price_czk,
1975    );
1976    document.getElementById("summary-total").textContent = money(
1977      q.final_price_czk,
1978    );
1979    const row = document.getElementById("summary-discount-row");
1980    row.hidden = !(Number(q.discount_amount_czk) > 0);
1981    document.getElementById("summary-discount").textContent =
1982      `−${money(q.discount_amount_czk)}`;
1983    submit.disabled = !edition?.registration_open;
1984  };
1985  try {
1986    if (complimentary) {
1987      complimentaryOffer = await registrationApi({
1988        action: "complimentary_offer",
1989        access_token: complimentaryAccessToken,
1990        invite_token: complimentaryInviteToken || null,
1991      });
1992      edition = complimentaryOffer.edition;
1993      quote = { base_price_czk: 0, discount_amount_czk: 0, final_price_czk: 0 };
1994      const hero = document.querySelector(".registration-hero");
1995      hero?.classList.add("complimentary-registration-hero");
1996      const title = hero?.querySelector("h1"), intro = hero?.querySelector("p");
1997      if (title) title.textContent = "Bezplatný vstup na konferenci";
1998      if (intro) intro.textContent = `Váš vstup zajišťuje ${complimentaryOffer.allocation.label}. Vyplňte stejné účastnické údaje jako při běžné registraci; fakturační údaje ani platbu nepotřebujete.`;
1999      document.getElementById("registration-price").textContent = "0 Kč";
2000      document.getElementById("registration-price-note").textContent = `${complimentaryOffer.allocation.remaining} z ${complimentaryOffer.allocation.seat_limit} vstupů zbývá`;
2001      const billingBlock = document.getElementById("billing-fields")?.closest(".registration-block");
2002      const discountBlock = document.querySelector(".discount-block");
2003      [billingBlock, discountBlock].forEach((block) => { if (block) block.hidden = true; });
2004      billingBlock?.querySelectorAll("input,select,textarea").forEach((input) => { input.required = false; input.disabled = true; });
2005      discountBlock?.querySelectorAll("input,button").forEach((input) => { input.disabled = true; });
2006      const side = document.querySelector(".registration-side > div");
2007      if (side) side.innerHTML = `<span class="kicker">Bezplatný vstup</span><h3>Po odeslání je místo potvrzené.</h3><ol><li><strong>1.</strong><span>Nevyplňujete fakturační údaje a nic neplatíte.</span></li><li><strong>2.</strong><span>Registraci automaticky označíme jako potvrzenou.</span></li><li><strong>3.</strong><span>Dostanete stejné organizační informace, výběr programu, certifikát i zpětnou vazbu jako běžný účastník.</span></li></ol><p class="registration-side-note">Vstup je určen pouze příjemcům tohoto partnerského odkazu.</p>`;
2008      const invitation = complimentaryOffer.invitation;
2009      if (invitation) {
2010        form.elements.email.value = invitation.email || "";
2011        form.elements.email.readOnly = true;
2012        form.elements.first_name.value = invitation.first_name || "";
2013        form.elements.last_name.value = invitation.last_name || "";
2014      }
2015      submit.disabled = !edition?.registration_open || Number(complimentaryOffer.allocation.remaining) < 1;
2016      submit.textContent = "Potvrdit bezplatný vstup";
2017      if (Number(complimentaryOffer.allocation.remaining) < 1)
2018        status.textContent = "Všechny bezplatné vstupy z tohoto partnerství už byly využity.";
2019    } else {
2020      const rows = await fetch(
2021        `${CMS_SUPABASE_URL}/rest/v1/conference_editions?year=eq.2026&status=eq.active&select=id,registration_open,capacity`,
2022        { headers: { apikey: CMS_SUPABASE_KEY } },
2023      ).then((r) => (r.ok ? r.json() : Promise.reject(r)));
2024      edition = rows[0];
2025      if (!edition) throw new Error("Ročník 2026 není dostupný.");
2026      try {
2027        setQuote(await registrationApi({ action: "quote", edition_id: edition.id }));
2028      } catch (err) {
2029        document.getElementById("registration-price").textContent = "Připravujeme";
2030        document.getElementById("registration-price-note").textContent = err.message;
2031      }
2032    }
2033    if (!edition.registration_open) {
2034      submit.disabled = true;
2035      status.textContent = "Registrace na ročník 2026 zatím není otevřena.";
2036    }
2037  } catch (err) {
2038    status.textContent = err.message;
2039    document.getElementById("registration-price").textContent = "Nedostupné";
2040  }
2041  const billingType = () =>
2042    form.querySelector('input[name="billing_type"]:checked').value;
2043  const syncBilling = () => {
2044    if (complimentary) return;
2045    const company = billingType() === "company";
2046    form
2047      .querySelectorAll(".company-only")
2048      .forEach((x) => (x.hidden = !company));
2049    form.elements.ico.required = company;
2050    if (!company) {
2051      aresPayload = null;
2052      form.elements.ico.value = "";
2053      form.elements.dic.value = "";
2054      const full = [
2055        form.elements.title_before.value,
2056        form.elements.first_name.value,
2057        form.elements.last_name.value,
2058        form.elements.title_after.value,
2059      ]
2060        .filter(Boolean)
2061        .join(" ");
2062      if (
2063        !form.elements.billing_name.value ||
2064        form.elements.billing_name.dataset.auto === "true"
2065      ) {
2066        form.elements.billing_name.value = full;
2067        form.elements.billing_name.dataset.auto = "true";
2068      }
2069    } else {
2070      if (form.elements.billing_name.dataset.auto === "true")
2071        form.elements.billing_name.value = "";
2072      form.elements.billing_name.dataset.auto = "false";
2073    }
2074  };
2075  form
2076    .querySelectorAll('input[name="billing_type"]')
2077    .forEach((r) => (r.onchange = syncBilling));
2078  ["title_before", "first_name", "last_name", "title_after"].forEach((n) =>
2079    form.elements[n].addEventListener("input", () => {
2080      if (billingType() === "person") {
2081        form.elements.billing_name.dataset.auto = "true";
2082        syncBilling();
2083      }
2084    }),
2085  );
2086  syncBilling();
2087  form.elements.ico.addEventListener("input", () => {
2088    form.elements.ico.value = form.elements.ico.value
2089      .replace(/\D/g, "")
2090      .slice(0, 8);
2091    aresPayload = null;
2092    document.getElementById("ares-status").textContent = "";
2093  });
2094  document.getElementById("ares-lookup").onclick = async () => {
2095    const el = document.getElementById("ares-status"),
2096      btn = document.getElementById("ares-lookup");
2097    if (!form.elements.ico.checkValidity()) {
2098      form.elements.ico.reportValidity();
2099      el.textContent = "IČO musí mít přesně 8 číslic.";
2100      return;
2101    }
2102    btn.disabled = true;
2103    btn.textContent = "Načítám…";
2104    el.textContent = "Načítám údaje z ARES…";
2105    try {
2106      const data = await registrationApi({
2107        action: "ares",
2108        ico: form.elements.ico.value,
2109      });
2110      aresPayload = data.raw || null;
2111      form.elements.billing_name.value = data.name || "";
2112      form.elements.ico.value = data.ico || form.elements.ico.value;
2113      form.elements.dic.value = data.dic || "";
2114      form.elements.street.value = data.street || "";
2115      form.elements.city.value = data.city || "";
2116      form.elements.postal_code.value = data.postal_code || "";
2117      el.textContent =
2118        "✓ Údaje načteny z ARES. Před odesláním je můžete upravit.";
2119    } catch (err) {
2120      el.textContent = err.message;
2121    } finally {
2122      btn.disabled = false;
2123      btn.textContent = "Načíst z ARES";
2124    }
2125  };
2126  form.elements.discount_code.addEventListener("input", () => {
2127    form.elements.discount_code.value =
2128      form.elements.discount_code.value.toUpperCase();
2129    if (normalizeCode() !== appliedDiscount) {
2130      discountStatus.textContent = normalizeCode()
2131        ? "Kód se změnil. Pro přepočet ceny klikněte na Použít kód."
2132        : "";
2133    }
2134  });
2135  document.getElementById("apply-discount").onclick = async () => {
2136    if (!edition) return;
2137    const btn = document.getElementById("apply-discount"),
2138      code = normalizeCode();
2139    btn.disabled = true;
2140    btn.textContent = "Ověřuji…";
2141    try {
2142      setQuote(
2143        await registrationApi({
2144          action: "quote",
2145          edition_id: edition.id,
2146          discount_code: code || null,
2147        }),
2148        code,
2149      );
2150      discountStatus.textContent = code
2151        ? Number(quote.discount_amount_czk) > 0
2152          ? "✓ Slevový kód byl uplatněn."
2153          : "Kód je platný, výsledná cena je uvedená níže."
2154        : "Cena byla přepočítána bez slevového kódu.";
2155      status.textContent = "";
2156    } catch (err) {
2157      discountStatus.textContent = err.message;
2158    } finally {
2159      btn.disabled = false;
2160      btn.textContent = "Použít kód";
2161    }
2162  };
2163  form.onsubmit = async (e) => {
2164    e.preventDefault();
2165    if (!edition || !quote || submit.disabled) return;
2166    if (form.elements.company_website.value) return;
2167    if (!form.reportValidity()) return;
2168    const currentCode = complimentary ? "" : normalizeCode();
2169    if (!complimentary && currentCode !== appliedDiscount) {
2170      discountStatus.textContent =
2171        "Nejdřív potvrďte aktuální slevový kód tlačítkem Použít kód.";
2172      form.elements.discount_code.focus();
2173      return;
2174    }
2175    submit.disabled = true;
2176    submit.textContent = "Ukládám registraci…";
2177    status.textContent = "";
2178    const fd = new FormData(form),
2179      clean = (v) => String(v || "").trim(),
2180      payload = {
2181        edition_id: edition.id,
2182        title_before: clean(fd.get("title_before")),
2183        first_name: clean(fd.get("first_name")),
2184        last_name: clean(fd.get("last_name")),
2185        title_after: clean(fd.get("title_after")),
2186        birth_date: fd.get("birth_date"),
2187        email: clean(fd.get("email")).toLowerCase(),
2188        phone: clean(fd.get("phone")),
2189        profession: clean(fd.get("profession")),
2190        workplace: clean(fd.get("workplace")),
2191        clk_number: clean(fd.get("clk_number")),
2192        diets: fd.getAll("diets"),
2193        diet_other: clean(fd.get("diet_other")),
2194        note: clean(fd.get("note")),
2195        discount_code: currentCode || null,
2196        terms_accepted: fd.get("terms_accepted") === "on",
2197        privacy_acknowledged: fd.get("privacy_acknowledged") === "on",
2198        dietary_consent: fd.get("dietary_consent") === "on",
2199        marketing_consent: fd.get("marketing_consent") === "on",
2200        _outreach_source_token: outreachSourceFromPage() || null,
2201        terms_version: legalVersions.terms || null,
2202        privacy_version: legalVersions.privacy || null,
2203        billing: {
2204          billing_type: fd.get("billing_type"),
2205          name: clean(fd.get("billing_name")),
2206          ico: clean(fd.get("ico")),
2207          dic: clean(fd.get("dic")),
2208          street: clean(fd.get("street")),
2209          city: clean(fd.get("city")),
2210          postal_code: clean(fd.get("postal_code")),
2211          ares_payload: aresPayload,
2212        },
2213      };
2214    try {
2215      const result = await registrationApi(
2216          complimentary
2217            ? { action: "complimentary_submit", access_token: complimentaryAccessToken, invite_token: complimentaryInviteToken || null, payload }
2218            : { action: "submit", payload },
2219          registrationIdempotencyKey,
2220        ),
2221        waitlist = result.registration_status === "waitlist";
2222      if (complimentary) {
2223        form.innerHTML = `<div class="registration-success complimentary"><span>✓</span><h2>Bezplatný vstup je potvrzený.</h2><p>Děkujeme. Vaše registrační číslo je <strong>${escapeHtml(result.registration_code)}</strong>.</p><div class="registration-success-summary"><span>K úhradě</span><strong>0 Kč</strong></div><p class="registration-payment-notice"><strong>Nic dalšího nemusíte potvrzovat ani platit.</strong> Organizační informace, výběr programu, certifikát a zpětnou vazbu dostanete stejně jako ostatní účastníci.</p><div class="registration-success-actions"><a class="btn btn-primary" href="/portal/conferences/${encodeURIComponent(result.id)}">Otevřít MedicRise Portal</a><a class="btn btn-secondary" href="/konference-2026.html">Zpět na konferenci</a></div></div>`;
2224      } else if (waitlist) {
2225        form.innerHTML = `<div class="registration-success waitlist"><span>✓</span><h2>Jste na čekací listině.</h2><p>Děkujeme. Kapacita konference je právě naplněná, proto jsme registraci <strong>${escapeHtml(result.registration_code)}</strong> zařadili na čekací listinu.</p><div class="registration-success-summary"><span>Cena při potvrzení místa</span><strong>${money(result.final_price_czk)}</strong></div><p class="registration-payment-notice"><strong>Zatím nic neplaťte.</strong> Fakturu vystavíme až po potvrzení místa.</p><div class="registration-success-actions"><a class="btn btn-primary" href="/portal/conferences/${encodeURIComponent(result.id)}">Otevřít MedicRise Portal</a><a class="btn btn-secondary" href="/konference-2026.html">Zpět na konferenci</a></div></div>`;
2226      } else {
2227        const invoiceNote = result.email_sent
2228          ? `Fakturu ${escapeHtml(result.invoice_number || "")} jsme právě odeslali na váš e-mail.`
2229          : result.invoice_created
2230            ? `Faktura ${escapeHtml(result.invoice_number || "")} byla vystavena a je připravená ke stažení.`
2231            : "Registraci jsme bezpečně uložili a organizační tým ji vidí v administraci MedicRise.";
2232        const invoiceAction = result.invoice_download_url
2233          ? `<a class="btn btn-primary" href="${escapeHtml(result.invoice_download_url)}" target="_blank" rel="noopener">Stáhnout fakturu v PDF</a>`
2234          : "";
2235        form.innerHTML = `<div class="registration-success"><span>
2235✓</span><h2>Registrace je hotová.</h2><p>Děkujeme. Vaše registrační číslo je <strong>${escapeHtml(result.registration_code)}</strong>.</p><div class="registration-success-summary"><span>Cena k úhradě</span><strong>${money(result.final_price_czk)}</strong></div><p>${invoiceNote}</p><p class="registration-payment-notice"><strong>Účast je potvrzena až po uhrazení účastnického poplatku.</strong></p><div class="registration-success-actions">${invoiceAction}<a class="btn btn-primary" href="/portal/conferences/${encodeURIComponent(result.id)}">Otevřít MedicRise Portal</a><a class="btn btn-secondary" href="/konference-2026.html">Zpět na konferenci</a></div></div>`;
2236      }
2237      window.scrollTo({ top: 0, behavior: "smooth" });
2238    } catch (err) {
2239      status.textContent = err.message;
2240      submit.disabled = false;
2241      submit.textContent = "Dokončit registraci";
2242    }
2243  };
2244}
2245
2246
2247function conference2026Experience() {
2248  return `<section class="section section-soft"><div class="container"><div class="section-head"><div><span class="kicker">Místo a atmosféra</span><h2>Zázemí pro soustředění i setkávání.</h2></div><p>Celý konferenční den proběhne v designovém prostředí Hermitage Hotel Praha – od odborného programu přes občerstvení až po večerní networking.</p></div><div class="experience-grid experience-grid-2026"><article class="experience-card"><img src="${CONF_ASSETS.hotel}" alt="Hermitage Hotel Praha"><div><span class="program-type lecture">Místo konání</span><h3>Hermitage Hotel Praha</h3><p>Osvědčené konferenční zázemí s prostory pro společný program i paralelní workshopy.</p></div></article><article class="experience-card"><img src="${CONF_ASSETS.catering}" alt="Občerstvení na konferenci"><div><span class="program-type workshop">Během dne</span><h3>Kompletní občerstvení</h3><p>Dopolední a odpolední coffee break a oběd formou bufetu přímo v hotelu.</p></div></article><article class="experience-card afterparty-card"><div><span class="program-type lecture">Po skončení programu</span><h3>After party v hotelovém baru</h3><p>Konference nekončí poslední přednáškou. Pokračujeme neformálním networkingem, sdílením zkušeností a rozhovory, na které během programu není čas. Součástí účasti je voucher na jeden nápoj.</p></div></article></div></div></section><section class="section"><div class="container"><div class="cta conference-registration-cta"><div><span class="kicker cta-kicker">Ročník 2026</span><h2>Chcete být u toho?</h2><p>Registrační systém automaticky použije cenu platnou pro dnešní den a zohlední případný slevový kód.</p><div class="conference-price-periods" id="conference-price-periods-2026" aria-live="polite"><p class="conference-price-empty">Načítám cenová pásma…</p></div></div><div class="cta-side"><span class="cta-side-eyebrow">Zdravé zdravotnictví</span><strong>Rezervujte si místo.</strong><span class="cta-side-note">Vyplnění registrace zabere jen pár minut.</span><div class="cta-actions"><a class="btn btn-primary" href="/registrace-na-konferenci">Přejít k registraci →</a></div></div></div></div></section>`;
2249}
2250
2251function conferenceArchiveOutro(year) {
2252  return `<section class="section section-soft"><div class="container"><div class="archive-next"><div><span class="kicker">Archiv ${year}</span><h2>Ročník ${year} je uzavřený.</h2><p>Prohlédněte si aktuálně připravovaný ročník 2026 – program, místo konání a praktické informace budeme doplňovat průběžně.</p></div><a class="btn btn-primary" href="konference-2026.html">Přejít na ročník 2026 →</a></div></div></section>`;
2253}
2254
2255function conference() {
2256  return conferenceHub();
2257}
2258
2259function legalPage(kind) {
2260  const privacy = kind === "privacy";
2261  return `<main class="legal-page"><section class="page-hero legal-hero"><div class="container"><span class="kicker">MedicRise · právní informace</span><h1>${privacy ? "Informace o zpracování osobních údajů" : "Obchodní podmínky"}</h1><p>Účinné od 12. srpna 2026 · verze ${privacy ? "GDPR-2026-08-12" : "OP-2026-08-12"}</p></div></section><section class="section"><article class="container legal-document">
2262  ${
2263    privacy
2264      ? `<h2>1. Správce osobních údajů</h2><p>Správcem je <strong>MedicRise z. s.</strong>, IČ 14176921, se sídlem Å kolská 660/3, Nové Město, 110 00 Praha 1. Kontakt: <a href="mailto:[email protected]">
2264[email protected]</a>.</p>
2265  <h2>2. Jaké údaje zpracováváme</h2><p>Podle konkrétní služby můžeme zpracovávat titul, jméno, příjmení, datum narození, kontaktní údaje, profesi, pracoviště, evidenční číslo ČLK, fakturační údaje, údaje o registraci a platbě, komunikaci s námi a technické záznamy nutné pro zabezpečení formuláře.</p><p>Dietu, alergii či jiné zdravotní omezení zpracováváme pouze tehdy, pokud je dobrovolně uvedete a udělíte k tomu samostatný výslovný souhlas.</p>
2266  <h2>3. Účely a právní základy</h2><ul><li>registrace, organizace akce, vystavení certifikátu a komunikace k účasti – plnění smlouvy nebo kroky před jejím uzavřením;</li><li>fakturace a účetnictví – plnění právních povinností;</li><li>ochrana formulářů, prevence zneužití a obhajoba právních nároků – oprávněný zájem;</li><li>diety a zdravotní omezení – výslovný souhlas;</li><li>newslettery a pozvánky nad rámec objednané akce – dobrovolný souhlas, lze kdykoli odvolat.</li></ul>
2267  <h2>4. Doba uchování</h2><p>Údaje nezbytné pro smlouvu uchováváme po dobu trvání vztahu a následně po dobu potřebnou k ochraně právních nároků. Účetní a daňové doklady uchováváme po zákonem stanovenou dobu. Údaje zpracovávané na základě souhlasu uchováváme do jeho odvolání nebo do zániku účelu. Dietní a zdravotní údaje odstraníme po organizačním uzavření příslušné akce, nejpozději do 90 dnů, nevyžaduje-li delší uchování řešení konkrétní události.</p>
2268  <h2>5. Příjemci a zpracovatelé</h2><p>Údaje mohou v nezbytném rozsahu zpracovávat poskytovatelé hostingu, databáze, e-mailového doručování, účetních služeb a místo konání akce. Údaje neprodáváme. Pokud služba zahrnuje předání mimo EHP, používáme odpovídající právní záruky.</p>
2269  <h2>6. Vaše práva</h2><p>Máte právo na přístup, opravu, výmaz, omezení zpracování, přenositelnost, námitku proti zpracování založenému na oprávněném zájmu a odvolání souhlasu. Odvolání nemá vliv na zákonnost předchozího zpracování. Můžete se obrátit také na Úřad pro ochranu osobních údajů.</p>
2270  <h2>7. Cookies a úložiště prohlížeče</h2><p>Web nepoužívá marketingové ani analytické cookies. Pro přihlášení, zabezpečení relace, ukládání průběhu kurzu a další výslovně vyžádané funkce používáme pouze nezbytné technické úložiště prohlížeče. U videí Mux je měření návštěvnosti a ukládání cookies vypnuté.</p>
2271  <h2>8. Automatizované rozhodování</h2><p>Nepoužíváme automatizované rozhodování s právními či obdobně významnými účinky. Systém automaticky vypočítává cenu podle veřejně nastaveného období, slevového kódu a kapacity konference.</p>`
2272      : `<h2>I. Organizátor</h2><p>Tyto podmínky vydává <strong>MedicRise z. s.</strong>, IČ 14176921, se sídlem Školská 660/3, Nové Město, 110 00 Praha 1 (dále jen „Organizátor“).</p>
2273  <h2>II. Objednávka a vznik smlouvy</h2><p>Podmínky upravují účast na kurzech, workshopech, přednáškách a konferencích Organizátora. Účastník odesláním registračního nebo objednávkového formuláře činí objednávku. Smlouva vzniká potvrzením objednávky Organizátorem. Registrace je vázaná na konkrétního účastníka; změnu lze po dohodě provést nejpozději 7 dnů před akcí.</p>
2274  <h2>III. Cena a platba</h2><p>Cena je uvedena u konkrétní akce a při registraci se ukládá její platný snapshot. MedicRise z. s. není plátcem DPH. Platba probíhá bankovním převodem podle vystavené faktury, zpravidla se splatností 14 dnů, nejpozději však před začátkem akce. Nárok na účast vzniká úplným uhrazením ceny, není-li písemně dohodnuto jinak.</p>
2275  <h2>IV. Povinnosti účastníka</h2><p>Účastník je povinen uvést pravdivé údaje, respektovat organizační a bezpečnostní pokyny a neomezovat ostatní. Obsah akce je chráněn autorským právem. Bez předchozího souhlasu není dovoleno pořizovat ani šířit audio či audiovizuální záznam.</p>
2276  <h2>V. Zrušení nebo změna akce</h2><p>Organizátor může akci zrušit nebo změnit zejména při nedostatečné kapacitě, nemoci lektora, zásahu vyšší moci nebo jiné závažné překážce. Může změnit jednotlivé řečníky, menší část programu nebo místo v rámci stejného města. Při úplném zrušení nabídne náhradní termín, nebo vrátí uhrazenou cenu nejpozději do 30 pracovních dnů.</p>
2277  <h2>VI. Závaznost registrace účastníka</h2><p>Registrace na konferenci je závazná. Účastník nemůže registraci jednostranně zrušit ani požadovat vrácení uhrazené ceny. U služby související s využitím volného času v určeném termínu se neuplatní běžná čtrnáctidenní lhůta pro odstoupení podle § 1837 písm. j) občanského zákoníku. Nedostavení se na akci nezakládá nárok na refund
2277aci.</p>
2278  <h2>VII. Reklamace a spotřebitelské spory</h2><p>Reklamaci lze podat na [email protected]; vyřídíme ji bez zbytečného odkladu, nejpozději do 30 dnů. Spotřebitel může využít mimosoudní řeÅ¡ení sporu u České obchodní inspekce, Ústřední inspektorát – oddělení ADR, Å těpánská 15, 120 00 Praha 2, <a href="https://coi.gov.cz/informace-o-adr/" target="_blank" rel="noopener">coi.gov.cz</a>. Dřívější evropská platforma ODR byla ukončena.</p>
2279  <h2>VIII. Závěrečná ustanovení</h2><p>Vztah se řídí právem České republiky. Neplatnost jednotlivého ustanovení nemá vliv na ostatní části podmínek. Informace o zpracování osobních údajů tvoří samostatný dokument.</p>`
2280  }
2281  <aside class="legal-contact"><strong>MedicRise z. s.</strong><span>Å kolská 660/3, Nové Město, 110 00 Praha 1</span><span>IČ 14176921 · 2102117954/2010</span><a href="mailto:[email protected]">[email protected]</a></aside>
2282  </article></section></main>`;
2283}
2284
2285function registrationPortalPage() {
2286  return `<main class="registration-page"><section class="registration-hero"><div class="container"><span class="kicker">Zdravé zdravotnictví 2026</span><h1>Moje registrace</h1><p>Bezpečný přehled registrace, faktury a vašeho programu konference.</p></div></section><section class="section"><div class="container portal-shell portal-registration-shell" id="registration-portal"><div class="portal-loading">Načítám registraci…</div></div></section></main>`;
2287}
2288
2289function portalProgrammeMarkup(programme) {
2290  const selected = Object.fromEntries((programme.selections || []).map((row) => [row.slot_key, row.program_item_id]));
2291  const settings = programme.settings || {};
2292  const windowText = settings.opens_at && settings.closes_at
2293    ? `${new Intl.DateTimeFormat("cs-CZ", { dateStyle: "medium", timeStyle: "short", timeZone: "Europe/Prague" }).format(new Date(settings.opens_at))} – ${new Intl.DateTimeFormat("cs-CZ", { dateStyle: "medium", timeStyle: "short", timeZone: "Europe/Prague" }).format(new Date(settings.closes_at))}`
2294    : "Termín online výběru zatím nebyl zveřejněn.";
2295  const blocks = (programme.blocks || []).map((block, index) => {
2296    const portalItemCard = (item, active = false, full = false, showAvailability = true) => {
2297      const typeClass = item.item_type === "Panelová diskuze" ? "panel" : item.item_type === "Přednáška" ? "lecture" : "workshop";
2298      const availability = item.capacity == null ? "Kapacita zatím není nastavena" : full ? "Obsazeno" : `${item.remaining} míst volných`;
2299      const speakers = item.speakers?.length ? `<div class="program-speaker-list">${item.speakers.map((speaker) => { const name = typeof speaker === "string" ? speaker : speaker.name || "", photo = typeof speaker === "string" ? "" : speaker.photo_url || ""; return `<div class="program-speaker">${photo ? `<img class="program-avatar" src="${escapeHtml(photo)}" alt="${escapeHtml(name)}" loading="lazy" decoding="async">` : `<span class="program-avatar program-avatar-empty" aria-hidden="true">${escapeHtml(name).slice(0,1)}</span>`}<span>${escapeHtml(name)}</span></div>`; }).join("")}</div>` : "";
2300      return `<span class="program-card programme-choice-card ${active ? "selected" : ""} ${full ? "full" : ""}"><span class="program-card-top"><span class="program-type ${typeClass}">${escapeHtml(item.item_type || "Příspěvek")}</span>${showAvailability ? `<span class="programme-choice-badge">${active ? "Vybráno ✓" : availability}</span>` : ""}</span><strong class="programme-card-title">${escapeHtml(item.title)}</strong>${speakers}${item.room ? `<span class="programme-card-room">${escapeHtml(item.room)}</span>` : ""}</span>`;
2301    };
2302    if (!block.selectable) {
2303      const item = block.items[0];
2304      return `<section class="program-row portal-programme-block common"><div class="program-time"><span>Společný program</span><strong>${String(block.start_time).slice(0,5)}–${String(block.end_time).slice(0,5)}</strong></div><div class="program-content"><div class="program-grid">${portalItemCard(item, false, false, false)}</div></div></section>`;
2305    }
2306    return `<section class="program-row portal-programme-block"><div class="program-time"><span>Blok ${index}</span><strong>${String(block.start_time).slice(0,5)}–${String(block.end_time).slice(0,5)}</strong></div><div class="program-content"><div class="program-grid portal-programme-options">${block.items.map((item) => {
2307      const active = selected[block.slot_key] === item.id;
2308      const full = Number(item.remaining) <= 0 && !active;
2309      return `<button type="button" class="programme-card-button" data-programme-slot="${escapeHtml(block.slot_key)}" data-programme-item="${item.id}" ${!settings.is_open || full || item.capacity == null ? "disabled" : ""}>${portalItemCard(item,active,full)}</button>`;
2310    }).join("")}</div>${selected[block.slot_key] ? `<button type="button" class="portal-programme-remove" data-programme-remove="${escapeHtml(block.slot_key)}" ${!settings.is_open ? "disabled" : ""}>Odstranit volbu</button>` : ""}</div></section>`;
2311  }).join("");
2312  return `<section class="portal-card portal-programme" id="muj-program"><header><div><span class="kicker">Můj program konference</span><h2>Sestavte si svůj den.</h2><p>V každém paralelním bloku můžete vybrat jeden příspěvek. Volbu lze během otevřeného období změnit nebo odstranit.</p></div><span class="portal-status ${settings.is_open ? "paid" : "unpaid"}">${settings.is_open ? "Výběr je otevřený" : "Výběr je uzavřený"}</span></header><p class="portal-programme-window">Časové údaje jsou v pásmu Praha · ${escapeHtml(windowText)}</p><div class="portal-programme-list program-timeline">${blocks}</div>
2312<p class="form-status" id="portal-programme-status" aria-live="polite"></p></section>`;
2313}
2314
2315function participantPortalLinks(token, programme) {
2316  const settings = programme.settings || {};
2317  const programmeStatus = settings.is_open ? "Výběr programu je otevřený" : "Zobrazit program konference";
2318  const programmeUrl = `/sestavit-program?token=${encodeURIComponent(token)}`;
2319  const feedbackUrl = `/zpetna-vazba?token=${encodeURIComponent(token)}`;
2320  return `<nav class="portal-participant-navigation" aria-label="Možnosti účastníka"><section class="portal-participant-link primary"><span class="portal-participant-link-icon" aria-hidden="true">1</span><span><small>Můj program</small><strong>Sestavit program konference</strong><em>${escapeHtml(programmeStatus)}</em></span><a class="btn btn-primary" href="${programmeUrl}">Sestavit program</a></section><section class="portal-participant-link secondary"><span class="portal-participant-link-icon" aria-hidden="true">2</span><span><small>Zpětná vazba</small><strong>Vyplnit zpětnou vazbu</strong><em>Otevřít anonymní formulář</em></span><a class="btn btn-secondary" href="${feedbackUrl}">Vyplnit zpětnou vazbu</a></section></nav>`;
2321}
2322
2323function decoratePortalProgramme(programme) {
2324  const root = document.querySelector(".portal-programme");
2325  if (!root) return;
2326  const settings = programme.settings || {};
2327  const opensAt = settings.opens_at ? new Date(settings.opens_at) : null;
2328  const openedByTime = Boolean(opensAt && Date.now() >= opensAt.getTime());
2329  const hasOpened = settings.is_open || settings.manual_override === "closed" || openedByTime;
2330  const status = root.querySelector(".portal-status");
2331  if (status) {
2332    status.textContent = settings.is_open ? "Výběr je otevřený" : hasOpened ? "Výběr je uzavřený" : "Výběr zatím nebyl otevřen";
2333    status.className = `portal-status portal-programme-state ${settings.is_open ? "paid" : "unpaid"}`;
2334  }
2335  const windowInfo = root.querySelector(".portal-programme-window");
2336  if (windowInfo) {
2337    windowInfo.hidden = settings.is_open;
2338    windowInfo.textContent = opensAt
2339      ? `Možnost sestavit si konkrétní program se otevře ${new Intl.DateTimeFormat("cs-CZ", { dateStyle: "medium", timeStyle: "short", timeZone: "Europe/Prague" }
2339).format(opensAt)}.`
2340      : "Termín otevření výběru programu zatím nebyl zveřejněn.";
2341  }
2342  root.querySelector(".portal-programme-saved")?.remove();
2343  if ((programme.selections || []).length) {
2344    root.insertAdjacentHTML("beforeend", '<p class="portal-programme-saved">✓ Váš program je bezpečně uložený. Můžete toto okno zavřít.</p>');
2345  }
2346}
2347
2348function programmePortalPage() {
2349  return `<main class="registration-page"><section class="registration-hero"><div class="container"><span class="kicker">MedicRise · Zdravé zdravotnictví 2026</span><h1>Sestavte si svůj program.</h1><p>Vyberte si příspěvky, kterých se chcete během konference zúčastnit.</p></div></section><section class="section"><div class="container portal-shell portal-registration-shell" id="programme-portal"><div class="portal-loading">Načítám váš program…</div></div></section></main>`;
2350}
2351
2352function bindPortalProgramme(token) {
2353  const updateProgramme = async (slot, item) => {
2354    const status = document.getElementById("portal-programme-status");
2355    if (status) status.textContent = "Ukládám volbu…";
2356    try {
2357      const result = await conferenceProgramApi({ action: "portal_select", token, slot_key: slot, program_item_id: item || null });
2358      const currentProgramme = document.querySelector(".portal-programme");
2359      if (currentProgramme) currentProgramme.outerHTML = portalProgrammeMarkup(result.programme);
2360      decoratePortalProgramme(result.programme);
2361      bindProgrammeButtons();
2362    } catch (error) {
2363      if (status) status.textContent = error.message;
2364    }
2365  };
2366  const bindProgrammeButtons = () => {
2367    document.querySelectorAll("[data-programme-item]").forEach((button) => button.addEventListener("click", () => updateProgramme(button.dataset.programmeSlot, button.dataset.programmeItem)));
2368    document.querySelectorAll("[data-programme-remove]").forEach((button) => button.addEventListener("click", () => updateProgramme(button.dataset.programmeRemove, null)));
2369  };
2370  bindProgrammeButtons();
2371}
2372
2373async function initProgrammePortal() {
2374  const root = document.getElementById("programme-portal");
2375  if (!root) return;
2376  const token = new URL(location.href).searchParams.get("token") || "";
2377  if (!token) {
2378    root.innerHTML = `<div class="portal-error"><h2>Odkaz není kompletní</h2><p>Otevřete prosím výběr programu ze svého rozhraní účastníka.</p></div>`;
2379    return;
2380  }
2381  try {
2382    const result = await conferenceProgramApi({ action: "portal_snapshot", token });
2383    root.innerHTML = `<a class="portal-page-back" href="/moje-registrace?token=${encodeURIComponent(token)}">← Zpět na moji registraci</a>${portalProgrammeMarkup(result.programme)}`;
2384    decoratePortalProgramme(result.programme);
2385    bindPortalProgramme(token);
2386  } catch (error) {
2387    root.innerHTML = `<div class="portal-error"><h2>Program se nepodařilo načíst</h2><p>${escapeHtml(error.message)}</p><a class="btn btn-secondary" href="/moje-registrace?token=${encodeURIComponent(token)}">Zpět na moji registraci</a></div>`;
2388  }
2389}
2390
2391function portalInvoiceStatus(invoice) {
2392  if (invoice.status === "paid") return ["paid", "Uhrazeno"];
2393  if (invoice.status === "overdue") return ["unpaid overdue", "Po splatnosti"];
2394  if (invoice.status === "cancelled") return ["unpaid", "Zrušeno"];
2395  if (["issued", "sent"].includes(invoice.status)) return ["unpaid", "Neuhrazeno"];
2396  return ["unpaid", "Koncept"];
2397}
2398
2399async function initRegistrationPortal() {
2400  const root = document.getElementById("registration-portal");
2401  if (!root) return;
2402  const token = new URL(location.href).searchParams.get("token") || "";
2403  if (!token) {
2404    root.innerHTML = `<div class="portal-error"><h2>Odkaz není kompletní</h2><p>Otevřete prosím osobní odkaz, který jste získali po registraci.</p><a class="btn btn-primary" href="/konference-2026.html">Zpět na konferenci</a></div>`;
2405    return;
2406  }
2407  try {
2408    const [{ registration: r, invoice }, programmeResult] = await Promise.all([
2409      registrationApi({ action: "portal", token }),
2410      conferenceProgramApi({ action: "portal_snapshot", token }),
2411    ]);
2412    const name = [
2413      r.title_before,
2414      r.first_name,
2415      r.last_name,
2416      r.title_after ? `, ${r.title_after}` : "",
2417    ]
2418      .filter(Boolean)
2419      .join(" ")
2420      .replace(" ,", ",");
2421    root.innerHTML = `<section class="portal-card"><header><div><span class="kicker">Registrace ${escapeHtml(r.registration_code)}</span><h2>${escapeHtml(name)}</h2><p>${escapeHtml(r.email)} · ${escapeHtml(r.workplace || r.profession || "Účastník konference")}</p></div><span class="portal-status ${r.payment_status}">${r.registration_status === "waitlist" ? "Čekací listina" : r.registration_status === "cancelled" ? "Zrušeno" : r.payment_status === "paid" ? "Uhrazeno" : "Čeká na platbu"}</span></header><div class="portal-facts"><div><span>Konference</span><strong>Zdravé zdravotnictví 2026</strong><small>27. 11. 2026 · Hermitage Hotel Praha</small></div><div><span>Cena</span><strong>
2421${money(r.final_price_czk)}</strong><small>${invoice ? `Faktura ${escapeHtml(invoice.invoice_number || "")}` : "Faktura bude vystavena po potvrzení místa"}</small></div><div><span>ČLK</span><strong>${escapeHtml(r.clk_number || "Neuvedeno")}</strong><small>${escapeHtml((r.diets || []).join(", ") || r.diet_other || "Bez dietního omezení")}</small></div></div><div class="portal-actions">${invoice ? '<button class="btn btn-primary" id="portal-download-invoice">Stáhnout fakturu</button>' : ""}</div></section>${participantPortalLinks(token, programmeResult.programme)}`;
2422    if (invoice) {
2423      const invoiceStatus = portalInvoiceStatus(invoice);
2424      const actions = root.querySelector(".portal-actions");
2425      actions?.insertAdjacentHTML("beforebegin", `<section class="portal-invoice-panel"><div><span>Faktura</span><strong>${escapeHtml(invoice.invoice_number || "Koncept")}</strong><small>${money(invoice.total_czk ?? r.final_price_czk)}</small></div><span class="portal-status ${invoiceStatus[0]}">${invoiceStatus[1]}</span></section>`);
2426    }
2427    document
2428      .getElementById("portal-download-invoice")
2429      ?.addEventListener("click", async (event) => {
2430        const button = event.currentTarget;
2431        button.disabled = true;
2432        button.textContent = "Připravuji PDF…";
2433        try {
2434          const result = await registrationApi({
2435            action: "portal_invoice",
2436            token,
2437          });
2438          window.open(result.signed_url, "_blank", "noopener");
2439        } catch (error) {
2440          alert(error.message);
2441        } finally {
2442          button.disabled = false;
2443          button.textContent = "Stáhnout fakturu";
2444        }
2445      });
2446  } catch (error) {
2447    root.innerHTML = `<div class="portal-error"><h2>Registraci se nepodařilo načíst</h2><p>${escapeHtml(error.message)}</p><a class="btn btn-primary" href="/konference-2026.html">Zpět na konferenci</a></div>`;
2448  }
2449}
2450
2451function feedbackPage() {
2452  return `<main class="registration-page"><section class="registration-hero"><div class="container"><span class="kicker">MedicRise · Zdravé zdravotnictví 2026</span><h1>Jaká byla konference?</h1><p>Vaše zkušenost nám pomůže připravit další ročník ještě lépe.</p></div></section><section class="section"><div class="container portal-shell portal-feedback-shell" id="feedback-root"><div class="portal-loading">Načítám anonymní formulář…</div></div></section></main>`;
2453}
2454function ratingField(name, label, left = "Velmi souhlasím", right = "Velmi nesouhlasím") {
2455  return `<fieldset class="feedback-rating"><legend>${escapeHtml(label)}</legend><div>${[1, 2, 3, 4, 5].map((value) => `<label><input type="radio" name="${escapeHtml(name)}" value="${value}" required><span>${value}</span></label>`).join("")}</div><small><b>1</b> = ${escapeHtml(left)} · <b>5</b> = ${escapeHtml(right)}</small></fieldset>`;
2456}
2457function feedbackProgrammeCard(item, selected = false) {
2458  const typeClass = item.item_type === "Panelová diskuze" ? "panel" : item.item_type === "Přednáška" ? "lecture" : "workshop";
2459  const speakers = item.speakers?.length ? `<div class="program-speaker-list">${item.speakers.map((speaker) => `<div class="program-speaker">${speaker.photo_url ? `<img class="program-avatar" src="${escapeHtml(speaker.photo_url)}" alt="${escapeHtml(speaker.name)}" loading="lazy" decoding="async">` : `<span class="program-avatar program-avatar-empty" aria-hidden="true">${escapeHtml(speaker.name).slice(0, 1)}</span>`}<span>${escapeHtml(speaker.name)}</span></div>`).join("")}</div>` : "";
2460  return `<span class="program-card programme-choice-card ${selected ? "selected" : ""}"><span class="program-card-top"><span class="program-type ${typeClass}">${escapeHtml(item.item_type || "Příspěvek")}</span>${selected ? '<span class="programme-choice-badge">Navštíveno ✓</span>' : ""}</span><strong class="programme-card-title">${escapeHtml(item.title)}</strong>${speakers}${item.room ? `<span class="programme-card-room">${escapeHtml(item.room)}</span>` : ""}</span>`;
2461}
2462function feedbackProgrammeMarkup(programme) {
2463  const selected = Object.fromEntries((programme.selections || []).map((row) => [row.slot_key, row.program_item_id]));
2464  return (programme.blocks || []).map((block, index) => {
2465    const currentId = selected[block.slot_key] || (!block.selectable ? block.items[0]?.id : "");
2466    const choices = block.selectable
2467      ? `<div class="program-grid portal-programme-options feedback-programme-options">${block.items.map((item) => `<label class="programme-card-radio"><input type="radio" name="feedback-program-${escapeHtml(block.slot_key)}" value="${item.id}" ${currentId === item.id ? "checked" : ""} required>${feedbackProgrammeCard(item, currentId === item.id)}</label>`).join("")}</div>`
2468      : `<div class="program-grid">${feedbackProgrammeCard(block.items[0], true)}</div><input type="hidden" name="feedback-program-${escapeHtml(block.slot_key)}" value="${block.items[0]?.id || ""}">`;
2469    return `<section class="program-row portal-programme-block feedback-programme-block" data-feedback-block="${escapeHtml(block.slot_key)}"><div class="program-time"><span>${block.selectable ? `Blok ${index}` : "Společný program"}</span><strong>${String(block.start_time).slice(0, 5)}–${String(block.end_time).slice(0, 5)}</strong></div><div class="program-content">${choices}<div class="feedback-programme-rating">${ratingField(`programme-rating-${block.slot_key}`, "Celková spokojenost s příspěvkem", "Velmi spokojen/a", "Velmi nespokojen/a")}<label>Jakýkoliv komentář k příspěvku<textarea name="programme-comment-${escapeHtml(block.slot_key)}" rows="3" maxlength="3000"></textarea></label></div></div></section>`;
2470  }).join("");
2471}
2472async function initFeedback() {
2473  const root = document.getElementById("feedback-root"),
2474    token = new URL(location.href).searchParams.get("token") || "";
2475  if (!token) {
2476    root.innerHTML =
2477      '<div class="portal-error"><h2>Odkaz není kompletní</h2><p>Otevřete prosím osobní odkaz z e-mailu po konferenci.</p></div>';
2478    return;
2479  }
2480  try {
2481    const data = await registrationApi({ action: "feedback", token });
2482    if (data.feedback?.submitted_at) {
2483      root.innerHTML =
2484        '<div class="portal-error"><h2>Děkujeme za zpětnou vazbu</h2><p>Váš formulář už byl úspěšně odeslán.</p></div>';
2485      return;
2486    }
2487    const discoveryOptions = ["Již jsem se konference účastnil/a", "Newsletter MedicRise", "Doporučení", "Webové stránky MedicRise", "E-mail přes zaměstnavatele", "Facebook", "Instagram", "LinkedIn", "Informační leták", "Jiné"];
2488    const likert = [
2489      ["expectations", "Konference Zdravé zdravotnictví naplnila má očekávání."],
2490      ["practice_use", "Dovednosti nabyté během konference využiji ve své další praxi."],
2491      ["return_next_year", "Pokud by se konal 6. ročník konference Zdravé zdravotnictví, opět bych se zúčastnil/a."],
2492      ["organization", "Byl/a jsem spokojen/a s celkovou organizací konference."],
2493      ["registration", "Byl/a jsem spokojen/a s procesem registrace na konferenci."],
2494      ["programme_registration", "Byl/a jsem spokojen/a s procesem registrace na jednotlivé části odborného programu."],
2495      ["information", "Před zahájením a v průběhu konference jsem měl/a k dispozici všechny potřebné informace."],
2496      ["catering", "Byl/a jsem spokojen/a se stravováním během konference."],
2497    ];
2498    root.innerHTML = `<form class="portal-card feedback-form" id="feedback-form"><div class="feedback-anonymous-banner"><span aria-hidden="true">✓</span><div><strong>Zpětná vazba zůstává anonymní</strong><p>Vaše odpovědi se v administraci nepropojí se jménem ani kontaktními údaji. Osobní odkaz používáme pouze k bezpečnému p
2498ředvyplnění navštíveného programu.</p></div></div><header><div><span class="kicker">${escapeHtml(data.edition?.name || "Zdravé zdravotnictví 2026")}</span><h2>Váš pohled je důležitý.</h2><p>Otázky vycházejí z loňského formuláře a jsou aktualizované pro ročník 2026.</p></div></header><section class="feedback-section"><span class="kicker">O konferenci</span><h3>Celková zkušenost</h3><fieldset class="feedback-checklist"><legend>Jak jste se dozvěděli o konferenci Zdravé zdravotnictví?</legend>${discoveryOptions.map((option) => `<label><input type="checkbox" name="discovery" value="${escapeHtml(option)}"><span>${escapeHtml(option)}</span></label>`).join("")}</fieldset><label>Pokud jste zvolili „Jiné“, upřesněte prosím<input name="discovery_other" maxlength="500"></label><label>Pokud byste se účastnil/a 6. ročníku konference, o jaká témata byste měl/a zájem?<textarea name="future_topics" rows="4" maxlength="3000"></textarea></label><div class="feedback-grid feedback-likert-grid">${likert.map(([key, label]) => ratingField(`likert-${key}`, label)).join("")}</div><label>Jakýkoliv komentář<textarea name="general_comment" rows="4" maxlength="3000"></textarea></label></section><section class="feedback-section feedback-programme"><span class="kicker">Odborný program</span><h3>Kterých příspěvků jste se skutečně zúčastnil/a?</h3><p>Program jsme předvolili podle vaší registrace. Pokud jste nakonec navštívili jiný příspěvek, jednoduše jej zde označte.</p><div class="program-timeline">${feedbackProgrammeMarkup(data.programme)}</div></section><section class="feedback-section"><span class="kicker">Na závěr</span><h3>Je ještě něco, co nám chcete předat?</h3><label>Jakýkoliv komentář na závěr<textarea name="final_comment" rows="5" maxlength="3000"></textarea></label></section><div class="feedback-submit-row"><button class="btn btn-primary">Odeslat anonymní zpětnou vazbu</button><p class="form-status" role="status" aria-live="polite"></p></div></form>`;
2499    const form = document.getElementById("feedback-form");
2500    form.querySelectorAll('.programme-card-radio input[type="radio"]').forEach((input) => input.addEventListener("change", () => {
2501      const block = input.closest("[data-feedback-block]");
2502      block?.querySelectorAll(".programme-choice-card").forEach((card) => {
2503        const active = card.closest("label")?.querySelector("input")?.checked;
2504        card.classList.toggle("selected", Boolean(active));
2505        card.querySelector(".programme-choice-badge")?.remove();
2506        if (active) card.querySelector(".program-card-top")?.insertAdjacentHTML("beforeend", '<span class="programme-choice-badge">Navštíveno ✓</span>');
2507      });
2508    }));
2509    form.onsubmit = async (event) => {
2510      event.preventDefault();
2511      const button = form.querySelector("button"),
2512        status = form.querySelector(".form-status"),
2513        fd = new FormData(form);
2514      button.disabled = true;
2515      button.textContent = "Odesílám…";
2516      try {
2517        const discovery = fd.getAll("discovery");
2518        if (!discovery.length) throw new Error("Vyberte prosím, jak jste se o konferenci dozvěděli.");
2519        const likertAnswers = Object.fromEntries(likert.map(([key]) => [key, Number(fd.get(`likert-${key}`))]));
2520        const selections = {};
2521        const programmeAnswers = (data.programme.blocks || []).map((block) => {
2522          const itemId = String(fd.get(`feedback-program-${block.slot_key}`) || "");
2523          if (block.selectable) selections[block.slot_key] = itemId;
2524          return { slot_key: block.slot_key, program_item_id: itemId, rating: Number(fd.get(`programme-rating-${block.slot_key}`)), comment: fd.get(`programme-comment-${block.slot_key}`) || "" };
2525        });
2526        const result = await registrationApi({
2527          action: "feedback_submit",
2528          token,
2529          answers: { discovery, discovery_other: fd.get("discovery_other"), future_topics: fd.get("future_topics"), likert: likertAnswers, general_comment: fd.get("general_comment"), final_comment: fd.get("final_comment") },
2530          selections,
2531          programme_answers: programmeAnswers,
2532        });
2533        root.innerHTML = `<div class="portal-error"><h2>Děkujeme.</h2><p>${escapeHtml(result.message)}</p><a class="btn btn-primary" href="/">Zpět na MedicRise</a></div>`;
2534      } catch (error) {
2535        status.textContent = error.message;
2536        button.disabled = false;
2537        button.textContent = "Odeslat zpětnou vazbu";
2538      }
2539    };
2540  } catch (error) {
2541    root.innerHTML = `<div class="portal-error"><h2>Formulář se nepodařilo načíst</h2><p>${escapeHtml(error.message)}</p></div>`;
2542  }
2543}
2544function auditVideo(slot, label) {
2545  return `<div class="audit-video" data-public-audit-video="${slot}"><div class="audit-video-placeholder"><span>▶</span><strong>${label}</strong><small>Video se načítá…</small></div></div>`;
2546}
2547
2548function auditLandingPage() {
2549  return `<main class="audit-page"><section class="audit-hero"><div class="container audit-hero-grid"><div><span class="kicker">Audit komunikace zdarma</span><h1>Podívejte se na komunikaci svého týmu novýma očima.</h1><p>Praktický audit od Barbory Mechúrové vám pomůže pojmenovat silné stránky i místa, která ve spolupráci zdravotnického týmu zbytečně drhnou.</p><div class="audit-points"><span>✓ Přehledná sebereflexe týmu</span><span>✓ Konkrétní témata k rozhovoru</span><span>✓ PDF ihned po odeslání formuláře</span></div></div>${auditVideo("audit_intro", "Pozvánka k Auditu komunikace")}</div></section><section class="section section-soft"><div class="container audit-form-layout"><div><span class="kicker">Jak audit získat</span><h2>Stačí vyplnit krátký formulář.</h2><p>Na další stránce najdete instruktážní video i audit v PDF. Údaje použijeme také k navazující praktické e-mailové podpoře; odběr můžete kdykoli ukončit.</p></div><form class="audit-form-card" data-audit-download-form><div class="audit-fields"><label>Jméno *<input name="first_name" autocomplete="given-name" required maxlength="100"></label><label>Příjmení *<input name="last_name" autocomplete="family-name" required maxlength="100"></label><label class="audit-field-wide">E-mail *<input name="email" type="email" autocomplete="email" required maxlength="320"></label></div><label class="registration-consent"><input name="marketing_consent" type="checkbox" required><span>Souhlasím se zasláním auditu a navazujících praktických e-mailů. Souhlas lze kdykoli odvolat.</span></label><label class="footer-newsletter-honeypot" aria-hidden="true">Web<input name="website" tabindex="-1" autocomplete="off"></label><button class="btn btn-primary" type="submit" disabled>Získat audit zdarma →</button><p class="form-status" aria-live="polite">Načítáme bezpečný formulář…</p><small>Podrobnosti najdete v <a href="/gdpr" target="_blank" rel="noopener">zásadách ochrany osobních údajů</a>.</small></form></div></section></main>`;
2550}
2551
2552function auditContentPage() {
2553  return `<main class="audit-page"><section class="audit-hero audit-hero-content"><div class="container"><span class="kicker">Audit komunikace</span><h1>Váš audit je připravený.</h1><p>Než začnete, pusťte si krátkou instrukci od Báry. Pomůže vám z auditu získat maximum.</p>${auditVideo("audit_instructions", "Jak s Auditem komunikace pracovat")}</div></section><section class="section"><div class="container audit-download-grid"><article class="audit-download-card"><span class="kicker">Pracovní materiál</span><h2>Audit komunikace v PDF</h2><p>Otevřete si audit v nové záložce, vyplňte ho elektronicky nebo si jej vytiskněte.</p><a class="btn btn-primary" href="/assets/guides/audit-komunikace.pdf" target="_blank" rel="noopener">Otevřít audit v PDF ↗</a></article><article class="audit-tip-card"><strong>Doporučení</strong><p>Vyhraďte si klidných 15–20 minut. Odpovídejte podle skutečné každodenní zkušenosti, ne podle ideálního stavu.</p></article></div></section><section class="section section-soft"><div class="container audit-consultation"><div><span class="kicker">Bezplatná konzultace s Bárou</span><h2>Chcete výsledky projít společně?</h2><p>Pošlete Báře pár základních informací. Ozve se vám osobně a domluvíte si krátkou nezávaznou konzultaci.</p></div><form class="audit-form-card" data-audit-consultation-form><div class="audit-fields"><label>Jméno *<input name="first_name" autocomplete="given-name" required maxlength="100"></label><label>Příjmení *<input name="last_name" autocomplete="family-name" required maxlength="100"></label><label>E-mail *<input name="email" type="email" autocomplete="email" required maxlength="320"></label><label>Telefon *<input name="phone" type="tel" autocomplete="tel" required maxlength="40"></label><label>Organizace<input name="company" autocomplete="organization" maxlength="200"></label><label>Pozice<input name="job_title" autocomplete="organization-title" maxlength="200"></label><label class="audit-field-wide">Co byste chtěli na konzultaci probrat? *<textarea name="challenge" required maxlength="2000" rows="5"></textarea></label></div><label class="footer-newsletter-honeypot" aria-hidden="true">Web<input name="website" tabindex="-1" autocomplete="off"></label><button class="btn btn-primary" type="submit" disabled>Požádat o konzultaci →</button>
2553<p class="form-status" aria-live="polite">Načítáme bezpečný formulář…</p><small>Údaje použijeme pouze k vyřízení vaší poptávky. <a href="/gdpr" target="_blank" rel="noopener">Ochrana osobních údajů</a>.</small></form></div></section></main>`;
2554}
2555
2556async function initPublicAuditVideos() {
2557  for (const host of document.querySelectorAll("[data-public-audit-video]")) {
2558    try {
2559      const response = await fetch(`${CMS_SUPABASE_URL}/functions/v1/mux-video`, {
2560        method: "POST",
2561        headers: { apikey: CMS_SUPABASE_KEY, Authorization: `Bearer ${CMS_SUPABASE_KEY}`, "content-type": "application/json" },
2562        body: JSON.stringify({ action: "public-playback", slot: host.dataset.publicAuditVideo }),
2563      });
2564      const data = await response.json().catch(() => ({}));
2565      if (!response.ok) throw new Error(data.error || "Video zatím není nahrané.");
2566      host.innerHTML = `<mux-player playback-id="${escapeHtml(data.playback_id)}" playback-token="${escapeHtml(data.token)}" stream-type="on-demand" disable-tracking disable-cookies accent-color="#8f2d45" primary-color="#ffffff" secondary-color="#1e2924"></mux-player>`;
2567    } catch (error) {
2568      host.innerHTML = `<div class="audit-video-placeholder audit-video-unavailable"><span>▶</span><strong>Video připravujeme</strong><small>${escapeHtml(error.message)}</small></div>`;
2569    }
2570  }
2571}
2572
2573async function initAuditForm(selector, slug, consultation = false) {
2574  const form = document.querySelector(selector);
2575  if (!form) return;
2576  const button = form.querySelector('button[type="submit"]');
2577  const status = form.querySelector(".form-status");
2578  let definition;
2579  try {
2580    definition = (await newsletterApi({ action: "definition", slug })).form;
2581    button.disabled = false;
2582    status.textContent = "";
2583  } catch {
2584    status.textContent = "Formulář je nyní dočasně nedostupný. Zkuste to prosím později.";
2585    return;
2586  }
2587  form.addEventListener("submit", async (event) => {
2588    event.preventDefault();
2589    if (!form.reportValidity()) return;
2590    const data = new FormData(form);
2591    const payload = {};
2592    ["first_name", "last_name", "phone", "company", "job_title", "challenge"].forEach((key) => {
2593      const value = String(data.get(key) || "").trim();
2594      if (value) payload[key] = value;
2595    });
2596    button.disabled = true;
2597    button.textContent = "Odesíláme…";
2598    status.textContent = "Bezpečně odesíláme vaše údaje…";
2599    try {
2600      const result = await newsletterApi({ form_id: definition.id, public_token: definition.public_token, email: String(data.get("email") || ""), payload, consent: consultation ? false : data.has("marketing_consent"), website: String(data.get("website") || ""), page_url: location.href, referrer: document.referrer }, `${crypto.randomUUID()}:${Date.now()}`);
2601      if (!consultation) {
2602        window.location.assign("/audit-komunikace-bara-video-a-audit-ke-stazeni");
2603        return;
2604      }
2605      form.innerHTML = `<div class="audit-form-success"><span>✓</span><h3>${escapeHtml(result.success_title || "Děkujeme za zprávu.")}</h3><p>${escapeHtml(result.success_message || "Bára se vám osobně ozve.")}</p></div>`;
2606    } catch (error) {
2607      button.disabled = false;
2608      button.textContent = consultation ? "Požádat o konzultaci →" : "Získat audit zdarma →";
2609      status.textContent = error.message;
2610    }
2611  });
2612}
2613
2614function partners() {
2615  const goldPartners = [
2616      ["Nadační fond EUC Elucidate", "/assets/partners/euc-elucidate.png"],
2617      ["Canadian Medical", "/assets/partners/canadian-medical.png"],
2618    ],
2619    generalPartners = [
2620      ["MediClinic", "/assets/partners/mediclinic.png"],
2621      ["Moje Ambulance", "/assets/general-partners/moje-ambulance.png"],
2622      ["MSD", "/assets/general-partners/msd.png"],
2623      ["Medevio", "/assets/general-partners/medevio.png"],
2624      ["Bombus", "/assets/general-partners/bombus.png"],
2625      ["PPŽIVĚ", "/assets/general-partners/ppzive.png"],
2626      ["Sonnentor", "/assets/general-partners/sonnentor.png"],
2627      ["Darujme.cz", "/assets/general-partners/darujme.png"],
2628      ["Po Medině", "/assets/general-partners/po-medine.png"],
2629      ["Biora Cosmetics", "/assets/general-partners/biora.png"],
2630      ["Aconte", "/assets/general-partners/aconte.png"],
2631      ["Nakladatelství Portál", "/assets/general-partners/portal.png"],
2632      ["Fluidum", "/assets/general-partners/fluidum.png"],
2633      ["Srdcem Robinson", "/assets/general-partners/srdcem-robinson.png"],
2634    ],
2635    logoCards = (items, cardClass = "") =>
2636      items
2637        .map(
2638          ([name, src]) =>
2639            `<figure class="partner-logo-card ${cardClass}"><img src="${src}" alt="${name}"></figure>`,
2640        )
2641        .join("");
2642
2643  return `<main><section class="page-hero"><div class="container page-hero-grid"><div><span class="kicker">Partneři MedicRise</span><h1>Silné partnerství pro zdravější zdravotnictví.</h1><p>Spolupracujeme s organizacemi, které sdílejí zájem o kvalitní vzdělávání zdravotníků, komunikaci a dlouhodobý rozvoj zdravotnického prostředí.</p><div class="hero-actions" style="margin-top:24px"><a class="btn btn-primary" href="mailto:[email protected]?subject=Partnerství MedicRise">Chci se stát partnerem</a></div></div><div class="page-hero-art"><img src="${ASSETS.partners}" alt="Partnerství"></div></div></section><section class="section partners-gold-section"><div class="container"><div class="section-head"><div><span class="kicker">Zlaté partnerství</span><h2>Zlatí partneři MedicRise pro rok 2026</h2></div><p>Děkujeme partnerům, kteří dlouhodobě podporují naÅ¡i vizi zdravějšího zdravotnictví.</p></div><div class="partners-logo-grid partners-logo-grid-gold">${logoCards(goldPartners, "partner-logo-card-gold")}</div></div></section><section class="section section-soft partners-all-section"><div class="container"><div class="section-head"><div><span class="kicker">Společně tvoříme změnu</span><h2>Další partneři MedicRise</h2></div><p>Vážíme si vÅ¡ech organizací, které se s námi podílejí na vzdělávání, osvětě a rozvoji zdravotnického prostředí.</p></div><div class="partners-logo-grid partners-logo-grid-all">${logoCards(generalPartners)}</div></div></section><section class="section"><div class="container"><div class="cta"><div><span class="kicker cta-kicker">Partnerství na míru</span><h2>Najděme model, který dává smysl oběma stranám.</h2><p>Nabídku partnerství lze sestavit podle vaÅ¡ich cílů – od podpory vzdělávání zaměstnanců po viditelnost na konferenci Zdravé zdravotnictví.</p></div><div class="cta-side"><span class="cta-side-eyebrow">JednoduÅ¡e a nezávazně</span><strong>Pojďme to posunout dál.</strong><span class="cta-side-note">Stačí krátká zpráva. Společně vybereme vhodný další krok podle vaÅ¡ich potřeb.</span><div class="cta-actions"><a class="btn btn-primary" href="mailto:[email protected]?subject=Partnerství MedicRise">Napsat nám</a></div></div></div></div></section></main>`;
2644}
2645
2646const isPortalRoute = location.pathname === "/portal" || location.pathname.startsWith("/portal/");
2647const isLegacyLmsDashboard = [
2648  "/moje-kurzy",
2649  "/moje-kurzy.html",
2650].includes(location.pathname);
2651const isLmsRoute = [
2652  "/kurz",
2653  "/kurz.html",
2654].includes(location.pathname);
2655if (isLegacyLmsDashboard) {
2656  location.replace(`/portal/courses${location.search || ""}`);
2657} else if (isPortalRoute) {
2658  document.body.classList.add("portal-page");
2659  if (window.MedicRisePortal)
2660    window.MedicRisePortal.mount({
2661      url: CMS_SUPABASE_URL,
2662      key: CMS_SUPABASE_KEY,
2663      logo: ASSETS.logo,
2664    });
2665  else
2666    document.body.innerHTML =
2667      '<main class="admin-fatal">MedicRise Portal se nepodařilo načíst.</main>';
2668} else if (isLmsRoute) {
2669  document.body.classList.add("lms-page");
2670  if (window.MedicRiseLMS)
2671    window.MedicRiseLMS.mount({
2672      url: CMS_SUPABASE_URL,
2673      key: CMS_SUPABASE_KEY,
2674      logo: ASSETS.logo,
2675      courseMode: location.pathname.startsWith("/kurz"),
2676    });
2677  else
2678    document.body.innerHTML =
2679      '<main class="admin-fatal">Studijní portál se nepodařilo načíst.</main>';
2680} else if (isAdminRoute) {
2681  document.body.classList.add("admin-page");
2682  if (window.MedicRiseAdmin)
2683    window.MedicRiseAdmin.mount({
2684      url: CMS_SUPABASE_URL,
2685      key: CMS_SUPABASE_KEY,
2686      logo: ASSETS.logo,
2687    });
2688  else
2689    document.body.innerHTML =
2690      '<main class="admin-fatal">Administrace se nepodařila načíst.</main>';
2691} else if (current === "index.html") {
2692  layout(home());
2693  hydrateConferenceEditions();
2694}
2695else if (coursePages[current]) {
2696  layout(coursePage(courses[coursePages[current]]));
2697  initCourseGuideForm();
2698}
2699else if (current === "vzdelavani.html") layout(education());
2700else if (current === "o-nas.html") layout(about());
2701else if (current === "online-kurzy.html") {
2702  layout(online());
2703  initOnlineStore();
2704} else if (current === "konference.html") {
2705  layout(conferenceHub());
2706  hydrateConferenceEditions();
2707}
2708else if (isRegistrationRoute) {
2709  layout(registrationPage());
2710  initRegistration();
2711} else if (isRegistrationPortalRoute) {
2712  layout(registrationPortalPage());
2713  initRegistrationPortal();
2714} else if (isProgrammePortalRoute) {
2715  layout(programmePortalPage());
2716  initProgrammePortal();
2717} else if (isFeedbackRoute) {
2718  layout(feedbackPage());
2719  initFeedback();
2720} else if (isPrivacyRoute) {
2721  layout(legalPage("privacy"));
2722} else if (isTermsRoute) {
2723  layout(legalPage("terms"));
2724} else if (isEmailPreferencesRoute) {
2725  layout(emailPreferencePage());
2726  initEmailPreferences();
2727} else if (isDistributionKitRoute) {
2728  layout(distributionKitPage());
2729  initDistributionKit();
2730} else if (isAuditLandingRoute) {
2731  layout(auditLandingPage());
2732  initPublicAuditVideos();
2733  initAuditForm("[data-audit-download-form]", AUDIT_DOWNLOAD_FORM_SLUG);
2734} else if (isAuditContentRoute) {
2735  layout(auditContentPage().replace('<section class="section section-soft">', '<section class="section section-soft" id="rezervace-konzultace">'));
2736  initPublicAuditVideos();
2737  initAuditForm("[data-audit-consultation-form]", AUDIT_CONSULTATION_FORM_SLUG, true);
2738} else if (hostedMarketingFormMatch) {
2739  layout(hostedMarketingFormPage());
2740  initHostedMarketingForm(hostedMarketingFormMatch[1]);
2741} else if (conferenceYearMatch) {
2742  layout(conferenceYear(conferenceYearMatch[1]));
2743  hydrateConferenceEditions(conferenceYearMatch[1]);
2744  hydratePublishedProgram(conferenceYearMatch[1]);
2745  hydrateConferencePartners(conferenceYearMatch[1]);
2746  hydrateConferenceGallery(conferenceYearMatch[1]);
2747  if (conferenceYearMatch[1] === "2026")
2748    hydrateConferencePricePeriods(conferenceYearMatch[1]);
2749} else if (current === "partneri.html") layout(partners());
2750else layout(`<main class="page-hero"><div class="container"><span class="kicker">Chyba 404</span><h1>Tuto stránku jsme nenašli.</h1><p>Zkontrolujte adresu nebo se vraťte na hlavní stránku MedicRise.</p><a class="btn btn-primary" href="/">Zpět na hlavní stránku</a></div></main>`);

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.