HEROData: logo
theme.logo.default_url, name display_name (translated), cover site.cover_image, tagline site.content[lang].tagline (max 140), tags site.content[lang].tags (max 4 × 24 chars). The first tag can be highlighted (accent color).
- Without a cover: a gradient from the brand colors.
- "Join" follows
joinPolicy: open → "Join"; approval → "Request to join"; invite → hidden. - Mobile: text over the image bottom; buttons stack full width.
ABOUTData:
site.content[lang].about, plain text with line breaks (max 1,200 chars), and site.about_image.
- Stats (members, classes a week, coaches) were removed: phase 2 / pending decision whether to show real numbers.
- Without an image: the text takes the full width.
WHY HYGEARFixed block, the same in every studio, written and translated by HyGear (interface strings, not studio content). The studio can only switch it off. Icons: one per card, colored brand / secondary / accent.
◎
↗
⌁
SCHEDULELive data from the studio's real schedule (
studio_classes): GET /public-studio-sites/:slug → classes[] with starts_at, ends_at, duration, workout_name (in the page language), room_name, studio_name, difficulty_level (1–3).
- No coach and no capacity / spots: the API hides them on purpose, and there is no booking. Members check in at the studio by scanning the room's QR, so the button is "Log in to check in".
- 7 day tabs in the studio's timezone (
timezone); a dot marks days with classes. Cancelled classes are not returned. - Empty state: no classes in the 7 days (toggle it in the controls).
- Room / studio name: show the studio name only when the account has more than one studio.
- Times always LTR, also in Hebrew.
▣
GALLERYData:
site.gallery[], up to 6 images uploaded in the admin (JPG/PNG/WebP, max 5 MB each, stored on Cloudinary). No text, the same in both languages.
- 1 image: full width; 2–4: a simple grid; 5–6: this mosaic.
- No images: the section is hidden automatically even if it is switched on.
- Tap opens a lightbox (design needed).
VISITData:
site.content[lang].place_name, site.content[lang].address (per language); once for all languages: site.hours[] ({days: [0–6], open, close}), site.contact (phone, whatsapp, email, website, instagram, facebook).
- Map: Google Maps embed of the address (no API key needed for the basic embed).
- Today's row is highlighted (secondary green). Day names come from the browser (
Intl), so they are always in the page language. - Each contact line appears only when filled. WhatsApp opens
wa.me. - Pending decision: hours typed by hand (current) or derived from the schedule.
JOINEverything runs in the web app; there are no store links. "Join" →
/join/<slug> (the existing signup, already branded per studio); "Log in" → /login. The PWA hint shows only where the browser can install the site (Chrome/Android: install prompt; iOS Safari: "Share → Add to Home Screen" instructions screen that already exists).