This commit is contained in:
2026-09-03 21:57:48 +02:00
parent 496e8bdb27
commit 6d31656ce4
13 changed files with 1090 additions and 51 deletions
+11 -3
View File
@@ -25,6 +25,7 @@ SoftSPM is a Polish software house (**25+ years in IT**) that has served the wat
- **Offer card order is fixed**: aplikacje pod klucz, konsulting, szkolenia, webinaria.
- The **ecosystem is a headline theme**, not a footnote. `index.html#ekosystem` carries it with a diagram and three "loop" cards.
- **[wodociagowiec.pl](https://wodociagowiec.pl/)** - trade portal: articles, cases, regulatory analysis, reader Q&A. Reader questions become webinar topics.
- **[akademiawodociagowca.pl](https://akademiawodociagowca.pl/)** - the **course platform**, added 2026-09-03. Live webinars, on-demand courses, tests, certificates, one company account. `index.html#platforma` (section 05) is its marketing home; it also feeds the schedule tables via its public API. Section 05 carries the "knowledge propagation" argument: taught first-hand, knowledge stays in-house, questions feed the programme.
- **[gieldakomunalna.pl](https://gieldakomunalna.pl/)** - a **new, free marketplace** for the municipal sector (owner brief, 2026-08-31). Two jobs, and copy must reflect both: (1) users **list, buy, sell and swap free of charge** the stock **sitting idle in their warehouses** - project surplus, equipment replaced in upgrades, spares for discontinued machines; (2) it **brings together manufacturers and contractors** in a genuinely **niche, specialist** trade where there may be only a handful nationwide. It is **not** a tender/RFQ portal - do not describe it as "zapytania ofertowe", "miejsce wymiany ofert" or a procurement channel.
- Content language is **Polish**, plus an English mirror of the homepage.
@@ -36,7 +37,7 @@ SoftSPM is a Polish software house (**25+ years in IT**) that has served the wat
| Page | Sections |
|---|---|
| [index.html](index.html) | Hero · By the numbers (spec sheet) · Offer · Subject tracks · Upcoming dates · Course platform · **Ecosystem** · Testimonial · Newsletter |
| [index.html](index.html) | Hero · By the numbers (spec sheet) · Offer · Subject tracks · Upcoming dates · **Akademia Wodociągowca** (05) · Turnkey software · **Ecosystem** · Testimonial · Newsletter |
| [szkolenia.html](szkolenia.html) | Hero · Training format (spec sheet) · Course catalogue (tracks 01-08 with prices, plus an RFQ workshop) · Schedule · Annual webinar package · Signup form |
| [konsulting.html](konsulting.html) | Hero · 7 service areas (02.7 is `.cell-span` full-width) · 3-step process · Deliverables + projects (spec sheet) · Team · Testimonial · Contact form |
| [index-en.html](index-en.html) | English mirror of the homepage - same eight tracks, same ecosystem loop cards; keep it in step when the Polish homepage changes |
@@ -47,6 +48,8 @@ The **eight** subject tracks on `index.html#tematy`, in fixed order: 01 water la
A **light wireframe/blueprint** aesthetic: light technical ground, a single steel-blue accent, square corners with registration-cross ("+") marks, cards drawn as line art with no fill, photos duotoned in the accent. Numbers and parameters are presented as **technical spec sheets** — a table with a title block carrying a drawing code, revision, and sheet number.
Motion: entrance animations are opt-in via `data-reveal` + [assets/reveal.js](assets/reveal.js) (IntersectionObserver, counters in `.stats`). The hiding styles only apply once the script adds `.js-reveal` to `<html>`, so content stays visible without JavaScript, and everything is disabled under `prefers-reduced-motion`. `assets/img/akademia.svg` animates itself: line-drawing via `pathLength="1"` plus dashoffset, a flowing arrow dash and a pulsing LIVE dot, all declared inside the file so they run from an `<img>`, with the same reduced-motion guard.
Rule: **one accent color only.** No extra decorative colors; hover/pressed states come from the tonal 100–900 ramp.
| Role | Value |
@@ -90,9 +93,8 @@ The spec lists these as unconfirmed — do not present them as fact, and flag th
- Stats in "Doświadczenie w liczbach" (480+ trained, 24 webinars/year, 60+ advisory projects) — sample values.
- Testimonials and quotes — samples, to be replaced with real references.
- Course platform name and URL (the "Przejdź do platformy" links point at `#`).
- Course prices and the annual webinar package price.
- **Training dates are deliberately absent.** The schedule tables carry a `.tag-soon` "Wkrótce" / "Soon" chip instead of a date, and the CTA is "Powiadom mnie" / "Notify me" — the site collects interest rather than taking bookings. Do not reintroduce concrete dates until real ones are confirmed; if they are, restore the `Data`/`Date` column heading and the "Zapisz się" CTAs together with them.
- **The static schedule rows are the fallback, not the plan.** The `.tag-soon` "Wkrótce" / "Soon" rows in `index.html` (8) and `szkolenia.html` (11) show only when the Akademia API returns nothing or is unreachable — see **Schedule** below. Keep them plausible and on-topic; they are what visitors see when the platform has no open dates.
- Team member names — the consulting page lists teams, not people, pending real bios.
- Nothing else. The forms are live — see **Forms** below.
@@ -106,6 +108,12 @@ Known real details: SoftSPM · Ławy, 97-400 Bełchatów · info@softspm.com.
The folder also holds AI-generated photos from the previous direction (SCADA, control rooms, dashboards, blog imagery). **The built pages no longer reference any of them** — they belong to the dropped pump/monitoring theme. [images/README.md](images/README.md) is inherited from an even earlier "AquaNexis" branding and is stale; verify a file exists before referencing it.
## Schedule
The `04 · Harmonogram` tables in `index.html` and `szkolenia.html` are filled at runtime by [assets/schedule.js](assets/schedule.js) from [api/schedule.php](api/schedule.php), which proxies the **Akademia Wodociągowca** public API v1 (`/webinars` + `/courses`, `X-API-Key` header, docs at `akademiawodociagowca/docs/api_public_v1.html`). Key stays server-side, response cached 5 min. Setup is in [api/README.md](api/README.md).
When live items arrive the script swaps the rows, the `Termin` → `Data` column heading, the section heading, the intro and the sheet note (`data-schedule-*` hooks mark all of them). When the API is empty, unreachable or unconfigured it does nothing, so the static rows in the HTML stay — never leave that table without plausible static rows.
## Forms
All four forms — newsletter (`index.html`), contact (`index-en.html`), training signup (`szkolenia.html`), consulting (`konsulting.html`) — post JSON to [api/send.php](api/send.php), which relays to `api.softspm.pl/send_email_api.php` ([doc/manual_send_email.html](doc/manual_send_email.html)). **Never call that API from the browser**: it authenticates with a Bearer key, and a key in client-side JS is a public key. The proxy keeps it server-side and pins the recipient to server config, so the endpoint cannot be used to mail arbitrary addresses.
+63 -2
View File
@@ -43,6 +43,30 @@ return [
];
```
### Akademia Wodociągowca (harmonogram w sekcji 04)
Tabela terminów na stronie głównej i na `szkolenia.html` pobiera webinary
i kursy z publicznego API Akademii. Dopisz klucz wygenerowany w panelu
Akademii (Ustawienia -> Klucze API - pokazywany tylko raz):
```php
// wariant ze stałymi
define('SOFTSPM_AKADEMIA_API_KEY', 'tutaj_klucz_z_panelu_akademii');
// wariant z tablicą
'softspm_akademia_api_key' => 'tutaj_klucz_z_panelu_akademii',
```
Adres API domyślnie to `https://akademiawodociagowca.pl/api/public/v1`.
Zmieniasz go tylko wtedy, gdy instancja stoi pod innym adresem:
```php
define('SOFTSPM_AKADEMIA_API_URL', 'https://inna-domena/api/public/v1');
```
Bez tego klucza nic się nie psuje: `api/schedule.php` zwraca
`source: "error"`, a w tabeli zostają statyczne wiersze wpisane w HTML.
### Wpisy opcjonalne
```php
@@ -145,7 +169,44 @@ szablon endpointu nie zamienia `\n` na `<br>`. `send_email_api.php` jest wasz,
więc najczystszą poprawką jest tam jedno `nl2br()` na sanityzowanej treści -
nic po stronie tej strony nie wymaga wtedy zmian.
## 5. Zabezpieczenia przed botami
## 5. Harmonogram z Akademii Wodociągowca
[schedule.php](schedule.php) pobiera `GET /webinars` i `GET /courses` z API
Akademii (dokumentacja: `akademiawodociagowca/docs/api_public_v1.html`), scala
je w jedną listę i oddaje stronie jako JSON. Klucz `X-API-Key` zostaje na
serwerze - punkt 9 dokumentacji Akademii wprost tego wymaga.
- **Cache:** odpowiedź leży w `api/storage/cache/schedule.json` przez
`akademia_cache_ttl` (domyślnie 300 s, zgodnie z zaleceniem 1-5 minut).
Pusty wynik i błąd trzymane są krócej, maksymalnie minutę.
- **Kolejność:** webinary z terminem rosnąco, potem kursy na żądanie.
- **Linki:** akceptujemy wyłącznie adresy prowadzące do domeny Akademii -
gdyby API zwróciło cokolwiek innego, link jest pomijany.
- **Limit:** `?limit=` (1-40). Strona główna prosi o 8 pozycji,
`szkolenia.html` o 12.
Odpowiedź ma pole `source`:
| `source` | Znaczenie | Co widzi użytkownik |
|---|---|---|
| `api` | Są pozycje | Tabela, nagłówki i notatka podmienione na dane z Akademii; kolumna wraca z „Termin" na „Data" |
| `empty` | API odpowiedziało pustą tablicą | Statyczne wiersze z HTML-a zostają nietknięte |
| `error` | Brak klucza albo API nie odpowiada | Jak wyżej |
Statyczne wiersze to pełnoprawny plan awaryjny: działają bez JavaScriptu,
bez klucza i przy niedostępnym API. Strona nigdy nie pokazuje pustej tabeli.
Są w `index.html` (8 pozycji) i `szkolenia.html` (11 pozycji) - jeśli chcesz
zmienić te tematy zastępcze, edytujesz je wprost w HTML.
Test:
```bash
curl -s "https://softspm.com/api/schedule.php?limit=5" | head -c 400
```
Oczekiwane: `{"ok":true,"source":"api",...}` z tablicą `items`.
## 6. Zabezpieczenia przed botami
Suwak sam z siebie niczego nie gwarantuje - jest elementem interfejsu, a nie
kryptografią. Blokady, które faktycznie odsiewają automaty, działają na
@@ -166,7 +227,7 @@ Jeśli mimo to zacznie przechodzić spam, następnym krokiem jest Cloudflare
Turnstile albo hCaptcha - obie działają na tej samej zasadzie co obecny token,
więc podmiana dotknie tylko `send.php` i `assets/forms.js`.
## 6. Test po wdrożeniu
## 7. Test po wdrożeniu
```bash
# 1. Token - powinien zwrócić {"ok":true,"token":"t.szkolenia...."}
+23
View File
@@ -26,6 +26,14 @@ $config = [
// Dowolny losowy ciąg, min. 32 znaki. Zmiana unieważnia otwarte formularze.
'token_secret' => '',
// --- Akademia Wodociągowca: publiczne API v1 (webinary i kursy) --------
// Dokumentacja: akademiawodociagowca/docs/api_public_v1.html
// Klucz z panelu Akademii: Ustawienia -> Klucze API. Pokazywany raz.
'akademia_api_url' => 'https://akademiawodociagowca.pl/api/public/v1',
'akademia_api_key' => '',
// Bufor odpowiedzi w sekundach. Dokumentacja Akademii zaleca 1-5 minut.
'akademia_cache_ttl' => 300,
// --- Adresat zgłoszeń --------------------------------------------------
// Ustawiany wyłącznie po stronie serwera. Nigdy nie pochodzi z formularza -
// inaczej endpoint stałby się otwartym przekaźnikiem spamu.
@@ -70,6 +78,8 @@ $config = [
$fromEnv = [
'api_key' => getenv('SOFTSPM_MAIL_API_KEY'),
'token_secret' => getenv('SOFTSPM_FORM_SECRET'),
'akademia_api_key' => getenv('SOFTSPM_AKADEMIA_API_KEY'),
'akademia_api_url' => getenv('SOFTSPM_AKADEMIA_API_URL'),
];
foreach ($fromEnv as $key => $value) {
if (is_string($value) && $value !== '') {
@@ -133,6 +143,19 @@ if (is_file($sharedPath) && is_readable($sharedPath)) {
}
}
// Klucz do publicznego API Akademii Wodociągowca.
if ($config['akademia_api_key'] === '') {
$value = $pick('SOFTSPM_AKADEMIA_API_KEY', 'softspm_akademia_api_key');
if (is_string($value)) {
$config['akademia_api_key'] = $value;
}
}
// Adres API Akademii - tylko gdy instancja stoi pod innym adresem.
$value = $pick('SOFTSPM_AKADEMIA_API_URL', 'softspm_akademia_api_url');
if (is_string($value)) {
$config['akademia_api_url'] = $value;
}
// Adresat zgłoszeń - jeden dla wszystkich formularzy.
$value = $pick('SOFTSPM_FORM_RECIPIENT', 'softspm_form_recipient');
if (is_string($value)) {
+266
View File
@@ -0,0 +1,266 @@
<?php
declare(strict_types=1);
/**
* SoftSPM - harmonogram szkoleń i webinariów.
*
* Pośrednik do publicznego API Akademii Wodociągowca
* (dokumentacja: akademiawodociagowca/docs/api_public_v1.html).
* Klucz X-API-Key zostaje na serwerze - dokumentacja Akademii wprost tego
* wymaga (punkt 9: "Trzymaj klucz po stronie serwera").
*
* GET api/schedule.php[?limit=8]
* -> {"ok":true,"source":"api|empty|error","items":[...]}
*
* source = "api" - są pozycje, strona podmienia tabelę
* source = "empty" - API odpowiedziało pustą tablicą
* source = "error" - API niedostępne albo brak klucza
* W obu ostatnich przypadkach strona zostawia statyczne wiersze z HTML-a.
*/
$config = require __DIR__ . '/config.php';
header('Content-Type: application/json; charset=utf-8');
header('X-Content-Type-Options: nosniff');
function sched_respond(int $status, array $payload): void
{
http_response_code($status);
echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
exit;
}
function sched_storage(array $config): string
{
$root = rtrim((string) $config['storage_dir'], '/\\');
$dir = $root . '/cache';
if (!is_dir($dir)) {
@mkdir($dir, 0770, true);
}
$guard = $root . '/.htaccess';
if (!is_file($guard)) {
@file_put_contents($guard, "Require all denied\n<IfModule !mod_authz_core.c>\nDeny from all\n</IfModule>\n");
}
return $dir;
}
/** @return array{status:int, body:string} */
function sched_get(string $url, string $key, int $timeout): array
{
$headers = ['Accept: application/json', 'X-API-Key: ' . $key];
if (function_exists('curl_init')) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 6,
CURLOPT_TIMEOUT => $timeout,
]);
$body = curl_exec($ch);
$status = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
return ['status' => $status, 'body' => is_string($body) ? $body : ''];
}
$context = stream_context_create([
'http' => [
'method' => 'GET',
'header' => implode("\r\n", $headers),
'timeout' => $timeout,
'ignore_errors' => true,
],
]);
$body = @file_get_contents($url, false, $context);
$status = 0;
foreach ($http_response_header ?? [] as $line) {
if (preg_match('#^HTTP/\S+\s+(\d{3})#', $line, $m) === 1) {
$status = (int) $m[1];
}
}
return ['status' => $status, 'body' => is_string($body) ? $body : ''];
}
function sched_text($value, int $max = 300): string
{
if (!is_string($value)) {
return '';
}
$value = preg_replace('/[\x00-\x1F\x7F]/u', ' ', $value) ?? '';
$value = trim(preg_replace('/\s{2,}/u', ' ', $value) ?? '');
return mb_substr($value, 0, $max);
}
/** Link trafia na stronę, więc musi prowadzić do Akademii, nie gdziekolwiek. */
function sched_url($value, array $allowedHosts): ?string
{
if (!is_string($value) || $value === '') {
return null;
}
$parts = parse_url($value);
if (!is_array($parts) || !isset($parts['scheme'], $parts['host'])) {
return null;
}
if (!in_array(strtolower($parts['scheme']), ['http', 'https'], true)) {
return null;
}
$host = strtolower($parts['host']);
foreach ($allowedHosts as $allowed) {
$allowed = strtolower((string) $allowed);
if ($allowed === '') {
continue;
}
if ($host === $allowed || substr($host, -strlen('.' . $allowed)) === '.' . $allowed) {
return $value;
}
}
return null;
}
// ---------------------------------------------------------------------------
if (strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'GET') {
sched_respond(405, ['ok' => false, 'source' => 'error', 'error' => 'method_not_allowed', 'items' => []]);
}
$limit = isset($_GET['limit']) ? (int) $_GET['limit'] : 12;
$limit = max(1, min(40, $limit));
$base = rtrim((string) $config['akademia_api_url'], '/');
$key = (string) $config['akademia_api_key'];
if ($base === '' || $key === '') {
sched_respond(200, ['ok' => true, 'source' => 'error', 'error' => 'not_configured', 'items' => []]);
}
$linkHosts = [];
$hostFromBase = parse_url($base, PHP_URL_HOST);
if (is_string($hostFromBase) && $hostFromBase !== '') {
$linkHosts[] = preg_replace('/^www\./', '', $hostFromBase);
}
// --- Cache ------------------------------------------------------------------
// Dokumentacja Akademii zaleca buforowanie 1-5 minut; dane zmieniają się rzadko.
$ttl = (int) $config['akademia_cache_ttl'];
$dir = sched_storage($config);
$cache = $dir . '/schedule.json';
if ($ttl > 0 && is_file($cache) && (time() - (int) @filemtime($cache)) < $ttl) {
$raw = @file_get_contents($cache);
if (is_string($raw) && $raw !== '') {
$cached = json_decode($raw, true);
if (is_array($cached) && isset($cached['items']) && is_array($cached['items'])) {
$cached['items'] = array_slice($cached['items'], 0, $limit);
$cached['cached'] = true;
sched_respond(200, $cached);
}
}
}
// --- Pobranie danych --------------------------------------------------------
$items = [];
$failed = 0;
$webinars = sched_get($base . '/webinars', $key, (int) $config['timeout']);
if ($webinars['status'] === 200) {
$decoded = json_decode($webinars['body'], true);
if (is_array($decoded)) {
foreach ($decoded as $row) {
if (!is_array($row)) {
continue;
}
$title = sched_text($row['title'] ?? '', 160);
$start = sched_text($row['startsAt'] ?? '', 40);
if ($title === '' || $start === '') {
continue;
}
$capacity = isset($row['capacity']) && is_numeric($row['capacity']) ? (int) $row['capacity'] : null;
$registered = isset($row['registeredCount']) && is_numeric($row['registeredCount']) ? (int) $row['registeredCount'] : 0;
$items[] = [
'kind' => 'webinar',
'title' => $title,
'startsAt' => $start,
'endsAt' => sched_text($row['endsAt'] ?? '', 40),
'timezone' => sched_text($row['timezone'] ?? '', 64),
'url' => sched_url($row['registrationUrl'] ?? null, $linkHosts),
'open' => !isset($row['registrationOpen']) || (bool) $row['registrationOpen'],
'soldOut' => $capacity !== null && $registered >= $capacity,
'seatsLeft' => $capacity !== null ? max(0, $capacity - $registered) : null,
];
}
}
} else {
$failed++;
}
$courses = sched_get($base . '/courses', $key, (int) $config['timeout']);
if ($courses['status'] === 200) {
$decoded = json_decode($courses['body'], true);
if (is_array($decoded)) {
foreach ($decoded as $row) {
if (!is_array($row)) {
continue;
}
$title = sched_text($row['title'] ?? '', 160);
if ($title === '') {
continue;
}
$level = strtoupper(sched_text($row['level'] ?? '', 20));
$items[] = [
'kind' => 'course',
'title' => $title,
'subtitle' => sched_text($row['subtitle'] ?? '', 160),
'category' => sched_text($row['category'] ?? '', 80),
'level' => in_array($level, ['BASIC', 'INTERMEDIATE', 'ADVANCED'], true) ? $level : null,
'minutes' => isset($row['durationMinutes']) && is_numeric($row['durationMinutes']) ? (int) $row['durationMinutes'] : null,
'free' => !empty($row['free']),
'url' => sched_url($row['courseUrl'] ?? null, $linkHosts),
];
}
}
} else {
$failed++;
}
// Webinary z terminem idą pierwsze i rosnąco, kursy na żądanie na końcu.
usort($items, static function (array $a, array $b): int {
$aDated = $a['kind'] === 'webinar';
$bDated = $b['kind'] === 'webinar';
if ($aDated !== $bDated) {
return $aDated ? -1 : 1;
}
if ($aDated) {
return strcmp((string) $a['startsAt'], (string) $b['startsAt']);
}
return 0;
});
$source = 'api';
if ($items === []) {
$source = $failed > 0 ? 'error' : 'empty';
}
$payload = [
'ok' => true,
'source' => $source,
'fetchedAt' => gmdate('c'),
'items' => $items,
];
if ($ttl > 0) {
@file_put_contents($cache, json_encode($payload, JSON_UNESCAPED_UNICODE), LOCK_EX);
// Pusty wynik i błąd trzymamy krócej, żeby świeże pozycje pojawiły się szybko.
if ($source !== 'api') {
$short = min($ttl, 60);
@touch($cache, time() - ($ttl - $short));
}
}
$payload['items'] = array_slice($payload['items'], 0, $limit);
sched_respond(200, $payload);
+130
View File
@@ -0,0 +1,130 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 344" role="img" aria-labelledby="t-akad">
<title id="t-akad">Akademia Wodociągowca - obieg kompetencji: praktyka wdrożeniowa i doradcza zasila platformę kursową, a wyszkolone zespoły wracają z pytaniami z rynku</title>
<defs>
<pattern id="akad-grid" width="20" height="20" patternUnits="userSpaceOnUse">
<path d="M20 0H0v20" fill="none" stroke="#5980a6" stroke-opacity=".13" stroke-width="1"/>
</pattern>
<marker id="akad-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M0 0 10 5 0 10z" fill="#5980a6"/>
</marker>
<style><![CDATA[
/* Rysunek powstaje jak na ploterze: linie same się kreślą.
pathLength="1" sprawia, że długość ścieżki nie ma znaczenia. */
.draw { stroke-dasharray: 1; stroke-dashoffset: 1; animation: akad-draw 1.1s ease-out forwards; }
.d1 { animation-delay: .05s } .d2 { animation-delay: .3s }
.d3 { animation-delay: .55s } .d4 { animation-delay: .8s }
@keyframes akad-draw { to { stroke-dashoffset: 0 } }
.fade { opacity: 0; animation: akad-fade .5s ease-out forwards; }
.f1 { animation-delay: .5s } .f2 { animation-delay: .75s }
.f3 { animation-delay: 1s } .f4 { animation-delay: 1.25s }
@keyframes akad-fade { to { opacity: 1 } }
/* Przepływ wiedzy - kreska sunie w stronę odbiorcy. */
.flow { stroke-dasharray: 7 7; animation: akad-flow 1.4s linear infinite; }
@keyframes akad-flow { to { stroke-dashoffset: -14 } }
.pulse { animation: akad-pulse 2s ease-in-out infinite; }
@keyframes akad-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .3 } }
@media (prefers-reduced-motion: reduce) {
.draw, .fade, .flow, .pulse {
animation: none; stroke-dashoffset: 0; stroke-dasharray: none; opacity: 1;
}
}
]]></style>
</defs>
<rect width="720" height="344" fill="#eaeef2"/>
<rect width="720" height="344" fill="url(#akad-grid)"/>
<!-- ── Źródła wiedzy ─────────────────────────────────────────────────── -->
<g stroke="#416180" stroke-width="1.5" fill="#f2f2f3">
<rect class="draw d1" pathLength="1" x="40" y="66" width="150" height="44"/>
<rect class="draw d1" pathLength="1" x="40" y="124" width="150" height="44"/>
<rect class="draw d1" pathLength="1" x="40" y="182" width="150" height="44"/>
</g>
<g fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif" font-size="10.5"
text-anchor="middle" class="fade f1">
<text x="115" y="92" textLength="120" lengthAdjust="spacingAndGlyphs">WDROŻENIA U KLIENTÓW</text>
<text x="115" y="150" textLength="112" lengthAdjust="spacingAndGlyphs">PROJEKTY DORADCZE</text>
<text x="115" y="208" textLength="120" lengthAdjust="spacingAndGlyphs">PYTANIA CZYTELNIKÓW</text>
</g>
<text x="40" y="46" fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
font-size="11" textLength="90" lengthAdjust="spacingAndGlyphs" opacity=".72" class="fade f1">CO ROBIMY</text>
<!-- zbiorczy przepływ do Akademii -->
<g stroke="#5980a6" stroke-width="1.8" fill="none">
<path class="draw d2" pathLength="1" d="M190 88 H228 V146"/>
<path class="draw d2" pathLength="1" d="M190 146 H228"/>
<path class="draw d2" pathLength="1" d="M190 204 H228 V146"/>
<path class="flow" d="M228 146 H266" marker-end="url(#akad-arrow)"/>
</g>
<!-- ── Akademia Wodociągowca ─────────────────────────────────────────── -->
<rect class="draw d3" pathLength="1" x="270" y="96" width="200" height="150"
fill="#5980a6" stroke="#416180" stroke-width="1.5"/>
<rect x="270" y="96" width="200" height="34" fill="#1d2d3d" class="fade f2"/>
<text x="370" y="119" fill="#f2f2f3" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
font-size="14" font-weight="600" text-anchor="middle"
textLength="164" lengthAdjust="spacingAndGlyphs" class="fade f2">AKADEMIA WODOCIĄGOWCA</text>
<!-- znacznik transmisji na żywo -->
<g class="fade f3">
<rect x="286" y="144" width="46" height="15" fill="#1d2d3d"/>
<circle cx="295" cy="151.5" r="3" fill="#f2f2f3" class="pulse"/>
<text x="303" y="155" fill="#f2f2f3" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
font-size="9.5" textLength="24" lengthAdjust="spacingAndGlyphs">LIVE</text>
</g>
<!-- ekran kursu -->
<g stroke="#f2f2f3" stroke-width="1.6" fill="none" class="fade f3">
<rect x="286" y="170" width="52" height="36"/>
<path d="M352 178h102M352 188h74M352 198h102"/>
</g>
<path d="m305 182 12 6-12 6z" fill="#f2f2f3" class="fade f3"/>
<text x="370" y="230" fill="#f2f2f3" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
font-size="10" text-anchor="middle" opacity=".92"
textLength="176" lengthAdjust="spacingAndGlyphs" class="fade f3">WEBINARIA · KURSY · CERTYFIKATY</text>
<!-- ── Zespoły w przedsiębiorstwach ──────────────────────────────────── -->
<path class="flow" d="M470 171 H516" stroke="#5980a6" stroke-width="1.8" fill="none" marker-end="url(#akad-arrow)"/>
<text x="493" y="160" fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
font-size="9.5" text-anchor="middle" textLength="44" lengthAdjust="spacingAndGlyphs" class="fade f3">WIEDZA</text>
<rect class="draw d4" pathLength="1" x="520" y="96" width="160" height="150" fill="#f2f2f3" stroke="#416180" stroke-width="1.5"/>
<rect x="520" y="96" width="160" height="30" fill="#5980a6" fill-opacity=".2" class="fade f4"/>
<text x="600" y="116" fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
font-size="11" font-weight="600" text-anchor="middle"
textLength="132" lengthAdjust="spacingAndGlyphs" class="fade f4">ZESPOŁY W FIRMACH</text>
<g stroke="#416180" stroke-width="1.5" fill="none" class="fade f4">
<circle cx="558" cy="152" r="11"/><path d="M542 182v-12a16 16 0 0 1 32 0v12"/>
<circle cx="600" cy="152" r="11"/><path d="M584 182v-12a16 16 0 0 1 32 0v12"/>
<circle cx="642" cy="152" r="11"/><path d="M626 182v-12a16 16 0 0 1 32 0v12"/>
</g>
<g fill="#5980a6" fill-opacity=".24" class="fade f4">
<circle cx="558" cy="152" r="11"/><circle cx="600" cy="152" r="11"/><circle cx="642" cy="152" r="11"/>
</g>
<!-- certyfikat -->
<g class="fade f4">
<rect x="556" y="200" width="88" height="26" fill="#f2f2f3" stroke="#416180" stroke-width="1.4"/>
<path d="m566 213 5 5 9-11" stroke="#5980a6" stroke-width="2" fill="none"/>
<text x="608" y="217" fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
font-size="9.5" text-anchor="middle" textLength="46" lengthAdjust="spacingAndGlyphs">CERTYFIKAT</text>
</g>
<!-- ── Pętla zwrotna: rynek podpowiada tematy ────────────────────────── -->
<path class="draw d4" pathLength="1" d="M600 246 V310 H115 V232" stroke="#5980a6" stroke-width="1.8"
fill="none" marker-end="url(#akad-arrow)"/>
<rect x="276" y="298" width="172" height="22" fill="#eaeef2" class="fade f4"/>
<text x="362" y="314" fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
font-size="10.5" text-anchor="middle" textLength="158" lengthAdjust="spacingAndGlyphs" class="fade f4">PYTANIA WRACAJĄ DO PROGRAMU</text>
<!-- ── krzyżyki rejestracyjne ────────────────────────────────────────── -->
<g stroke="#1d1f20" stroke-width="1" opacity=".45">
<path d="M263 89v13M257 95h13"/>
<path d="M477 89v13M471 95h13"/>
<path d="M263 241v13M257 247h13"/>
<path d="M477 241v13M471 247h13"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

+130
View File
@@ -0,0 +1,130 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 344" role="img" aria-labelledby="t-akad">
<title id="t-akad">Akademia Wodociagowca - the competence loop: our delivery and advisory practice feeds the course platform, and trained teams come back with questions from the market</title>
<defs>
<pattern id="akad-grid" width="20" height="20" patternUnits="userSpaceOnUse">
<path d="M20 0H0v20" fill="none" stroke="#5980a6" stroke-opacity=".13" stroke-width="1"/>
</pattern>
<marker id="akad-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M0 0 10 5 0 10z" fill="#5980a6"/>
</marker>
<style><![CDATA[
/* Rysunek powstaje jak na ploterze: linie same sie kresla.
pathLength="1" sprawia, ze dlugosc sciezki nie ma znaczenia. */
.draw { stroke-dasharray: 1; stroke-dashoffset: 1; animation: akad-draw 1.1s ease-out forwards; }
.d1 { animation-delay: .05s } .d2 { animation-delay: .3s }
.d3 { animation-delay: .55s } .d4 { animation-delay: .8s }
@keyframes akad-draw { to { stroke-dashoffset: 0 } }
.fade { opacity: 0; animation: akad-fade .5s ease-out forwards; }
.f1 { animation-delay: .5s } .f2 { animation-delay: .75s }
.f3 { animation-delay: 1s } .f4 { animation-delay: 1.25s }
@keyframes akad-fade { to { opacity: 1 } }
/* Przeplyw wiedzy - kreska sunie w strone odbiorcy. */
.flow { stroke-dasharray: 7 7; animation: akad-flow 1.4s linear infinite; }
@keyframes akad-flow { to { stroke-dashoffset: -14 } }
.pulse { animation: akad-pulse 2s ease-in-out infinite; }
@keyframes akad-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .3 } }
@media (prefers-reduced-motion: reduce) {
.draw, .fade, .flow, .pulse {
animation: none; stroke-dashoffset: 0; stroke-dasharray: none; opacity: 1;
}
}
]]></style>
</defs>
<rect width="720" height="344" fill="#eaeef2"/>
<rect width="720" height="344" fill="url(#akad-grid)"/>
<!-- ── Zrodla wiedzy ─────────────────────────────────────────────────── -->
<g stroke="#416180" stroke-width="1.5" fill="#f2f2f3">
<rect class="draw d1" pathLength="1" x="40" y="66" width="150" height="44"/>
<rect class="draw d1" pathLength="1" x="40" y="124" width="150" height="44"/>
<rect class="draw d1" pathLength="1" x="40" y="182" width="150" height="44"/>
</g>
<g fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif" font-size="10.5"
text-anchor="middle" class="fade f1">
<text x="115" y="92" textLength="120" lengthAdjust="spacingAndGlyphs">CLIENT ROLLOUTS</text>
<text x="115" y="150" textLength="112" lengthAdjust="spacingAndGlyphs">ADVISORY PROJECTS</text>
<text x="115" y="208" textLength="120" lengthAdjust="spacingAndGlyphs">READER QUESTIONS</text>
</g>
<text x="40" y="46" fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
font-size="11" textLength="90" lengthAdjust="spacingAndGlyphs" opacity=".72" class="fade f1">WHAT WE DO</text>
<!-- zbiorczy przepływ do Akademii -->
<g stroke="#5980a6" stroke-width="1.8" fill="none">
<path class="draw d2" pathLength="1" d="M190 88 H228 V146"/>
<path class="draw d2" pathLength="1" d="M190 146 H228"/>
<path class="draw d2" pathLength="1" d="M190 204 H228 V146"/>
<path class="flow" d="M228 146 H266" marker-end="url(#akad-arrow)"/>
</g>
<!-- ── Akademia Wodociągowca ─────────────────────────────────────────── -->
<rect class="draw d3" pathLength="1" x="270" y="96" width="200" height="150"
fill="#5980a6" stroke="#416180" stroke-width="1.5"/>
<rect x="270" y="96" width="200" height="34" fill="#1d2d3d" class="fade f2"/>
<text x="370" y="119" fill="#f2f2f3" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
font-size="14" font-weight="600" text-anchor="middle"
textLength="164" lengthAdjust="spacingAndGlyphs" class="fade f2">AKADEMIA WODOCIAGOWCA</text>
<!-- znacznik transmisji na żywo -->
<g class="fade f3">
<rect x="286" y="144" width="46" height="15" fill="#1d2d3d"/>
<circle cx="295" cy="151.5" r="3" fill="#f2f2f3" class="pulse"/>
<text x="303" y="155" fill="#f2f2f3" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
font-size="9.5" textLength="24" lengthAdjust="spacingAndGlyphs">LIVE</text>
</g>
<!-- ekran kursu -->
<g stroke="#f2f2f3" stroke-width="1.6" fill="none" class="fade f3">
<rect x="286" y="170" width="52" height="36"/>
<path d="M352 178h102M352 188h74M352 198h102"/>
</g>
<path d="m305 182 12 6-12 6z" fill="#f2f2f3" class="fade f3"/>
<text x="370" y="230" fill="#f2f2f3" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
font-size="10" text-anchor="middle" opacity=".92"
textLength="176" lengthAdjust="spacingAndGlyphs" class="fade f3">WEBINARS · COURSES · CERTIFICATES</text>
<!-- ── Zespoly w przedsiebiorstwach ──────────────────────────────────── -->
<path class="flow" d="M470 171 H516" stroke="#5980a6" stroke-width="1.8" fill="none" marker-end="url(#akad-arrow)"/>
<text x="493" y="160" fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
font-size="9.5" text-anchor="middle" textLength="38" lengthAdjust="spacingAndGlyphs" class="fade f3">KNOW-HOW</text>
<rect class="draw d4" pathLength="1" x="520" y="96" width="160" height="150" fill="#f2f2f3" stroke="#416180" stroke-width="1.5"/>
<rect x="520" y="96" width="160" height="30" fill="#5980a6" fill-opacity=".2" class="fade f4"/>
<text x="600" y="116" fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
font-size="11" font-weight="600" text-anchor="middle"
textLength="132" lengthAdjust="spacingAndGlyphs" class="fade f4">TEAMS AT UTILITIES</text>
<g stroke="#416180" stroke-width="1.5" fill="none" class="fade f4">
<circle cx="558" cy="152" r="11"/><path d="M542 182v-12a16 16 0 0 1 32 0v12"/>
<circle cx="600" cy="152" r="11"/><path d="M584 182v-12a16 16 0 0 1 32 0v12"/>
<circle cx="642" cy="152" r="11"/><path d="M626 182v-12a16 16 0 0 1 32 0v12"/>
</g>
<g fill="#5980a6" fill-opacity=".24" class="fade f4">
<circle cx="558" cy="152" r="11"/><circle cx="600" cy="152" r="11"/><circle cx="642" cy="152" r="11"/>
</g>
<!-- certyfikat -->
<g class="fade f4">
<rect x="556" y="200" width="88" height="26" fill="#f2f2f3" stroke="#416180" stroke-width="1.4"/>
<path d="m566 213 5 5 9-11" stroke="#5980a6" stroke-width="2" fill="none"/>
<text x="608" y="217" fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
font-size="9.5" text-anchor="middle" textLength="46" lengthAdjust="spacingAndGlyphs">CERTIFICATE</text>
</g>
<!-- ── Petla zwrotna: rynek podpowiada tematy ────────────────────────── -->
<path class="draw d4" pathLength="1" d="M600 246 V310 H115 V232" stroke="#5980a6" stroke-width="1.8"
fill="none" marker-end="url(#akad-arrow)"/>
<rect x="276" y="298" width="172" height="22" fill="#eaeef2" class="fade f4"/>
<text x="362" y="314" fill="#416180" font-family="'Barlow Condensed','Arial Narrow',sans-serif"
font-size="10.5" text-anchor="middle" textLength="158" lengthAdjust="spacingAndGlyphs" class="fade f4">QUESTIONS FEED THE PROGRAMME</text>
<!-- ── krzyzyki rejestracyjne ────────────────────────────────────────── -->
<g stroke="#1d1f20" stroke-width="1" opacity=".45">
<path d="M263 89v13M257 95h13"/>
<path d="M477 89v13M471 95h13"/>
<path d="M263 241v13M257 247h13"/>
<path d="M477 241v13M471 247h13"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

+55
View File
@@ -0,0 +1,55 @@
/* ============================================================================
SoftSPM - wejście bloków i liczniki
Delikatne pojawianie się sekcji przy przewijaniu oraz naliczanie liczb
w pasku Akademii. Wszystko wyłącza się przy prefers-reduced-motion,
a bez JavaScriptu treść i tak jest widoczna - styl startowy nakładamy
dopiero po dodaniu klasy js-reveal.
========================================================================= */
(function () {
'use strict';
var supported = 'IntersectionObserver' in window && 'classList' in document.documentElement;
var reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!supported || reduced) {
return;
}
document.documentElement.classList.add('js-reveal');
function countUp(node) {
var target = parseInt(node.getAttribute('data-count'), 10);
if (isNaN(target)) { return; }
var suffix = node.getAttribute('data-suffix') || '';
var duration = 900;
var started = null;
function step(now) {
if (started === null) { started = now; }
var progress = Math.min(1, (now - started) / duration);
// wyhamowanie na końcu - liczba dobija do wartości, nie przeskakuje
var eased = 1 - Math.pow(1 - progress, 3);
node.textContent = Math.round(target * eased) + suffix;
if (progress < 1) { requestAnimationFrame(step); }
}
node.textContent = '0' + suffix;
requestAnimationFrame(step);
}
var observer = new IntersectionObserver(function (entries) {
for (var i = 0; i < entries.length; i++) {
var entry = entries[i];
if (!entry.isIntersecting) { continue; }
var node = entry.target;
node.classList.add('is-in');
observer.unobserve(node);
var counters = node.querySelectorAll('[data-count]');
for (var c = 0; c < counters.length; c++) { countUp(counters[c]); }
}
}, { rootMargin: '0px 0px -12% 0px', threshold: 0.12 });
var blocks = document.querySelectorAll('[data-reveal]');
for (var i = 0; i < blocks.length; i++) { observer.observe(blocks[i]); }
})();
+208
View File
@@ -0,0 +1,208 @@
/* ============================================================================
SoftSPM - harmonogram z Akademii Wodociągowca
Podmienia wiersze tabeli terminów danymi z api/schedule.php.
Gdy API nie odpowiada albo nie ma pozycji, statyczne wiersze z HTML-a
zostają nietknięte - strona nigdy nie pokazuje pustej tabeli.
========================================================================= */
(function () {
'use strict';
var DICT = {
pl: {
dateHead: 'Data',
webinar: 'Webinar',
training: 'Szkolenie',
online: 'Online',
elearning: 'Online · e-learning',
onDemand: 'Na żądanie',
signUp: 'Zapisz się →',
viewCourse: 'Zobacz kurs →',
soldOut: 'Brak miejsc',
soon: 'Zapisy wkrótce',
note: 'Terminy pobierane na bieżąco z platformy kursowej Akademia Wodociągowca. Zapisy prowadzi Akademia.',
plateTitle: 'Terminy i kursy otwarte',
heading: 'Najbliższe szkolenia i webinaria',
intro: 'Poniżej pozycje otwarte w tej chwili na naszej platformie kursowej. Zapisy prowadzi Akademia Wodociągowca.',
locale: 'pl-PL',
levels: { BASIC: 'podstawowy', INTERMEDIATE: 'średni', ADVANCED: 'zaawansowany' }
},
en: {
dateHead: 'Date',
webinar: 'Webinar',
training: 'Training',
online: 'Online',
elearning: 'Online · e-learning',
onDemand: 'On demand',
signUp: 'Sign up →',
viewCourse: 'View course →',
soldOut: 'Fully booked',
soon: 'Registration soon',
note: 'Dates are pulled live from our Akademia Wodociagowca course platform, which also handles registration.',
plateTitle: 'Open dates and courses',
heading: 'Upcoming training and webinars',
intro: 'Everything currently open on our course platform. Registration is handled by Akademia Wodociagowca.',
locale: 'en-GB',
levels: { BASIC: 'basic', INTERMEDIATE: 'intermediate', ADVANCED: 'advanced' }
}
};
var lang = (document.documentElement.getAttribute('lang') || 'pl').slice(0, 2).toLowerCase();
var t = DICT[lang] || DICT.pl;
function el(tag, className, text) {
var node = document.createElement(tag);
if (className) { node.className = className; }
if (text !== undefined && text !== null) { node.textContent = text; }
return node;
}
function formatDate(iso, timezone) {
var date = new Date(iso);
if (isNaN(date.getTime())) { return null; }
var zone = timezone || 'Europe/Warsaw';
var opts = { day: '2-digit', month: '2-digit', year: 'numeric', timeZone: zone };
var timeOpts = { hour: '2-digit', minute: '2-digit', timeZone: zone };
try {
return {
day: new Intl.DateTimeFormat(t.locale, opts).format(date),
hour: new Intl.DateTimeFormat(t.locale, timeOpts).format(date)
};
} catch (e) {
// Nieznana strefa albo stary silnik - pokazujemy datę bez strefy.
return { day: date.toLocaleDateString(t.locale), hour: date.toLocaleTimeString(t.locale) };
}
}
function actionCell(item) {
var cell = el('td', 's-act');
if (item.kind === 'webinar' && item.soldOut) {
cell.appendChild(el('span', 'text-muted', t.soldOut));
return cell;
}
if (item.kind === 'webinar' && item.open === false) {
cell.appendChild(el('span', 'text-muted', t.soon));
return cell;
}
if (!item.url) {
cell.appendChild(el('span', 'text-muted', item.kind === 'webinar' ? t.soon : ''));
return cell;
}
var link = el('a', 'sign', item.kind === 'webinar' ? t.signUp : t.viewCourse);
link.href = item.url;
link.target = '_blank';
link.rel = 'noopener';
cell.appendChild(link);
return cell;
}
function buildRow(item) {
var row = document.createElement('tr');
var date = el('td', 's-date');
if (item.kind === 'webinar') {
var parts = formatDate(item.startsAt, item.timezone);
if (parts) {
date.appendChild(document.createTextNode(parts.day));
date.appendChild(el('span', 's-hour', parts.hour));
} else {
date.appendChild(el('span', 'tag tag-soon', t.onDemand));
}
} else {
date.appendChild(el('span', 'tag tag-soon', t.onDemand));
}
row.appendChild(date);
// textContent, nie innerHTML - tytuły przychodzą z innego systemu.
row.appendChild(el('td', 's-topic', item.title));
var form = el('td', 's-form');
form.appendChild(el(
'span',
item.kind === 'webinar' ? 'tag tag-outline' : 'tag tag-accent',
item.kind === 'webinar' ? t.webinar : t.training
));
row.appendChild(form);
var place = item.kind === 'webinar' ? t.online : t.elearning;
if (item.kind === 'course' && item.level && t.levels[item.level]) {
place = t.elearning + ' · ' + t.levels[item.level];
}
row.appendChild(el('td', 's-place', place));
row.appendChild(actionCell(item));
return row;
}
function render(table, items) {
var body = table.querySelector('tbody');
if (!body) { return; }
var fresh = document.createDocumentFragment();
for (var i = 0; i < items.length; i++) {
fresh.appendChild(buildRow(items[i]));
}
body.innerHTML = '';
body.appendChild(fresh);
// Mamy realne daty, więc nagłówek kolumny wraca z "Termin" na "Data".
var head = table.querySelector('[data-schedule-datehead]');
if (head) { head.textContent = t.dateHead; }
// Przy realnych terminach nagłówki mówiące "ustalamy terminy" przestają
// być prawdziwe, więc lecą razem z wierszami.
var section = table.closest ? table.closest('section') : null;
if (section) {
var heading = section.querySelector('[data-schedule-heading]');
if (heading) { heading.textContent = t.heading; }
var intro = section.querySelector('[data-schedule-intro]');
if (intro) { intro.textContent = t.intro; }
}
var plate = table.closest ? table.closest('.plate') : null;
var plateTitle = plate ? plate.querySelector('[data-schedule-title]') : null;
if (plateTitle) { plateTitle.textContent = t.plateTitle; }
var note = plate ? plate.querySelector('[data-schedule-note]') : null;
if (note) {
note.textContent = t.note + ' ';
var link = el('a', null, 'akademiawodociagowca.pl');
link.href = 'https://akademiawodociagowca.pl/';
link.target = '_blank';
link.rel = 'noopener';
note.appendChild(link);
}
table.setAttribute('data-schedule-state', 'live');
}
function boot() {
var tables = document.querySelectorAll('table[data-schedule]');
if (!tables.length) { return; }
var limit = 0;
for (var i = 0; i < tables.length; i++) {
limit = Math.max(limit, parseInt(tables[i].getAttribute('data-schedule-limit'), 10) || 12);
}
fetch('api/schedule.php?limit=' + limit, {
credentials: 'same-origin',
headers: { 'Accept': 'application/json' }
})
.then(function (response) { return response.json(); })
.then(function (data) {
if (!data || data.source !== 'api' || !data.items || !data.items.length) { return; }
for (var i = 0; i < tables.length; i++) {
var own = parseInt(tables[i].getAttribute('data-schedule-limit'), 10) || data.items.length;
render(tables[i], data.items.slice(0, own));
}
})
.catch(function () {
// Cisza jest właściwą reakcją: w tabeli zostają statyczne wiersze.
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', boot);
} else {
boot();
}
})();
+36
View File
@@ -272,6 +272,11 @@ textarea.input { min-height: 110px; resize: vertical; }
.s-prop { font-weight: 500; }
.s-rem { font-size: 14px; color: color-mix(in srgb, var(--color-text) 68%, transparent); }
.s-date { white-space: nowrap; font-weight: 500; width: 1%; }
/* godzina webinaru pod datą - pobierana z Akademii Wodociągowca */
.s-hour {
display: block; font-weight: 400; font-size: 12.5px;
color: color-mix(in srgb, var(--color-text) 55%, transparent);
}
.s-topic { font-family: var(--font-heading); font-weight: 600; font-size: 18px; }
.s-act { text-align: right; white-space: nowrap; }
.sign {
@@ -475,6 +480,35 @@ textarea.input { min-height: 110px; resize: vertical; }
color: var(--color-accent-700);
}
/* -- pasek liczb (Akademia) --------------------------------------------- */
.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: calc(1.5 * var(--leading)); }
@media (max-width: 820px) { .stats { grid-template-columns: repeat(2, 1fr); } }
.stat { padding-top: var(--space-4); border-top: 2px solid var(--color-accent); }
.stat-num {
display: block;
font-family: var(--font-heading); font-weight: 700;
font-size: clamp(38px, 4.4vw, 52px); line-height: 1;
color: var(--color-accent-700);
font-feature-settings: "tnum" 1;
}
.stat-label {
display: block; margin-top: var(--space-3);
font-size: 13.5px; line-height: var(--half);
color: color-mix(in srgb, var(--color-text) 62%, transparent);
}
/* -- wejście bloków przy przewijaniu -----------------------------------
Styl startowy nakładamy dopiero, gdy skrypt zgłosi gotowość (klasa
js-reveal na <html>). Bez JavaScriptu treść jest widoczna od razu. */
.js-reveal [data-reveal] {
opacity: 0; transform: translateY(16px);
transition: opacity .55s ease-out, transform .55s ease-out;
}
.js-reveal [data-reveal].is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
.js-reveal [data-reveal] { opacity: 1; transform: none; transition: none; }
}
/* -- newsletter / CTA --------------------------------------------------- */
.cta-band {
position: relative; padding: calc(2 * var(--leading)) var(--space-8);
@@ -608,6 +642,8 @@ textarea.input { min-height: 110px; resize: vertical; }
}
.table.sched td { border: 0; padding: 0; }
.table.sched .s-date { order: 1; font-size: 14px; color: var(--color-accent-700); }
/* w karcie mobilnej data i godzina mieszczą się w jednej linii */
.table.sched .s-hour { display: inline; font-size: 13px; margin-left: var(--space-2); }
.table.sched .s-form { order: 2; text-align: right; }
.table.sched .s-topic { grid-column: 1 / -1; order: 3; }
.table.sched .s-place { order: 4; font-size: 13.5px; color: color-mix(in srgb, var(--color-text) 60%, transparent); }
+74 -15
View File
@@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SoftSPM - training, consulting and custom software for the water sector</title>
<meta name="description" content="Training, webinars, consulting and turnkey software for municipal water and wastewater utilities. 25+ years in IT, in the water sector since 2014. Web and mobile applications, our own course platform, the Wodociagowiec.pl portal and the Gielda Komunalna marketplace.">
<meta name="description" content="Training, webinars, consulting and turnkey software for municipal water and wastewater utilities. 25+ years in IT, in the water sector since 2014. Web and mobile applications, the Akademia Wodociagowca course platform, the Wodociagowiec.pl portal and the Gielda Komunalna marketplace.">
<link rel="icon" href="images/favicon.ico" sizes="any">
<link rel="stylesheet" href="assets/styles.css">
</head>
@@ -16,7 +16,7 @@
<a href="#offer">Offer</a>
<a href="#topics">Topics</a>
<a href="#dates">Dates</a>
<a href="#platform">Platform</a>
<a href="#platform">Academy</a>
<a href="#software">Software</a>
<a href="#ecosystem">Ecosystem</a>
<a class="nav-keep" href="index.html" title="Wersja polska">PL</a>
@@ -30,8 +30,8 @@
<section class="hero">
<div class="hero-grid">
<div>
<h1 class="display"><span class="line">Water sector</span> <span class="line">know-how since 2014.</span></h1>
<p class="sub">Training, webinars, consulting and <strong>turnkey software</strong> for municipal water and wastewater utilities. We teach what we do ourselves - we have been building software for over 25 years, and doing it for the water sector since 2014. We run the <strong>Wodociagowiec.pl</strong> portal and <strong>Gielda Komunalna</strong>, a free marketplace where the sector buys, sells and swaps equipment.</p>
<h1 class="display"><span class="line">Water sector know-how</span> <span class="line">that stays in-house.</span></h1>
<p class="sub"><strong>Turnkey software</strong>, consulting, training and webinars for municipal water and wastewater utilities. Over 25 years in IT, in the water sector since 2014. We do not leave a slide deck to gather dust: what stays behind is a working system, a list of decisions to take, or competence that keeps working once we are gone.</p>
<div class="row">
<a class="btn btn-primary btn-lg" href="#dates">See the programme</a>
<a class="btn btn-ghost btn-lg" href="#contact">Book a consultation</a>
@@ -71,7 +71,7 @@
<tr><td class="s-num">03</td><td class="s-prop">Specialists trained</td><td class="s-val">480+</td><td class="s-rem">Classroom and online</td></tr>
<tr><td class="s-num">04</td><td class="s-prop">Webinars per year</td><td class="s-val">24</td><td class="s-rem">On our own course platform</td></tr>
<tr><td class="s-num">05</td><td class="s-prop">Advisory projects</td><td class="s-val">60+</td><td class="s-rem">Tariffs, organisation, procurement, digitisation</td></tr>
<tr><td class="s-num">06</td><td class="s-prop">Ecosystem reach</td><td class="s-val">2 services</td><td class="s-rem">Wodociagowiec.pl trade portal and Gielda Komunalna marketplace</td></tr>
<tr><td class="s-num">06</td><td class="s-prop">Ecosystem reach</td><td class="s-val">3 services</td><td class="s-rem">Wodociagowiec.pl, Gielda Komunalna and Akademia Wodociagowca</td></tr>
</tbody>
</table>
<p class="sheet-note">Sample figures - to be confirmed before publication.</p>
@@ -270,14 +270,69 @@
</div>
</section>
<!-- ── 05 · Platform ────────────────────────────────────────────────── -->
<!-- ── 05 · Akademia Wodociagowca ───────────────────────────────────── -->
<section class="section" id="platform">
<span class="kicker">05 · Course platform</span>
<span class="kicker">05 · Akademia Wodociagowca</span>
<hr class="caption-rule">
<div class="section-intro" data-reveal>
<h2>Competence that stays with the utility</h2>
<p class="text-muted">A training course ends on Friday; the competence is supposed to last for years. That is why we run our own course platform: live webinars, courses to work through at your own pace, working materials and certificates - all under a single company account. It is how we keep knowledge circulating around the sector instead of settling in one person's notebook.</p>
</div>
<figure class="figure" style="margin: 0 0 calc(1.5 * var(--leading))" data-reveal>
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
<img class="plot" src="assets/img/en/akademia.svg" alt="Diagram: client rollouts, advisory projects and reader questions feed Akademia Wodociagowca, and trained teams send questions back into the programme" width="720" height="344">
<figcaption>Diagram 01 - The competence loop: practice feeds the Academy, trained teams come back with questions</figcaption>
</figure>
<div class="stats" data-reveal>
<div class="stat">
<span class="stat-num" data-count="24">24</span>
<span class="stat-label">webinars a year, two every month</span>
</div>
<div class="stat">
<span class="stat-num" data-count="8">8</span>
<span class="stat-label">subject tracks in the catalogue</span>
</div>
<div class="stat">
<span class="stat-num" data-count="12">12</span>
<span class="stat-label">people at most in a group</span>
</div>
<div class="stat">
<span class="stat-num" data-count="480" data-suffix="+">480+</span>
<span class="stat-label">specialists trained across the sector</span>
</div>
</div>
<p class="sheet-note" style="margin-bottom: calc(1.5 * var(--leading))">Webinar count and people trained - sample figures, to be confirmed before publication.</p>
<div class="cells cells-3" style="margin-bottom: calc(2 * var(--leading))">
<div class="cell-frame" data-reveal>
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
<span class="cell-num">05.1</span>
<svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3 2 8l10 5 10-5Z"/><path d="M6 10.5V16c0 1.7 2.7 3 6 3s6-1.3 6-3v-5.5"/></svg>
<h3>Taught first-hand</h3>
<p>Our trainers walk into the room straight from rollouts and advisory projects. The example on the slide has a date and consequences; it is not an illustration copied from a textbook.</p>
</div>
<div class="cell-frame" data-reveal>
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
<span class="cell-num">05.2</span>
<svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="4" width="18" height="14" rx="1"/><path d="M3 9h18M8 22h8M12 18v4"/></svg>
<h3>Knowledge stays in-house</h3>
<p>Recordings, document templates and checklists stay on the company account. When someone leaves, the competence stays - the next person starts from the material, not from scratch.</p>
</div>
<div class="cell-frame" data-reveal>
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
<span class="cell-num">05.3</span>
<svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><path d="M21 12a9 9 0 1 1-3-6.7"/><path d="M21 4v5h-5"/></svg>
<h3>Questions feed the programme</h3>
<p>A topic that comes up for the third time in a webinar chat or at the Wodociagowiec desk goes into the catalogue. The market writes the programme; we just put it in order.</p>
</div>
</div>
<div class="split">
<div>
<h2 class="split-title">Webinars and e-learning in one place</h2>
<p class="lead">Our own platform for running courses: live webinars, on-demand courses, tests and certificates. The material stays with you - come back to the recordings and documents whenever you need them.</p>
<div data-reveal>
<h3 class="split-title">What the utility gets</h3>
<p class="lead">One company account, many employees, and material available when it is actually needed - not only on the day of the course.</p>
<ul class="checks">
<li>Live webinars with chat and a Q&amp;A session</li>
<li>Recordings available indefinitely with your access</li>
@@ -286,9 +341,12 @@
<li>Learner dashboard with progress and your team's training history</li>
<li>Company account - one subscription for many employees</li>
</ul>
<a class="btn btn-primary" href="#">Go to the platform</a>
<div class="row" style="display:flex; gap:var(--space-4); flex-wrap:wrap">
<a class="btn btn-primary" href="https://akademiawodociagowca.pl/" target="_blank" rel="noopener">Go to the Academy</a>
<a class="btn btn-ghost" href="#dates">See upcoming dates</a>
</div>
<figure class="figure split-media" style="margin:0">
</div>
<figure class="figure split-media" style="margin:0" data-reveal>
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
<img class="plot" src="assets/img/en/platforma.svg" alt="Learner dashboard: course progress, webinar agenda and a live indicator" width="640" height="360">
<figcaption>Fig. 02 - Learner dashboard: progress and upcoming webinars</figcaption>
@@ -360,13 +418,13 @@
<hr class="caption-rule">
<div class="section-intro">
<h2>A trade portal, a marketplace and a course platform - one loop</h2>
<p class="text-muted">We do not start from a blank page. We run two services the Polish water sector uses daily, and they tell us what to teach. Readers' questions become webinar topics, listings on the marketplace show what the sector actually needs, and course participants meet the manufacturers and contractors they used to hunt for through word of mouth.</p>
<p class="text-muted">We do not start from a blank page. We run three services the Polish water sector uses daily, and they tell us what to teach. Readers' questions become webinar topics, listings on the marketplace show what the sector actually needs, and course participants meet the manufacturers and contractors they used to hunt for through word of mouth.</p>
</div>
<figure class="figure" style="margin: 0 0 calc(1.5 * var(--leading))">
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
<img class="plot" src="assets/img/en/ekosystem.svg" alt="Diagram: the Wodociagowiec.pl portal and the Gielda Komunalna marketplace exchange topics, practitioners and listings with the course platform at the centre" width="720" height="400">
<figcaption>Diagram 01 - The knowledge loop: the portal and the marketplace feed the platform, graduates return to the market</figcaption>
<figcaption>Diagram 02 - The knowledge loop: the portal and the marketplace feed the platform, graduates return to the market</figcaption>
</figure>
<div class="cells cells-2" style="margin-bottom: calc(1.5 * var(--leading))">
@@ -481,7 +539,7 @@
<li><a href="szkolenia.html#webinaria">Webinars (PL)</a></li>
<li><a href="konsulting.html">Consulting (PL)</a></li>
<li><a href="#software">Custom software</a></li>
<li><a href="#platform">Course platform</a></li>
<li><a href="#platform">Akademia Wodociagowca</a></li>
</ul>
</div>
<div>
@@ -508,6 +566,7 @@
</div>
</footer>
<script src="assets/reveal.js" defer></script>
<script src="assets/forms.js" defer></script>
</body>
</html>
+81 -21
View File
@@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SoftSPM - szkolenia, konsulting i aplikacje dla branży wod-kan</title>
<meta name="description" content="Szkolenia, webinaria, konsulting i aplikacje pod klucz dla przedsiębiorstw wodociągowych i komunalnych. Ponad 25 lat w IT, od 2014 w branży wod-kan. Systemy webowe i mobilne, platforma kursowa, portal Wodociągowiec.pl i Giełda Komunalna.">
<meta name="description" content="Szkolenia, webinaria, konsulting i aplikacje pod klucz dla przedsiębiorstw wodociągowych i komunalnych. Ponad 25 lat w IT, od 2014 w branży wod-kan. Systemy webowe i mobilne, platforma kursowa Akademia Wodociągowca, portal Wodociągowiec.pl i Giełda Komunalna.">
<link rel="icon" href="images/favicon.ico" sizes="any">
<link rel="stylesheet" href="assets/styles.css">
</head>
@@ -16,7 +16,7 @@
<a href="#oferta">Oferta</a>
<a href="#tematy">Tematy</a>
<a href="#terminy">Terminy</a>
<a href="#platforma">Platforma</a>
<a href="#platforma">Akademia</a>
<a href="#aplikacje">Aplikacje</a>
<a href="#ekosystem">Ekosystem</a>
<a href="szkolenia.html">Szkolenia</a>
@@ -32,8 +32,8 @@
<section class="hero">
<div class="hero-grid">
<div>
<h1 class="display"><span class="line">Wiedza wod-kan.</span> <span class="line">W praktyce od 2014.</span></h1>
<p class="sub">Szkolenia, webinaria, konsulting i <strong>aplikacje pod klucz</strong> dla przedsiębiorstw wodociągowych i komunalnych. Uczymy tego, co sami robimy - bo od ponad 25 lat piszemy oprogramowanie, a od 2014 roku robimy to dla branży wod-kan. Prowadzimy portal <strong>Wodociągowiec.pl</strong> oraz <strong>Giełdę Komunalną</strong> - bezpłatny marketplace, na którym branża kupuje, sprzedaje i wymienia sprzęt.</p>
<h1 class="display"><span class="line">Wiedza wod-kan,</span> <span class="line">która zostaje w firmie.</span></h1>
<p class="sub"><strong>Aplikacje pod klucz</strong>, konsulting, szkolenia i webinaria dla przedsiębiorstw wodociągowych i komunalnych. Ponad 25 lat w IT, od 2014 roku w wod-kan. Nie zostawiamy prezentacji do odłożenia na półkę: zostaje działający system, lista decyzji do podjęcia albo kompetencja, która pracuje dalej, gdy nas już nie ma.</p>
<div class="row">
<a class="btn btn-primary btn-lg" href="#terminy">Zobacz program szkoleń</a>
<a class="btn btn-ghost btn-lg" href="konsulting.html#kontakt">Umów konsultację</a>
@@ -73,7 +73,7 @@
<tr><td class="s-num">03</td><td class="s-prop">Przeszkoleni specjaliści</td><td class="s-val">480+</td><td class="s-rem">Szkolenia stacjonarne i online</td></tr>
<tr><td class="s-num">04</td><td class="s-prop">Webinariów rocznie</td><td class="s-val">24</td><td class="s-rem">Na własnej platformie kursowej</td></tr>
<tr><td class="s-num">05</td><td class="s-prop">Projekty doradcze</td><td class="s-val">60+</td><td class="s-rem">Organizacja, taryfy, cyfryzacja, zamówienia</td></tr>
<tr><td class="s-num">06</td><td class="s-prop">Zasięg ekosystemu</td><td class="s-val">2 serwisy</td><td class="s-rem">Portal Wodociągowiec.pl i marketplace Giełda Komunalna</td></tr>
<tr><td class="s-num">06</td><td class="s-prop">Zasięg ekosystemu</td><td class="s-val">3 serwisy</td><td class="s-rem">Wodociągowiec.pl, Giełda Komunalna i Akademia Wodociągowca</td></tr>
</tbody>
</table>
<p class="sheet-note">Wartości przykładowe - do potwierdzenia przed publikacją.</p>
@@ -251,20 +251,20 @@
<span class="kicker">04 · Harmonogram</span>
<hr class="caption-rule">
<div class="section-intro">
<h2>Terminy najbliższej edycji ustalamy</h2>
<p class="text-muted">Poniżej tematy, które wchodzą do programu jako pierwsze. Daty podajemy, gdy domkniemy salę i skład prowadzących - zwykle z sześciotygodniowym wyprzedzeniem.</p>
<h2 data-schedule-heading>Terminy najbliższej edycji ustalamy</h2>
<p class="text-muted" data-schedule-intro>Poniżej tematy, które wchodzą do programu jako pierwsze. Daty podajemy, gdy domkniemy salę i skład prowadzących - zwykle z sześciotygodniowym wyprzedzeniem.</p>
</div>
<div class="plate">
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
<header class="title-block">
<span class="tb-title">Tematy w przygotowaniu</span>
<span class="tb-title" data-schedule-title>Tematy w przygotowaniu</span>
<span class="tb-cell">SPM-2026</span>
<span class="tb-cell">Rev A</span>
<span class="tb-cell">Arkusz 02 z 06</span>
</header>
<table class="table sched">
<table class="table sched" data-schedule data-schedule-limit="8">
<thead>
<tr><th scope="col">Termin</th><th scope="col">Temat</th><th scope="col">Forma</th><th scope="col">Miejsce</th><th scope="col"><span class="visually-hidden">Zapisy</span></th></tr>
<tr><th scope="col" data-schedule-datehead>Termin</th><th scope="col">Temat</th><th scope="col">Forma</th><th scope="col">Miejsce</th><th scope="col"><span class="visually-hidden">Zapisy</span></th></tr>
</thead>
<tbody>
<tr>
@@ -325,18 +325,73 @@
</tr>
</tbody>
</table>
<p class="sheet-note">Ustalamy terminy najbliższej edycji. Zostaw kontakt przy wybranym temacie na stronie <a href="szkolenia.html#terminy">Szkolenia i webinaria</a> - odezwiemy się, gdy otworzymy zapisy.</p>
<p class="sheet-note" data-schedule-note>Ustalamy terminy najbliższej edycji. Zostaw kontakt przy wybranym temacie na stronie <a href="szkolenia.html#terminy">Szkolenia i webinaria</a> - odezwiemy się, gdy otworzymy zapisy.</p>
</div>
</section>
<!-- ── 05 · Platforma kursowa ───────────────────────────────────────── -->
<!-- ── 05 · Akademia Wodociągowca ───────────────────────────────────── -->
<section class="section" id="platforma">
<span class="kicker">05 · Platforma kursowa</span>
<span class="kicker">05 · Akademia Wodociągowca</span>
<hr class="caption-rule">
<div class="section-intro" data-reveal>
<h2>Kompetencja, która zostaje w przedsiębiorstwie</h2>
<p class="text-muted">Szkolenie kończy się w piątek, kompetencja ma zostać na lata. Dlatego prowadzimy własną platformę kursową: webinaria na żywo, kursy do przerobienia we własnym tempie, materiały robocze i certyfikaty - wszystko pod jednym firmowym kontem. To nasz sposób na to, żeby wiedza krążyła po branży, zamiast osiadać w notatkach pojedynczych osób.</p>
</div>
<figure class="figure" style="margin: 0 0 calc(1.5 * var(--leading))" data-reveal>
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
<img class="plot" src="assets/img/akademia.svg" alt="Schemat: wdrożenia, projekty doradcze i pytania czytelników zasilają Akademię Wodociągowca, przeszkolone zespoły wracają z pytaniami do programu" width="720" height="344">
<figcaption>Schemat 01 - Obieg kompetencji: praktyka zasila Akademię, a przeszkolone zespoły wracają z pytaniami</figcaption>
</figure>
<div class="stats" data-reveal>
<div class="stat">
<span class="stat-num" data-count="24">24</span>
<span class="stat-label">webinaria rocznie, dwa w miesiącu</span>
</div>
<div class="stat">
<span class="stat-num" data-count="8">8</span>
<span class="stat-label">ścieżek tematycznych w katalogu</span>
</div>
<div class="stat">
<span class="stat-num" data-count="12">12</span>
<span class="stat-label">osób maksymalnie w grupie</span>
</div>
<div class="stat">
<span class="stat-num" data-count="480" data-suffix="+">480+</span>
<span class="stat-label">przeszkolonych specjalistów branży</span>
</div>
</div>
<p class="sheet-note" style="margin-bottom: calc(1.5 * var(--leading))">Liczba webinariów i przeszkolonych - wartości przykładowe, do potwierdzenia przed publikacją.</p>
<div class="cells cells-3" style="margin-bottom: calc(2 * var(--leading))">
<div class="cell-frame" data-reveal>
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
<span class="cell-num">05.1</span>
<svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3 2 8l10 5 10-5Z"/><path d="M6 10.5V16c0 1.7 2.7 3 6 3s6-1.3 6-3v-5.5"/></svg>
<h3>Uczymy z pierwszej ręki</h3>
<p>Prowadzący wracają na salę prosto z wdrożeń i projektów doradczych. Przykład na zajęciach ma datę i konsekwencje, nie jest ilustracją przepisaną z podręcznika.</p>
</div>
<div class="cell-frame" data-reveal>
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
<span class="cell-num">05.2</span>
<svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="4" width="18" height="14" rx="1"/><path d="M3 9h18M8 22h8M12 18v4"/></svg>
<h3>Wiedza zostaje w firmie</h3>
<p>Nagrania, wzory pism i listy kontrolne zostają na koncie przedsiębiorstwa. Gdy odchodzi pracownik, kompetencja zostaje - następna osoba zaczyna od materiału, nie od zera.</p>
</div>
<div class="cell-frame" data-reveal>
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
<span class="cell-num">05.3</span>
<svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><path d="M21 12a9 9 0 1 1-3-6.7"/><path d="M21 4v5h-5"/></svg>
<h3>Pytania wracają do programu</h3>
<p>Temat, który trzeci raz wraca w czacie webinaru albo w redakcji Wodociągowca, wchodzi do katalogu. Program pisze rynek, my go tylko porządkujemy.</p>
</div>
</div>
<div class="split">
<div>
<h2 class="split-title">Webinary i e-learning w jednym miejscu</h2>
<p class="lead">Nasza własna platforma do prowadzenia kursów: webinaria na żywo, kursy on-demand, testy i certyfikaty. Materiał zostaje z Tobą - wracasz do nagrań i dokumentów, kiedy ich potrzebujesz.</p>
<div data-reveal>
<h3 class="split-title">Co dostaje przedsiębiorstwo</h3>
<p class="lead">Jedno konto firmowe, wielu pracowników, materiał dostępny wtedy, kiedy jest potrzebny - a nie tylko w dniu szkolenia.</p>
<ul class="checks">
<li>Webinaria na żywo z czatem i sesją pytań do prowadzącego</li>
<li>Nagrania dostępne bezterminowo w ramach wykupionego dostępu</li>
@@ -345,9 +400,12 @@
<li>Panel kursanta z postępem nauki i historią szkoleń całego zespołu</li>
<li>Konto firmowe - jeden dostęp dla wielu pracowników</li>
</ul>
<a class="btn btn-primary" href="#">Przejdź do platformy</a>
<div class="row" style="display:flex; gap:var(--space-4); flex-wrap:wrap">
<a class="btn btn-primary" href="https://akademiawodociagowca.pl/" target="_blank" rel="noopener">Przejdź do Akademii</a>
<a class="btn btn-ghost" href="#terminy">Zobacz najbliższe terminy</a>
</div>
<figure class="figure split-media" style="margin:0">
</div>
<figure class="figure split-media" style="margin:0" data-reveal>
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
<img class="plot" src="assets/img/platforma.svg" alt="Panel kursanta: postęp kursów, agenda webinariów i znacznik transmisji na żywo" width="640" height="360">
<figcaption>Rys. 02 - Panel kursanta: postęp nauki i nadchodzące webinaria</figcaption>
@@ -419,13 +477,13 @@
<hr class="caption-rule">
<div class="section-intro">
<h2>Portal, marketplace i platforma kursowa - jeden obieg</h2>
<p class="text-muted">Nie zaczynamy od pustej kartki. Prowadzimy dwa serwisy, z których branża wod-kan korzysta na co dzień - i to one podpowiadają nam, czego uczyć. Pytania czytelników portalu stają się tematami webinariów, ogłoszenia na giełdzie pokazują, czego rynkowi realnie brakuje, a uczestnicy szkoleń poznają tam producentów i wykonawców, których wcześniej szukali po znajomych.</p>
<p class="text-muted">Nie zaczynamy od pustej kartki. Prowadzimy trzy serwisy, z których branża wod-kan korzysta na co dzień - i to one podpowiadają nam, czego uczyć. Pytania czytelników portalu stają się tematami webinariów, ogłoszenia na giełdzie pokazują, czego rynkowi realnie brakuje, a uczestnicy szkoleń poznają tam producentów i wykonawców, których wcześniej szukali po znajomych.</p>
</div>
<figure class="figure" style="margin: 0 0 calc(1.5 * var(--leading))">
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
<img class="plot" src="assets/img/ekosystem.svg" alt="Schemat: portal Wodociągowiec.pl i marketplace Giełda Komunalna wymieniają tematy, praktyków i ogłoszenia z platformą kursową w centrum" width="720" height="400">
<figcaption>Schemat 01 - Obieg wiedzy: portal i marketplace zasilają platformę kursową, absolwenci wracają na rynek</figcaption>
<figcaption>Schemat 02 - Obieg wiedzy: portal i marketplace zasilają platformę kursową, absolwenci wracają na rynek</figcaption>
</figure>
<div class="cells cells-2" style="margin-bottom: calc(1.5 * var(--leading))">
@@ -541,7 +599,7 @@
<li><a href="szkolenia.html#webinaria">Webinaria</a></li>
<li><a href="konsulting.html">Konsulting</a></li>
<li><a href="#aplikacje">Aplikacje pod klucz</a></li>
<li><a href="#platforma">Platforma kursowa</a></li>
<li><a href="#platforma">Akademia Wodociągowca</a></li>
</ul>
</div>
<div>
@@ -568,6 +626,8 @@
</div>
</footer>
<script src="assets/reveal.js" defer></script>
<script src="assets/forms.js" defer></script>
<script src="assets/schedule.js" defer></script>
</body>
</html>
+1
View File
@@ -353,6 +353,7 @@
</div>
</footer>
<script src="assets/reveal.js" defer></script>
<script src="assets/forms.js" defer></script>
</body>
</html>
+10 -8
View File
@@ -242,20 +242,20 @@
<span class="kicker">04 · Harmonogram</span>
<hr class="caption-rule">
<div class="section-intro">
<h2>Terminy najbliższej edycji ustalamy</h2>
<p class="text-muted">Poniżej tematy, które wchodzą do programu jako pierwsze. Daty podamy, gdy domkniemy salę i skład prowadzących - zwykle z sześciotygodniowym wyprzedzeniem. Zostaw kontakt przy interesującym Cię temacie, a odezwiemy się z terminem, zanim ogłosimy go publicznie.</p>
<h2 data-schedule-heading>Terminy najbliższej edycji ustalamy</h2>
<p class="text-muted" data-schedule-intro>Poniżej tematy, które wchodzą do programu jako pierwsze. Daty podamy, gdy domkniemy salę i skład prowadzących - zwykle z sześciotygodniowym wyprzedzeniem. Zostaw kontakt przy interesującym Cię temacie, a odezwiemy się z terminem, zanim ogłosimy go publicznie.</p>
</div>
<div class="plate">
<span class="corner tl"></span><span class="corner tr"></span><span class="corner bl"></span><span class="corner br"></span>
<header class="title-block">
<span class="tb-title">Tematy w przygotowaniu</span>
<span class="tb-title" data-schedule-title>Tematy w przygotowaniu</span>
<span class="tb-cell">SPM-2026</span>
<span class="tb-cell">Rev A</span>
<span class="tb-cell">Arkusz 05 z 06</span>
</header>
<table class="table sched">
<table class="table sched" data-schedule data-schedule-limit="12">
<thead>
<tr><th scope="col">Termin</th><th scope="col">Temat</th><th scope="col">Forma</th><th scope="col">Miejsce</th><th scope="col"><span class="visually-hidden">Zapisy</span></th></tr>
<tr><th scope="col" data-schedule-datehead>Termin</th><th scope="col">Temat</th><th scope="col">Forma</th><th scope="col">Miejsce</th><th scope="col"><span class="visually-hidden">Zapisy</span></th></tr>
</thead>
<tbody>
<tr><td class="s-date"><span class="tag tag-soon">Wkrótce</span></td><td class="s-topic">Wniosek taryfowy - przygotowanie i obrona</td><td class="s-form"><span class="tag tag-accent">Szkolenie</span></td><td class="s-place">Bełchatów</td><td class="s-act"><a class="sign" href="#zgloszenie">Powiadom mnie →</a></td></tr>
@@ -271,7 +271,7 @@
<tr><td class="s-date"><span class="tag tag-soon">Wkrótce</span></td><td class="s-topic">NIS2 i infrastruktura krytyczna - obowiązki przedsiębiorstwa</td><td class="s-form"><span class="tag tag-outline">Webinar</span></td><td class="s-place">Online</td><td class="s-act"><a class="sign" href="#zgloszenie">Powiadom mnie →</a></td></tr>
</tbody>
</table>
<p class="sheet-note">Ustalamy terminy najbliższej edycji. Zostaw kontakt przy wybranym temacie - poinformujemy Cię, gdy tylko otworzymy zapisy.</p>
<p class="sheet-note" data-schedule-note>Ustalamy terminy najbliższej edycji. Zostaw kontakt przy wybranym temacie - poinformujemy Cię, gdy tylko otworzymy zapisy.</p>
</div>
</section>
@@ -282,10 +282,10 @@
<div class="split">
<div>
<h2 class="split-title">Pakiet roczny webinariów</h2>
<p class="lead">Dwadzieścia cztery spotkania w roku - dwa w miesiącu, po dwie godziny. Jedno o przepisach i taryfach, jedno o organizacji i rynku. Jedna opłata dla całego przedsiębiorstwa, bez limitu uczestników.</p>
<p class="lead">Dwadzieścia cztery spotkania w roku - dwa w miesiącu, po dwie godziny. Jedno o przepisach i taryfach, jedno o organizacji i rynku. Jedna opłata dla całego przedsiębiorstwa, bez limitu uczestników. Wszystko prowadzimy na własnej platformie <a href="https://akademiawodociagowca.pl/" target="_blank" rel="noopener">Akademia Wodociągowca</a>, więc nagranie i materiały zostają na koncie firmy także wtedy, gdy ktoś nie mógł być na żywo.</p>
<ul class="checks">
<li>24 webinaria rocznie na żywo, z sesją pytań do prowadzącego</li>
<li>Nagrania i materiały dostępne bezterminowo na platformie kursowej</li>
<li>Nagrania i materiały bezterminowo na koncie firmy w Akademii Wodociągowca</li>
<li>Bez limitu uczestników z jednego przedsiębiorstwa</li>
<li>Archiwum wcześniejszych spotkań w cenie pakietu</li>
<li>Możliwość zgłoszenia tematu do programu na kolejny kwartał</li>
@@ -422,6 +422,8 @@
</div>
</footer>
<script src="assets/reveal.js" defer></script>
<script src="assets/forms.js" defer></script>
<script src="assets/schedule.js" defer></script>
</body>
</html>