diff --git a/src/components/CookieBanner.astro b/src/components/CookieBanner.astro
new file mode 100644
index 0000000..414c096
--- /dev/null
+++ b/src/components/CookieBanner.astro
@@ -0,0 +1,81 @@
+---
+// Own cookie banner (no iubenda). Not a cookie wall: a floating card, the site
+// stays usable behind it. Reject and Accept have the same weight. The only
+// thing that needs consent is Google Analytics, loaded by window.loadAnalytics
+// (BaseLayout) only after "Accept".
+---
+
+
+
Cookies, in short
+
+ The site works without cookies. If you accept, Google Analytics tells me which pages people
+ read. If you reject, nothing changes for you.
+ Cookie Policy
+
- window.__iubBannerOpen || !!document.getElementById("iubenda-cs-banner");
+ // Never on top of the cookie banner: if it's open, wait for the answer
+ const bannerOpen = () => {
+ const banner = document.getElementById("cookie-banner");
+ return !!banner && !banner.classList.contains("hidden");
+ };
const showToast = () => {
if (bannerOpen()) {
- window.addEventListener("iub-consent", () => setTimeout(showToast, 1500), { once: true });
+ window.addEventListener("cookie-consent", () => setTimeout(showToast, 1500), {
+ once: true,
+ });
return;
}
const isDarkMode = document.documentElement.classList.contains("dark");
@@ -336,27 +310,7 @@ const fullCanonicalUrl = canonicalUrl || `${siteUrl}${Astro.url.pathname}`;
toast.classList.remove("hidden");
}
};
-
- // Never on top of the cookie banner: wait for iubenda, and if the banner
- // is open wait for the visitor's answer. If iubenda never loads
- // (blocked), show it anyway after a few seconds.
- let scheduled = false;
- const scheduleToast = (delay) => {
- if (scheduled) return;
- scheduled = true;
- setTimeout(showToast, delay);
- };
- const afterIubenda = () => {
- scheduleToast(2000);
- };
- if (window.__iubReady) {
- afterIubenda();
- } else {
- window.addEventListener("iub-ready", afterIubenda, { once: true });
- setTimeout(() => {
- if (!window.__iubReady) scheduleToast(0);
- }, 6000);
- }
+ setTimeout(showToast, 2000);
// Enable dark mode
if (tryButton) {
diff --git a/src/layouts/LegalLayout.astro b/src/layouts/LegalLayout.astro
new file mode 100644
index 0000000..223c1a6
--- /dev/null
+++ b/src/layouts/LegalLayout.astro
@@ -0,0 +1,32 @@
+---
+import Footer from "../components/Footer.astro";
+import Navbar from "../components/Navbar.astro";
+import BaseLayout from "./BaseLayout.astro";
+import "../styles/prose.css";
+
+const { frontmatter } = Astro.props;
+---
+
+
+
+
+
+
+
{frontmatter.title}
+
+ Last updated: {frontmatter.updated}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/cookie-policy.md b/src/pages/cookie-policy.md
new file mode 100644
index 0000000..1d805b6
--- /dev/null
+++ b/src/pages/cookie-policy.md
@@ -0,0 +1,43 @@
+---
+layout: ../layouts/LegalLayout.astro
+title: "Cookie Policy"
+description: "Which cookies and local storage lorenzoiovino.com uses and how to change your choice."
+updated: "September 29, 2026"
+---
+
+The site works without cookies. Analytics cookies are set only if you accept them.
+
+## Technical storage (always on)
+
+The site saves a few small values in your browser's local storage. They don't track you and are never sent to anyone. They only remember your choices:
+
+| Name | What it does | Duration |
+|---|---|---|
+| `theme` | light or dark mode | until you clear it |
+| `darkModeToastDismissed` | don't show the "Try Dark Mode?" hint again | until you clear it |
+| `cookieConsent`, `cookieConsentDate` | your answer to the cookie banner | 6 months, then the banner asks again |
+
+These are strictly necessary for the features you use, so they don't need consent.
+
+## Analytics cookies (only if you accept)
+
+If you click **Accept**, Google Analytics 4 sets these cookies to count visits:
+
+| Name | Provider | Duration |
+|---|---|---|
+| `_ga` | Google | 2 years |
+| `_ga_` | Google | 2 years |
+
+More details on how Google uses this data are in the [Privacy Policy](/privacy-policy) and in [Google's documentation](https://policies.google.com/technologies/cookies).
+
+## Third-party content
+
+Some blog posts embed videos from YouTube and GIFs from Giphy. These services may set their own cookies when you open those posts. They are ruled by the cookie policies of [Google](https://policies.google.com/technologies/cookies) and [Giphy](https://support.giphy.com/hc/en-us/articles/360032872931-GIPHY-Privacy-Policy).
+
+## Change your choice
+
+Use **Cookie settings** in the footer to open the banner again. If you switch from Accept to Reject, the Google Analytics cookies are deleted. You can also delete cookies and local storage from your browser settings at any time.
+
+## Contact
+
+[thisloke@proton.me](mailto:thisloke@proton.me)
diff --git a/src/pages/privacy-policy.md b/src/pages/privacy-policy.md
new file mode 100644
index 0000000..5888d70
--- /dev/null
+++ b/src/pages/privacy-policy.md
@@ -0,0 +1,56 @@
+---
+layout: ../layouts/LegalLayout.astro
+title: "Privacy Policy"
+description: "What personal data lorenzoiovino.com processes, why and what your rights are."
+updated: "September 29, 2026"
+---
+
+This is a personal website with a blog. It has no accounts, no forms and no newsletter, so it collects very little. This page explains what.
+
+## Who is responsible
+
+**Lorenzo Iovino** is the data controller.
+
+Contact: [thisloke@proton.me](mailto:thisloke@proton.me)
+
+## What data and why
+
+### Server logs
+
+The site is hosted on a server of **OVH Srl** in Milan, Italy. Like any web server, it records technical data for every request: IP address, date and time, requested page, browser and operating system. These logs are used only to keep the site running and secure (errors, abuse, attacks) and are kept for a limited time.
+
+Legal basis: legitimate interest in running a secure website (art. 6.1.f GDPR).
+
+### Google Analytics (only if you accept)
+
+If you click **Accept** on the cookie banner, the site loads **Google Analytics 4** (Google Ireland Limited / Google LLC) to count visits and see which pages people read. It uses cookies and collects usage data: pages visited, time on page, device, browser, approximate location. Google Analytics 4 does not store the full IP address.
+
+Data may be transferred to the United States. Google LLC is certified under the EU-US Data Privacy Framework.
+
+If you click **Reject**, Google Analytics is never loaded. You can change your choice at any time with **Cookie settings** in the footer.
+
+Legal basis: your consent (art. 6.1.a GDPR).
+
+### Google Fonts
+
+The pages load the Inter font from Google's servers (fonts.gstatic.com). To send the font, Google receives your IP address and browser information.
+
+Legal basis: legitimate interest in displaying the site correctly (art. 6.1.f GDPR).
+
+### Embedded content
+
+Some blog posts embed videos from **YouTube** and GIFs from **Giphy**. When you open one of those posts, your browser connects to these services, which may collect data and set their own cookies, according to their privacy policies ([YouTube / Google](https://policies.google.com/privacy), [Giphy](https://support.giphy.com/hc/en-us/articles/360032872931-GIPHY-Privacy-Policy)).
+
+### Email
+
+If you write to me, I use your email address and what you write only to answer you.
+
+## Your rights
+
+You can ask me to access, correct or delete your data, to restrict or object to its processing and to receive it in a portable format. You can withdraw your consent to analytics at any time, without affecting what happened before.
+
+Write to [thisloke@proton.me](mailto:thisloke@proton.me). If you think your data is processed unlawfully, you can also file a complaint with the Italian data protection authority, the [Garante per la protezione dei dati personali](https://www.garanteprivacy.it).
+
+## Cookies
+
+The details about cookies and local storage are in the [Cookie Policy](/cookie-policy).