Skip to content

Instantly share code, notes, and snippets.

@palladius
Created September 15, 2026 08:34
Show Gist options
  • Select an option

  • Save palladius/c9993bd84f1168d6dd9c5fa0a5f44b51 to your computer and use it in GitHub Desktop.

Select an option

Save palladius/c9993bd84f1168d6dd9c5fa0a5f44b51 to your computer and use it in GitHub Desktop.
🏫 Herbstferien 2026 Ferienkurse Planner β€” Ale & Seba

🏫 Herbstferien 2026 β€” Ferienkurse Strategic Plan

Goal: Maximize hours both kids are out of the house (= parents can work πŸ’»), with fun activities for two boys, at the same location when possible.

🏠 Home base: Tâdistrasse 27, 8002 Zürich

πŸ“… Confirmed Dates

Herbstferien ZΓΌrich 2026: October 5–16 (2 weeks)

Week Dates Registration deadline
Week 1 Mon 5 – Fri 9 Oct ⚠️ 29 September (2 weeks from now!)
Week 2 Mon 12 – Fri 16 Oct 6 October

πŸ‘¦πŸ‘¦ Our Heroes

Born Age (Oct 2026) Notes
πŸ§’ Ale (Alessandro) 31 Jan 2018 8 years More options available
πŸ‘¦ Seba (Sebastian Leonardo) 18 May 2020 6 years Fewer full-week options

🚫 Rules / Regole

  1. Duration must justify travel time β€” the further away, the longer the activity must be:

    Travel time Min. activity Example
    ~5 min (≀500m) 1h OK Tekmania, Coding a Story
    ~10 min (~1km) 2h+ Tanz-Fabrik, Budokan
    ~15–20 min (~1.5–2km) 3h+ Heuried, Rote Fabrik
    ~30 min (3km+) 5h+ AlbisgΓΌtli, Falletsche, Mittelleimbach

    Meglio la TV che portarli mezz'ora per un'ora di attivitΓ !

  2. Same location for both preferred β€” stesso posto se possibile

  3. Closer is better β€” walking distance ideal

  4. Full day (6h+) activities are gold ⭐⭐⭐


πŸ“ Key Locations (sorted by distance)

Dist. Location Address Both kids possible?
🟒 ~500m Tekmania Waffenplatzstr. 11 Seba only
🟒 ~600m Gabler Sports Hall Schulhausstr. 21 βœ… Both (Acrobatics)
🟒 ~800m TC Belvoir Strandbadweg 15 Ale only
🟒 ~900m Mythenquai Mythenquai 95 Ale only
🟑 ~1km TANZ-FABRIK Bederstrasse 77 Ale only
🟑 ~2km Rote Fabrik Seestrasse 395 Ale only
🟠 ~2.5km Entlisberg Balberstrasse 71 Ale only
🟠 ~3.5km Albisgütli Uetlibergstr. 341 Mixed
🟠 ~4km Falletsche Rebenstrasse 73 βœ… Both (Mini Basketball)
πŸ”΄ ~5km Mittelleimbach Im Mittelleimbach 19 βœ… Both (Animals, single days)
πŸ”΄ ~5km TC Uetliberg Uetlibergstr. 420 βœ… Both (Tennis)

⭐ COURSES WHERE BOTH KIDS CAN GO TOGETHER

These are the gold options β€” same place, same time, both boys:

Course Week Time Duration Location Dist. Availability Age range
Acrobatics & Clowning (AKCL 11) 🀸 1 10:00–15:00 5h/day Gabler ~600m 🟒 🟒 πŸ• Warteliste 2017–2020 βœ…
Mini Basketball Camp (MICA 11) πŸ€ 1 9:30–15:30 6h/day Falletsche ~4km 🟠 🟠 βœ… Many spots 2013–2020 βœ…
Tennis Camp Uetliberg (TEUE 11) 🎾 1 9:45–16:30 6h45/day TC Uetliberg ~5km πŸ”΄ πŸ”΄ πŸ• Warteliste 2014–2020 βœ…
Tennis Camp Uetliberg (TEUE 21) 🎾 2 9:45–16:30 6h45/day TC Uetliberg ~5km πŸ”΄ πŸ”΄ πŸ• Warteliste 2014–2020 βœ…
Coding & Electronic Art (FCO 21) πŸ’»πŸŽ¨ 2 9:30–16:00 6h30/day Coding a Story ~200m 🟒 🟒 πŸ• Warteliste 2014–2021 βœ…

Important

πŸ† Best "Both kids together" options:

  1. Mini Basketball (MICA 11) β€” 6h/day, spots available, great for boys! πŸ€ But 4km away.
  2. Acrobatics & Clowning (AKCL 11) β€” 5h/day, only 600m! 🀸 But waitlist.
  3. Tennis Uetliberg (TEUE 11/21) β€” nearly 7h/day! 🎾 But waitlist + 5km.
  4. Coding & Art (FCO 21) β€” 6.5h/day, 200m from home! πŸ’» But waitlist.

Single-day activities together (both kids) 🐾

Day Course Time Duration Location Availability
Week 1, Tue SchΓΆflis (FSC 12) πŸ‘ 10:00–15:00 5h Mittelleimbach ~5km πŸ• Warteliste
Week 1, Thu Nature Heroes (FEN 11) 🦎 10:00–15:00 5h Mittelleimbach ~5km ⚠️ Few spots
Week 2, Tue SchΓΆflis (FNS 21) πŸ‘ 10:00–15:00 5h Mittelleimbach ~5km πŸ• Warteliste
Week 2, Tue No fear of dogs (FAH 21) 🐢 13:00–17:00 4h AlbisgΓΌtli ~3.5km βœ… Many spots
Week 2, Wed Animal Heroes (FTA 21) 🦸 10:00–15:00 5h Mittelleimbach ~5km βœ… Many spots
Week 2, Thu SchΓΆflis (FSC 21) πŸ‘ 10:00–15:00 5h Mittelleimbach ~5km πŸ• Warteliste
Week 2, Fri SchΓΆflis (FSC 22) πŸ‘ 10:00–15:00 5h Mittelleimbach ~5km βœ… Many spots

πŸ“‹ COURSES BY KID (3h+ only)

πŸ§’ Ale only (born 2018)

Course Week Time Duration Location Dist. Availability
SUP & Kayak (SUP 11) πŸ„ 1 8:30–11:30 3h/day Mythenquai ~900m 🟒 🟒 βœ… Many
Capoeira Camp (CACA 11) πŸ₯‹ 1 9:00–16:00 7h/day ⭐ Entlisberg ~2.5km 🟠 βœ… Many
Printing/crafting (FDB 11) 🎨 1 9:00–15:00 6h/day Rote Fabrik ~2km 🟑 ⚠️ Few spots
Tennis Belvoir (TEBE 11) 🎾 1 10:00–16:00 6h/day TC Belvoir ~800m 🟒 🟒 πŸ• Warteliste
Dance Camp (TACA 11) πŸ’ƒ 1 10:00–16:00 6h/day Tanz-Fabrik ~1km 🟑 βœ… Many
Dance Camp (TACA 21) πŸ’ƒ 2 10:00–16:00 6h/day Tanz-Fabrik ~1km 🟑 βœ… Many
Tennis Belvoir (TEBE 21) 🎾 2 10:00–16:00 6h/day TC Belvoir ~800m 🟒 🟒 ⚠️ Few spots
Creative Camp (GRDA 21) 🎀 2 9:00–16:00 7h/day ⭐ GZ Heuried ~1.5km 🟑 βœ… Many
Basketball (BASC 11) πŸ€ 1 9:00–16:00 7h/day ⭐ RebhΓΌgel ~3km 🟠 βœ… Many
Basketball (BASC 21) πŸ€ 2 9:00–16:00 7h/day ⭐ RebhΓΌgel ~3km 🟠 βœ… Many
Riding camp (RECA 11) 🐴 1 10:00–16:00 6h/day Kolbenhof ~4.5km πŸ”΄ πŸ• Warteliste
Vaulting Camp (VOCA 21) 🐴 2 10:00–16:00 6h/day Kolbenhof ~4.5km πŸ”΄ πŸ• Warteliste

πŸ‘¦ Seba only (born 2020)

Course Week Time Duration Location Dist. Availability
Robotics AM (FRO 11) πŸ€– 1 8:30–12:00 3.5h/day Tekmania ~500m 🟒 🟒 βœ… Many
Robotics PM (FRO 12) πŸ€– 1 13:00–16:30 3.5h/day Tekmania ~500m 🟒 🟒 βœ… Many
↳ Combined πŸ€–πŸ€– 1 8:30–16:30 7h/day ⭐ Tekmania ~500m 🟒 🟒 βœ… Many

Warning

Seba has very few full-week options beyond Robotics + the "both kids" courses above. Single-day animal activities fill the gaps well.


πŸ† RECOMMENDED PLAN "PAPΓ€ & MAMMA LAVORANO"

Week 1 (Oct 5–9) β€” Settimana 1

Mon 5 Tue 6 Wed 7 Thu 8 Fri 9
πŸ§’ Ale Capoeira πŸ₯‹ 9–16 (7h) Capoeira πŸ₯‹ 9–16 (7h) Capoeira πŸ₯‹ 9–16 (7h) Capoeira πŸ₯‹ 9–16 (7h) Capoeira πŸ₯‹ 9–16 (7h)
πŸ‘¦ Seba Robot πŸ€– 8:30–16:30 (7h) Robot πŸ€– 8:30–16:30 (7h) Robot πŸ€– 8:30–16:30 (7h) Robot πŸ€– 8:30–16:30 (7h) Robot πŸ€– 8:30–16:30 (7h)
β˜• Free hours 9:00–16:00 πŸŽ‰ 9:00–16:00 πŸŽ‰ 9:00–16:00 πŸŽ‰ 9:00–16:00 πŸŽ‰ 9:00–16:00 πŸŽ‰

Week 1 total free hours: ~35h πŸŽ‰πŸŽ‰πŸŽ‰

Alternative for Ale: Basketball (BASC 11, 9–16, 7h, 3km) or Dance (TACA 11, 10–16, 6h, 1km)

Alternative "both together": Mini Basketball (MICA 11, 9:30–15:30, 6h, 4km) β€” both go to same place!

Week 2 (Oct 12–16) β€” Settimana 2

Option A: Ale full week + Seba mix

Mon 12 Tue 13 Wed 14 Thu 15 Fri 16
πŸ§’ Ale Creative 🎀 9–16 (7h) Creative 🎀 9–16 (7h) Creative 🎀 9–16 (7h) Creative 🎀 9–16 (7h) Creative 🎀 9–16 (7h)
πŸ‘¦ Seba β€” πŸ˜… Dogs 🐢 13–17 (4h) Animals 🦸 10–15 (5h) β€” πŸ˜… SchΓΆflis πŸ‘ 10–15 (5h)
β˜• Free hours Seba home 😒 13:00–16:00 (3h) 10:00–15:00 (5h) Seba home 😒 10:00–15:00 (5h)

Option B: Both together at Tennis ⭐ (if waitlist clears)

Mon–Fri
πŸ‘« Both Tennis Uetliberg 🎾 9:45–16:30 (6h45/day, 5km)
β˜• Free hours ~34h/week πŸŽ‰ but far away and waitlist

Option C: Ale at Basketball + Seba at animal days

Mon 12 Tue 13 Wed 14 Thu 15 Fri 16
πŸ§’ Ale Basketball πŸ€ 9–16 (7h) Basketball πŸ€ 9–16 (7h) Basketball πŸ€ 9–16 (7h) Basketball πŸ€ 9–16 (7h) Basketball πŸ€ 9–16 (7h)
πŸ‘¦ Seba β€” πŸ˜… Dogs 🐢 13–17 (4h) Animals 🦸 10–15 (5h) β€” πŸ˜… SchΓΆflis πŸ‘ 10–15 (5h)

πŸ“Š Coverage Summary / Riepilogo copertura

Plan Week Ale (h/week) Seba (h/week) Both out (h) Notes
Recommended 1 35h (Capoeira) 35h (Robot x2) 35h ⭐⭐⭐ Different locations but max coverage
Alt "Together" 1 30h (Mini Basket) 30h (Mini Basket) 30h ⭐⭐ Same place! But 4km
Recommended 2 35h (Creative) ~14h (animal days) ~13h ⭐ Seba has gaps Mon/Thu
Alt Tennis 2 34h (Tennis) 34h (Tennis) 34h ⭐⭐⭐ Waitlist + 5km

βœ… ACTION ITEMS β€” Deadline: 29 September!

Caution

Only 2 weeks left to register for Week 1! Solo 2 settimane per iscriversi alla 1Βͺ settimana!

Must-do (spots available βœ…)

  • Register Seba β†’ Robotics FRO 11 + FRO 12 (Week 1, Tekmania)
  • Register Ale β†’ Capoeira CACA 11 (Week 1, Entlisberg) OR Basketball BASC 11 (Week 1, RebhΓΌgel)
  • Register Ale β†’ Creative Camp GRDA 21 (Week 2, GZ Heuried) OR Basketball BASC 21 (Week 2, RebhΓΌgel)

Try waitlist (put names down πŸ•)

  • Register both β†’ Acrobatics AKCL 11 (Week 1, Gabler β€” only 600m! 5h!)
  • Register both β†’ Tennis Uetliberg TEUE 11 + TEUE 21 (6h45/day!)
  • Register both β†’ Coding & Art FCO 21 (Week 2, 200m from home!)

Single-day activities (Week 2 gaps for Seba)

  • Register both β†’ Animal Heroes FTA 21 (Week 2, Wed)
  • Register both β†’ SchΓΆflis FSC 22 (Week 2, Fri β€” spots available!)
  • Register both β†’ No fear of dogs FAH 21 (Week 2, Tue PM)

πŸ”— Stadt ZΓΌrich Ferienkurse Portal

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>🏫 Herbstferien 2026 β€” Ferienkurse Planner</title>
<style>
:root {
--ale: #4A90D9;
--seba: #E67E22;
--both: #27AE60;
--bg: #f5f5f5;
--card: #fff;
--shadow: 0 2px 8px rgba(0,0,0,0.08);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
background: var(--bg); color: #333; padding: 16px;
max-width: 1400px; margin: 0 auto;
}
h1 { font-size: 1.5em; margin-bottom: 4px; }
.subtitle { color: #666; margin-bottom: 12px; font-size: 0.9em; }
.stats-bar {
display: flex; gap: 16px; flex-wrap: wrap;
background: var(--card); border-radius: 12px; padding: 12px 20px;
box-shadow: var(--shadow); margin-bottom: 12px; align-items: center;
}
.stat { text-align: center; min-width: 80px; }
.stat .num { font-size: 1.8em; font-weight: 700; }
.stat .label { font-size: 0.7em; color: #888; text-transform: uppercase; }
.stat.ale .num { color: var(--ale); }
.stat.seba .num { color: var(--seba); }
.stat.free .num { color: var(--both); }
.legend { display: flex; gap: 10px; margin-left: auto; font-size: 0.8em; align-items: center; }
.legend-dot { width: 12px; height: 12px; border-radius: 50%; display: inline-block; margin-right: 3px; vertical-align: middle; }
.deadline-banner {
background: #fff3e0; border: 2px solid #ff9800; border-radius: 8px;
padding: 10px 16px; margin-bottom: 12px; font-size: 0.85em;
}
.deadline-banner strong { color: #e65100; }
.week-tabs { display: flex; gap: 8px; margin-bottom: 12px; }
.week-tab {
padding: 8px 20px; border-radius: 8px; cursor: pointer;
background: var(--card); border: 2px solid #ddd; font-weight: 600; font-size: 0.9em;
transition: all 0.2s; user-select: none;
}
.week-tab.active { border-color: var(--both); background: #e8f5e9; }
.week-tab:hover { border-color: #999; }
.main-grid { display: grid; grid-template-columns: 280px 1fr; gap: 12px; }
@media (max-width: 900px) { .main-grid { grid-template-columns: 1fr; } }
.activities-panel {
background: var(--card); border-radius: 12px; padding: 12px;
box-shadow: var(--shadow); max-height: 75vh; overflow-y: auto;
}
.activities-panel h3 { margin-bottom: 8px; font-size: 0.95em; }
.section-label {
font-size: 0.75em; font-weight: 700; color: #888;
text-transform: uppercase; margin: 10px 0 4px; letter-spacing: 0.5px;
border-bottom: 1px solid #eee; padding-bottom: 3px;
}
.activity-card {
border: 2px solid #eee; border-radius: 8px; padding: 8px 10px;
margin-bottom: 6px; cursor: pointer; transition: all 0.15s;
position: relative; user-select: none;
}
.activity-card:hover { border-color: #bbb; transform: translateX(2px); }
.activity-card.active.ale-only { border-color: var(--ale); background: #e8f0fa; }
.activity-card.active.seba-only { border-color: var(--seba); background: #fdf0e4; }
.activity-card.active.both-kids { border-color: var(--both); background: #e8f5e9; }
.activity-card .name { font-weight: 700; font-size: 0.85em; }
.activity-card .meta { font-size: 0.7em; color: #888; margin-top: 2px; }
.badge {
display: inline-block; padding: 1px 5px; border-radius: 3px;
font-size: 0.65em; font-weight: 600; color: #fff; margin-right: 3px;
}
.badge.ale { background: var(--ale); }
.badge.seba { background: var(--seba); }
.badge.both { background: var(--both); }
.badge.avail-green { background: #27ae60; }
.badge.avail-yellow { background: #f39c12; }
.badge.avail-red { background: #e74c3c; }
.activity-card .link {
position: absolute; top: 6px; right: 8px; font-size: 0.75em;
color: #4A90D9; text-decoration: none;
}
.activity-card .link:hover { text-decoration: underline; }
.calendar-panel {
background: var(--card); border-radius: 12px; padding: 12px;
box-shadow: var(--shadow); overflow-x: auto;
}
.cal-grid {
display: grid; grid-template-columns: 50px repeat(5, 1fr);
gap: 1px; min-width: 500px;
}
.cal-header {
text-align: center; font-weight: 700; font-size: 0.8em;
padding: 6px 2px; background: #f8f8f8; border-radius: 4px;
}
.cal-time {
font-size: 0.65em; color: #aaa; text-align: right; padding-right: 4px;
height: 24px; display: flex; align-items: center; justify-content: flex-end;
}
.cal-cell { height: 24px; position: relative; border-bottom: 1px solid #f8f8f8; }
.cal-block {
position: absolute; left: 1px; right: 1px; border-radius: 3px;
font-size: 0.55em; color: #fff; padding: 1px 3px;
overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
z-index: 2; cursor: pointer; font-weight: 600;
display: flex; align-items: center;
}
.cal-block.ale { background: var(--ale); }
.cal-block.seba { background: var(--seba); }
.cal-block.both { background: var(--both); }
.cal-block:hover { opacity: 0.85; filter: brightness(1.1); }
.free-row {
display: grid; grid-template-columns: 50px repeat(5, 1fr);
gap: 1px; margin-top: 8px; padding-top: 8px; border-top: 2px dashed #ddd;
min-width: 500px;
}
.free-cell {
text-align: center; font-size: 0.75em; padding: 5px;
border-radius: 4px; font-weight: 700;
}
.free-cell.good { background: #e8f5e9; color: #27ae60; }
.free-cell.ok { background: #fff8e1; color: #f57f17; }
.free-cell.bad { background: #fce4ec; color: #c62828; }
.conflict-warn {
background: #fce4ec; border: 1px solid #e57373; border-radius: 6px;
padding: 6px 12px; margin-bottom: 8px; font-size: 0.8em; color: #c62828;
display: none;
}
.conflict-warn.show { display: block; }
</style>
</head>
<body>
<h1>🏫 Herbstferien 2026 β€” Ferienkurse Planner</h1>
<p class="subtitle">πŸ“ TΓΆdistrasse 27, 8002 ZΓΌrich &nbsp;|&nbsp; πŸ§’ Ale (2018) &nbsp; πŸ‘¦ Seba (2020) &nbsp;|&nbsp; Click activities to toggle on/off</p>
<div class="deadline-banner">
⚠️ <strong>Week 1 deadline: 29 Sep 2026</strong> (2 weeks!) &nbsp;|&nbsp; Week 2 deadline: 6 Oct 2026 &nbsp;|&nbsp;
<a href="https://www.stadt-zuerich.ch/sport-portal/angebot/ferienkurse" target="_blank">πŸ”— Stadt ZΓΌrich Portal</a>
</div>
<div class="stats-bar">
<div class="stat ale"><div class="num" id="stat-ale">0h</div><div class="label">πŸ§’ Ale covered</div></div>
<div class="stat seba"><div class="num" id="stat-seba">0h</div><div class="label">πŸ‘¦ Seba covered</div></div>
<div class="stat free"><div class="num" id="stat-free">0h</div><div class="label">β˜• Both out (2 wks)</div></div>
<div class="legend">
<span><span class="legend-dot" style="background:var(--ale)"></span>Ale</span>
<span><span class="legend-dot" style="background:var(--seba)"></span>Seba</span>
<span><span class="legend-dot" style="background:var(--both)"></span>Both</span>
</div>
</div>
<div class="conflict-warn" id="conflict-warn">⚠️ Schedule conflict detected!</div>
<div class="week-tabs">
<div class="week-tab active" onclick="showWeek(1)">πŸ“… Week 1 β€” Oct 5–9</div>
<div class="week-tab" onclick="showWeek(2)">πŸ“… Week 2 β€” Oct 12–16</div>
</div>
<div class="main-grid">
<div class="activities-panel" id="activities-panel"></div>
<div class="calendar-panel">
<div class="cal-grid" id="cal-grid"></div>
<div class="free-row" id="free-row"></div>
</div>
</div>
<script>
const BASE_URL='https://www.stadt-zuerich.ch/sport-portal/angebot/ferienkurse';
const COURSES=[
{id:'FRO11',name:'Robotics πŸ€– (AM)',code:'FRO 11',week:1,days:[0,1,2,3,4],start:8.5,end:12,kid:'seba',loc:'Tekmania',dist:'500m 🟒',avail:'green',availText:'Many spots',age:'2019–2021'},
{id:'FRO12',name:'Robotics πŸ€– (PM)',code:'FRO 12',week:1,days:[0,1,2,3,4],start:13,end:16.5,kid:'seba',loc:'Tekmania',dist:'500m 🟒',avail:'green',availText:'Many spots',age:'2019–2021'},
{id:'AKCL11',name:'Acrobatics & Clowning 🀸',code:'AKCL 11',week:1,days:[0,1,2,3,4],start:10,end:15,kid:'both',loc:'Gabler',dist:'600m 🟒',avail:'yellow',availText:'Waitlist',age:'2017–2020'},
{id:'MICA11',name:'Mini Basketball πŸ€',code:'MICA 11',week:1,days:[0,1,2,3,4],start:9.5,end:15.5,kid:'both',loc:'Falletsche',dist:'4km 🟠',avail:'green',availText:'Many spots',age:'2013–2020'},
{id:'TEUE11',name:'Tennis Uetliberg 🎾',code:'TEUE 11',week:1,days:[0,1,2,3,4],start:9.75,end:16.5,kid:'both',loc:'TC Uetliberg',dist:'5km πŸ”΄',avail:'yellow',availText:'Waitlist',age:'2014–2020'},
{id:'BASC11',name:'Basketball πŸ€',code:'BASC 11',week:1,days:[0,1,2,3,4],start:9,end:16,kid:'ale',loc:'RebhΓΌgel',dist:'3km 🟠',avail:'green',availText:'Many spots',age:'2010–2019'},
{id:'CACA11',name:'Capoeira πŸ₯‹',code:'CACA 11',week:1,days:[0,1,2,3,4],start:9,end:16,kid:'ale',loc:'Entlisberg',dist:'2.5km 🟠',avail:'green',availText:'Many spots',age:'2014–2019'},
{id:'FDB11',name:'Printing & Crafting 🎨',code:'FDB 11',week:1,days:[0,1,2,3,4],start:9,end:15,kid:'ale',loc:'Rote Fabrik',dist:'2km 🟑',avail:'yellow',availText:'Few spots',age:'2016–2019'},
{id:'SUP11',name:'SUP & Kayak πŸ„',code:'SUP 11',week:1,days:[0,1,2,3,4],start:8.5,end:11.5,kid:'ale',loc:'Mythenquai',dist:'900m 🟒',avail:'green',availText:'Many spots',age:'2015–2019'},
{id:'TACA11',name:'Dance Camp πŸ’ƒ',code:'TACA 11',week:1,days:[0,1,2,3,4],start:10,end:16,kid:'ale',loc:'TANZ-FABRIK',dist:'1km 🟑',avail:'green',availText:'Many spots',age:'2015–2019'},
{id:'TEBE11',name:'Tennis Belvoir 🎾',code:'TEBE 11',week:1,days:[0,1,2,3,4],start:10,end:16,kid:'ale',loc:'TC Belvoir',dist:'800m 🟒',avail:'yellow',availText:'Waitlist',age:'2014–2018'},
{id:'RECA11',name:'Riding Camp 🐴',code:'RECA 11',week:1,days:[0,1,2,3,4],start:10,end:16,kid:'ale',loc:'Kolbenhof',dist:'4.5km πŸ”΄',avail:'yellow',availText:'Waitlist',age:'2015–2018'},
{id:'FSC12',name:'SchΓΆflis πŸ‘ (Tue)',code:'FSC 12',week:1,days:[1],start:10,end:15,kid:'both',loc:'Mittelleimbach',dist:'5km πŸ”΄',avail:'yellow',availText:'Waitlist',age:'2014–2020'},
{id:'FEN11',name:'Nature Heroes 🦎 (Thu)',code:'FEN 11',week:1,days:[3],start:10,end:15,kid:'both',loc:'Mittelleimbach',dist:'5km πŸ”΄',avail:'yellow',availText:'Few spots',age:'2014–2020'},
{id:'FWH11',name:'Dog Walking πŸ•β€πŸ¦Ί (Mon)',code:'FWH 11',week:1,days:[0],start:13,end:17,kid:'ale',loc:'AlbisgΓΌtli',dist:'3.5km 🟠',avail:'yellow',availText:'Few spots',age:'2014–2018'},
{id:'FZH11',name:'Dog Expert πŸ• (Tue)',code:'FZH 11',week:1,days:[1],start:13,end:17,kid:'ale',loc:'AlbisgΓΌtli',dist:'3.5km 🟠',avail:'yellow',availText:'Waitlist',age:'2014–2018'},
{id:'FCO21',name:'Coding & Art πŸ’»πŸŽ¨',code:'FCO 21',week:2,days:[0,1,2,3,4],start:9.5,end:16,kid:'both',loc:'Coding a Story',dist:'200m 🟒',avail:'yellow',availText:'Waitlist',age:'2014–2021'},
{id:'TEUE21',name:'Tennis Uetliberg 🎾',code:'TEUE 21',week:2,days:[0,1,2,3,4],start:9.75,end:16.5,kid:'both',loc:'TC Uetliberg',dist:'5km πŸ”΄',avail:'yellow',availText:'Waitlist',age:'2014–2020'},
{id:'TACA21',name:'Dance Camp πŸ’ƒ',code:'TACA 21',week:2,days:[0,1,2,3,4],start:10,end:16,kid:'ale',loc:'TANZ-FABRIK',dist:'1km 🟑',avail:'green',availText:'Many spots',age:'2015–2019'},
{id:'TEBE21',name:'Tennis Belvoir 🎾',code:'TEBE 21',week:2,days:[0,1,2,3,4],start:10,end:16,kid:'ale',loc:'TC Belvoir',dist:'800m 🟒',avail:'yellow',availText:'Few spots',age:'2014–2018'},
{id:'BASC21',name:'Basketball πŸ€',code:'BASC 21',week:2,days:[0,1,2,3,4],start:9,end:16,kid:'ale',loc:'RebhΓΌgel',dist:'3km 🟠',avail:'green',availText:'Many spots',age:'2010–2019'},
{id:'GRDA21',name:'Creative Camp 🎀',code:'GRDA 21',week:2,days:[0,1,2,3,4],start:9,end:16,kid:'ale',loc:'GZ Heuried',dist:'1.5km 🟑',avail:'green',availText:'Many spots',age:'2011–2018'},
{id:'VOCA21',name:'Vaulting Camp 🐴',code:'VOCA 21',week:2,days:[0,1,2,3,4],start:10,end:16,kid:'ale',loc:'Kolbenhof',dist:'4.5km πŸ”΄',avail:'yellow',availText:'Waitlist',age:'2015–2018'},
{id:'BUD21',name:'Budo πŸ₯‹',code:'BUD 21',week:2,days:[0,1,2,3,4],start:9,end:10.5,kid:'ale',loc:'Budokan',dist:'1.2km 🟑',avail:'green',availText:'Many spots',age:'2012–2019'},
{id:'FAH21',name:'No Fear of Dogs 🐢 (Tue)',code:'FAH 21',week:2,days:[1],start:13,end:17,kid:'both',loc:'AlbisgΓΌtli',dist:'3.5km 🟠',avail:'green',availText:'Many spots',age:'2016–2020'},
{id:'FTA21',name:'Animal Heroes 🦸 (Wed)',code:'FTA 21',week:2,days:[2],start:10,end:15,kid:'both',loc:'Mittelleimbach',dist:'5km πŸ”΄',avail:'green',availText:'Many spots',age:'2014–2020'},
{id:'FNS21',name:'SchΓΆflis πŸ‘ (Tue)',code:'FNS 21',week:2,days:[1],start:10,end:15,kid:'both',loc:'Mittelleimbach',dist:'5km πŸ”΄',avail:'yellow',availText:'Waitlist',age:'2014–2020'},
{id:'FSC21',name:'SchΓΆflis πŸ‘ (Thu)',code:'FSC 21',week:2,days:[3],start:10,end:15,kid:'both',loc:'Mittelleimbach',dist:'5km πŸ”΄',avail:'yellow',availText:'Waitlist',age:'2014–2020'},
{id:'FSC22',name:'SchΓΆflis πŸ‘ (Fri)',code:'FSC 22',week:2,days:[4],start:10,end:15,kid:'both',loc:'Mittelleimbach',dist:'5km πŸ”΄',avail:'green',availText:'Many spots',age:'2014–2020'},
{id:'FZH21',name:'Dog Expert πŸ• (Mon)',code:'FZH 21',week:2,days:[0],start:13,end:17,kid:'ale',loc:'AlbisgΓΌtli',dist:'3.5km 🟠',avail:'yellow',availText:'Waitlist',age:'2013–2018'},
{id:'FWH21',name:'Dog Walking πŸ•β€πŸ¦Ί (Fri)',code:'FWH 21',week:2,days:[4],start:13,end:17,kid:'ale',loc:'AlbisgΓΌtli',dist:'3.5km 🟠',avail:'yellow',availText:'Waitlist',age:'2014–2018'},
];
const DAYS=['Mon','Tue','Wed','Thu','Fri'];
const DATES={1:['Oct 5','Oct 6','Oct 7','Oct 8','Oct 9'],2:['Oct 12','Oct 13','Oct 14','Oct 15','Oct 16']};
const T0=8,T1=17;
let curWeek=1,active=new Set(['FRO11','FRO12','CACA11','TACA21','FTA21','FSC22']);
function showWeek(w){curWeek=w;document.querySelectorAll('.week-tab').forEach((t,i)=>t.classList.toggle('active',i===w-1));render();}
function toggle(id){active.has(id)?active.delete(id):active.add(id);render();}
function fmtT(t){const h=Math.floor(t),m=Math.round((t-h)*60);return h+':'+String(m).padStart(2,'0');}
function render(){renderActs();renderCal();renderStats();}
function renderActs(){
const p=document.getElementById('activities-panel');
const wc=COURSES.filter(c=>c.week===curWeek);
const full=wc.filter(c=>c.days.length===5),single=wc.filter(c=>c.days.length<5);
let h=`<h3>Week ${curWeek} Activities</h3>`;
const sections=[['πŸ‘« Both kids','both'],['πŸ§’ Ale only','ale'],['πŸ‘¦ Seba only','seba']];
for(const[label,kid]of sections){
const items=[...full,...single].filter(c=>c.kid===kid);
if(!items.length)continue;
h+=`<div class="section-label">${label}</div>`;
items.forEach(c=>{
const on=active.has(c.id);
const dur=c.end-c.start,dh=Math.floor(dur),dm=Math.round((dur-dh)*60);
const ds=dm>0?dh+'h'+dm+'m':dh+'h';
const ts=fmtT(c.start)+'–'+fmtT(c.end);
const dayStr=c.days.length===5?'Mon–Fri':c.days.map(d=>DAYS[d]).join(', ');
const kc=c.kid==='ale'?'ale-only':c.kid==='seba'?'seba-only':'both-kids';
h+=`<div class="activity-card ${on?'active':''} ${kc}" onclick="toggle('${c.id}')">
<a class="link" href="${BASE_URL}?q=${encodeURIComponent(c.code)}" target="_blank" onclick="event.stopPropagation()">πŸ”—</a>
<div class="name">${on?'βœ…':'⬜'} ${c.name}</div>
<div class="meta">
<span class="badge ${c.kid}">${c.kid==='both'?'πŸ‘« Both':c.kid==='ale'?'πŸ§’ Ale':'πŸ‘¦ Seba'}</span>
<span class="badge avail-${c.avail}">${c.availText}</span>
${ds} Β· ${ts} Β· ${dayStr}
</div>
<div class="meta">πŸ“ ${c.loc} (${c.dist}) Β· Ages ${c.age}</div>
</div>`;
});
}
p.innerHTML=h;
}
function renderCal(){
const g=document.getElementById('cal-grid');
const slots=(T1-T0)*2,dates=DATES[curWeek];
let h='<div class="cal-header"></div>';
for(let d=0;d<5;d++)h+=`<div class="cal-header">${DAYS[d]}<br><span style="font-weight:400;font-size:0.75em">${dates[d]}</span></div>`;
for(let s=0;s<slots;s++){
const t=T0+s*0.5;
h+=s%2===0?`<div class="cal-time">${fmtT(t)}</div>`:`<div class="cal-time" style="color:#ddd">:30</div>`;
for(let d=0;d<5;d++)h+=`<div class="cal-cell" data-d="${d}" data-s="${s}"></div>`;
}
g.innerHTML=h;
const ac=COURSES.filter(c=>c.week===curWeek&&active.has(c.id));
// Check conflicts per kid per day
let hasConflict=false;
ac.forEach(c=>{
c.days.forEach(d=>{
const ss=Math.round((c.start-T0)*2),se=Math.round((c.end-T0)*2);
const height=(se-ss)*25-1;
const cells=g.querySelectorAll(`[data-d="${d}"][data-s="${ss}"]`);
if(!cells.length)return;
const cell=cells[0];
const existing=cell.querySelectorAll('.cal-block');
const blk=document.createElement('div');
blk.className=`cal-block ${c.kid==='both'?'both':c.kid}`;
blk.style.height=height+'px';blk.style.top='0';
if(existing.length>0){
// Check if same kid or overlapping
blk.style.left=c.kid==='seba'||c.kid==='both'?'50%':'1px';
blk.style.width='48%';
if(existing[0].style.width!=='48%'){existing[0].style.width='48%';}
}
const shortName=c.name.replace(/[^\w\s&]/gu,'').trim().split(' ').slice(0,2).join(' ');
blk.textContent=shortName;
blk.title=`${c.name}\n${fmtT(c.start)}–${fmtT(c.end)}\n${c.loc} (${c.dist})`;
cell.appendChild(blk);
});
});
// Free hours
const fr=document.getElementById('free-row');
let fh='<div class="free-cell" style="font-size:0.65em;color:#888">β˜• Both<br>out</div>';
for(let d=0;d<5;d++){
const dc=ac.filter(c=>c.days.includes(d));
const aleS=new Set(),sebaS=new Set();
dc.forEach(c=>{for(let t=c.start;t<c.end;t+=0.5){const s=Math.round(t*2);if(c.kid==='ale'||c.kid==='both')aleS.add(s);if(c.kid==='seba'||c.kid==='both')sebaS.add(s);}});
let bo=0;for(const s of aleS)if(sebaS.has(s))bo+=0.5;
const cls=bo>=5?'good':bo>=3?'ok':'bad';
fh+=`<div class="free-cell ${cls}">${bo}h</div>`;
}
fr.innerHTML=fh;
}
function renderStats(){
let at=0,st=0,bt=0;
for(let w=1;w<=2;w++){
const ac=COURSES.filter(c=>c.week===w&&active.has(c.id));
for(let d=0;d<5;d++){
const dc=ac.filter(c=>c.days.includes(d));
const aleS=new Set(),sebaS=new Set();
dc.forEach(c=>{for(let t=c.start;t<c.end;t+=0.5){const s=Math.round(t*2);if(c.kid==='ale'||c.kid==='both')aleS.add(s);if(c.kid==='seba'||c.kid==='both')sebaS.add(s);}});
at+=aleS.size*0.5;st+=sebaS.size*0.5;
for(const s of aleS)if(sebaS.has(s))bt+=0.5;
}
}
document.getElementById('stat-ale').textContent=at+'h';
document.getElementById('stat-seba').textContent=st+'h';
document.getElementById('stat-free').textContent=bt+'h';
}
render();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment