New features available Learn more

Od pomysłu do działającej aplikacji w 7 dni!

Z AI, bez programowania. Wyjdziesz z kursu z **działającą aplikacją,** nawet jeśli jesteś humanistą!

close-up-friends-hanging-out

Korzyści

Co zyskujesz w 7 dni?

Do końca przedsprzedaży zostało:

NaN
DNI
:
NaN
GODZIN
:
NaN
MINUT
:
NaN
SEKUND

Co otrzymujesz

Wszystko, czego potrzebujesz w jednym miejscu

Moduły

7 modułów - 7 dni pracy

Kurs podzielony na 7 modułów ułożonych jako 7 dni pracy - od pomysłu do działającej aplikacji.

Lekcje

42 lekcje krok po kroku

42 lekcje prowadzące Cię przez każdy etap budowania aplikacji - konkretnie, bez zbędnej teorii.

Bonusy

5 bonusów i gotowe prompty

5 bonusów, które ułatwią Ci start i przyspieszą efekty - w tym gotowe prompty startowe do pracy z AI.

Certyfikat

Certyfikat ukończenia kursu

Certyfikat ukończenia kursu do portfolio i profilu zawodowego - potwierdzenie Twoich nowych kompetencji.

Dostęp

Dożywotni dostęp

Dożywotni dostęp do kursu i wszystkich aktualizacji - wracaj do materiałów kiedy tylko potrzebujesz.

Do końca przedsprzedaży zostało:

NaN
DNI
:
NaN
GODZIN
:
NaN
MINUT
:
NaN
SEKUND

Autor kursu

Our talented team brings years of expertise and passion, delivering innovative solutions that drive success for our clients.

<section id="secure-payments-footer"> <style> #secure-payments-footer { padding-top: 16px; padding-bottom: 32px; padding-inline: var(--et-spacing-horizontal, 16px); background-color: transparent; color: var(--et-colors-text, #ffffff); } @media (min-width: 1024px) { #secure-payments-footer { padding-top: 20px; padding-bottom: 40px; padding-inline: var(--et-spacing-horizontal-lg, 24px); } } #secure-payments-footer .container { max-width: 1100px; margin-inline: auto; display: flex; flex-direction: column; align-items: center; } #secure-payments-footer .sp-header { display: inline-flex; align-items: center; gap: 8px; color: #ffffff; font-size: 12px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.16em; margin: 0 0 16px 0; line-height: 1; } #secure-payments-footer .sp-header svg { width: 14px; height: 14px; color: var(--et-colors-brand, #2e7d53); flex-shrink: 0; } #secure-payments-footer .sp-tiles { display: flex; flex-wrap: nowrap; width: 100%; gap: 6px; margin: 0 0 14px 0; } @media (min-width: 768px) { #secure-payments-footer .sp-tiles { gap: 10px; } } #secure-payments-footer .sp-tile { flex: 1 1 0; min-width: 0; background-color: var(--et-colors-card-background, #16142b); border: 1px solid var(--et-colors-border, #262340); border-radius: 8px; padding: 8px 4px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; text-align: center; } @media (min-width: 768px) { #secure-payments-footer .sp-tile { flex-direction: row; padding: 10px 8px; gap: 8px; } } #secure-payments-footer .sp-icon { width: 16px; height: 16px; flex-shrink: 0; color: #ffffff; } #secure-payments-footer .sp-icon svg { width: 100%; height: 100%; display: block; } #secure-payments-footer .sp-name { font-size: clamp(8px, 1.1vw, 13px); line-height: 1.15; font-weight: 400; overflow: visible; white-space: nowrap; } #secure-payments-footer .sp-name.sp-bold { font-weight: 600; } #secure-payments-footer .sp-note { font-size: 12px; opacity: 0.55; text-align: center; margin: 0; line-height: 1.4; } </style> <div class="container"> <h2 class="sp-header"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect> <path d="M7 11V7a5 5 0 0 1 10 0v4"></path> </svg> Bezpieczne płatności </h2> <div class="sp-tiles"> <div class="sp-tile"> <div class="sp-icon"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <rect x="5" y="2" width="14" height="20" rx="2"></rect> <line x1="12" y1="18" x2="12.01" y2="18"></line> </svg> </div> <span class="sp-name sp-bold">BLIK</span> </div> <div class="sp-tile"> <div class="sp-icon"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <rect x="4" y="3" width="8" height="18" rx="2"></rect> <path d="M16 8.5a5 5 0 0 1 0 7"></path> <path d="M19 5.5a9 9 0 0 1 0 13"></path> </svg> </div> <span class="sp-name">Apple&nbsp;Pay</span> </div> <div class="sp-tile"> <div class="sp-icon"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <rect x="1" y="4" width="22" height="16" rx="2"></rect> <line x1="1" y1="10" x2="23" y2="10"></line> </svg> </div> <span class="sp-name">Google&nbsp;Pay</span> </div> <div class="sp-tile"> <div class="sp-icon"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <rect x="1" y="4" width="22" height="16" rx="2"></rect> <line x1="1" y1="10" x2="23" y2="10"></line> </svg> </div> <span class="sp-name sp-bold">VISA</span> </div> <div class="sp-tile"> <div class="sp-icon"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <rect x="1" y="4" width="22" height="16" rx="2"></rect> <line x1="1" y1="10" x2="23" y2="10"></line> </svg> </div> <span class="sp-name">Mastercard</span> </div> <div class="sp-tile"> <div class="sp-icon"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <circle cx="12" cy="12" r="10"></circle> <polyline points="12 6 12 12 16 14"></polyline> </svg> </div> <span class="sp-name">Klarna</span> </div> </div> <p class="sp-note">Płatność obsługiwana przez operatora · szyfrowanie SSL</p> </div> </section>
<section id="nexala-offer-section"> <style> #nexala-offer-section { background-color: #0b0a16; padding: 40px 16px; font-family: inherit; display: flex; justify-content: center; align-items: center; } #nexala-offer-section * { box-sizing: border-box; margin: 0; padding: 0; } #nexala-offer-section .nexala-container { width: 100%; max-width: 530px; margin: 0 auto; } #nexala-offer-section .nexala-card { background-color: #141226; border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 18px; padding: 32px; } #nexala-offer-section .nexala-top-bar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 28px; } #nexala-offer-section .nexala-brand { text-transform: uppercase; letter-spacing: 0.25em; font-weight: 600; color: #ffffff; font-size: 15px; margin-right: -0.25em; } #nexala-offer-section .nexala-badge { background-color: #235c3f; color: #a3f7c9; padding: 6px 14px; border-radius: 9999px; font-size: 13px; font-weight: 600; border: 1px solid rgba(62, 207, 142, 0.25); white-space: nowrap; } #nexala-offer-section .nexala-title { color: #ffffff; font-size: clamp(22px, 4vw, 28px); font-weight: 700; line-height: 1.25; text-align: center; margin-bottom: 10px; } #nexala-offer-section .nexala-subtitle { color: #8f8da6; font-size: clamp(13px, 3.2vw, 15px); text-align: center; margin-bottom: 28px; line-height: 1.4; white-space: nowrap; } #nexala-offer-section .nexala-features { list-style: none; display: flex; flex-direction: column; gap: 14px; margin-bottom: 28px; } #nexala-offer-section .nexala-features li { display: flex; align-items: center; gap: 12px; color: #e2e1ec; font-size: clamp(13px, 3.2vw, 15px); font-weight: 500; line-height: 1.35; white-space: nowrap; } #nexala-offer-section .nexala-features svg { width: 20px; height: 20px; flex-shrink: 0; color: #3ecf8e; } #nexala-offer-section .nexala-pricing-box { background-color: #0a0915; border: 1px solid #1f1c35; border-radius: 12px; padding: 22px 18px; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; margin-bottom: 20px; } #nexala-offer-section .nexala-price-display { position: relative; display: flex; justify-content: center; align-items: center; margin-bottom: 12px; width: 100%; } #nexala-offer-section .nexala-price-old { color: #6f6d85; font-size: clamp(18px, 3.5vw, 22px); font-weight: 500; text-decoration: none; position: absolute; right: calc(50% + 72px); top: 50%; transform: translateY(-50%); white-space: nowrap; display: inline-block; } #nexala-offer-section .nexala-price-old::after { content: ''; position: absolute; left: -4px; right: -4px; top: 50%; height: 2px; background-color: #7b7894; transform: translateY(-50%) rotate(-18deg); border-radius: 2px; pointer-events: none; } #nexala-offer-section .nexala-price-new { color: #34d399; font-size: clamp(40px, 7vw, 48px); font-weight: 800; line-height: 1; letter-spacing: -0.02em; text-align: center; } #nexala-offer-section .nexala-price-note { color: #9b98b0; font-size: 14px; line-height: 1.4; text-align: center; } #nexala-offer-section .nexala-cta { display: flex; width: 100%; justify-content: center; align-items: center; background-color: #38a169; color: #ffffff; font-size: 17px; font-weight: 700; padding: 15px 20px; border-radius: 10px; text-decoration: none; transition: all 0.2s ease; box-shadow: 0 4px 18px rgba(46, 125, 83, 0.45); } #nexala-offer-section .nexala-cta:hover { background-color: #369b62; transform: translateY(-2px); box-shadow: 0 6px 20px rgba(46, 125, 83, 0.6); } #nexala-offer-section .nexala-cta:active { transform: translateY(0); } #nexala-offer-section .nexala-guarantee { display: flex; justify-content: center; align-items: center; gap: 8px; margin-top: 14px; color: #8e8ca2; font-size: 13.5px; line-height: 1.3; text-align: center; } #nexala-offer-section .nexala-guarantee svg { width: 16px; height: 16px; flex-shrink: 0; } @media (max-width: 480px) { #nexala-offer-section .nexala-card { padding: 24px 18px; } #nexala-offer-section .nexala-top-bar { margin-bottom: 20px; } #nexala-offer-section .nexala-subtitle { margin-bottom: 20px; } #nexala-offer-section .nexala-features { margin-bottom: 20px; gap: 12px; } #nexala-offer-section .nexala-price-old { right: calc(50% + 60px); } } </style> <div class="nexala-container"> <div class="nexala-card"> <div class="nexala-top-bar"> <span class="nexala-brand">N E X A L A</span> <span class="nexala-badge">Przedsprzedaż</span> </div> <h2 class="nexala-title">Zbuduj swoją pierwszą działającą aplikację w 7 dni</h2> <p class="nexala-subtitle">Bez programowania, nawet jeśli jesteś humanistą!</p> <ul class="nexala-features"> <li> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg> <span>7 modułów wideo - od pomysłu do wdrożenia</span> </li> <li> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg> <span>Gotowe prompty i szablony do skopiowania</span> </li> <li> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg> <span>Dożywotni dostęp z aktualizacjami</span> </li> </ul> <div class="nexala-pricing-box"> <div class="nexala-price-display"> <span class="nexala-price-old">599 zł</span> <span class="nexala-price-new">199 zł</span> </div> <p class="nexala-price-note">Cena w przedsprzedaży.</p> <p class="nexala-price-note" style="white-space: nowrap;">Po zakończeniu przedsprzedaży 599 zł.</p> <p class="nexala-price-note">Kurs otwiera się 20 listopada 2026.</p> </div> <a href="https://koszyk.nexala.io/checkout/apikacja" target="_blank" rel="noopener noreferrer" class="nexala-cta">Dołączam za 199 zł</a> <div class="nexala-guarantee"> <svg viewBox="0 0 24 24" fill="none" stroke="#3ecf8e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg> <span>Zwrot bez pytań w ciągu 30 dni od startu kursu</span> </div> </div> </div> </section>
<section id="section-social-proof-avatars"> <style> #section-social-proof-avatars { padding-block: 32px; padding-inline: var(--et-spacing-horizontal, 16px); background-color: var(--et-colors-background, #0b0a16); } @media (min-width: 1024px) { #section-social-proof-avatars { padding-block: 40px; padding-inline: var(--et-spacing-horizontal-lg, 24px); } } #section-social-proof-avatars .container { margin-inline: auto; max-width: 42rem; display: flex; flex-direction: column; align-items: center; justify-content: center; } @media (min-width: 640px) { #section-social-proof-avatars .container { flex-direction: row; } } #section-social-proof-avatars .avatars { display: flex; align-items: center; justify-content: center; margin-bottom: 16px; } @media (min-width: 640px) { #section-social-proof-avatars .avatars { margin-bottom: 0; margin-right: 20px; } } #section-social-proof-avatars .avatar { width: 48px; height: 48px; border-radius: 50%; object-fit: cover; border: 3px solid var(--et-colors-background, #0b0a16); margin-left: -12px; background-color: color-mix(in srgb, var(--et-colors-background, #0b0a16) 80%, white); position: relative; } #section-social-proof-avatars .avatar:first-child { margin-left: 0; } #section-social-proof-avatars .rating-content { display: flex; flex-direction: column; align-items: center; text-align: center; } @media (min-width: 640px) { #section-social-proof-avatars .rating-content { align-items: flex-start; text-align: left; } } #section-social-proof-avatars .stars { display: flex; gap: 4px; margin-bottom: 8px; } #section-social-proof-avatars .star { width: 20px; height: 20px; fill: #f59e0b; } #section-social-proof-avatars .paragraph { color: var(--et-colors-text, #ffffff); font-size: 16px; line-height: 1.4; opacity: 0.9; margin-bottom: 0; text-wrap: balance; } #section-social-proof-avatars strong { font-weight: 600; color: var(--et-colors-text, #ffffff); } #section-social-proof-avatars .last { margin-bottom: 0 !important; } </style> <div class="container"> <div class="avatars"> <img class="avatar" src="/api/pages/generated/avatar/f/1" alt="" loading="lazy" width="48" height="48"> <img class="avatar" src="/api/pages/generated/avatar/m/1" alt="" loading="lazy" width="48" height="48"> <img class="avatar" src="/api/pages/generated/avatar/f/2" alt="" loading="lazy" width="48" height="48"> <img class="avatar" src="/api/pages/generated/avatar/m/2" alt="" loading="lazy" width="48" height="48"> <img class="avatar" src="/api/pages/generated/avatar/f/3" alt="" loading="lazy" width="48" height="48"> </div> <div class="rating-content"> <div class="stars" aria-label="Ocena 4,75 na 5 gwiazdek"> <svg class="star" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg> <svg class="star" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg> <svg class="star" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg> <svg class="star" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg> <svg class="star" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg> </div> <p class="paragraph last">Ocena <strong>4,75/5</strong> od ponad <strong>2,000</strong> kursantów</p> </div> </div> </section>
<section id="custom-audio-testimonial"> <style> #custom-audio-testimonial { padding-top: 4px; padding-bottom: 8px; padding-inline: var(--et-spacing-horizontal, 16px); background-color: transparent; } @media (min-width: 1024px) { #custom-audio-testimonial { padding-top: 6px; padding-bottom: 12px; padding-inline: var(--et-spacing-horizontal-lg, 24px); } } #custom-audio-testimonial .container { margin-inline: auto; max-width: 480px; display: flex; flex-direction: column; align-items: center; } #custom-audio-testimonial .player-pill { background: #141226; border: 1px solid rgba(255, 255, 255, 0.12); border-radius: 9999px; padding: 6px 14px 6px 6px; display: flex; align-items: center; gap: 12px; width: 100%; box-shadow: 0 8px 24px -4px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.08); transition: all 0.25s ease; } #custom-audio-testimonial .avatar-wrap { width: 46px; height: 46px; border-radius: 50%; border: 2px solid rgba(255, 255, 255, 0.2); overflow: hidden; flex-shrink: 0; background-color: #262340; } #custom-audio-testimonial .avatar { width: 100%; height: 100%; object-fit: cover; object-position: center top; display: block; } #custom-audio-testimonial .play-btn { width: 42px; height: 42px; border-radius: 50%; background: #2e7d53; border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; flex-shrink: 0; transition: transform 0.15s ease, background-color 0.2s ease, box-shadow 0.2s ease; color: #ffffff; box-shadow: 0 4px 14px rgba(46, 125, 83, 0.45); } #custom-audio-testimonial .play-btn:hover { background-color: #389e69; transform: scale(1.05); } #custom-audio-testimonial .play-btn:active { transform: scale(0.95); } #custom-audio-testimonial .play-btn svg { width: 18px; height: 18px; fill: currentColor; } #custom-audio-testimonial .play-btn .icon-play { margin-left: 2px; } #custom-audio-testimonial .waveform-container { display: flex; align-items: center; gap: 2.5px; flex-grow: 1; height: 38px; cursor: pointer; padding: 0 2px; position: relative; user-select: none; } #custom-audio-testimonial .waveform-bar { flex: 1; background-color: rgba(255, 255, 255, 0.22); border-radius: 999px; transition: background-color 0.12s ease; min-height: 4px; } #custom-audio-testimonial .waveform-bar.played { background-color: #3ecf8e; box-shadow: 0 0 5px rgba(62, 207, 142, 0.5); } #custom-audio-testimonial .time-display { color: #ffffff; font-size: 14px; font-weight: 500; font-variant-numeric: tabular-nums; opacity: 0.95; flex-shrink: 0; min-width: 36px; text-align: center; font-family: inherit; } #custom-audio-testimonial .speed-btn { width: 38px; height: 38px; border-radius: 50%; background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.12); color: #ffffff; font-size: 12.5px; font-weight: 600; cursor: pointer; flex-shrink: 0; transition: all 0.2s ease; display: flex; align-items: center; justify-content: center; font-family: inherit; } #custom-audio-testimonial .speed-btn:hover { background-color: rgba(255, 255, 255, 0.16); color: #a3f7c9; } #custom-audio-testimonial .hidden-audio { display: none; } @media (max-width: 480px) { #custom-audio-testimonial .player-pill { gap: 8px; padding: 5px 10px 5px 5px; } #custom-audio-testimonial .avatar-wrap { width: 40px; height: 40px; } #custom-audio-testimonial .play-btn { width: 36px; height: 36px; } #custom-audio-testimonial .play-btn svg { width: 15px; height: 15px; } #custom-audio-testimonial .waveform-container { gap: 2px; height: 32px; } #custom-audio-testimonial .time-display { font-size: 12px; min-width: 30px; } #custom-audio-testimonial .speed-btn { width: 34px; height: 34px; font-size: 11px; } } </style> <div class="container"> <div class="player-pill"> <div class="avatar-wrap"> <img class="avatar" src="/files/pages/assets/2026-09-08/zrzut-ekranu-2026-05-28-o-163620_mtt2xp8y.png" alt="Kursant" loading="lazy" width="46" height="46"> </div> <button class="play-btn" aria-label="Odtwórz / Wstrzymaj nagranie" type="button"> <svg class="icon-play" viewBox="0 0 24 24"> <polygon points="6 3 20 12 6 21 6 3"></polygon> </svg> <svg class="icon-pause" viewBox="0 0 24 24" style="display: none;"> <rect x="5" y="4" width="4" height="16" rx="1"></rect> <rect x="15" y="4" width="4" height="16" rx="1"></rect> </svg> </button> <div class="waveform-container" role="slider" aria-label="Postęp nagrania" tabindex="0" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"> <div class="waveform-bar" style="height: 25%"></div> <div class="waveform-bar" style="height: 35%"></div> <div class="waveform-bar" style="height: 55%"></div> <div class="waveform-bar" style="height: 75%"></div> <div class="waveform-bar" style="height: 95%"></div> <div class="waveform-bar" style="height: 65%"></div> <div class="waveform-bar" style="height: 45%"></div> <div class="waveform-bar" style="height: 35%"></div> <div class="waveform-bar" style="height: 25%"></div> <div class="waveform-bar" style="height: 45%"></div> <div class="waveform-bar" style="height: 65%"></div> <div class="waveform-bar" style="height: 85%"></div> <div class="waveform-bar" style="height: 100%"></div> <div class="waveform-bar" style="height: 85%"></div> <div class="waveform-bar" style="height: 55%"></div> <div class="waveform-bar" style="height: 35%"></div> <div class="waveform-bar" style="height: 45%"></div> <div class="waveform-bar" style="height: 75%"></div> <div class="waveform-bar" style="height: 95%"></div> <div class="waveform-bar" style="height: 85%"></div> <div class="waveform-bar" style="height: 65%"></div> <div class="waveform-bar" style="height: 45%"></div> <div class="waveform-bar" style="height: 25%"></div> <div class="waveform-bar" style="height: 35%"></div> <div class="waveform-bar" style="height: 55%"></div> <div class="waveform-bar" style="height: 85%"></div> <div class="waveform-bar" style="height: 95%"></div> <div class="waveform-bar" style="height: 75%"></div> <div class="waveform-bar" style="height: 55%"></div> <div class="waveform-bar" style="height: 35%"></div> <div class="waveform-bar" style="height: 25%"></div> <div class="waveform-bar" style="height: 45%"></div> <div class="waveform-bar" style="height: 65%"></div> <div class="waveform-bar" style="height: 55%"></div> <div class="waveform-bar" style="height: 35%"></div> <div class="waveform-bar" style="height: 25%"></div> </div> <div class="time-display" aria-live="polite">0:00</div> <button class="speed-btn" aria-label="Zmień tempo odtwarzania" type="button">1x</button> </div> <audio class="hidden-audio" src="https://files.catbox.moe/dne6qp.mp3" preload="metadata"></audio> </div> <script> (function initCustomAudioPlayer() { const section = document.getElementById('custom-audio-testimonial'); if (!section) return; const audio = section.querySelector('.hidden-audio'); const playBtn = section.querySelector('.play-btn'); const iconPlay = section.querySelector('.icon-play'); const iconPause = section.querySelector('.icon-pause'); const waveform = section.querySelector('.waveform-container'); const bars = section.querySelectorAll('.waveform-bar'); const timeDisplay = section.querySelector('.time-display'); const speedBtn = section.querySelector('.speed-btn'); if (!audio || !playBtn || !waveform || !timeDisplay || !speedBtn) return; const formatTime = (seconds) => { if (isNaN(seconds) || seconds < 0) return "0:00"; const m = Math.floor(seconds / 60); const s = Math.floor(seconds % 60); return m + ":" + (s < 10 ? '0' : '') + s; }; let isPlaying = false; let animInterval = null; let simulatedSec = 0; const fallbackDuration = 17; const setPlayState = (playing) => { isPlaying = playing; if (playing) { iconPlay.style.display = 'none'; iconPause.style.display = 'block'; } else { iconPlay.style.display = 'block'; iconPause.style.display = 'none'; } }; const updateBars = (progress) => { const activeBarsCount = Math.floor(progress * bars.length); bars.forEach((bar, index) => { if (index < activeBarsCount) { bar.classList.add('played'); } else { bar.classList.remove('played'); } }); waveform.setAttribute('aria-valuenow', Math.round(progress * 100)); }; playBtn.addEventListener('click', (e) => { e.preventDefault(); const hasSrc = audio.getAttribute('src') && audio.getAttribute('src').trim() !== ''; if (!hasSrc) { setPlayState(!isPlaying); if (isPlaying) { clearInterval(animInterval); animInterval = setInterval(() => { simulatedSec += 0.25; if (simulatedSec > fallbackDuration) { simulatedSec = 0; setPlayState(false); clearInterval(animInterval); } timeDisplay.textContent = formatTime(simulatedSec); updateBars(simulatedSec / fallbackDuration); }, 250); } else { clearInterval(animInterval); } return; } if (audio.paused) { audio.play().then(() => setPlayState(true)).catch(() => setPlayState(true)); } else { audio.pause(); setPlayState(false); } }); audio.addEventListener('timeupdate', () => { const current = audio.currentTime; const duration = audio.duration || fallbackDuration; timeDisplay.textContent = formatTime(current); if (duration > 0) { updateBars(current / duration); } }); audio.addEventListener('loadedmetadata', () => { if (audio.duration && !isNaN(audio.duration)) { timeDisplay.textContent = formatTime(audio.duration); } }); waveform.addEventListener('click', (e) => { const rect = waveform.getBoundingClientRect(); const clickX = e.clientX - rect.left; const percentage = Math.max(0, Math.min(1, clickX / rect.width)); const hasSrc = audio.getAttribute('src') && audio.getAttribute('src').trim() !== ''; if (hasSrc && audio.duration) { audio.currentTime = percentage * audio.duration; } else { simulatedSec = percentage * fallbackDuration; timeDisplay.textContent = formatTime(simulatedSec); updateBars(percentage); } }); const speeds = [1, 1.5, 2]; let speedIndex = 0; speedBtn.addEventListener('click', (e) => { e.preventDefault(); speedIndex = (speedIndex + 1) % speeds.length; const speed = speeds[speedIndex]; audio.playbackRate = speed; speedBtn.textContent = speed + 'x'; }); audio.addEventListener('ended', () => { setPlayState(false); audio.currentTime = 0; bars.forEach(bar => bar.classList.remove('played')); }); })(); </script> </section>