Skip to content

Commit 6209122

Browse files
authored
Merge pull request #73 from miloshb/content
Dual-site hosting for corporate and brand; expand about section [major]
2 parents a76e59f + 6dd8a22 commit 6209122

69 files changed

Lines changed: 1173 additions & 150 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

doc/README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,15 @@ Cloudflare Pages compiles every single active branch in the repository into its
4040

4141
| Git Branch | Deployment Target URL | Environment Purpose |
4242
| :--- | :--- | :--- |
43-
| **`main`** | [studiosunandsea.com](https://studiosunandsea.com) | Live Production Site |
44-
| **`stage`** | [stage.studio-sun-and-sea.pages.dev](https://stage.studio-sun-and-sea.pages.dev) | QA Staging Sandbox |
43+
| **`main`** | [studiosunandsea.com](https://studiosunandsea.com) and [eireneintentiocitta.hr](https://eireneintentiocitta.hr) | Live Production Sites |
44+
| **`stage`** (default) | [stage.studio-sun-and-sea.pages.dev](https://stage.studio-sun-and-sea.pages.dev) | QA Staging Sandbox |
4545
| `mindbody` | [mindbody.studio-sun-and-sea.pages.dev](https://mindbody.studio-sun-and-sea.pages.dev) | Widget Testing Environment |
4646
| `content` | [content.studio-sun-and-sea.pages.dev](https://content.studio-sun-and-sea.pages.dev) | Content and UI Testing |
4747
| `cm` | [cm.studio-sun-and-sea.pages.dev](https://cm.studio-sun-and-sea.pages.dev) | Configuration Management Testing |
4848
| `feat/*` | `[branch-name].studio-sun-and-sea.pages.dev` | Automated Preview Link |
4949

50+
Cloudflare hosts both [studiosunandsea.com](https://studiosunandsea.com) and [eireneintentiocitta.hr](https://eireneintentiocitta.hr) from this GitHub repo. The environment variable **SITE_MODE** is used to drive different site behaviour where needed (*brand* => Studio Sun & Sea; *corp* => Eirene Intentio Citta d.o.o.).
51+
5052
---
5153

5254
## 3. Automated CI/CD Testing & Production Validation
@@ -59,7 +61,9 @@ Automated test pipeline is configured natively via GitHub Actions inside `.githu
5961
3. **Configuration Sync**: Verifies that `public/robots.txt` contains the correct absolute path matching the sitemap tracking string.
6062
4. **Link Health Integrity ([Linkinator](https://jbeckwith.com/projects/linkinator))**: Spins up a local background preview server instance to mimic Cloudflare's serving layer and crawls all links to guarantee **zero internal 404 broken routes**.
6163
5. **Automated Release Bumping**: Semantic versions (`npm version`) are calculated and pushed **only** upon successful validation landing on the `main` branch.
64+
* If the commit message contains [major] or [minor], it bumps accordingly.
65+
* Otherwise, it defaults to a standard patch bump (e.g. 0.0.2 -> 0.0.3).
6266
6. **AI Agent Scan**: Check conformance to standards for agentic commerce: [AI Agent Scan Remediation](../security/com.isitagentready.md)
6367

6468
---
65-
*Last Checked: June 2026*
69+
*Last Checked: September 2026*

public/styles/global.css

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,3 +321,37 @@ img {
321321
object-fit: cover;
322322
border-radius: var(--radius-md);
323323
}
324+
325+
/* ---------------------------------
326+
11. TOOLTIPS
327+
---------------------------------- */
328+
329+
.tooltip {
330+
position: relative;
331+
cursor: help;
332+
border-bottom: 1px dotted #999;
333+
}
334+
335+
.tooltip .tooltiptext {
336+
visibility: hidden;
337+
opacity: 0;
338+
width: max-content;
339+
max-width: 240px;
340+
background-color: #333;
341+
color: #fff;
342+
text-align: left;
343+
padding: 6px 10px;
344+
border-radius: 4px;
345+
position: absolute;
346+
z-index: 10;
347+
bottom: 125%;
348+
left: 0;
349+
font-size: 0.85rem;
350+
line-height: 1.3;
351+
transition: opacity 0.2s ease-in-out;
352+
}
353+
354+
.tooltip:hover .tooltiptext {
355+
visibility: visible;
356+
opacity: 1;
357+
}

src/components/Footer.astro

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ const footerTopOffset = footerOffset ? "var(--offset-lg)" : "0px";
2525

2626
<!-- Row 2: Left side -->
2727
<div class="footer-copy">
28-
© {new Date().getFullYear()} Studio Sun & Sea
28+
© {new Date().getFullYear()}
29+
<a href="https://eireneintentiocitta.hr">Eirene Intentio Citta d.o.o.</a>
30+
All rights reserved.
2931
</div>
3032

3133
<!-- Row 2: Right side -->

src/components/Header.astro

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
---
22
// src/components/Header.astro
33
import { ROUTES, LANGS, HEADER_ROUTE_KEYS, getLangRoot, getLangUrl } from "../data/routes.js";
4-
import { LANG_STORE_KEY } from "../data/constants.js";
4+
import { getSiteConfig, LANG_STORE_KEY } from "../data/config.ts";
55
import MindbodyLogin from "./widgets/MindbodyLoginLinkV1.astro";
66
7+
const site = getSiteConfig();
8+
79
const { lang, page } = Astro.props;
810
---
911

@@ -13,7 +15,7 @@ const { lang, page } = Astro.props;
1315
<div class="header-brand">
1416
<a class="brand" href={getLangRoot(lang)}>
1517
<img class="aspect-square" src="/favicon/favicon-48.png" alt="Studio Sun & Sea Logo" />
16-
Studio Sun & Sea
18+
{site.name}
1719
</a>
1820
</div>
1921

src/components/Hero.astro

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,41 @@
11
---
2-
const { lang } = Astro.props;
2+
import { getSiteConfig } from "../data/config.ts";
3+
4+
const site = getSiteConfig();
5+
const { lang } = Astro.props as { lang?: "en" | "de" };
36
47
const TEXT = {
5-
en: {
6-
title: "Breathe. Flow. Relax.",
7-
subtitle: "Mindful movement for a calm, connected life.",
8-
cta: "View Schedule",
9-
ctaHref: "/en/schedule-overview",
8+
brand: {
9+
en: {
10+
title: "Breathe. Flow. Relax.",
11+
subtitle: "Mindful movement for a calm, connected life.",
12+
cta: "View Schedule",
13+
ctaHref: "/en/schedule-overview",
14+
},
15+
de: {
16+
title: "Atme. Fließe. Entspanne.",
17+
subtitle: "Achtsame Bewegung für ein ruhiges, verbundenes Leben.",
18+
cta: "Stundenplan ansehen",
19+
ctaHref: "/de/schedule-overview",
20+
},
1021
},
11-
de: {
12-
title: "Atme. Fließe. Entspanne.",
13-
subtitle: "Achtsame Bewegung für ein ruhiges, verbundenes Leben.",
14-
cta: "Stundenplan ansehen",
15-
ctaHref: "/de/schedule-overview",
22+
corp: {
23+
en: {
24+
title: "Peace · Intention · Consciousness",
25+
subtitle: "Mindful movement for a calm, connected life.",
26+
cta: "Who we are?",
27+
ctaHref: "/en/about",
28+
},
29+
de: {
30+
title: "Frieden · Intention · Geist",
31+
subtitle: "Achtsame Bewegung für ein ruhiges, verbundenes Leben.",
32+
cta: "Wer sind wir?",
33+
ctaHref: "/de/about",
34+
},
1635
},
1736
};
1837
19-
const copy = TEXT[lang] ?? TEXT.en;
38+
const copy = TEXT[site.mode]?.[lang ?? "en"] || TEXT.brand[lang ?? "en"];
2039
---
2140

2241
<section class="hero">

src/components/Hreflang.astro

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
---
2+
// src/components/Hreflang.astro
23
import { HREFLANG } from "../data/routes.js";
4+
import { getSiteConfig } from "../data/config.ts";
5+
36
const { page } = Astro.props;
47
const urls = HREFLANG[page];
5-
const base = "https://studiosunandsea.com";
8+
const base = getSiteConfig().canonical;
69
---
710

811
<link rel="alternate" hreflang="en" href={base + urls.en} />

src/components/NextSteps.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { NEXT_STEPS_ROUTE_KEYS, ROUTES } from "../data/routes.js";
44
const { lang = "en", page = null } = Astro.props;
55
66
// Filter out the current page
7-
const stepKeys = NEXT_STEPS_ROUTE_KEYS.filter(key => key !== page);
7+
const stepKeys = (NEXT_STEPS_ROUTE_KEYS ?? []).filter(key => key !== page);
88
---
99

1010
<section class="next-steps">

src/components/Operator.astro

Lines changed: 102 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,127 @@
11
---
22
// src/components/Operator.astro
3-
43
/**
54
* Output consistently the Owner/Operator of the business for legal content.
65
*/
6+
import { getSiteConfig } from "../data/config.ts";
77
interface Props {
88
lang: "en" | "de";
99
heading: string;
1010
}
1111
1212
const { lang, heading } = Astro.props;
1313
14-
const commonContent = {
15-
labelCompany: "Studio Sun & Sea",
16-
ownerName: "Miloš Borojević",
17-
officeAddress1: "Pfarrtannen 7",
18-
officeAddress2: "49808 Lingen, Deutschland",
19-
secondaryAddress1: "9215 Jasmine Lane",
20-
secondaryAddress2: "Irving, TX 75063, USA",
21-
emailLabel: "E-Mail: privacy@studiosunandsea.com"
14+
const site = getSiteConfig();
15+
16+
const companyData = {
17+
brand: "Studio Sun & Sea",
18+
legal: "Eirene Intentio Citta d.o.o.",
19+
20+
// HR Legal Identifiers
21+
oib: "79177761868", // OIB (Personal Identification Number)
22+
vat: "HR79177761868", // PDV (VAT) ID
23+
mbs: "060519793", // Court Registry Number (MBS)
24+
euid: "HRSR.060519793", // European Unique Identifier (EUID)
25+
court: "Trgovački sud u Splitu",
26+
registryUrl: "https://sudreg.pravosudje.hr/ords/r/esudreg/public/podaci-o-poslovnom-subjektu?p28_sbt_mbs=060519793",
27+
capital: "2.500,00 EUR (u cijelosti uplaćen)",
28+
director: "Miloš Borojević",
29+
30+
// Registered Head Office (Split)
31+
adminAddress1: "Varaždinska 2B",
32+
adminAddress2: "21000 Split, Hrvatska",
33+
34+
// Future Studio Location (Supetar, Brač)
35+
studioAddress1: "Jadranska 35",
36+
studioAddress2: "21400 Supetar, Otok Brač, Hrvatska",
37+
38+
// Secondary Offices
39+
germanyAddress1: "Pfarrtannen 7",
40+
germanyAddress2: "49808 Lingen, Deutschland",
41+
usaAddress1: "9215 Jasmine Lane",
42+
usaAddress2: "Irving, TX 75063, USA",
43+
44+
email: site.email,
2245
}
2346
24-
const localeContent = {
47+
const localeLabel = {
2548
de: {
26-
ownerLabel: "Inhaber:",
27-
officeTitle: "Sitz des Unternehmens (Deutschland):",
28-
secondaryTitle: "Weitere Betriebsstätte (USA):",
49+
brand: "Betriebsmarke: ",
50+
legal: "Medieninhaber & Betreiber der Website: ",
51+
registeredSeat: "Sitz der Gesellschaft (Verwaltungssitz): ",
52+
studio: "Künftiger Studiostandort (in Fertigstellung): ",
53+
represented: "Vertreten durch den Geschäftsführer: ",
54+
court: "Handelsregister: ",
55+
registry: "Eintrag im Handelsregister (Sudski registar)",
56+
vat: "USt-IdNr. (PDV): ",
57+
germanyTitle: "Weitere Betriebsstätte (Deutschland): ",
58+
usaTitle: "Weitere Betriebsstätte (USA): ",
2959
},
3060
en: {
31-
ownerLabel: "Owner:",
32-
officeTitle: "Registered Office (Germany):",
33-
secondaryTitle: "Secondary Location (USA):",
61+
brand: "Operating Brand: ",
62+
legal: "Legal Entity & Website Operator: ",
63+
registeredSeat: "Registered Head Office: ",
64+
studio: "Future Studio Location (Under Construction): ",
65+
represented: "Represented by Managing Director: ",
66+
court: "Commercial Register: ",
67+
registry: "Official Court Registry Entry (Sudski registar)",
68+
vat: "VAT ID (PDV): ",
69+
germanyTitle: "Secondary Office (Germany): ",
70+
usaTitle: "Secondary Office (USA): ",
3471
}
3572
}[lang];
3673
---
3774

38-
<section>
75+
<section class="legal-operator">
3976
<h2>{heading}</h2>
77+
78+
<p>
79+
{localeLabel.brand} <strong>{companyData.brand}</strong><br />
80+
{localeLabel.legal} <strong>{companyData.legal}</strong>
81+
</p>
82+
83+
<p>
84+
<strong>{localeLabel.registeredSeat}</strong><br />
85+
{companyData.adminAddress1}<br />
86+
{companyData.adminAddress2}
87+
<br /><br />
88+
<strong>{localeLabel.studio}</strong><br />
89+
{companyData.studioAddress1}<br />
90+
{companyData.studioAddress2}
91+
</p>
92+
93+
<p>
94+
{localeLabel.represented} {companyData.director}<br />
95+
OIB: {companyData.oib}<br />
96+
EUID: {companyData.euid}<br />
97+
{companyData.vat ? (
98+
<>{localeLabel.vat} {companyData.vat}<br /></>
99+
) : (
100+
<small>Nije u sustavu PDV-a prema čl. 90. Zakona o PDV-u<br /></small>
101+
)}
102+
{localeLabel.court} {companyData.court}<br />
103+
MBS: {companyData.mbs}
104+
<small><a
105+
href={companyData.registryUrl}
106+
target="_blank"
107+
rel="noopener noreferrer"
108+
class="registry-link"
109+
>
110+
{localeLabel.registry}
111+
</a></small>
112+
</p>
113+
114+
<p>
115+
<strong>{localeLabel.germanyTitle}</strong><br />
116+
{companyData.germanyAddress1}<br />
117+
{companyData.germanyAddress2}
118+
<br /><br />
119+
<strong>{localeLabel.usaTitle}</strong><br />
120+
{companyData.usaAddress1}<br />
121+
{companyData.usaAddress2}
122+
</p>
123+
40124
<p>
41-
<strong>{commonContent.labelCompany}</strong><br />
42-
{localeContent.ownerLabel} {commonContent.ownerName}<br />
43-
<strong>{localeContent.officeTitle}</strong><br />
44-
{commonContent.officeAddress1}<br />
45-
{commonContent.officeAddress2}<br />
46-
<strong>{localeContent.secondaryTitle}</strong><br />
47-
{commonContent.secondaryAddress1}<br />
48-
{commonContent.secondaryAddress2}<br />
49-
{commonContent.emailLabel}
125+
E-Mail: <a href={`mailto:${companyData.email}`}>{companyData.email}</a>
50126
</p>
51127
</section>

src/components/ScheduleGrid.astro

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
---
22
// src/components/ScheduleGrid.astro
33
import { scheduleData } from "../data/schedule.ts";
4-
import { TZ_STORE_KEY } from "../data/constants.js";
4+
import { TZ_STORE_KEY, debug } from "../data/config.ts";
55
66
const { lang = "en" } = Astro.props;
7-
const debug = false; // Set to true to show debug info
87
98
const dayHeader = {
109
en: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],

src/data/config.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// src/data/config.ts
2+
export const LANG_STORE_KEY = "preferredLanguage";
3+
export const TZ_STORE_KEY = "preferredTimezone";
4+
5+
// Supported lanugages (first listed is primary/default)
6+
export const LANGS = ["en", "de"];
7+
8+
export const debug = false; // Set to true to show debug logs
9+
export const info = true; // Set to true to show info logs
10+
11+
export interface SiteConfig {
12+
name: string;
13+
mode: "brand" | "corp";
14+
canonical: string;
15+
description: string;
16+
email: string;
17+
}
18+
19+
export function getSiteConfig(): SiteConfig {
20+
// Read environment variable set in Cloudflare Pages dashboard
21+
const mode = import.meta.env.SITE_MODE || "brand";
22+
23+
if (mode === "corp") {
24+
return {
25+
name: "Eirene Intentio Citta",
26+
mode: mode,
27+
canonical: "https://eireneintentiocitta.hr",
28+
description: "Eirene Intentio Citta – Peace · Intention · Consciousness",
29+
email: "privacy@eireneintentiocitta.hr",
30+
};
31+
}
32+
33+
// Default build for Studio Sun & Sea
34+
return {
35+
name: "Studio Sun & Sea",
36+
mode: mode,
37+
canonical: "https://studiosunandsea.com",
38+
description: "Yoga classes, private sessions, and workshops with Studio Sun & Sea.",
39+
email: "privacy@studiosunandsea.com",
40+
};
41+
}

0 commit comments

Comments
 (0)