50 Commits

Author SHA1 Message Date
4d548b2b19 Merge pull request #30 from javaxman/feat/security-disclosure-policy
Policy responsible disclosure (security@)
2026-07-31 22:10:39 +02:00
6704a9a6e1 Aggiunge la policy di responsible disclosure come allegato operativo.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-31 18:17:59 +02:00
60ccc4ca25 Merge pull request #29 from javaxman/feat/piano-formazione-codice-etico
Piano formazione Codice Etico (90 giorni + annuale)
2026-07-31 17:59:51 +02:00
62e8f6fffc Aggiunge il piano formazione 90 giorni e annuale come allegato operativo.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-31 15:36:58 +02:00
314011c17a Merge pull request #27 from javaxman/feat/complete-en-th-translations
Completa traduzioni EN/TH e fix sezioni home
2026-07-31 14:39:12 +02:00
a0c9dd9141 Merge branch 'main' into feat/complete-en-th-translations
Risolve i conflitti su codice-etico mantenendo traduzioni EN/TH, companyConfig e link agli allegati.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-31 14:36:35 +02:00
ee4bd0b2df Merge pull request #28 from javaxman/feat/codice-etico-allegati
Aggiunge allegati operativi al Codice Etico
2026-07-31 11:52:46 +02:00
3d43febe21 Aggiunge i 9 template operativi del Codice Etico e la config societaria da compilare.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-31 11:49:18 +02:00
b687bc8ac2 Riduce i micro-blocchi allo scroll alleggerendo canvas e spine SVG.
Il canvas particelle era alto quanto tutto il documento e ridisegnava ogni frame; ora resta sul viewport e si ferma durante lo scroll.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-30 11:59:58 +02:00
ef343a31b5 Impedisce l'auto-traduzione del browser e fissa il locale TH/EN esplicito.
Chrome traduceva il tailandese in inglese allo scroll; html translate=no e prop locale sulle route.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-30 11:39:27 +02:00
04a1892c0d Completa le traduzioni EN/TH e corregge sezioni home ancora solo in italiano.
Stats e prodotti non usavano il locale, quindi dopo lo switch lingua lo scroll mostrava ancora l'italiano.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 23:53:46 +02:00
3b52e5815c Invia i contatti direttamente a Gmail invece che a pro@ via Routing. (#26)
Con DMARC p=reject l inoltro Email Routing spesso non consegna le mail di Email Sending.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 17:49:03 +02:00
7f4bb2f184 Elimina il falso successo del form contatti e mostra conferma invio. (#25)
Rimuove l honeypot autofillato, rifiuta mode non-live in produzione e TO su pro@nexstudio.ai.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 17:34:26 +02:00
03aabd29cc Corregge destinatario contatti e cursore sul pulsante Invia. (#24)
Ignora override INFO malformati, preferisce il mittente newsletter e usa cursor-pointer.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 17:20:08 +02:00
866d07b8b2 Sincronizza le Variable contatti nel wrangler locale. (#23)
Allinea FROM/TO al dashboard Cloudflare e ripristina CONTACT_FORM_FROM_EMAIL in deploy.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 17:09:23 +02:00
5606ff696e Mostra errori utente generici sul form contatti. (#22)
I dettagli di configurazione restano solo nei log Worker, non sulla landing.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 17:01:56 +02:00
6673843a00 Chiarisce la config email del form contatti e usa fallback newsletter. (#21)
Messaggio di errore specifico su cosa manca; FROM/TO possono riusare NEWSLETTER_*.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 16:59:52 +02:00
9ac9139b35 Resetta Turnstile dopo un invio contatti fallito. (#20)
Il token viene consumato al primo POST: senza reset i retry falliscono la verifica anti-spam.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 16:52:40 +02:00
6339325e6a Rimuove la nota interna dalle impostazioni cookie del footer. (#19)
Il testo tra parentesi non è destinato alla UI di produzione.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 15:29:01 +02:00
4b70c940db Gestisce errori email del form contatti senza 500 opaco. (#18)
Invia via fetch/JSON, cattura fallimenti Cloudflare Email (con retry senza Reply-To) e reindirizza a #contatti.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 15:24:28 +02:00
2c3df8371d chore: rebuild per PUBLIC_CONTACT_TURNSTILE_SITE_KEY
Rilancia Workers Builds dopo l'aggiunta della site key Turnstile in Build variables.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 15:04:53 +02:00
daa2a245a5 Merge pull request #17 from javaxman/fix/newsletter-confirm-unsubscribe-link
Link disiscrizione sulla pagina di conferma newsletter
2026-07-29 14:49:06 +02:00
770db3067d Mostra il link di disiscrizione dopo la conferma newsletter.
Passa il token unsub alla pagina di conferma e lo include anche nell'email di double opt-in.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 14:45:54 +02:00
6ba58c3a86 Merge pull request #16 from javaxman/fix/newsletter-turnstile-optional
Fix Turnstile sul form newsletter
2026-07-29 14:40:21 +02:00
8a99290209 Non richiedere Turnstile contatti sul form newsletter.
Il footer non ha il widget Turnstile: evita il fallimento usando solo NEWSLETTER_TURNSTILE_SECRET_KEY.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 14:39:52 +02:00
c04d43d9d7 Merge pull request #15 from javaxman/fix/astro6-cloudflare-workers-env
Fix env Cloudflare per Astro 6 (contatti e newsletter)
2026-07-29 14:08:25 +02:00
c4adc116d6 Corregge l'accesso all'env Worker per Astro 6.
Sostituisce locals.runtime.env con import da cloudflare:workers, ripristinando contatti e newsletter.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 14:06:16 +02:00
44007f99e1 Merge pull request #14 from javaxman/feat/cloudflare-email-sending
Sostituisce Resend con Cloudflare Email Service
2026-07-29 09:55:05 +02:00
0743521d48 Sostituisce Resend con Cloudflare Email Service.
Usa il binding EMAIL per contatti e newsletter, eliminando la dipendenza da Resend e dalle relative API key.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 09:54:12 +02:00
2b470c778a Merge pull request #13 from javaxman/feat/newsletter-custom
Aggiunge newsletter custom con D1 e double opt-in
2026-07-28 23:27:00 +02:00
7aff1abcfb Aggiunge newsletter custom con D1, double opt-in e invio campagne.
Attiva il form footer, API subscribe/confirm/unsubscribe e endpoint admin protetti, riusando Resend per le email.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-28 23:23:19 +02:00
9a8566a3f5 test: verifica definitiva Workers Builds su main
Conferma che il token cloudflare_nexstudio_build_worker_07_2026 funziona sul percorso di produzione.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-28 23:02:49 +02:00
ed14c7f6a9 Merge pull request #12 from javaxman/docs/legal-texts-update
Aggiorna testi legali cookie, privacy, GDPR e condizioni d'uso
2026-07-28 12:33:10 +02:00
5854401de3 Aggiorna i testi legali del sito alla versione 1.1.
Allinea condizioni d'uso, cookie policy, privacy e GDPR a NexStudio S.r.l. (Italia), mantenendo placeholder per indirizzo, P.IVA e data.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-28 12:31:54 +02:00
c793688762 Merge pull request #11 from javaxman/feat/issue-5-i18n-home-menu-footer
Fix caricamento CSS su route EN e TH
2026-04-27 13:43:55 +02:00
d12826b314 Aggiunge un link esplicito al CSS compilato in BaseLayout.
Garantisce che le route /en e /th carichino sempre il foglio stile Astro anche quando il wrapping della homepage non aggancia automaticamente l'asset.

Made-with: Cursor
2026-04-27 13:33:50 +02:00
85e470f58a Merge pull request #10 from javaxman/feat/issue-5-i18n-home-menu-footer
Rollback regressione CSS globale su BaseLayout
2026-04-27 11:43:46 +02:00
b7e051ec34 Ripristina il caricamento CSS compilato di Astro nel BaseLayout.
Rimuove l'iniezione inline del file sorgente Tailwind che aveva rotto lo stile anche sulla home italiana in produzione.

Made-with: Cursor
2026-04-27 11:39:05 +02:00
ffdb6979ff Merge pull request #9 from javaxman/feat/issue-5-i18n-home-menu-footer
Fix CSS globale su route localizzate EN TH
2026-04-27 11:23:22 +02:00
c6d13418f7 Forza il caricamento del CSS globale nel BaseLayout con iniezione inline.
Evita il caso in cui le route localizzate /en e /th perdono il link stylesheet hashed e rendono la pagina senza stili.

Made-with: Cursor
2026-04-27 11:19:50 +02:00
ee610a8853 Merge pull request #8 from javaxman/feat/issue-5-i18n-home-menu-footer
Avvia i18n reale home IT EN TH e corregge rendering locale
2026-04-27 10:20:29 +02:00
01e3815820 Corregge il rendering della home localizzata spostando l'inizializzazione del carosello dopo stackSection.
Evita l'errore runtime su /en e /th che causava pagina vuota o UI rotta nel selettore lingua.

Made-with: Cursor
2026-04-27 09:49:40 +02:00
0714ec1335 Avvia i18n reale della home su IT EN TH.
Introduco contenuti localizzati per menu, footer e principali sezioni della homepage, includendo il form contatti e i messaggi client per mantenere coerenza UX tra le tre lingue.

Made-with: Cursor
2026-04-27 09:12:00 +02:00
ddab32d3ef Riduce il routing del contact form pubblico a info, privacy e careers.
Allinea backend, configurazione env e documentazione al nuovo modello vetrina con destinatari meno granulari.

Made-with: Cursor
2026-04-24 21:32:15 +02:00
f33d29fe7b Rende operativa la pagina news e sostituisce il README starter con un runbook di progetto.
Allinea la documentazione a setup, deploy Cloudflare e gestione contatti per supportare il go-live.

Made-with: Cursor
2026-04-24 20:59:53 +02:00
9017c933f4 Completa base SEO con sitemap robots e meta canonical OG
Aggiunge integrazione @astrojs/sitemap, pubblica robots.txt su nexstudio.ai e imposta canonical/OG/Twitter base nel layout principale.

Made-with: Cursor
2026-04-24 17:14:22 +02:00
856e20c19c Imposta dominio produzione nexstudio.ai e allinea note operative
Conferma site in Astro config sul dominio effettivo e documenta hostnames Turnstile consigliati per produzione e locale.

Made-with: Cursor
2026-04-24 15:58:03 +02:00
6ca0256bf7 Allinea il flusso contatti a info support no-reply
Completa il routing reparto con account support e aggiorna documentazione/env per usare info come fallback e no-reply come mittente tecnico.

Made-with: Cursor
2026-04-24 11:36:58 +02:00
70759d6c1c Aggiunge routing contatti per reparto con endpoint Cloudflare e Resend
Introduce selettore reparto nel form, instradamento verso destinatari dedicati via variabili ambiente e fallback al destinatario generale. Mantiene modalita dev/live e validazione Turnstile lato server.

Made-with: Cursor
2026-04-23 21:12:45 +02:00
5ab329adbf Implementa endpoint contatti Cloudflare con supporto Resend e modalita dev/live
Configura il form per usare /api/contact di default, aggiunge API server-side con verifica Turnstile opzionale e invio Resend in live mode, e documenta variabili ambiente in README/.env.example.

Made-with: Cursor
2026-04-23 21:05:31 +02:00
79 changed files with 11236 additions and 2476 deletions

21
.env.example Normal file
View File

@ -0,0 +1,21 @@
PUBLIC_CONTACT_FORM_ACTION_URL=/api/contact
PUBLIC_CONTACT_FORM_SUCCESS_REDIRECT=http://localhost:4321/#contatti
PUBLIC_CONTACT_FORM_MAILTO=
PUBLIC_CONTACT_TURNSTILE_SITE_KEY=
PUBLIC_SITE_URL=http://localhost:4321
CONTACT_FORM_MODE=dev
CONTACT_FORM_FROM_EMAIL=no-reply@nexstudio.ai
CONTACT_FORM_TO_EMAIL=javaxman69@gmail.com
CONTACT_FORM_TO_EMAIL_INFO=
CONTACT_FORM_TO_EMAIL_PRIVACY=
CONTACT_FORM_TO_EMAIL_CAREERS=
CONTACT_TURNSTILE_SECRET_KEY=
# Newsletter (custom D1 + Cloudflare Email Service, double opt-in)
# In produzione: NEWSLETTER_MODE=live (oppure CONTACT_FORM_MODE=live come fallback)
NEWSLETTER_MODE=dev
NEWSLETTER_FROM_EMAIL=no-reply@nexstudio.ai
NEWSLETTER_NOTIFY_TO_EMAIL=javaxman69@gmail.com
NEWSLETTER_ADMIN_TOKEN=
NEWSLETTER_TURNSTILE_SECRET_KEY=

1
.gitignore vendored
View File

@ -16,6 +16,7 @@ pnpm-debug.log*
# environment variables
.env
.env.production
.dev.vars
# macOS-specific files
.DS_Store

174
README.md
View File

@ -1,52 +1,160 @@
# Astro Starter Kit: Minimal
# NexStudio Website Runbook
```sh
npm create astro@latest -- --template minimal
```
Sito istituzionale NexStudio basato su Astro, con deploy su Cloudflare e form contatti protetto da Turnstile.
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
## Obiettivo del repository
## 🚀 Project Structure
- Presentazione aziendale e servizi (homepage e pagine legali).
- Entry point unico per richieste commerciali e supporto.
- Base pronta per contenuti multilingua IT/EN/TH.
- Pipeline di deploy stabile su Cloudflare.
Inside of your Astro project, you'll see the following folders and files:
## Stack tecnico
```text
/
├── public/
├── src/
│ └── pages/
│ └── index.astro
└── package.json
```
- Astro + Tailwind CSS
- Adapter Cloudflare (`@astrojs/cloudflare`)
- Sitemap (`@astrojs/sitemap`)
- Form backend su endpoint `/api/contact`
- Turnstile + Cloudflare Email Service per anti-spam e invio email
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
## Struttura essenziale
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
- `src/pages/`: pagine pubbliche e route API (`src/pages/api/contact.ts`)
- `src/components/`: componenti UI condivisi
- `src/data/`: contenuti testuali e configurazioni pagine
- `public/`: asset statici (loghi, immagini, robots)
- `.env.example`: variabili locali di riferimento
Any static assets, like images, can be placed in the `public/` directory.
## Prerequisiti
## 🧞 Commands
- Node.js 20+
- npm 10+
- Account Cloudflare (Workers + Turnstile + Email Service con dominio onboardato, es. `nexstudio.ai`)
All commands are run from the root of the project, from a terminal:
## Avvio locale
| Command | Action |
| :------------------------ | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:4321` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI |
1. Installazione dipendenze:
- `npm install`
2. Copia variabili di ambiente:
- copia `.env.example` in `.env`
3. Avvio sviluppo:
- `npm run dev`
4. Se Vite resta in cache incoerente:
- `npm run dev:fresh`
## 👀 Want to learn more?
## Comandi utili
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
- `npm run dev`: sviluppo locale
- `npm run dev:fresh`: reset cache `.vite` e `.astro` + avvio dev server
- `npm run build`: build produzione
- `npm run preview`: preview locale della build
- `npm run deploy:cf`: build + deploy con wrangler su output Astro
## Cloudflare deploy notes
## Configurazione contatti
If you deploy with Wrangler in CI/Cloudflare, build first and deploy using Astro's generated Worker config:
Variabili pubbliche (frontend):
- `PUBLIC_CONTACT_FORM_ACTION_URL` (default consigliato: `/api/contact`)
- `PUBLIC_CONTACT_FORM_SUCCESS_REDIRECT` (URL assoluto della pagina post-submit)
- `PUBLIC_CONTACT_FORM_MAILTO` (fallback se action URL non disponibile)
- `PUBLIC_CONTACT_TURNSTILE_SITE_KEY`
Variabili server (Cloudflare/local):
- `CONTACT_FORM_MODE` (`dev` oppure `live`)
- `CONTACT_FORM_FROM_EMAIL` (consigliato: `no-reply@nexstudio.ai`, dominio verificato su Email Service)
- `CONTACT_FORM_TO_EMAIL` (casella reale dove ricevi, es. Gmail; evita indirizzi solo Email Routing se DMARC è `p=reject`)
- `CONTACT_FORM_TO_EMAIL_INFO` (opzionale, override esplicito per area "info")
- `CONTACT_FORM_TO_EMAIL_PRIVACY`
- `CONTACT_FORM_TO_EMAIL_CAREERS`
- `CONTACT_TURNSTILE_SECRET_KEY`
Comportamento:
- `dev`: accetta submit e scrive payload nei log (senza invio reale)
- `live`: valida Turnstile e invia email via binding `EMAIL` (Cloudflare Email Service)
Routing pubblico consigliato (portale vetrina):
- `info`: informazioni generali/commerciali
- `privacy`: richieste privacy e diritti interessato
- `careers`: candidature e collaborazioni
Indirizzi consigliati portale:
- `info@nexstudio.ai`
- `privacy@nexstudio.ai`
- `careers@nexstudio.ai`
- `no-reply@nexstudio.ai` (solo invio comunicazioni/newsletter, non destinazione inbound)
## Configurazione newsletter
Sistema custom su Cloudflare D1 + Cloudflare Email Service (double opt-in).
Prerequisito: dominio (es. `nexstudio.ai`) onboardato in **Email Service** con SPF/DKIM, e binding `send_email``EMAIL` nel Worker.
Variabili server:
- `NEWSLETTER_MODE` (`dev` oppure `live`; se assente usa `CONTACT_FORM_MODE`)
- `NEWSLETTER_FROM_EMAIL` (fallback: `CONTACT_FORM_FROM_EMAIL`, consigliato `no-reply@nexstudio.ai`)
- `NEWSLETTER_NOTIFY_TO_EMAIL` (notifiche admin; fallback: `CONTACT_FORM_TO_EMAIL`)
- `NEWSLETTER_ADMIN_TOKEN` (Bearer token per endpoint admin)
- `NEWSLETTER_TURNSTILE_SECRET_KEY` (opzionale; fallback: `CONTACT_TURNSTILE_SECRET_KEY`)
- `PUBLIC_SITE_URL` (origine usata nei link di conferma/disiscrizione)
Endpoint pubblici:
- `POST /api/newsletter/subscribe` — iscrizione (form footer)
- `GET /api/newsletter/confirm?token=…` — conferma double opt-in
- `GET|POST /api/newsletter/unsubscribe?token=…` — disiscrizione
Endpoint admin (header `Authorization: Bearer <NEWSLETTER_ADMIN_TOKEN>`):
- `GET /api/newsletter/admin/subscribers?status=active|pending|unsubscribed`
- `POST /api/newsletter/admin/send` — body JSON `{ "subject", "text", "html?", "dryRun?" }`
Nota: Email Service è pensato per email **transazionali** (conferme, contatti, notifiche). Le campagne marketing di massa non sono ancora il target ufficiale del servizio.
Database:
- D1 `nexstudio-newsletter` (binding `NEWSLETTER_DB`)
- Migrazioni in `migrations/`
- `npm run db:migrate:local` / `npm run db:migrate:remote`
Comportamento:
- `dev`: accetta submit e logga (senza D1/Email obbligatori)
- `live`: scrive su D1, invia email di conferma via Cloudflare Email Service, richiede binding D1 + EMAIL
## Deploy Cloudflare
Impostazioni consigliate:
- Build command: `npm run build`
- Deploy command: `npm run deploy:cf`
This avoids entrypoint errors like `@astrojs/cloudflare/entrypoints/server` not found.
Questa configurazione evita errori tipo:
`The entry-point file at "@astrojs/cloudflare/entrypoints/server" was not found.`
## Dominio e sicurezza
- Dominio ufficiale: `https://nexstudio.ai`
- Hostname Turnstile da mantenere allineati:
- `nexstudio.ai`
- `www.nexstudio.ai` (se usato)
- `localhost` (test locale)
- Accesso pre-go-live gestito con Cloudflare Zero Trust Access
## Stato operativo attuale
- Issue #1 (contact form live): implementata
- Issue #2 (anti-spam Turnstile): implementata
- Issue #3 (SEO base): implementata (sitemap, robots, canonical/OG/Twitter)
- Issue #4: in avanzamento (contenuti non placeholder)
- Issue #5: da completare (localizzazione reale IT/EN/TH)
## Note operative
- Per modifiche non minori usare branch dedicato (`feat/*`, `fix/*`) e merge su `main` dopo verifica.
- Evitare commit su `main` per attivita complesse.
- Rigenerare eventuali API key esposte e aggiornare subito i secret su Cloudflare.

View File

@ -4,6 +4,7 @@ import { defineConfig } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';
import cloudflare from '@astrojs/cloudflare';
import alpinejs from '@astrojs/alpinejs';
import sitemap from '@astrojs/sitemap';
/**
* Mitiga race su richieste SSR parallele durante lottimizzazione dipendenze
@ -34,7 +35,7 @@ function viteSsrOptimizeIgnoreOutdated() {
// https://astro.build/config
export default defineConfig({
/** Usato da Astro per URL assoluti (es. sitemap). Imposta il dominio di produzione. */
// site: 'https://www.tuodominio.com',
site: 'https://nexstudio.ai',
output: 'server', // per il deployment su Cloudflare Pages
@ -43,7 +44,7 @@ export default defineConfig({
},
adapter: cloudflare(),
integrations: [alpinejs()],
integrations: [alpinejs(), sitemap()],
i18n: {
defaultLocale: 'it',
locales: ['it', 'en', 'th'],

View File

@ -0,0 +1,49 @@
-- Newsletter subscribers and campaign send log
CREATE TABLE IF NOT EXISTS newsletter_subscribers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT NOT NULL COLLATE NOCASE,
status TEXT NOT NULL CHECK (status IN ('pending', 'active', 'unsubscribed')),
locale TEXT NOT NULL DEFAULT 'it',
confirm_token TEXT,
unsubscribe_token TEXT NOT NULL,
source TEXT NOT NULL DEFAULT 'footer',
created_at TEXT NOT NULL DEFAULT (datetime('now')),
confirmed_at TEXT,
unsubscribed_at TEXT,
UNIQUE (email)
);
CREATE INDEX IF NOT EXISTS idx_newsletter_subscribers_status
ON newsletter_subscribers (status);
CREATE INDEX IF NOT EXISTS idx_newsletter_subscribers_confirm_token
ON newsletter_subscribers (confirm_token);
CREATE INDEX IF NOT EXISTS idx_newsletter_subscribers_unsubscribe_token
ON newsletter_subscribers (unsubscribe_token);
CREATE TABLE IF NOT EXISTS newsletter_campaigns (
id INTEGER PRIMARY KEY AUTOINCREMENT,
subject TEXT NOT NULL,
body_text TEXT NOT NULL,
body_html TEXT,
status TEXT NOT NULL DEFAULT 'draft' CHECK (status IN ('draft', 'sending', 'sent', 'failed')),
created_at TEXT NOT NULL DEFAULT (datetime('now')),
sent_at TEXT,
created_by TEXT
);
CREATE TABLE IF NOT EXISTS newsletter_campaign_sends (
id INTEGER PRIMARY KEY AUTOINCREMENT,
campaign_id INTEGER NOT NULL,
subscriber_id INTEGER NOT NULL,
email TEXT NOT NULL,
status TEXT NOT NULL CHECK (status IN ('queued', 'sent', 'failed', 'skipped')),
error TEXT,
sent_at TEXT,
FOREIGN KEY (campaign_id) REFERENCES newsletter_campaigns (id),
FOREIGN KEY (subscriber_id) REFERENCES newsletter_subscribers (id)
);
CREATE INDEX IF NOT EXISTS idx_newsletter_campaign_sends_campaign
ON newsletter_campaign_sends (campaign_id);

67
package-lock.json generated
View File

@ -10,6 +10,7 @@
"dependencies": {
"@astrojs/alpinejs": "^0.5.0",
"@astrojs/cloudflare": "^13.1.10",
"@astrojs/sitemap": "^3.7.2",
"@tailwindcss/vite": "^4.2.2",
"@types/alpinejs": "^3.13.11",
"alpinejs": "^3.15.11",
@ -105,6 +106,17 @@
"node": ">=22.12.0"
}
},
"node_modules/@astrojs/sitemap": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.7.2.tgz",
"integrity": "sha512-PqkzkcZTb5ICiyIR8VoKbIAP/laNRXi5tw616N1Ckk+40oNB8Can1AzVV56lrbC5GKSZFCyJYUVYqVivMisvpA==",
"license": "MIT",
"dependencies": {
"sitemap": "^9.0.0",
"stream-replace-string": "^2.0.0",
"zod": "^4.3.6"
}
},
"node_modules/@astrojs/telemetry": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.1.tgz",
@ -2095,6 +2107,24 @@
"@types/unist": "*"
}
},
"node_modules/@types/node": {
"version": "24.12.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz",
"integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==",
"license": "MIT",
"dependencies": {
"undici-types": "~7.16.0"
}
},
"node_modules/@types/sax": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz",
"integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/unist": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
@ -2156,6 +2186,12 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/arg": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
"license": "MIT"
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@ -4992,6 +5028,25 @@
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
"license": "MIT"
},
"node_modules/sitemap": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/sitemap/-/sitemap-9.0.1.tgz",
"integrity": "sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==",
"license": "MIT",
"dependencies": {
"@types/node": "^24.9.2",
"@types/sax": "^1.2.1",
"arg": "^5.0.0",
"sax": "^1.4.1"
},
"bin": {
"sitemap": "dist/esm/cli.js"
},
"engines": {
"node": ">=20.19.5",
"npm": ">=10.8.2"
}
},
"node_modules/smol-toml": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz",
@ -5023,6 +5078,12 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/stream-replace-string": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz",
"integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==",
"license": "MIT"
},
"node_modules/stringify-entities": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
@ -5207,6 +5268,12 @@
"node": ">=20.18.1"
}
},
"node_modules/undici-types": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"license": "MIT"
},
"node_modules/unenv": {
"version": "2.0.0-rc.24",
"resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz",

View File

@ -12,11 +12,14 @@
"deploy:cf": "npm run build && wrangler deploy --config dist/server/wrangler.json",
"preview": "astro preview",
"astro": "astro",
"generate-types": "wrangler types"
"generate-types": "wrangler types",
"db:migrate:local": "wrangler d1 migrations apply nexstudio-newsletter --local",
"db:migrate:remote": "wrangler d1 migrations apply nexstudio-newsletter --remote"
},
"dependencies": {
"@astrojs/alpinejs": "^0.5.0",
"@astrojs/cloudflare": "^13.1.10",
"@astrojs/sitemap": "^3.7.2",
"@tailwindcss/vite": "^4.2.2",
"@types/alpinejs": "^3.13.11",
"alpinejs": "^3.15.11",

View File

@ -1,27 +1,28 @@
/**
* Rete di particelle + linee (canvas #particleCanvas).
* Deve stare DENTRO il contenitore della pagina (es. .nx-grid-bg), non dietro body,
* altrimenti lo sfondo opaco della pagina lo copre.
* Limitato al viewport (non allaltezza documento): evita canvas giganti che
* bloccano lo scroll sulla home lunga.
*/
(function () {
if (typeof window === 'undefined') return;
var canvas = document.getElementById('particleCanvas');
if (!canvas) return;
var ctx = canvas.getContext('2d');
var ctx = canvas.getContext('2d', { alpha: true });
if (!ctx) return;
var reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var particles = [];
var mouse = { x: null, y: null, radius: 150 };
var raf = 0;
var running = false;
var cachedW = 0;
var cachedH = 0;
var scrollPauseTimer = 0;
function canvasSize() {
var p = canvas.parentElement;
if (!p) return { w: 0, h: 0 };
var w = Math.max(1, Math.floor(p.clientWidth));
var h = Math.max(1, Math.floor(p.clientHeight));
function viewportSize() {
var w = Math.max(1, Math.floor(window.innerWidth || document.documentElement.clientWidth || 1));
var h = Math.max(1, Math.floor(window.innerHeight || document.documentElement.clientHeight || 1));
return { w: w, h: h };
}
@ -29,8 +30,8 @@
this.x = Math.random() * w;
this.y = Math.random() * h;
this.size = Math.random() * 2 + 1;
this.speedX = (Math.random() - 0.5) * 1.5;
this.speedY = (Math.random() - 0.5) * 1.5;
this.speedX = (Math.random() - 0.5) * 1.2;
this.speedY = (Math.random() - 0.5) * 1.2;
}
Particle.prototype.draw = function () {
@ -58,46 +59,55 @@
var fx = dx / distance;
var fy = dy / distance;
var force = (mouse.radius - distance) / mouse.radius;
this.x -= fx * force * 5;
this.y -= fy * force * 5;
this.x -= fx * force * 4;
this.y -= fy * force * 4;
}
};
function init() {
var sz = canvasSize();
var sz = viewportSize();
var w = sz.w;
var h = sz.h;
if (w < 8 || h < 8) return;
cachedW = w;
cachedH = h;
var dpr = Math.min(window.devicePixelRatio || 1, 2);
var dpr = Math.min(window.devicePixelRatio || 1, 1.5);
canvas.width = Math.floor(w * dpr);
canvas.height = Math.floor(h * dpr);
canvas.style.width = w + 'px';
canvas.style.height = h + 'px';
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
particles = [];
var n = Math.floor((w * h) / 9000);
if (n > 130) n = 130;
if (n < 45) n = 45;
var area = w * h;
var n = Math.floor(area / 14000);
if (n > 80) n = 80;
if (n < 28) n = 28;
if (w < 768) {
n = Math.min(n, 40);
}
for (var i = 0; i < n; i++) {
particles.push(new Particle(w, h));
}
}
function connect(w, h) {
function connect(w) {
var maxDist = w < 768 ? 110 : 140;
var a;
var b;
var dx;
var dy;
var distance;
var opacity;
for (a = 0; a < particles.length; a++) {
for (b = a + 1; b < particles.length; b++) {
var len = particles.length;
for (a = 0; a < len; a++) {
for (b = a + 1; b < len; b++) {
dx = particles[a].x - particles[b].x;
dy = particles[a].y - particles[b].y;
distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 150) {
opacity = (1 - distance / 150) * 0.22;
ctx.strokeStyle = 'rgba(59, 130, 246, ' + opacity + ')';
if (distance < maxDist) {
opacity = (1 - distance / maxDist) * 0.2;
ctx.strokeStyle = 'rgba(59, 130, 246,' + opacity + ')';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(particles[a].x, particles[a].y);
@ -109,9 +119,9 @@
}
function frame() {
var sz = canvasSize();
var w = sz.w;
var h = sz.h;
if (!running) return;
var w = cachedW;
var h = cachedH;
ctx.clearRect(0, 0, w, h);
var i;
@ -119,29 +129,33 @@
particles[i].update(w, h);
particles[i].draw();
}
connect(w, h);
connect(w);
raf = requestAnimationFrame(frame);
}
function syncCanvasCssSize() {
var sz = canvasSize();
canvas.style.width = sz.w + 'px';
canvas.style.height = sz.h + 'px';
function start() {
if (reduced || document.hidden || running || !particles.length) return;
running = true;
raf = requestAnimationFrame(frame);
}
function onResize() {
cancelAnimationFrame(raf);
syncCanvasCssSize();
init();
if (!reduced && particles.length) {
raf = requestAnimationFrame(frame);
function stop() {
running = false;
if (raf) {
cancelAnimationFrame(raf);
raf = 0;
}
}
function onResize() {
stop();
init();
start();
}
function setMouseFromEvent(e) {
var rect = canvas.getBoundingClientRect();
mouse.x = e.clientX - rect.left;
mouse.y = e.clientY - rect.top;
mouse.x = e.clientX;
mouse.y = e.clientY;
}
window.addEventListener(
@ -152,22 +166,26 @@
{ passive: true },
);
var parent = canvas.parentElement;
if (parent) {
var ro = new ResizeObserver(onResize);
ro.observe(parent);
}
window.addEventListener(
'scroll',
function () {
// Libera il main thread durante lo scroll; riprende subito dopo.
stop();
if (scrollPauseTimer) window.clearTimeout(scrollPauseTimer);
scrollPauseTimer = window.setTimeout(function () {
start();
}, 120);
},
{ passive: true },
);
document.addEventListener('visibilitychange', function () {
if (document.hidden) stop();
else start();
});
window.addEventListener('resize', onResize, { passive: true });
syncCanvasCssSize();
init();
if (reduced) {
return;
}
if (particles.length) {
raf = requestAnimationFrame(frame);
}
start();
})();

4
public/robots.txt Normal file
View File

@ -0,0 +1,4 @@
User-agent: *
Allow: /
Sitemap: https://nexstudio.ai/sitemap-index.xml

View File

@ -1,6 +1,11 @@
---
import { chat } from '../data/home';
import { getChat } from '../data/home';
import type { SupportedLocale } from '../data/home/navigation';
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = (localeMatch?.[1] ?? 'it') as SupportedLocale;
const chat = getChat(currentLocale);
const pc = chat.prechat;
const hasFormspree =
typeof pc.actionUrl === 'string' && pc.actionUrl.length > 0;

View File

@ -1,5 +1,10 @@
---
import { cta } from '../data/home';
import { getCta } from '../data/home';
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = (localeMatch?.[1] ?? 'it') as 'it' | 'en' | 'th';
const cta = getCta(currentLocale);
const cf = cta.contactForm;
const model = {
@ -11,8 +16,19 @@ const model = {
fieldName: cf.fieldName,
fieldCompany: cf.fieldCompany,
fieldCountry: cf.fieldCountry,
fieldDepartment: cf.fieldDepartment,
fieldEmail: cf.fieldEmail,
fieldMessage: cf.fieldMessage,
successMessage: cf.successMessage,
turnstileError: cf.errors.turnstile,
configError: cf.errors.config,
mailtoFallbackSubject: cf.mailtoFallbackSubject,
mailtoLabelName: cf.mailtoLabels.name,
mailtoLabelCompany: cf.mailtoLabels.company,
mailtoLabelEmail: cf.mailtoLabels.email,
mailtoLabelCountry: cf.mailtoLabels.country,
mailtoLabelDepartment: cf.mailtoLabels.department,
mailtoDefaultDepartment: cf.mailtoLabels.defaultDepartment,
};
const hasFormspree =
typeof cf.actionUrl === 'string' && cf.actionUrl.length > 0;
@ -97,18 +113,6 @@ const fieldClass =
<input type="hidden" name="_subject" value={notifySubject} />
) : null
}
{
hasFormspree ? (
<input
type="text"
name="_gotcha"
tabindex="-1"
autocomplete="off"
aria-hidden="true"
class="pointer-events-none absolute -left-[9999px] h-0 w-0 opacity-0"
/>
) : null
}
<div>
<label class="block text-xs font-medium text-nx-muted" for="nx-cf-name">
@ -161,6 +165,20 @@ const fieldClass =
class={fieldClass}
/>
</div>
<div>
<label class="block text-xs font-medium text-nx-muted" for="nx-cf-department">
{cf.departmentLabel}
</label>
<select
id="nx-cf-department"
name={cf.fieldDepartment}
class={fieldClass}
>
{cf.departmentOptions.map((opt) => (
<option value={opt.value}>{opt.label}</option>
))}
</select>
</div>
<div>
<label class="block text-xs font-medium text-nx-muted" for="nx-cf-msg">
{cf.messageLabel}
@ -174,6 +192,13 @@ const fieldClass =
></textarea>
</div>
<p
id="nx-contact-form-success"
class="hidden text-sm leading-relaxed text-emerald-400/90"
role="status"
>
</p>
<p
id="nx-contact-form-error"
class="hidden text-sm leading-relaxed text-red-400/90"
@ -188,11 +213,11 @@ const fieldClass =
class="cf-turnstile"
data-sitekey={cf.turnstileSiteKey}
data-theme="dark"
data-language="it"
data-language={cf.turnstileLanguage}
data-size="normal"
></div>
<p class="mt-1.5 text-xs leading-relaxed text-nx-muted">
Verifica anti-spam: completa il controllo prima di inviare il messaggio.
{cf.turnstileHint}
</p>
</div>
) : null
@ -201,7 +226,7 @@ const fieldClass =
<div class="pt-1">
<button
type="submit"
class="inline-flex h-10 w-full items-center justify-center rounded-lg bg-nx-fg px-5 text-sm font-semibold text-nx-bg hover:bg-white sm:w-auto sm:min-w-[10rem]"
class="inline-flex h-10 w-full cursor-pointer items-center justify-center rounded-lg bg-nx-fg px-5 text-sm font-semibold text-nx-bg hover:bg-white sm:w-auto sm:min-w-[10rem]"
>
{cf.submitLabel}
</button>

View File

@ -1,5 +1,18 @@
---
import { cookieConsentMeta, cookieCategories } from '../data/cookie-consent';
import {
cookieConsentMeta,
getCookieCategories,
getCookieConsentUi,
} from '../data/cookie-consent';
import type { SupportedLocale } from '../data/home/navigation';
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = (localeMatch?.[1] ?? 'it') as SupportedLocale;
const cookieCategories = getCookieCategories(currentLocale);
const ui = getCookieConsentUi(currentLocale);
const cookiesHref =
currentLocale === 'it' ? '/cookies' : `/${currentLocale}/cookies`;
const model = {
storageKey: cookieConsentMeta.storageKey,
@ -28,15 +41,14 @@ const model = {
id="nx-cc-banner-title"
class="text-sm font-semibold tracking-tight text-nx-fg"
>
Utilizziamo cookie e memorizzazione locale
{ui.bannerTitle}
</p>
<p
id="nx-cc-banner-desc"
class="mt-1 text-xs leading-relaxed text-nx-muted sm:text-sm"
>
Seleziona le categorie che preferisci. I necessari restano attivi per
registrare la tua scelta. Dettagli nella{' '}
<a class="text-sky-400/90 underline-offset-2 hover:text-sky-300 hover:underline" href="/cookies">informativa cookie</a>.
{ui.bannerDescBefore}{' '}
<a class="text-sky-400/90 underline-offset-2 hover:text-sky-300 hover:underline" href={cookiesHref}>{ui.bannerPolicyLinkLabel}</a>.
</p>
</div>
<div
@ -47,21 +59,21 @@ const model = {
data-nx-cc-reject
class="order-3 h-10 rounded-lg border border-nx-border px-4 text-sm font-medium text-nx-muted hover:border-sky-500/30 hover:text-nx-fg sm:order-1"
>
Rifiuta non essenziali
{ui.rejectLabel}
</button>
<button
type="button"
data-nx-cc-customize
class="order-2 h-10 rounded-lg border border-nx-border px-4 text-sm font-medium text-nx-fg hover:border-sky-500/40 sm:order-2"
>
Personalizza
{ui.customizeLabel}
</button>
<button
type="button"
data-nx-cc-accept
class="order-1 inline-flex h-10 items-center justify-center rounded-lg bg-gradient-to-r from-sky-500 to-violet-500 px-4 text-sm font-semibold text-white shadow-lg shadow-sky-500/20 hover:brightness-110 sm:order-3"
>
Accetta tutto
{ui.acceptLabel}
</button>
</div>
</div>
@ -86,11 +98,10 @@ const model = {
>
<div class="border-b border-nx-border px-5 py-4">
<h2 id="nx-cc-dialog-title" class="text-base font-semibold tracking-tight">
Preferenze cookie
{ui.dialogTitle}
</h2>
<p class="mt-1 text-xs leading-relaxed text-nx-muted">
Versione informativa v{cookieConsentMeta.policyVersion}. Puoi modificare
le scelte in qualsiasi momento dal footer.
{ui.dialogIntroBefore}{cookieConsentMeta.policyVersion}{ui.dialogIntroAfter}
</p>
</div>
<div
@ -108,7 +119,7 @@ const model = {
</div>
{cat.required ? (
<span class="shrink-0 rounded-md bg-nx-border/40 px-2 py-1 text-[10px] font-semibold uppercase tracking-wide text-nx-muted">
Sempre attivi
{ui.alwaysOnLabel}
</span>
) : (
<label
@ -138,14 +149,14 @@ const model = {
data-nx-cc-cancel
class="h-10 rounded-lg border border-nx-border px-4 text-sm font-medium text-nx-muted hover:text-nx-fg"
>
Chiudi
{ui.cancelLabel}
</button>
<button
type="button"
data-nx-cc-save
class="h-10 rounded-lg bg-nx-fg px-4 text-sm font-semibold text-nx-bg hover:bg-white"
>
Salva preferenze
{ui.saveLabel}
</button>
</div>
</div>

View File

@ -3,32 +3,20 @@
* Continuazione dei flussi dati dell'hero: dalla destra verso sinistra,
* inclinazione verso il basso, fino alla fascia del titolo "Prodotti".
* Dietro a testo e card (main ha stacking; questo blocco è z-0).
* Senza feGaussianBlur: il filtro SVG su un layer alto quanto il main
* rendeva lo scroll a scatti.
*/
---
<div class="nx-dataflow-spine" aria-hidden="true">
<svg
class="nx-dataflow-spine-svg"
viewBox="0 0 1480 2800"
viewBox="0 0 1480 1600"
preserveAspectRatio="none"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
<filter
id="nx-spine-glow"
x="-30%"
y="-30%"
width="160%"
height="160%"
color-interpolation-filters="sRGB"
>
<feGaussianBlur stdDeviation="2.2" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<linearGradient id="nx-spine-stroke" x1="100%" y1="0%" x2="0%" y2="60%">
<stop offset="0%" stop-color="#a5f3fc" stop-opacity="0.75" />
<stop offset="40%" stop-color="#38bdf8" stop-opacity="0.9" />
@ -36,8 +24,8 @@
</linearGradient>
</defs>
<g filter="url(#nx-spine-glow)" opacity="0.72">
<!-- Entrata da dx (fuori canvas) → curva verso sx con pendenza verso il basso; fascia ~y 9801280 = titolo Prodotti su viewport tipiche -->
<g opacity="0.72">
<!-- Entrata da dx → curva verso sx; fascia titolo Prodotti -->
<path
class="nx-flow-line nx-flow-line--a"
vector-effect="non-scaling-stroke"

View File

@ -1,5 +1,10 @@
---
import { footer } from '../data/home';
import { getFooter } from '../data/home';
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = (localeMatch?.[1] ?? 'it') as 'it' | 'en' | 'th';
const footer = getFooter(currentLocale);
const hasNewsletterAction =
typeof footer.newsletter.actionUrl === 'string' &&
@ -17,9 +22,6 @@ const notifySubject =
const linkClass =
'text-sm text-sky-400/90 transition-colors hover:text-sky-300';
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = localeMatch?.[1] ?? 'it';
const withLocale = (href: string) => {
if (currentLocale === 'it') return href;
if (href.startsWith(`/${currentLocale}`)) return href;
@ -91,7 +93,7 @@ const withLocale = (href: string) => {
) : (
<span
class="cursor-not-allowed text-nx-muted opacity-50"
title="URL da configurare in footer.ts"
title={footer.labels.socialPlaceholderTitle}
>
{item.label}
</span>
@ -115,7 +117,7 @@ const withLocale = (href: string) => {
{link.label}
</a>
) : (
<span class="text-sm text-nx-muted" title="Collegamento in arrivo">
<span class="text-sm text-nx-muted" title={footer.labels.companyPlaceholderTitle}>
{link.label}
</span>
)}
@ -131,7 +133,7 @@ const withLocale = (href: string) => {
>
<div class="min-w-0">
<p class="text-xs font-semibold uppercase tracking-wider text-nx-fg">
Impostazioni cookie
{footer.labels.cookieTitle}
</p>
<p class="mt-1.5 max-w-xl text-xs leading-relaxed text-nx-muted">
{footer.cookiePreferences.hint}
@ -164,6 +166,8 @@ const withLocale = (href: string) => {
{notifySubject ? (
<input type="hidden" name="_subject" value={notifySubject} />
) : null}
<input type="hidden" name="locale" value={currentLocale} />
<input type="hidden" name="source" value="footer" />
<input
type="text"
name="_gotcha"
@ -204,7 +208,7 @@ const withLocale = (href: string) => {
{nl.buttonLabel}
</button>
<p id="nx-newsletter-hint" class="text-xs leading-relaxed text-nx-muted">
Iscrizione in arrivo: configura lendpoint nel progetto per attivare il modulo.
{footer.labels.newsletterComingSoon}
</p>
</div>
)

208
src/data/about.ts Normal file
View File

@ -0,0 +1,208 @@
/**
* Pagina Chi siamo / About / เกี่ยวกับเรา — contenuti localizzati.
*/
import type { SupportedLocale } from './home/navigation';
export type AboutBullet = {
title: string;
text: string;
};
export type AboutSection = {
title: string;
paragraphs?: readonly string[];
bullets?: readonly AboutBullet[];
};
type AboutLocaleBundle = {
page: {
title: string;
description: string;
heading: string;
lead: string;
};
intro: string;
sections: readonly AboutSection[];
};
const aboutByLocale: Record<SupportedLocale, AboutLocaleBundle> = {
it: {
page: {
title: 'Chi siamo — NexStudio',
description:
'Missione, valori e come lavoriamo con studi legali e strutture sanitarie: qualità, verticalità e impegno nel tempo.',
heading: 'Chi siamo',
lead:
'Siamo un team internazionale — tra Bangkok e lItalia — che unisce ingegneria del software, intelligenza artificiale applicata e conoscenza dei domini regolamentati, per strumenti professionali usabili e sostenibili nel tempo.',
},
intro:
'Ci impegniamo a rendere la tecnologia invisibile nel lavoro quotidiano. Progettiamo software che assume su di sé processi strutturati, controlli e complessità computazionale, così che chi lavora su norme, pazienti o documentazione possa concentrarsi su giudizio, responsabilità e relazione — non su menu, formati o incertezze operative. Nascondere la complessità sotto interfaccia essenziale, tempi reattivi e tracciabilità è al centro di come disegniamo il prodotto, non un optional estetico.',
sections: [
{
title: 'La nostra missione',
paragraphs: [
'Operiamo dove un errore pesa: Legal Tech e Health Tech. Qui servono precisione, tracciamento delle scelte e rispetto dei vincoli normativi, non sperimenti generici. La nostra missione è fornire piattaforme SaaS mature — come LexAura e MediAura — che integrino modelli dintelligenza artificiale in flussi già vissuti dallo studio o dalla struttura, con continuità tra automazione, revisione umana e responsabilità riconoscibile.',
],
},
{
title: 'Perché possiamo meritare la tua fiducia',
paragraphs: [
'La fiducia non si ottiene con un motto, ma con ingegneria, chiarezza e continuità. Significa architetture provate, aggiornamenti regolari dei modelli danalisi, assistenza e formazione laddove ladozione chiede metodo oltre al click. Significa anche onestà sui confini: lIA nelle nostre offerte è pensata per estendere e accelerare il lavoro professionale, lasciando a avvocato, medico o organizzazione la decisione finale e la documentazione che la norma richiede.',
],
},
{
title: 'Come lavoriamo, in sintesi',
bullets: [
{
title: 'Verticalità e dominio.',
text: 'Less genericismo, più requisiti operativi (normative, flussi, audit) incorporati fin dalla progettazione.',
},
{
title: 'Qualità e sicurezza come priorità, non retorica.',
text: 'Ingegneria, test e infrastrutture coerenti con lesposizione a dati sensibili.',
},
{
title: 'Supporto e evoluzione.',
text: 'Il rapporto con il cliente non finisce allabbonamento: manutenzione evolutiva, assistenza e procedure chiare in caso di urgenze operative, dove serve.',
},
{
title: 'Comunicazione trasparente.',
text: 'Documentazione, informativa privacy (dove obbligo) e aspettative reciproche esplicite, perché lonboarding non diventi il primo ostacolo.',
},
],
},
{
title: 'Team e cultura',
paragraphs: [
'Siamo developer, product e specialisti di dominio in un modello distribuito: fusi orari diversi e linguaggi comuni, un solo obiettivo prodotto. Ci muoviamo con iterazioni rapide laddove serve innovazione, e con governance laddove un cambiamento impatta validazione, conformità o continuità del servizio. La diversità del gruppo (competenze, contesti) è per noi vantaggio competitivo: riduce lassunto implicito e costringe a rendere esplicito ciò che il software promette, e a chi.',
],
},
{
title: 'Impegno nel tempo',
paragraphs: [
'Il settore che ci sceglie non cerca un prototipo, ma un percorso sostenibile: allineamento normativo, adozione in team, affiancamento quando qualcosa non gira. Costruiamo e manteniamo il prodotto con quella lente, perché nelle vostre professioni la continuità non è comodità, è requisito.',
],
},
],
},
en: {
page: {
title: 'About us — NexStudio',
description:
'Mission, values, and how we work with law firms and healthcare organizations: quality, domain focus, and long-term commitment.',
heading: 'About us',
lead:
'We are an international team — between Bangkok and Italy — combining software engineering, applied artificial intelligence, and knowledge of regulated domains, for professional tools that are usable and sustainable over time.',
},
intro:
'We aim to make technology invisible in everyday work. We design software that takes on structured processes, controls, and computational complexity, so that people working with norms, patients, or documentation can focus on judgment, responsibility, and relationships — not on menus, formats, or operational uncertainty. Hiding complexity behind an essential interface, responsive performance, and traceability is central to how we design the product, not an aesthetic optional.',
sections: [
{
title: 'Our mission',
paragraphs: [
'We operate where a mistake carries weight: Legal Tech and Health Tech. Here precision, choice tracking, and respect for regulatory constraints are required — not generic experiments. Our mission is to provide mature SaaS platforms — such as LexAura and MediAura — that integrate AI models into flows already lived by the firm or organization, with continuity between automation, human review, and recognizable responsibility.',
],
},
{
title: 'Why we can earn your trust',
paragraphs: [
'Trust is not won with a slogan, but with engineering, clarity, and continuity. It means proven architectures, regular updates to analysis models, and support and training where adoption needs method beyond the click. It also means honesty about boundaries: AI in our offerings is designed to extend and accelerate professional work, leaving the final decision and the documentation the rules require to the lawyer, clinician, or organization.',
],
},
{
title: 'How we work, in brief',
bullets: [
{
title: 'Verticality and domain.',
text: 'Less genericism, more operational requirements (regulations, flows, audits) built in from design.',
},
{
title: 'Quality and security as priorities, not rhetoric.',
text: 'Engineering, testing, and infrastructure aligned with exposure to sensitive data.',
},
{
title: 'Support and evolution.',
text: 'The customer relationship does not end with the subscription: evolutionary maintenance, assistance, and clear procedures for operational emergencies where needed.',
},
{
title: 'Transparent communication.',
text: 'Documentation, privacy notices (where required), and explicit mutual expectations, so onboarding is not the first obstacle.',
},
],
},
{
title: 'Team and culture',
paragraphs: [
'We are developers, product people, and domain specialists in a distributed model: different time zones and shared languages, one product goal. We move with rapid iterations where innovation is needed, and with governance where a change affects validation, compliance, or service continuity. The diversity of the group (skills, contexts) is a competitive advantage for us: it reduces implicit assumptions and forces us to make explicit what the software promises, and to whom.',
],
},
{
title: 'Long-term commitment',
paragraphs: [
'The sector that chooses us is not looking for a prototype, but a sustainable path: regulatory alignment, team adoption, and support when something does not work. We build and maintain the product with that lens, because in your professions continuity is not a convenience — it is a requirement.',
],
},
],
},
th: {
page: {
title: 'เกี่ยวกับเรา — NexStudio',
description:
'พันธกิจ คุณค่า และวิธีที่เราทำงานกับสำนักงานกฎหมายและองค์กรด้านสุขภาพ: คุณภาพ ความเชี่ยวชาญเชิงโดเมน และความมุ่งมั่นระยะยาว',
heading: 'เกี่ยวกับเรา',
lead:
'เราเป็นทีมนานาชาติ — ระหว่างกรุงเทพฯ และอิตาลี — ที่ผสานวิศวกรรมซอฟต์แวร์ ปัญญาประดิษฐ์เชิงประยุกต์ และความรู้ในโดเมนที่มีการกำกับดูแล เพื่อเครื่องมือระดับมืออาชีพที่ใช้งานได้และยั่งยืนในระยะยาว',
},
intro:
'เรามุ่งทำให้เทคโนโลยีมองไม่เห็นในงานประจำวัน เราออกแบบซอฟต์แวร์ที่รับภาระกระบวนการที่มีโครงสร้าง การควบคุม และความซับซ้อนเชิงคำนวณ เพื่อให้ผู้ที่ทำงานกับกฎเกณฑ์ ผู้ป่วย หรือเอกสาร มุ่งเน้นที่การตัดสินใจ ความรับผิดชอบ และความสัมพันธ์ — ไม่ใช่เมนู รูปแบบ หรือความไม่แน่นอนในการปฏิบัติงาน การซ่อนความซับซ้อนไว้ใต้ส่วนติดต่อที่จำเป็น ประสิทธิภาพที่ตอบสนอง และการตรวจสอบย้อนกลับได้ เป็นหัวใจของการออกแบบผลิตภัณฑ์ ไม่ใช่ทางเลือกด้านสุนทรียะ',
sections: [
{
title: 'พันธกิจของเรา',
paragraphs: [
'เราดำเนินงานในที่ที่ความผิดพลาดมีน้ำหนัก: Legal Tech และ Health Tech ที่นี่ต้องการความแม่นยำ การติดตามการตัดสินใจ และการเคารพข้อจำกัดด้านกฎระเบียบ — ไม่ใช่การทดลองทั่วไป พันธกิจของเราคือการมอบแพลตฟอร์ม SaaS ที่เติบโตแล้ว — เช่น LexAura และ MediAura — ที่ผสานโมเดลปัญญาประดิษฐ์เข้ากับเวิร์กโฟลว์ที่สำนักงานหรือองค์กรใช้อยู่แล้ว โดยมีความต่อเนื่องระหว่างระบบอัตโนมัติ การตรวจสอบโดยมนุษย์ และความรับผิดชอบที่ระบุได้',
],
},
{
title: 'เหตุใดเราจึงสมควรได้รับความไว้วางใจ',
paragraphs: [
'ความไว้วางใจไม่ได้มาจากคำขวัญ แต่มาจากวิศวกรรม ความชัดเจน และความต่อเนื่อง หมายถึงสถาปัตยกรรมที่พิสูจน์แล้ว การอัปเดตโมเดลการวิเคราะห์อย่างสม่ำเสมอ รวมถึงการสนับสนุนและการอบรมเมื่อการนำมาใช้ต้องการวิธีการมากกว่าการคลิก และยังหมายถึงความซื่อสัตย์เรื่องขอบเขต: AI ในข้อเสนอของเราออกแบบมาเพื่อขยายและเร่งงานระดับมืออาชีพ โดยปล่อยให้ทนายความ แพทย์ หรือองค์กรเป็นผู้ตัดสินใจขั้นสุดท้ายและจัดทำเอกสารตามที่กฎกำหนด',
],
},
{
title: 'วิธีที่เราทำงาน โดยสรุป',
bullets: [
{
title: 'ความเชี่ยวชาญเชิงแนวตั้งและโดเมน',
text: 'น้อยลงในความเป็นทั่วไป มากขึ้นในข้อกำหนดเชิงปฏิบัติการ (กฎระเบียบ เวิร์กโฟลว์ การตรวจสอบ) ที่ฝังตั้งแต่การออกแบบ',
},
{
title: 'คุณภาพและความปลอดภัยเป็นลำดับแรก ไม่ใช่คำพูดสวยหรู',
text: 'วิศวกรรม การทดสอบ และโครงสร้างพื้นฐานที่สอดคล้องกับการสัมผัสข้อมูลที่ละเอียดอ่อน',
},
{
title: 'การสนับสนุนและการพัฒนาต่อเนื่อง',
text: 'ความสัมพันธ์กับลูกค้าไม่จบที่การสมัครสมาชิก: การบำรุงรักษาเชิงวิวัฒนาการ ความช่วยเหลือ และขั้นตอนที่ชัดเจนเมื่อมีเหตุฉุกเฉินในการดำเนินงานตามที่จำเป็น',
},
{
title: 'การสื่อสารที่โปร่งใส',
text: 'เอกสาร ประกาศความเป็นส่วนตัว (เมื่อกฎหมายกำหนด) และความคาดหวังร่วมกันที่ชัดเจน เพื่อไม่ให้ออนบอร์ดดิ้งเป็นอุปสรรคแรก',
},
],
},
{
title: 'ทีมและวัฒนธรรม',
paragraphs: [
'เราเป็นนักพัฒนา ผู้ดูแลผลิตภัณฑ์ และผู้เชี่ยวชาญโดเมนในโมเดลแบบกระจาย: เขตเวลาต่างกันและภาษาที่ร่วมกัน เป้าหมายผลิตภัณฑ์เดียว เราเคลื่อนไหวด้วยการทำซ้ำอย่างรวดเร็วเมื่อต้องการนวัตกรรม และด้วยการกำกับดูแลเมื่อการเปลี่ยนแปลงกระทบการตรวจสอบ ความสอดคล้อง หรือความต่อเนื่องของบริการ ความหลากหลายของกลุ่ม (ทักษะ บริบท) เป็นข้อได้เปรียบในการแข่งขันสำหรับเรา: ลดสมมติฐานโดยนัยและบังคับให้ทำให้ชัดเจนว่าซอฟต์แวร์สัญญาอะไร และกับใคร',
],
},
{
title: 'ความมุ่งมั่นในระยะยาว',
paragraphs: [
'ภาคส่วนที่เลือกเราไม่ได้มองหาต้นแบบ แต่เป็นเส้นทางที่ยั่งยืน: การปรับให้สอดคล้องกับกฎระเบียบ การนำไปใช้ในทีม และการสนับสนุนเมื่อมีสิ่งที่ไม่ทำงาน เราสร้างและดูแลผลิตภัณฑ์ด้วยมุมมองนั้น เพราะในวิชาชีพของท่าน ความต่อเนื่องไม่ใช่ความสะดวก — เป็นข้อกำหนด',
],
},
],
},
};
export const getAbout = (locale: SupportedLocale) => aboutByLocale[locale];

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,8 @@
* Consenso cookie — NexStudio CMP (dimostrativo / produzione light).
* Allineare `policyVersion` quando cambiano categorie o testi legali rilevanti.
*/
import type { SupportedLocale } from './home/navigation';
export const cookieConsentMeta = {
storageKey: 'nexstudio.cookieConsent',
policyVersion: '1.0',
@ -13,33 +15,153 @@ export type CookieCategoryId =
| 'analytics'
| 'marketing';
export const cookieCategories = [
{
id: 'necessary' as const,
label: 'Strettamente necessari',
description:
'Memorizzazione delle preferenze di consenso, sicurezza e funzioni indispensabili al sito. Non possono essere disattivati dal pannello.',
required: true,
const cookieCategoriesByLocale = {
it: [
{
id: 'necessary' as const,
label: 'Strettamente necessari',
description:
'Memorizzazione delle preferenze di consenso, sicurezza e funzioni indispensabili al sito. Non possono essere disattivati dal pannello.',
required: true,
},
{
id: 'preferences' as const,
label: 'Preferenze e funzionalità',
description:
'Ricordare impostazioni opzionali (es. lingua, tema) se introdotte in futuro.',
required: false,
},
{
id: 'analytics' as const,
label: 'Misurazione e statistica',
description:
'Cookie o SDK per capire come viene usato il sito (es. analytics), solo se attivati dal codice dopo consenso.',
required: false,
},
{
id: 'marketing' as const,
label: 'Marketing e profilazione leggera',
description:
'Strumenti pubblicitari, remarketing o pixel social, solo se collegati e dopo consenso esplicito.',
required: false,
},
],
en: [
{
id: 'necessary' as const,
label: 'Strictly necessary',
description:
'Storage of consent preferences, security, and essential site functions. These cannot be disabled from the panel.',
required: true,
},
{
id: 'preferences' as const,
label: 'Preferences and functionality',
description:
'Remember optional settings (e.g. language, theme) if introduced in the future.',
required: false,
},
{
id: 'analytics' as const,
label: 'Measurement and statistics',
description:
'Cookies or SDKs to understand how the site is used (e.g. analytics), only if activated by code after consent.',
required: false,
},
{
id: 'marketing' as const,
label: 'Marketing and light profiling',
description:
'Advertising tools, remarketing, or social pixels, only if connected and after explicit consent.',
required: false,
},
],
th: [
{
id: 'necessary' as const,
label: 'จำเป็นอย่างยิ่ง',
description:
'จัดเก็บการตั้งค่าความยินยอม ความปลอดภัย และฟังก์ชันที่จำเป็นต่อเว็บไซต์ ไม่สามารถปิดได้จากแผงควบคุม',
required: true,
},
{
id: 'preferences' as const,
label: 'การตั้งค่าและฟังก์ชัน',
description:
'จดจำการตั้งค่าเพิ่มเติม (เช่น ภาษา ธีม) หากมีการเพิ่มในอนาคต',
required: false,
},
{
id: 'analytics' as const,
label: 'การวัดผลและสถิติ',
description:
'คุกกี้หรือ SDK เพื่อทำความเข้าใจการใช้งานเว็บไซต์ (เช่น analytics) จะเปิดใช้หลังได้รับความยินยอมเท่านั้น',
required: false,
},
{
id: 'marketing' as const,
label: 'การตลาดและการทำโปรไฟล์แบบเบา',
description:
'เครื่องมือโฆษณา remarketing หรือพิกเซลโซเชียล เฉพาะเมื่อเชื่อมต่อแล้วและหลังได้รับความยินยอมอย่างชัดเจน',
required: false,
},
],
} as const;
const cookieConsentUiByLocale = {
it: {
bannerTitle: 'Utilizziamo cookie e memorizzazione locale',
bannerDescBefore:
'Seleziona le categorie che preferisci. I necessari restano attivi per registrare la tua scelta. Dettagli nella',
bannerPolicyLinkLabel: 'informativa cookie',
rejectLabel: 'Rifiuta non essenziali',
customizeLabel: 'Personalizza',
acceptLabel: 'Accetta tutto',
dialogTitle: 'Preferenze cookie',
dialogIntroBefore: 'Versione informativa v',
dialogIntroAfter:
'. Puoi modificare le scelte in qualsiasi momento dal footer.',
alwaysOnLabel: 'Sempre attivi',
cancelLabel: 'Chiudi',
saveLabel: 'Salva preferenze',
},
{
id: 'preferences' as const,
label: 'Preferenze e funzionalità',
description:
'Ricordare impostazioni opzionali (es. lingua, tema) se introdotte in futuro.',
required: false,
en: {
bannerTitle: 'We use cookies and local storage',
bannerDescBefore:
'Choose the categories you prefer. Necessary cookies stay active to record your choice. Details in the',
bannerPolicyLinkLabel: 'cookie policy',
rejectLabel: 'Reject non-essential',
customizeLabel: 'Customize',
acceptLabel: 'Accept all',
dialogTitle: 'Cookie preferences',
dialogIntroBefore: 'Policy version v',
dialogIntroAfter: '. You can change your choices anytime from the footer.',
alwaysOnLabel: 'Always on',
cancelLabel: 'Close',
saveLabel: 'Save preferences',
},
{
id: 'analytics' as const,
label: 'Misurazione e statistica',
description:
'Cookie o SDK per capire come viene usato il sito (es. analytics), solo se attivati dal codice dopo consenso.',
required: false,
th: {
bannerTitle: 'เราใช้คุกกี้และการจัดเก็บในเครื่อง',
bannerDescBefore:
'เลือกหมวดหมู่ที่คุณต้องการ คุกกี้ที่จำเป็นยังคงเปิดอยู่เพื่อบันทึกทางเลือกของคุณ รายละเอียดอยู่ใน',
bannerPolicyLinkLabel: 'นโยบายคุกกี้',
rejectLabel: 'ปฏิเสธที่ไม่จำเป็น',
customizeLabel: 'ปรับแต่ง',
acceptLabel: 'ยอมรับทั้งหมด',
dialogTitle: 'การตั้งค่าคุกกี้',
dialogIntroBefore: 'เวอร์ชันนโยบาย v',
dialogIntroAfter: ' คุณสามารถเปลี่ยนตัวเลือกได้ตลอดเวลาจากส่วนท้ายเว็บไซต์',
alwaysOnLabel: 'เปิดเสมอ',
cancelLabel: 'ปิด',
saveLabel: 'บันทึกการตั้งค่า',
},
{
id: 'marketing' as const,
label: 'Marketing e profilazione leggera',
description:
'Strumenti pubblicitari, remarketing o pixel social, solo se collegati e dopo consenso esplicito.',
required: false,
},
] as const;
} as const;
export const getCookieCategories = (locale: SupportedLocale) =>
cookieCategoriesByLocale[locale];
export const getCookieConsentUi = (locale: SupportedLocale) =>
cookieConsentUiByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const cookieCategories = cookieCategoriesByLocale.it;

829
src/data/cookies.ts Normal file
View File

@ -0,0 +1,829 @@
/**
* Cookie Policy del portale NexStudio.
* Centralizza i contenuti di /cookies per revisioni legali e localizzazione IT/EN/TH.
*/
import type { SupportedLocale } from './home/navigation';
export type CookiesRichPart =
| { t: 'text'; v: string }
| { t: 'link'; href: string; label: string; external?: boolean }
| { t: 'em'; v: string }
| { t: 'strong'; v: string };
export type CookiesDefinition = {
term: string;
definition: string;
};
export type CookiesSubsection = {
title: string;
purposeLabel?: string;
purpose?: string;
legalBasisLabel?: string;
legalBasis?: string;
};
export type CookiesBullet =
| string
| { strong: string; after?: string }
| { before: string; strong: string; after?: string };
export type CookiesSection = {
id: string;
title: string;
paragraphs?: readonly string[];
richParagraphs?: readonly (readonly CookiesRichPart[])[];
definitions?: readonly CookiesDefinition[];
subsections?: readonly CookiesSubsection[];
bullets?: readonly CookiesBullet[];
paragraphsAfterBullets?: readonly (string | readonly CookiesRichPart[])[];
table?: {
headers: readonly string[];
emptyNote: string;
};
};
type CookiesLocaleBundle = {
document: { version: string; inVigoreDal: string };
riferimenti: readonly { label: string; href: string }[];
sections: readonly CookiesSection[];
page: {
title: string;
description: string;
heading: string;
lead: string;
versionLabel: string;
inForceFromLabel: string;
consentVersionLabel: string;
relatedDocsLabel: string;
indexLabel: string;
};
};
const cookiesByLocale: Record<SupportedLocale, CookiesLocaleBundle> = {
it: {
document: {
version: '1.1',
inVigoreDal: '[DATA]',
},
riferimenti: [
{ label: 'Informativa privacy', href: '/privacy' },
{ label: "Condizioni d'uso", href: '/terms' },
{ label: 'Diritti GDPR', href: '/gdpr' },
],
sections: [
{
id: 'premessa',
title: '1. Premessa',
richParagraphs: [
[
{
t: 'text',
v: 'Questa Cookie Policy descrive le tipologie di cookie e tecnologie simili utilizzate sul sito web di NexStudio S.r.l. (di seguito «NexStudio», «noi»), con sede legale in Italia, [INDIRIZZO_COMPLETO], e spiega come gestire le preferenze. Per il trattamento dei dati personali e per i diritti degli interessati si rimanda all',
},
{ t: 'link', href: '/privacy', label: 'Informativa privacy' },
{ t: 'text', v: '.' },
],
[
{
t: 'text',
v: 'Per le cookie policy dei prodotti SaaS LexAura e MediAura, consultare le rispettive pagine sui portali dedicati.',
},
],
],
},
{
id: 'definizioni',
title: '2. Definizioni',
definitions: [
{
term: 'Cookie',
definition:
'piccolo file di testo inviato dal sito al browser e memorizzato sul dispositivo, re-inviato al sito alle visite successive.',
},
{
term: 'Cookie di prima parte',
definition: 'impostati dal dominio del sito che lutente visita.',
},
{
term: 'Cookie di terze parti',
definition:
'impostati da domini diversi (fornitori esterni, embed, CDN, strumenti di misura).',
},
{
term: 'Tecnologie simili',
definition:
'web beacon, pixel, local storage, e in generale identificatori lato client.',
},
],
},
{
id: 'finalita',
title: '3. Finalità dei cookie e categorie',
subsections: [
{
title: '3.1 Cookie strettamente necessari (essenziali)',
purposeLabel: 'Finalità',
purpose:
'navigazione, sicurezza, gestione sessione, memorizzazione delle preferenze di consenso.',
legalBasisLabel: 'Base giuridica',
legalBasis:
'legittimo interesse del titolare (Art. 6.1.f GDPR) a garantire il funzionamento del servizio richiesto dallutente.',
},
{
title: '3.2 Cookie di preferenza / funzionali',
purposeLabel: 'Finalità',
purpose: 'lingua, regione, tema o altre impostazioni di interfaccia.',
legalBasisLabel: 'Base giuridica',
legalBasis: 'consenso (Art. 6.1.a GDPR).',
},
{
title: '3.3 Cookie di performance / analytics',
purposeLabel: 'Finalità',
purpose: 'statistiche aggregate su visite e percorsi per migliorare il sito.',
legalBasisLabel: 'Base giuridica',
legalBasis: 'consenso (Art. 6.1.a GDPR).',
},
{
title: '3.4 Cookie per advertising e targeting',
purposeLabel: 'Finalità',
purpose: 'pubblicità mirata, remarketing, misura delle campagne.',
legalBasisLabel: 'Base giuridica',
legalBasis: 'consenso esplicito.',
},
{
title: '3.5 Cookie di terze parti e integrazioni social',
purposeLabel: 'Finalità',
purpose:
'video incorporati, pulsanti social, mappe, chat di terze parti. Il trattamento da parte di terzi è regolato dalle rispettive policy.',
},
],
},
{
id: 'elenco',
title: '4. Elenco dei cookie utilizzati',
paragraphs: [
'Elenco in appendice tecnica (da mantenere aggiornato con scansione periodica).',
],
table: {
headers: ['Nome', 'Provider', 'Categoria', 'Durata', 'Tipo', 'Base giuridica'],
emptyNote: 'Da compilare dopo scansione del sito in staging e produzione.',
},
},
{
id: 'base-giuridica',
title: '5. Base giuridica',
bullets: [
'Cookie tecnici essenziali: legittimo interesse del titolare (Art. 6.1.f GDPR).',
'Cookie analitici e di marketing: consenso libero, informato e revocabile (Art. 6.1.a GDPR).',
],
},
{
id: 'consenso',
title: '6. Modalità di raccolta del consenso e registro',
paragraphs: [
'Il sito utilizza una CMP first-party. Al primo accesso viene mostrato un banner con:',
],
bullets: [
{ strong: 'Rifiuta non essenziali' },
{ strong: 'Personalizza', after: ' (apertura pannello categorie)' },
{ strong: 'Accetta tutto' },
],
paragraphsAfterBullets: [
[
{ t: 'text', v: 'La prova della scelta è memorizzata nel browser in ' },
{ t: 'em', v: 'local storage' },
{ t: 'text', v: '.' },
],
],
},
{
id: 'revoca',
title: '7. Gestione e revoca del consenso',
bullets: [
{
before: 'Tramite il banner o il pannello ',
strong: 'Personalizza',
after: ' al primo accesso.',
},
{
before: 'In qualsiasi momento tramite ',
strong: 'Impostazioni cookie',
after: ' nel footer del sito.',
},
'Cancellando i dati locali del sito dalle impostazioni del browser.',
],
},
{
id: 'browser',
title: '8. Disabilitazione via browser e impatto',
paragraphs: [
'È possibile bloccare o cancellare i cookie dalle impostazioni del browser (Chrome, Firefox, Safari, Edge e altri).',
],
},
{
id: 'terze-parti',
title: '9. Cookie di terze parti e responsabilità',
paragraphs: [
'Il sito può integrare strumenti di terze parti (analytics, CDN, social). Le terze parti possono impostare cookie propri e trattare dati secondo le loro policy.',
],
},
{
id: 'retention',
title: '10. Conservazione e criteri di retention',
paragraphs: [
'Le durate dei singoli cookie seguono le tabelle pubblicate e le policy dei fornitori. Cookie di consenso: 365 giorni, rinnovabili. Log di sicurezza: massimo 30 giorni.',
],
},
{
id: 'trasferimenti',
title: '11. Trasferimenti internazionali',
paragraphs: [
'Alcuni fornitori possono trattare dati in Paesi terzi (es. Stati Uniti). Ove applicabile si adottano garanzie come clausole contrattuali standard, decisioni di adeguatezza o altre basi previste dalla legge.',
],
},
{
id: 'minori',
title: '12. Minori età',
paragraphs: ['Il sito non è rivolto alla raccolta intenzionale di dati di minori.'],
},
{
id: 'sicurezza',
title: '13. Misure di sicurezza',
paragraphs: [
'Si applicano misure tecniche e organizzative appropriate: trasmissione su TLS, accesso limitato ai log, principio di minimizzazione.',
],
},
{
id: 'advertising',
title: '14. Consenso per scopi pubblicitari e profiling',
paragraphs: [
'Profilazione e pubblicità comportano consenso esplicito e tracciabilità della scelta.',
],
},
{
id: 'effetti',
title: '15. Effetti di rifiuto o accettazione',
paragraphs: [
'Accettando le categorie opzionali si abilitano preferenze avanzate, analytics e funzioni di marketing. Rifiutando i non essenziali il sito resta navigabile.',
],
},
{
id: 'modifiche',
title: '16. Modifiche alla Cookie Policy',
paragraphs: [
'La Policy può essere aggiornata quando cambiano cookie, fornitori o norme. Sarà indicata la data di revisione.',
],
},
{
id: 'diritti',
title: '17. Diritti degli interessati e contatti',
richParagraphs: [
[
{
t: 'text',
v: 'Diritti: accesso, rettifica, cancellazione, limitazione, portabilità, opposizione e revoca del consenso. Per esercitarli: ',
},
{ t: 'link', href: 'mailto:privacy@nexstudio.com', label: 'privacy@nexstudio.com' },
{
t: 'text',
v: ' e tramite il form su nexstudio.ai. Autorità di controllo: Garante per la protezione dei dati personali (',
},
{
t: 'link',
href: 'https://www.garanteprivacy.it',
label: 'www.garanteprivacy.it',
external: true,
},
{ t: 'text', v: ').' },
],
],
},
],
page: {
title: 'Cookie Policy — NexStudio',
description:
'Cookie Policy NexStudio S.r.l.: categorie, base giuridica, consenso, terze parti, diritti e contatti. Sede legale in Italia.',
heading: 'Cookie Policy',
lead:
'Titolare del trattamento: NexStudio S.r.l., con sede legale in Italia, [INDIRIZZO_COMPLETO].',
versionLabel: 'Versione',
inForceFromLabel: 'In vigore dal',
consentVersionLabel: 'Versione tecnica consenso',
relatedDocsLabel: 'Documenti connessi',
indexLabel: 'Indice',
},
},
en: {
document: {
version: '1.1',
inVigoreDal: '[DATA]',
},
riferimenti: [
{ label: 'Privacy notice', href: '/privacy' },
{ label: 'Terms of use', href: '/terms' },
{ label: 'GDPR rights', href: '/gdpr' },
],
sections: [
{
id: 'premessa',
title: '1. Introduction',
richParagraphs: [
[
{
t: 'text',
v: 'This Cookie Policy describes the types of cookies and similar technologies used on the website of NexStudio S.r.l. (hereinafter “NexStudio”, “we”), with registered office in Italy, [INDIRIZZO_COMPLETO], and explains how to manage preferences. For personal data processing and data subject rights, please refer to the ',
},
{ t: 'link', href: '/privacy', label: 'Privacy notice' },
{ t: 'text', v: '.' },
],
[
{
t: 'text',
v: 'For the cookie policies of the LexAura and MediAura SaaS products, please consult the dedicated pages on the respective portals.',
},
],
],
},
{
id: 'definizioni',
title: '2. Definitions',
definitions: [
{
term: 'Cookie',
definition:
'a small text file sent by the site to the browser and stored on the device, then sent back to the site on subsequent visits.',
},
{
term: 'First-party cookie',
definition: 'set by the domain of the site the user is visiting.',
},
{
term: 'Third-party cookie',
definition:
'set by different domains (external providers, embeds, CDNs, measurement tools).',
},
{
term: 'Similar technologies',
definition:
'web beacons, pixels, local storage, and client-side identifiers in general.',
},
],
},
{
id: 'finalita',
title: '3. Cookie purposes and categories',
subsections: [
{
title: '3.1 Strictly necessary (essential) cookies',
purposeLabel: 'Purpose',
purpose:
'navigation, security, session management, storage of consent preferences.',
legalBasisLabel: 'Legal basis',
legalBasis:
'legitimate interest of the controller (Art. 6.1.f GDPR) in ensuring the operation of the service requested by the user.',
},
{
title: '3.2 Preference / functional cookies',
purposeLabel: 'Purpose',
purpose: 'language, region, theme, or other interface settings.',
legalBasisLabel: 'Legal basis',
legalBasis: 'consent (Art. 6.1.a GDPR).',
},
{
title: '3.3 Performance / analytics cookies',
purposeLabel: 'Purpose',
purpose: 'aggregate statistics on visits and journeys to improve the site.',
legalBasisLabel: 'Legal basis',
legalBasis: 'consent (Art. 6.1.a GDPR).',
},
{
title: '3.4 Advertising and targeting cookies',
purposeLabel: 'Purpose',
purpose: 'targeted advertising, remarketing, campaign measurement.',
legalBasisLabel: 'Legal basis',
legalBasis: 'explicit consent.',
},
{
title: '3.5 Third-party cookies and social integrations',
purposeLabel: 'Purpose',
purpose:
'embedded videos, social buttons, maps, third-party chat. Processing by third parties is governed by their respective policies.',
},
],
},
{
id: 'elenco',
title: '4. List of cookies used',
paragraphs: [
'Technical appendix list (to be kept up to date with periodic scanning).',
],
table: {
headers: ['Name', 'Provider', 'Category', 'Duration', 'Type', 'Legal basis'],
emptyNote: 'To be completed after scanning the site in staging and production.',
},
},
{
id: 'base-giuridica',
title: '5. Legal basis',
bullets: [
'Essential technical cookies: legitimate interest of the controller (Art. 6.1.f GDPR).',
'Analytics and marketing cookies: free, informed, and revocable consent (Art. 6.1.a GDPR).',
],
},
{
id: 'consenso',
title: '6. Consent collection method and record',
paragraphs: [
'The site uses a first-party CMP. On first visit a banner is shown with:',
],
bullets: [
{ strong: 'Reject non-essential' },
{ strong: 'Customize', after: ' (opens category panel)' },
{ strong: 'Accept all' },
],
paragraphsAfterBullets: [
[
{ t: 'text', v: 'Proof of the choice is stored in the browser in ' },
{ t: 'em', v: 'local storage' },
{ t: 'text', v: '.' },
],
],
},
{
id: 'revoca',
title: '7. Managing and withdrawing consent',
bullets: [
{
before: 'Via the banner or the ',
strong: 'Customize',
after: ' panel on first visit.',
},
{
before: 'At any time via ',
strong: 'Cookie settings',
after: ' in the site footer.',
},
'By clearing the sites local data from browser settings.',
],
},
{
id: 'browser',
title: '8. Browser disabling and impact',
paragraphs: [
'You can block or delete cookies from browser settings (Chrome, Firefox, Safari, Edge, and others).',
],
},
{
id: 'terze-parti',
title: '9. Third-party cookies and responsibility',
paragraphs: [
'The site may integrate third-party tools (analytics, CDN, social). Third parties may set their own cookies and process data under their policies.',
],
},
{
id: 'retention',
title: '10. Retention and retention criteria',
paragraphs: [
'Individual cookie durations follow the published tables and provider policies. Consent cookies: 365 days, renewable. Security logs: maximum 30 days.',
],
},
{
id: 'trasferimenti',
title: '11. International transfers',
paragraphs: [
'Some providers may process data in third countries (e.g. the United States). Where applicable, safeguards such as standard contractual clauses, adequacy decisions, or other bases provided by law are adopted.',
],
},
{
id: 'minori',
title: '12. Minors',
paragraphs: [
'The site is not directed at the intentional collection of minors data.',
],
},
{
id: 'sicurezza',
title: '13. Security measures',
paragraphs: [
'Appropriate technical and organisational measures apply: TLS transmission, limited log access, minimisation principle.',
],
},
{
id: 'advertising',
title: '14. Consent for advertising and profiling',
paragraphs: [
'Profiling and advertising require explicit consent and traceability of the choice.',
],
},
{
id: 'effetti',
title: '15. Effects of refusal or acceptance',
paragraphs: [
'Accepting optional categories enables advanced preferences, analytics, and marketing features. Rejecting non-essential cookies keeps the site navigable.',
],
},
{
id: 'modifiche',
title: '16. Changes to the Cookie Policy',
paragraphs: [
'The Policy may be updated when cookies, providers, or rules change. The revision date will be indicated.',
],
},
{
id: 'diritti',
title: '17. Data subject rights and contacts',
richParagraphs: [
[
{
t: 'text',
v: 'Rights: access, rectification, erasure, restriction, portability, objection, and withdrawal of consent. To exercise them: ',
},
{ t: 'link', href: 'mailto:privacy@nexstudio.com', label: 'privacy@nexstudio.com' },
{
t: 'text',
v: ' and via the form on nexstudio.ai. Supervisory authority: Garante per la protezione dei dati personali (',
},
{
t: 'link',
href: 'https://www.garanteprivacy.it',
label: 'www.garanteprivacy.it',
external: true,
},
{ t: 'text', v: ').' },
],
],
},
],
page: {
title: 'Cookie Policy — NexStudio',
description:
'NexStudio S.r.l. Cookie Policy: categories, legal basis, consent, third parties, rights, and contacts. Registered office in Italy.',
heading: 'Cookie Policy',
lead:
'Data controller: NexStudio S.r.l., with registered office in Italy, [INDIRIZZO_COMPLETO].',
versionLabel: 'Version',
inForceFromLabel: 'In force from',
consentVersionLabel: 'Technical consent version',
relatedDocsLabel: 'Related documents',
indexLabel: 'Index',
},
},
th: {
document: {
version: '1.1',
inVigoreDal: '[DATA]',
},
riferimenti: [
{ label: 'ประกาศความเป็นส่วนตัว', href: '/privacy' },
{ label: 'เงื่อนไขการใช้งาน', href: '/terms' },
{ label: 'สิทธิ GDPR', href: '/gdpr' },
],
sections: [
{
id: 'premessa',
title: '1. บทนำ',
richParagraphs: [
[
{
t: 'text',
v: 'นโยบายคุกกี้นี้อธิบายประเภทของคุกกี้และเทคโนโลยีที่คล้ายกันซึ่งใช้บนเว็บไซต์ของ NexStudio S.r.l. (ต่อไปนี้เรียกว่า «NexStudio» หรือ «เรา») สำนักงานจดทะเบียนในอิตาลี [INDIRIZZO_COMPLETO] และอธิบายวิธีจัดการการตั้งค่า สำหรับการประมวลผลข้อมูลส่วนบุคคลและสิทธิของเจ้าของข้อมูล โปรดดูที่ ',
},
{ t: 'link', href: '/privacy', label: 'ประกาศความเป็นส่วนตัว' },
{ t: 'text', v: '.' },
],
[
{
t: 'text',
v: 'สำหรับนโยบายคุกกี้ของผลิตภัณฑ์ SaaS LexAura และ MediAura โปรดดูหน้าที่เกี่ยวข้องบนพอร์ทัลเฉพาะ',
},
],
],
},
{
id: 'definizioni',
title: '2. คำจำกัดความ',
definitions: [
{
term: 'คุกกี้',
definition:
'ไฟล์ข้อความขนาดเล็กที่เว็บไซต์ส่งไปยังเบราว์เซอร์และเก็บไว้บนอุปกรณ์ แล้วส่งกลับไปยังเว็บไซต์ในการเข้าชมครั้งถัดไป',
},
{
term: 'คุกกี้ฝั่งเว็บไซต์ (first-party)',
definition: 'ตั้งโดยโดเมนของเว็บไซต์ที่ผู้ใช้เข้าชม',
},
{
term: 'คุกกี้ของบุคคลที่สาม',
definition:
'ตั้งโดยโดเมนอื่น (ผู้ให้บริการภายนอก สื่อฝัง CDN เครื่องมือวัดผล)',
},
{
term: 'เทคโนโลยีที่คล้ายกัน',
definition:
'web beacon, พิกเซล, local storage และตัวระบุฝั่งไคลเอนต์โดยทั่วไป',
},
],
},
{
id: 'finalita',
title: '3. วัตถุประสงค์และหมวดหมู่ของคุกกี้',
subsections: [
{
title: '3.1 คุกกี้ที่จำเป็นอย่างเคร่งครัด (สำคัญ)',
purposeLabel: 'วัตถุประสงค์',
purpose:
'การนำทาง ความปลอดภัย การจัดการเซสชัน และการจัดเก็บการตั้งค่าความยินยอม',
legalBasisLabel: 'ฐานทางกฎหมาย',
legalBasis:
'ประโยชน์โดยชอบด้วยกฎหมายของผู้ควบคุม (มาตรา 6.1.f GDPR) เพื่อให้บริการที่ผู้ใช้ร้องขอทำงานได้',
},
{
title: '3.2 คุกกี้การตั้งค่า / ฟังก์ชัน',
purposeLabel: 'วัตถุประสงค์',
purpose: 'ภาษา ภูมิภาค ธีม หรือการตั้งค่าอินเทอร์เฟซอื่น ๆ',
legalBasisLabel: 'ฐานทางกฎหมาย',
legalBasis: 'ความยินยอม (มาตรา 6.1.a GDPR)',
},
{
title: '3.3 คุกกี้ประสิทธิภาพ / การวิเคราะห์',
purposeLabel: 'วัตถุประสงค์',
purpose: 'สถิติรวมเกี่ยวกับการเข้าชมและเส้นทางเพื่อปรับปรุงเว็บไซต์',
legalBasisLabel: 'ฐานทางกฎหมาย',
legalBasis: 'ความยินยอม (มาตรา 6.1.a GDPR)',
},
{
title: '3.4 คุกกี้โฆษณาและการกำหนดเป้าหมาย',
purposeLabel: 'วัตถุประสงค์',
purpose: 'โฆษณาที่ตรงเป้าหมาย การ remarketing และการวัดผลแคมเปญ',
legalBasisLabel: 'ฐานทางกฎหมาย',
legalBasis: 'ความยินยอมอย่างชัดแจ้ง',
},
{
title: '3.5 คุกกี้ของบุคคลที่สามและการผสานโซเชียล',
purposeLabel: 'วัตถุประสงค์',
purpose:
'วิดีโอฝัง ปุ่มโซเชียล แผนที่ แชทของบุคคลที่สาม การประมวลผลโดยบุคคลที่สามอยู่ภายใต้นโยบายของตน',
},
],
},
{
id: 'elenco',
title: '4. รายการคุกกี้ที่ใช้',
paragraphs: [
'รายการในภาคผนวกทางเทคนิค (ต้องอัปเดตด้วยการสแกนเป็นระยะ)',
],
table: {
headers: ['ชื่อ', 'ผู้ให้บริการ', 'หมวดหมู่', 'ระยะเวลา', 'ประเภท', 'ฐานทางกฎหมาย'],
emptyNote: 'ต้องกรอกหลังสแกนเว็บไซต์ใน staging และ production',
},
},
{
id: 'base-giuridica',
title: '5. ฐานทางกฎหมาย',
bullets: [
'คุกกี้ทางเทคนิคที่จำเป็น: ประโยชน์โดยชอบด้วยกฎหมายของผู้ควบคุม (มาตรา 6.1.f GDPR)',
'คุกกี้วิเคราะห์และการตลาด: ความยินยอมที่อิสระ มีข้อมูล และเพิกถอนได้ (มาตรา 6.1.a GDPR)',
],
},
{
id: 'consenso',
title: '6. วิธีการเก็บความยินยอมและการบันทึก',
paragraphs: [
'เว็บไซต์ใช้ CMP ของฝั่งเว็บไซต์เอง ในการเข้าชมครั้งแรกจะแสดงแบนเนอร์พร้อม:',
],
bullets: [
{ strong: 'ปฏิเสธที่ไม่จำเป็น' },
{ strong: 'ปรับแต่ง', after: ' (เปิดแผงหมวดหมู่)' },
{ strong: 'ยอมรับทั้งหมด' },
],
paragraphsAfterBullets: [
[
{ t: 'text', v: 'หลักฐานการเลือกถูกเก็บไว้ในเบราว์เซอร์ใน ' },
{ t: 'em', v: 'local storage' },
{ t: 'text', v: '.' },
],
],
},
{
id: 'revoca',
title: '7. การจัดการและการเพิกถอนความยินยอม',
bullets: [
{
before: 'ผ่านแบนเนอร์หรือแผง ',
strong: 'ปรับแต่ง',
after: ' ในการเข้าชมครั้งแรก',
},
{
before: 'ได้ตลอดเวลาผ่าน ',
strong: 'การตั้งค่าคุกกี้',
after: ' ในส่วนท้ายของเว็บไซต์',
},
'โดยลบข้อมูลในเครื่องของเว็บไซต์จากการตั้งค่าเบราว์เซอร์',
],
},
{
id: 'browser',
title: '8. การปิดใช้งานผ่านเบราว์เซอร์และผลกระทบ',
paragraphs: [
'สามารถบล็อกหรือลบคุกกี้จากการตั้งค่าเบราว์เซอร์ได้ (Chrome, Firefox, Safari, Edge และอื่น ๆ)',
],
},
{
id: 'terze-parti',
title: '9. คุกกี้ของบุคคลที่สามและความรับผิดชอบ',
paragraphs: [
'เว็บไซต์อาจผสานเครื่องมือของบุคคลที่สาม (analytics, CDN, โซเชียล) บุคคลที่สามอาจตั้งคุกกี้ของตนเองและประมวลผลข้อมูลตามนโยบายของตน',
],
},
{
id: 'retention',
title: '10. การเก็บรักษาและเกณฑ์ retention',
paragraphs: [
'ระยะเวลาของคุกกี้แต่ละตัวเป็นไปตามตารางที่เผยแพร่และนโยบายของผู้ให้บริการ คุกกี้ความยินยอม: 365 วัน ต่ออายุได้ ล็อกความปลอดภัย: สูงสุด 30 วัน',
],
},
{
id: 'trasferimenti',
title: '11. การโอนข้อมูลระหว่างประเทศ',
paragraphs: [
'ผู้ให้บริการบางรายอาจประมวลผลข้อมูลในประเทศที่สาม (เช่น สหรัฐอเมริกา) เมื่อใช้ได้ จะใช้การรับประกัน เช่น ข้อสัญญามาตรฐาน การตัดสินใจว่าเพียงพอ หรือฐานอื่นตามกฎหมาย',
],
},
{
id: 'minori',
title: '12. ผู้เยาว์',
paragraphs: [
'เว็บไซต์ไม่ได้มุ่งเป้าไปที่การเก็บข้อมูลของผู้เยาว์โดยเจตนา',
],
},
{
id: 'sicurezza',
title: '13. มาตรการความปลอดภัย',
paragraphs: [
'ใช้มาตรการทางเทคนิคและองค์กรที่เหมาะสม: การส่งผ่าน TLS การเข้าถึงล็อกอย่างจำกัด หลักการลดข้อมูลให้เหลือน้อยที่สุด',
],
},
{
id: 'advertising',
title: '14. ความยินยอมเพื่อโฆษณาและการทำโปรไฟล์',
paragraphs: [
'การทำโปรไฟล์และโฆษณาต้องมีความยินยอมอย่างชัดแจ้งและความสามารถในการติดตามการเลือก',
],
},
{
id: 'effetti',
title: '15. ผลของการปฏิเสธหรือการยอมรับ',
paragraphs: [
'การยอมรับหมวดหมู่ที่ไม่บังคับจะเปิดใช้การตั้งค่าขั้นสูง analytics และฟังก์ชันการตลาด การปฏิเสธคุกกี้ที่ไม่จำเป็นยังคงทำให้เว็บไซต์ใช้งานได้',
],
},
{
id: 'modifiche',
title: '16. การเปลี่ยนแปลงนโยบายคุกกี้',
paragraphs: [
'นโยบายอาจได้รับการอัปเดตเมื่อคุกกี้ ผู้ให้บริการ หรือกฎระเบียบเปลี่ยน จะระบุวันที่แก้ไข',
],
},
{
id: 'diritti',
title: '17. สิทธิของเจ้าของข้อมูลและการติดต่อ',
richParagraphs: [
[
{
t: 'text',
v: 'สิทธิ: เข้าถึง แก้ไข ลบ จำกัด โอนย้าย คัดค้าน และเพิกถอนความยินยอม เพื่อใช้สิทธิ: ',
},
{ t: 'link', href: 'mailto:privacy@nexstudio.com', label: 'privacy@nexstudio.com' },
{
t: 'text',
v: ' และผ่านแบบฟอร์มบน nexstudio.ai หน่วยงานกำกับดูแล: Garante per la protezione dei dati personali (',
},
{
t: 'link',
href: 'https://www.garanteprivacy.it',
label: 'www.garanteprivacy.it',
external: true,
},
{ t: 'text', v: ')' },
],
],
},
],
page: {
title: 'นโยบายคุกกี้ — NexStudio',
description:
'นโยบายคุกกี้ของ NexStudio S.r.l.: หมวดหมู่ ฐานทางกฎหมาย ความยินยอม บุคคลที่สาม สิทธิ และการติดต่อ สำนักงานจดทะเบียนในอิตาลี',
heading: 'นโยบายคุกกี้',
lead:
'ผู้ควบคุมข้อมูล: NexStudio S.r.l. สำนักงานจดทะเบียนในอิตาลี [INDIRIZZO_COMPLETO]',
versionLabel: 'เวอร์ชัน',
inForceFromLabel: 'มีผลตั้งแต่',
consentVersionLabel: 'เวอร์ชันทางเทคนิคของความยินยอม',
relatedDocsLabel: 'เอกสารที่เกี่ยวข้อง',
indexLabel: 'สารบัญ',
},
},
};
export const getCookies = (locale: SupportedLocale) => cookiesByLocale[locale];

95
src/data/dove-siamo.ts Normal file
View File

@ -0,0 +1,95 @@
/**
* Pagina Dove siamo / Where we are / ที่ตั้ง — contenuti localizzati.
*/
import type { SupportedLocale } from './home/navigation';
export type DoveSiamoSection = {
title: string;
paragraphs: readonly string[];
/** When true, render "A — B" with both sides bold. */
locationLine?: boolean;
}
type DoveSiamoLocaleBundle = {
page: {
title: string;
description: string;
heading: string;
lead: string;
};
sections: readonly DoveSiamoSection[];
};
const doveSiamoByLocale: Record<SupportedLocale, DoveSiamoLocaleBundle> = {
it: {
page: {
title: 'Dove siamo — NexStudio',
description:
'Sede operativa a Bangkok, Thailandia; fusi orari e lingue per lavorare con clienti in Europa e altre regioni.',
heading: 'Dove siamo',
lead:
'NexStudio ha base a Bangkok, in Thailandia. Sotto, come organizziamo fusi orari e lingue rispetto a clienti in Europa e oltre.',
},
sections: [
{
title: 'Sede',
paragraphs: ['Thailandia — Bangkok'],
locationLine: true,
},
{
title: 'Fusi orari e lingue',
paragraphs: [
'Il sito e il supporto via web possono essere gestiti in italiano, inglese, tedesco, francese, spagnolo e tailandese, con orari da concordare in base al team e al fuso rispetto ai clienti in Europa e altre regioni.',
],
},
],
},
en: {
page: {
title: 'Where we are — NexStudio',
description:
'Operational base in Bangkok, Thailand; time zones and languages for working with clients in Europe and other regions.',
heading: 'Where we are',
lead:
'NexStudio is based in Bangkok, Thailand. Below, how we organize time zones and languages relative to clients in Europe and beyond.',
},
sections: [
{
title: 'Location',
paragraphs: ['Thailand — Bangkok'],
locationLine: true,
},
{
title: 'Time zones and languages',
paragraphs: [
'The site and web support can be handled in Italian, English, German, French, Spanish, and Thai, with hours to be agreed based on the team and time zone relative to clients in Europe and other regions.',
],
},
],
},
th: {
page: {
title: 'ที่ตั้ง — NexStudio',
description:
'ฐานปฏิบัติการที่กรุงเทพฯ ประเทศไทย เขตเวลาและภาษาสำหรับการทำงานกับลูกค้าในยุโรปและภูมิภาคอื่น',
heading: 'ที่ตั้ง',
lead:
'NexStudio มีฐานที่กรุงเทพฯ ประเทศไทย ด้านล่างคือวิธีที่เราจัดเขตเวลาและภาษาเมื่อเทียบกับลูกค้าในยุโรปและที่อื่น',
},
sections: [
{
title: 'สำนักงาน',
paragraphs: ['ประเทศไทย — กรุงเทพฯ'],
locationLine: true,
},
{
title: 'เขตเวลาและภาษา',
paragraphs: [
'เว็บไซต์และการสนับสนุนผ่านเว็บสามารถจัดการเป็นภาษาอิตาลี อังกฤษ เยอรมัน ฝรั่งเศส สเปน และไทย โดยตกลงเวลาตามทีมและเขตเวลาเทียบกับลูกค้าในยุโรปและภูมิภาคอื่น',
],
},
],
},
};
export const getDoveSiamo = (locale: SupportedLocale) => doveSiamoByLocale[locale];

View File

@ -1,96 +1,328 @@
/**
* Pagina GDPR: diritti degli interessati e modalita' operative.
* Pagina GDPR: diritti degli interessati e modalità operative.
* Documento di supporto alla privacy policy quadro del portale NexStudio.
*/
export const gdprDocument = {
version: '1.0',
inVigoreDal: '22 aprile 2026',
ultimoAggiornamento: '22 aprile 2026',
} as const;
export const gdprCrossReferences = {
lead:
"Per LexAura e MediAura, i dettagli operativi del singolo prodotto sono indicati nelle rispettive informative privacy pubblicate sui portali SaaS dedicati.",
} as const;
export const gdprRiferimenti: readonly { label: string; href: string }[] = [
{ label: 'Informativa privacy', href: '/privacy' },
{ label: 'Cookie policy', href: '/cookies' },
{ label: "Condizioni d'uso", href: '/terms' },
] as const;
import type { SupportedLocale } from './home/navigation';
export type GdprSection = {
id: string;
title: string;
paragraphs: readonly string[];
bullets?: readonly string[];
/** Paragrafi dopo lelenco puntato. */
paragraphsAfterBullets?: readonly string[];
};
export const gdprSections: readonly GdprSection[] = [
{
id: 'quadro',
title: '1. Quadro normativo e ambito',
paragraphs: [
"Il Regolamento (UE) 2016/679 (GDPR) tutela i diritti degli interessati e disciplina il trattamento dei dati personali. Questa pagina spiega come esercitare tali diritti verso NexStudio nel contesto del portale corporate e dei servizi collegati.",
"La presente pagina integra, ma non sostituisce, l'informativa privacy generale: per categorie dati, finalita' e basi giuridiche si rimanda a `/privacy`; per cookie e tecnologie simili si rimanda a `/cookies`.",
],
const gdprByLocale = {
it: {
document: {
version: '1.1',
inVigoreDal: '[DATA]',
ultimoAggiornamento: '[DATA]',
},
crossReferences: {
lead:
'Per LexAura e MediAura, i dettagli operativi del singolo prodotto sono indicati nelle rispettive informative privacy pubblicate sui portali SaaS dedicati.',
},
riferimenti: [
{ label: 'Informativa privacy', href: '/privacy' },
{ label: 'Cookie policy', href: '/cookies' },
{ label: "Condizioni d'uso", href: '/terms' },
] as const,
sections: [
{
id: 'quadro',
title: '1. Quadro normativo e ambito',
paragraphs: [
'Il Regolamento (UE) 2016/679 (GDPR) tutela i diritti degli interessati e disciplina il trattamento dei dati personali. Questa pagina spiega come esercitare tali diritti verso NexStudio nel contesto del portale corporate e dei servizi collegati.',
],
},
{
id: 'diritti',
title: '2. Diritti riconosciuti agli interessati',
paragraphs: [
"Nei limiti previsti dalla normativa applicabile, l'interessato può esercitare i seguenti diritti:",
],
bullets: [
'Accesso ai dati personali e ottenimento di copia in formato intelligibile;',
'Rettifica dei dati inesatti e integrazione dei dati incompleti;',
"Cancellazione dei dati (diritto all'oblio), quando ne ricorrono i presupposti;",
'Limitazione del trattamento in presenza delle condizioni previste dal GDPR;',
'Portabilità dei dati trattati con mezzi automatizzati sulla base di consenso o contratto;',
"Opposizione al trattamento fondato su legittimo interesse, inclusa l'opposizione a comunicazioni marketing;",
'Revoca del consenso in qualsiasi momento, senza pregiudicare la liceità del trattamento precedente;',
"Reclamo all'autorità di controllo competente.",
],
},
{
id: 'chi-contattare',
title: '3. Chi contattare per esercitare i diritti',
paragraphs: [],
bullets: [
'Email: privacy@nexstudio.com',
'Form: nexstudio.ai/contatti',
'Per trattamenti nei tenant SaaS (LexAura/MediAura): rivolgersi prima al cliente titolare (studio legale, struttura sanitaria). NexStudio supporta il cliente titolare secondo DPA e accordi contrattuali.',
],
paragraphsAfterBullets: [
'Autorità di controllo competente: Garante per la protezione dei dati personali (www.garanteprivacy.it) per i trattamenti soggetti al GDPR.',
],
},
{
id: 'identificazione',
title: "4. Verifica dell'identità e gestione della richiesta",
paragraphs: [
"Per tutelare riservatezza e sicurezza, NexStudio può richiedere informazioni minime aggiuntive necessarie a verificare l'identità del richiedente. Le richieste sono gestite con procedure tracciate e prese in carico nei tempi previsti dal GDPR; in caso di richieste complesse o multiple, i termini possono essere prorogati nei limiti consentiti dalla legge, con comunicazione motivata.",
],
},
{
id: 'limitazioni',
title: '5. Casi di limitazione o rigetto',
paragraphs: [
"L'esercizio dei diritti può essere limitato o non accolto quando sussistono obblighi legali prevalenti, esigenze di difesa in giudizio, tutela dei diritti di terzi o altre eccezioni previste dalla normativa. In tali casi NexStudio fornisce un riscontro motivato e indica i rimedi disponibili, incluso il diritto di proporre reclamo all'autorità competente.",
],
},
{
id: 'trasferimenti',
title: '6. Trasferimenti internazionali e garanzie',
paragraphs: [
'Qualora il trattamento comporti trasferimenti extra SEE/UE, NexStudio adotta le misure richieste dalla normativa (es. clausole contrattuali standard e misure supplementari). I dati risiedono primariamente in UE via infrastruttura Cloudflare.',
],
},
{
id: 'documenti-prodotto',
title: '7. Coordinamento con le privacy dei SaaS',
paragraphs: [
'Le privacy policy dei portali SaaS LexAura e MediAura dettagliano, per ciascun prodotto, flussi dati, tempi di conservazione, ruoli privacy e canali dedicati. In caso di differenze tra documenti, prevalgono i documenti specifici del servizio e gli accordi contrattuali applicabili.',
'Per consultare le privacy dei prodotti:',
],
bullets: [
'LexAura: vedere il portale dedicato',
'MediAura: mediaura.doctor/privacy',
],
},
] as const satisfies readonly GdprSection[],
page: {
title: 'GDPR — NexStudio',
description:
'Pagina GDPR NexStudio S.r.l.: diritti degli interessati, modalità operative, ruoli tra portale corporate e SaaS LexAura/MediAura, coordinamento con privacy dedicate.',
heading: 'GDPR e diritti degli interessati',
lead:
"Diritti degli interessati e modalità di esercizio ai sensi del Regolamento (UE) 2016/679, in coerenza con l'informativa privacy quadro di NexStudio S.r.l. (Italia, [INDIRIZZO_COMPLETO]). Per LexAura e MediAura, i dettagli operativi del singolo prodotto sono indicati nelle rispettive informative privacy pubblicate sui portali SaaS dedicati.",
versionLabel: 'Versione',
inForceFromLabel: 'In vigore dal',
lastUpdateLabel: 'Ultimo aggiornamento',
relatedDocsLabel: 'Documenti connessi',
indexLabel: 'Indice',
},
},
{
id: 'diritti',
title: '2. Diritti riconosciuti agli interessati',
paragraphs: [
"Nei limiti previsti dalla normativa applicabile, l'interessato puo' esercitare i seguenti diritti:",
],
bullets: [
"accesso ai dati personali e ottenimento di copia in formato intelligibile;",
"rettifica dei dati inesatti e integrazione dei dati incompleti;",
"cancellazione dei dati (diritto all'oblio), quando ne ricorrono i presupposti di legge;",
"limitazione del trattamento in presenza delle condizioni previste dal GDPR;",
"portabilita' dei dati trattati con mezzi automatizzati sulla base di consenso o contratto;",
"opposizione al trattamento fondato su legittimo interesse, inclusa l'opposizione a comunicazioni marketing ove applicabile;",
"revoca del consenso in qualsiasi momento, senza pregiudicare la liceita' del trattamento precedente;",
"reclamo all'autorita' di controllo competente.",
],
en: {
document: {
version: '1.1',
inVigoreDal: '[DATA]',
ultimoAggiornamento: '[DATA]',
},
crossReferences: {
lead:
'For LexAura and MediAura, product-specific operational details are set out in the privacy notices published on the dedicated SaaS portals.',
},
riferimenti: [
{ label: 'Privacy notice', href: '/privacy' },
{ label: 'Cookie policy', href: '/cookies' },
{ label: 'Terms of use', href: '/terms' },
] as const,
sections: [
{
id: 'quadro',
title: '1. Regulatory framework and scope',
paragraphs: [
'Regulation (EU) 2016/679 (GDPR) protects data subjects rights and governs the processing of personal data. This page explains how to exercise those rights vis-à-vis NexStudio in the context of the corporate portal and related services.',
],
},
{
id: 'diritti',
title: '2. Rights granted to data subjects',
paragraphs: [
'Within the limits provided by applicable law, the data subject may exercise the following rights:',
],
bullets: [
'Access to personal data and obtaining a copy in an intelligible format;',
'Rectification of inaccurate data and completion of incomplete data;',
'Erasure of data (right to be forgotten), where the relevant conditions are met;',
'Restriction of processing where the conditions set out in the GDPR apply;',
'Data portability for data processed by automated means on the basis of consent or contract;',
'Objection to processing based on legitimate interest, including objection to marketing communications;',
'Withdrawal of consent at any time, without affecting the lawfulness of processing carried out before withdrawal;',
'Complaint to the competent supervisory authority.',
],
},
{
id: 'chi-contattare',
title: '3. Who to contact to exercise rights',
paragraphs: [],
bullets: [
'Email: privacy@nexstudio.com',
'Form: nexstudio.ai/contatti',
'For processing in SaaS tenants (LexAura/MediAura): contact the customer controller first (law firm, healthcare organization). NexStudio supports the customer controller under the DPA and contractual agreements.',
],
paragraphsAfterBullets: [
'Competent supervisory authority: Italian Data Protection Authority — Garante per la protezione dei dati personali (www.garanteprivacy.it) — for processing subject to the GDPR.',
],
},
{
id: 'identificazione',
title: '4. Identity verification and request handling',
paragraphs: [
'To protect confidentiality and security, NexStudio may request the minimum additional information needed to verify the requesters identity. Requests are handled under tracked procedures and acknowledged within the time limits set by the GDPR; for complex or multiple requests, the deadlines may be extended within the limits permitted by law, with a reasoned notice.',
],
},
{
id: 'limitazioni',
title: '5. Cases of limitation or refusal',
paragraphs: [
'The exercise of rights may be limited or refused where overriding legal obligations, needs of defense in legal proceedings, protection of third-party rights, or other exceptions provided by law apply. In such cases NexStudio provides a reasoned response and indicates the available remedies, including the right to lodge a complaint with the competent authority.',
],
},
{
id: 'trasferimenti',
title: '6. International transfers and safeguards',
paragraphs: [
'Where processing involves transfers outside the EEA/EU, NexStudio adopts the measures required by law (e.g. standard contractual clauses and supplementary measures). Data primarily resides in the EU via Cloudflare infrastructure.',
],
},
{
id: 'documenti-prodotto',
title: '7. Coordination with SaaS privacy notices',
paragraphs: [
'The privacy policies of the LexAura and MediAura SaaS portals detail, for each product, data flows, retention periods, privacy roles, and dedicated channels. In case of differences between documents, the service-specific documents and applicable contractual agreements prevail.',
'To consult product privacy notices:',
],
bullets: [
'LexAura: see the dedicated portal',
'MediAura: mediaura.doctor/privacy',
],
},
] as const satisfies readonly GdprSection[],
page: {
title: 'GDPR — NexStudio',
description:
'NexStudio S.r.l. GDPR page: data subject rights, operational procedures, roles between the corporate portal and LexAura/MediAura SaaS, coordination with dedicated privacy notices.',
heading: 'GDPR and data subject rights',
lead:
'Data subject rights and how to exercise them under Regulation (EU) 2016/679, consistent with the framework privacy notice of NexStudio S.r.l. (Italy, [INDIRIZZO_COMPLETO]). For LexAura and MediAura, product-specific operational details are set out in the privacy notices published on the dedicated SaaS portals.',
versionLabel: 'Version',
inForceFromLabel: 'In force from',
lastUpdateLabel: 'Last updated',
relatedDocsLabel: 'Related documents',
indexLabel: 'Contents',
},
},
{
id: 'chi-contattare',
title: '3. Chi contattare per esercitare i diritti',
paragraphs: [
"Per trattamenti in cui NexStudio agisce come titolare (es. contatti dal sito, gestione richieste corporate, sicurezza infrastrutturale di propria competenza), la richiesta puo' essere inviata tramite i canali di contatto privacy indicati nei riferimenti istituzionali.",
"Per trattamenti svolti nei tenant SaaS del cliente (LexAura/MediAura), il primo punto di contatto resta di norma il cliente titolare (es. studio legale, struttura sanitaria, organizzazione cliente), che determina finalita' e mezzi del trattamento.",
"NexStudio, ove operi come responsabile del trattamento, supporta il cliente titolare nella gestione delle richieste ricevute, secondo DPA e accordi contrattuali applicabili.",
],
th: {
document: {
version: '1.1',
inVigoreDal: '[DATA]',
ultimoAggiornamento: '[DATA]',
},
crossReferences: {
lead:
'สำหรับ LexAura และ MediAura รายละเอียดการดำเนินงานเฉพาะผลิตภัณฑ์ระบุไว้ในประกาศความเป็นส่วนตัวที่เผยแพร่บนพอร์ทัล SaaS ที่เกี่ยวข้อง',
},
riferimenti: [
{ label: 'ประกาศความเป็นส่วนตัว', href: '/privacy' },
{ label: 'นโยบายคุกกี้', href: '/cookies' },
{ label: 'เงื่อนไขการใช้งาน', href: '/terms' },
] as const,
sections: [
{
id: 'quadro',
title: '1. กรอบกฎหมายและขอบเขต',
paragraphs: [
'ระเบียบ (EU) 2016/679 (GDPR) คุ้มครองสิทธิของเจ้าของข้อมูลและกำกับดูแลการประมวลผลข้อมูลส่วนบุคคล หน้านี้ชี้แจงวิธีการใช้สิทธิดังกล่าวกับ NexStudio ในบริบทของพอร์ทัลองค์กรและบริการที่เกี่ยวข้อง',
],
},
{
id: 'diritti',
title: '2. สิทธิที่เจ้าของข้อมูลได้รับ',
paragraphs: [
'ภายในขอบเขตที่กฎหมายที่ใช้บังคับกำหนด เจ้าของข้อมูลอาจใช้สิทธิต่อไปนี้:',
],
bullets: [
'เข้าถึงข้อมูลส่วนบุคคลและขอสำเนาในรูปแบบที่เข้าใจได้;',
'แก้ไขข้อมูลที่ไม่ถูกต้องและเติมเต็มข้อมูลที่ไม่ครบถ้วน;',
'ลบข้อมูล (สิทธิที่จะถูกลืม) เมื่อเข้าเงื่อนไข;',
'จำกัดการประมวลผลเมื่อเข้าเงื่อนไขตาม GDPR;',
'โอนย้ายข้อมูลที่ประมวลผลด้วยวิธีอัตโนมัติบนพื้นฐานของความยินยอมหรือสัญญา;',
'คัดค้านการประมวลผลบนพื้นฐานของประโยชน์โดยชอบด้วยกฎหมาย รวมถึงคัดค้านการสื่อสารการตลาด;',
'ถอนความยินยอมได้ทุกเมื่อ โดยไม่กระทบความชอบด้วยกฎหมายของการประมวลผลก่อนการถอน;',
'ร้องเรียนต่อหน่วยงานกำกับดูแลที่มีอำนาจ.',
],
},
{
id: 'chi-contattare',
title: '3. ติดต่อใครเพื่อใช้สิทธิ',
paragraphs: [],
bullets: [
'อีเมล: privacy@nexstudio.com',
'แบบฟอร์ม: nexstudio.ai/contatti',
'สำหรับการประมวลผลในเทแนนท์ SaaS (LexAura/MediAura): ติดต่อลูกค้าซึ่งเป็นผู้ควบคุมข้อมูลก่อน (สำนักงานกฎหมาย องค์กรด้านสุขภาพ) NexStudio สนับสนุนลูกค้าผู้ควบคุมตาม DPA และข้อตกลงตามสัญญา',
],
paragraphsAfterBullets: [
'หน่วยงานกำกับดูแลที่มีอำนาจ: Garante per la protezione dei dati personali (www.garanteprivacy.it) สำหรับการประมวลผลที่อยู่ภายใต้ GDPR',
],
},
{
id: 'identificazione',
title: '4. การยืนยันตัวตนและการจัดการคำขอ',
paragraphs: [
'เพื่อคุ้มครองความลับและความปลอดภัย NexStudio อาจขอข้อมูลเพิ่มเติมเท่าที่จำเป็นขั้นต่ำเพื่อยืนยันตัวตนของผู้ร้องขอ คำขอจะได้รับการจัดการด้วยกระบวนการที่ติดตามได้และรับเรื่องภายในระยะเวลาตาม GDPR หากคำขอซับซ้อนหรือมีหลายคำขอ อาจขยายระยะเวลาได้ภายในขอบเขตที่กฎหมายอนุญาต พร้อมแจ้งเหตุผล',
],
},
{
id: 'limitazioni',
title: '5. กรณีจำกัดหรือปฏิเสธ',
paragraphs: [
'การใช้สิทธิอาจถูกจำกัดหรือไม่ได้รับการตอบรับ เมื่อมีข้อผูกพันทางกฎหมายที่เหนือกว่า ความจำเป็นในการป้องกันคดี ความคุ้มครองสิทธิของบุคคลที่สาม หรือข้อยกเว้นอื่นตามกฎหมาย ในกรณีดังกล่าว NexStudio จะให้คำตอบพร้อมเหตุผลและระบุการเยียวยาที่มี รวมถึงสิทธิร้องเรียนต่อหน่วยงานที่มีอำนาจ',
],
},
{
id: 'trasferimenti',
title: '6. การโอนข้อมูลระหว่างประเทศและการคุ้มครอง',
paragraphs: [
'หากการประมวลผลเกี่ยวข้องกับการโอนนอก EEA/EU NexStudio จะใช้มาตรการที่กฎหมายกำหนด (เช่น ข้อสัญญามาตรฐานและมาตรการเสริม) ข้อมูลส่วนใหญ่อยู่ในสหภาพยุโรปผ่านโครงสร้างพื้นฐาน Cloudflare',
],
},
{
id: 'documenti-prodotto',
title: '7. การประสานกับประกาศความเป็นส่วนตัวของ SaaS',
paragraphs: [
'นโยบายความเป็นส่วนตัวของพอร์ทัล SaaS LexAura และ MediAura ระบุรายละเอียดสำหรับแต่ละผลิตภัณฑ์เกี่ยวกับกระแสข้อมูล ระยะเวลาเก็บรักษา บทบาทความเป็นส่วนตัว และช่องทางเฉพาะ หากเอกสารต่างกัน ให้ยึดเอกสารเฉพาะบริการและข้อตกลงตามสัญญาที่ใช้บังคับ',
'เพื่อดูประกาศความเป็นส่วนตัวของผลิตภัณฑ์:',
],
bullets: [
'LexAura: ดูที่พอร์ทัลเฉพาะ',
'MediAura: mediaura.doctor/privacy',
],
},
] as const satisfies readonly GdprSection[],
page: {
title: 'GDPR — NexStudio',
description:
'หน้า GDPR ของ NexStudio S.r.l.: สิทธิของเจ้าของข้อมูล วิธีการดำเนินงาน บทบาทระหว่างพอร์ทัลองค์กรและ SaaS LexAura/MediAura และการประสานกับประกาศความเป็นส่วนตัวเฉพาะ',
heading: 'GDPR และสิทธิของเจ้าของข้อมูล',
lead:
'สิทธิของเจ้าของข้อมูลและวิธีการใช้สิทธิตามระเบียบ (EU) 2016/679 ให้สอดคล้องกับประกาศความเป็นส่วนตัวกรอบของ NexStudio S.r.l. (อิตาลี, [INDIRIZZO_COMPLETO]) สำหรับ LexAura และ MediAura รายละเอียดการดำเนินงานเฉพาะผลิตภัณฑ์ระบุไว้ในประกาศความเป็นส่วนตัวที่เผยแพร่บนพอร์ทัล SaaS ที่เกี่ยวข้อง',
versionLabel: 'เวอร์ชัน',
inForceFromLabel: 'มีผลตั้งแต่',
lastUpdateLabel: 'อัปเดตล่าสุด',
relatedDocsLabel: 'เอกสารที่เกี่ยวข้อง',
indexLabel: 'สารบัญ',
},
},
{
id: 'identificazione',
title: "4. Verifica dell'identita' e gestione della richiesta",
paragraphs: [
"Per tutelare riservatezza e sicurezza, NexStudio puo' richiedere informazioni minime aggiuntive necessarie a verificare l'identita' del richiedente e la legittimazione ad agire.",
"Le richieste sono gestite con procedure tracciate e prese in carico nei tempi previsti dal GDPR; in caso di richieste complesse o multiple, i termini possono essere prorogati nei limiti consentiti dalla legge, con comunicazione motivata.",
],
},
{
id: 'limitazioni',
title: '5. Casi di limitazione o rigetto',
paragraphs: [
"L'esercizio dei diritti puo' essere limitato o non accolto, in tutto o in parte, quando sussistono obblighi legali prevalenti, esigenze di difesa in giudizio, tutela dei diritti di terzi o altre eccezioni previste dalla normativa.",
"In tali casi NexStudio fornisce, ove possibile, un riscontro motivato e indica i rimedi disponibili, incluso il diritto di proporre reclamo all'autorita' competente.",
],
},
{
id: 'trasferimenti',
title: '6. Trasferimenti internazionali e garanzie',
paragraphs: [
"Qualora il trattamento comporti trasferimenti extra SEE/UE, NexStudio adotta le misure richieste dalla normativa (es. clausole contrattuali standard e misure supplementari tecniche/organizzative) secondo quanto descritto nell'informativa privacy.",
"Le informazioni rilevanti sui trasferimenti possono essere fornite all'interessato nei limiti consentiti e in coerenza con obblighi contrattuali e di sicurezza.",
],
},
{
id: 'documenti-prodotto',
title: '7. Coordinamento con le privacy dei SaaS',
paragraphs: [
"Le privacy policy dei portali SaaS LexAura e MediAura dettagliano, per ciascun prodotto, flussi dati, tempi di conservazione, ruoli privacy e canali dedicati. In caso di differenze tra documenti, prevalgono i documenti specifici del servizio e gli accordi contrattuali applicabili.",
"Questa pagina mantiene funzione di riferimento generale sul quadro GDPR del gruppo di servizi NexStudio.",
],
},
] as const;
} as const;
export const getGdpr = (locale: SupportedLocale) => gdprByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const gdprDocument = gdprByLocale.it.document;
export const gdprCrossReferences = gdprByLocale.it.crossReferences;
export const gdprRiferimenti = gdprByLocale.it.riferimenti;
export const gdprSections = gdprByLocale.it.sections;

View File

@ -7,44 +7,20 @@
* con flusso token via email lasciare `false` e usare `successMessage`.
*/
/** Destinazione della chat dopo identificazione / click su «Entra» */
const entryUrl = '/#contatti';
import type { SupportedLocale } from './navigation';
/** Destinazione della chat dopo identificazione / click su «Entra» (path IT). */
const entryPath = '/#contatti';
// Esempio WhatsApp: `https://wa.me/TUO_NUMERO?text=` + encodeURIComponent('Ciao NexStudio...');
export const chat = {
entryUrl,
const chatShared = {
/** Nuova scheda solo per link esterni (wa.me, widget, ecc.) */
openInNewTab: entryUrl.startsWith('http'),
linkTitle:
'Accesso alla chat NexStudio: stato canale, email di verifica e assistenza in più lingue.',
/**
* Cosa vede lutente **prima** di inserire email: chi risponde (umano / bot / entrambi).
* Aggiorna i testi in base agli orari effettivi del team.
*/
availability: {
badge: 'Operatore umano in orario · chatbot sempre attivo',
body:
'In orario lavorativo un operatore NexStudio può intervenire in chat; allingresso il chatbot può raccogliere il contesto iniziale e smistare la richiesta. Fuori orario la conversazione può proseguire con il solo chatbot fino al rientro del team. Lo stato effettivo è indicato qui sopra prima di procedere.',
},
openInNewTab: entryPath.startsWith('http'),
prechat: {
title: 'Prima della chat',
intro:
'Per evitare attese inutili chiediamo lindirizzo email a cui inviarti un link o un codice monouso per aprire la sessione in modo sicuro. Dopo la verifica potrai usare la chat dal canale che ti indicheremo.',
nameLabel: 'Nome (facoltativo)',
emailLabel: 'Email',
submitLabel: 'Richiedi accesso alla chat',
/** Visibile dopo linvio riuscito: utile se il link nella mail punta allo stesso `entryUrl` */
enterChatLabel: 'Ho ricevuto il messaggio — apri la chat',
closeLabel: 'Chiudi',
successMessage:
'Controlla la posta in arrivo: ti abbiamo inviato le istruzioni (link o codice) per entrare in chat. Se non vedi nulla, controlla anche lo spam.',
/** Formspree dedicato alla richiesta accesso chat — vuoto = prova `mailto` */
actionUrl: '',
/** Se `actionUrl` è vuoto */
mailto: '',
notifySubject: 'Richiesta accesso chat — sito NexStudio',
successRedirect: '',
fieldName: 'name',
fieldEmail: 'email',
@ -55,3 +31,96 @@ export const chat = {
openChatAfterSubmit: false,
},
} as const;
const entryUrlFor = (locale: SupportedLocale) => {
if (entryPath.startsWith('http') || locale === 'it') return entryPath;
if (entryPath.startsWith('/#')) return `/${locale}${entryPath}`;
if (entryPath.startsWith('/')) return `/${locale}${entryPath}`;
return entryPath;
};
const chatCopyByLocale = {
it: {
linkTitle:
'Accesso alla chat NexStudio: stato canale, email di verifica e assistenza in più lingue.',
availability: {
badge: 'Operatore umano in orario · chatbot sempre attivo',
body:
'In orario lavorativo un operatore NexStudio può intervenire in chat; allingresso il chatbot può raccogliere il contesto iniziale e smistare la richiesta. Fuori orario la conversazione può proseguire con il solo chatbot fino al rientro del team. Lo stato effettivo è indicato qui sopra prima di procedere.',
},
prechat: {
title: 'Prima della chat',
intro:
'Per evitare attese inutili chiediamo lindirizzo email a cui inviarti un link o un codice monouso per aprire la sessione in modo sicuro. Dopo la verifica potrai usare la chat dal canale che ti indicheremo.',
nameLabel: 'Nome (facoltativo)',
emailLabel: 'Email',
submitLabel: 'Richiedi accesso alla chat',
enterChatLabel: 'Ho ricevuto il messaggio — apri la chat',
closeLabel: 'Chiudi',
successMessage:
'Controlla la posta in arrivo: ti abbiamo inviato le istruzioni (link o codice) per entrare in chat. Se non vedi nulla, controlla anche lo spam.',
notifySubject: 'Richiesta accesso chat — sito NexStudio',
},
},
en: {
linkTitle:
'NexStudio chat access: channel status, verification email, and multilingual support.',
availability: {
badge: 'Human operator during hours · chatbot always on',
body:
'During business hours a NexStudio operator can join the chat; at entry the chatbot can gather initial context and route the request. Outside hours the conversation may continue with the chatbot alone until the team is back. The live status is shown above before you proceed.',
},
prechat: {
title: 'Before chat',
intro:
'To avoid unnecessary waiting we ask for the email address where we can send a one-time link or code to open the session securely. After verification you can use chat on the channel we indicate.',
nameLabel: 'Name (optional)',
emailLabel: 'Email',
submitLabel: 'Request chat access',
enterChatLabel: 'I received the message — open chat',
closeLabel: 'Close',
successMessage:
'Check your inbox: we sent instructions (link or code) to enter the chat. If you see nothing, also check spam.',
notifySubject: 'Chat access request — NexStudio website',
},
},
th: {
linkTitle:
'เข้าใช้งานแชท NexStudio: สถานะช่องทาง อีเมลยืนยัน และการช่วยเหลือหลายภาษา',
availability: {
badge: 'มีเจ้าหน้าที่ในเวลาทำการ · แชทบอตพร้อมตลอดเวลา',
body:
'ในเวลาทำการ เจ้าหน้าที่ NexStudio สามารถเข้าร่วมแชทได้ และตอนเริ่มต้นแชทบอตอาจเก็บบริบทเบื้องต้นแล้วส่งต่อคำขอ นอกเวลาทำการ การสนทนาอาจดำเนินต่อด้วยแชทบอตจนกว่าทีมจะกลับมา สถานะจริงจะแสดงด้านบนก่อนดำเนินการต่อ',
},
prechat: {
title: 'ก่อนเริ่มแชท',
intro:
'เพื่อลดการรอที่ไม่จำเป็น เราขอที่อยู่อีเมลสำหรับส่งลิงก์หรือรหัสแบบใช้ครั้งเดียวเพื่อเปิดเซสชันอย่างปลอดภัย หลังยืนยันแล้ว คุณจะใช้แชทผ่านช่องทางที่เราระบุได้',
nameLabel: 'ชื่อ (ไม่บังคับ)',
emailLabel: 'อีเมล',
submitLabel: 'ขอเข้าใช้งานแชท',
enterChatLabel: 'ได้รับข้อความแล้ว — เปิดแชท',
closeLabel: 'ปิด',
successMessage:
'โปรดตรวจสอบกล่องจดหมาย: เราได้ส่งคำแนะนำ (ลิงก์หรือรหัส) สำหรับเข้าแชทแล้ว หากไม่พบ ให้ตรวจสแปมด้วย',
notifySubject: 'คำขอเข้าใช้งานแชท — เว็บไซต์ NexStudio',
},
},
} as const;
export const getChat = (locale: SupportedLocale) => {
const copy = chatCopyByLocale[locale];
return {
...chatShared,
entryUrl: entryUrlFor(locale),
linkTitle: copy.linkTitle,
availability: copy.availability,
prechat: {
...chatShared.prechat,
...copy.prechat,
},
};
};
/** Retro-compatibilità: fallback italiano. */
export const chat = getChat('it');

View File

@ -1,49 +1,174 @@
/** Call-to-action (blocco contatti / chiusura pagina) */
export const cta = {
/** Riga sezione (stesso ruolo di FAQ, Stack, …) */
eyebrow: 'Contatti',
const env = import.meta.env;
const contactActionUrl = (env.PUBLIC_CONTACT_FORM_ACTION_URL ?? '/api/contact').trim();
const contactSuccessRedirect = (
env.PUBLIC_CONTACT_FORM_SUCCESS_REDIRECT ?? '/#contatti'
).trim();
const contactMailto = (env.PUBLIC_CONTACT_FORM_MAILTO ?? '').trim();
const contactTurnstileSiteKey = (env.PUBLIC_CONTACT_TURNSTILE_SITE_KEY ?? '').trim();
/** Titolo CTA su due righe (dopo il punto) */
titleLine1: 'Automatizza il complesso.',
titleLine2: 'Velocizza il processo.',
body:
'Dai modelli LLM alla distribuzione globale in pochi millisecondi. La directory definitiva per il LegalTech e MedTech. Utilizza il modulo contatto qui a fianco e ti forniremo tutti i chiarimenti che ti servono.',
import type { SupportedLocale } from './navigation';
/**
* Modulo contatti in pagina (sezione Contatti).
*
* **Formspree:** crea un form su https://formspree.io e incolla lURL in `actionUrl`.
* **Solo mailto:** lascia `actionUrl` vuoto e imposta `mailto` con lindirizzo destinatario
* (si apre il client di posta dellutente).
*/
contactForm: {
/** Lasciare vuoti endpoint/chiavi nelle fasi di sola UI. */
title: 'Parliamone',
description:
'Lascia i tuoi dati e il messaggio: ti risponde il team NexStudio.',
nameLabel: 'Nome',
companyLabel: 'Azienda (opzionale)',
countryLabel: 'Paese',
emailLabel: 'Email',
messageLabel: 'Scrivi qui il tuo messaggio',
submitLabel: 'Invia messaggio',
/** Formspree: `https://formspree.io/f/xxxx` — vuoto = prova `mailto` */
actionUrl: '',
/** Formspree: redirect dopo invio (`_next`) — URL **assoluta** in produzione */
successRedirect: '',
/**
* Cloudflare Turnstile (anti-spam): inserisci qui la site key pubblica.
* Se vuota, il CAPTCHA non viene mostrato.
*/
turnstileSiteKey: '',
/** Formspree `_subject` e oggetto mailto */
notifySubject: 'Richiesta contatto — sito NexStudio',
/** Se `actionUrl` è vuoto: email per `mailto:` (es. `info@tuodominio.com`) */
mailto: '',
fieldName: 'name',
fieldCompany: 'company',
fieldCountry: 'country',
fieldEmail: 'email',
fieldMessage: 'message',
const ctaByLocale = {
it: {
eyebrow: 'Contatti',
titleLine1: 'Automatizza il complesso.',
titleLine2: 'Velocizza il processo.',
body:
'Dai modelli LLM alla distribuzione globale in pochi millisecondi. La directory definitiva per il LegalTech e MedTech. Utilizza il modulo contatto qui a fianco e ti forniremo tutti i chiarimenti che ti servono.',
contactForm: {
title: 'Parliamone',
description:
'Lascia i tuoi dati e il messaggio: ti risponde il team NexStudio.',
nameLabel: 'Nome',
companyLabel: 'Azienda (opzionale)',
countryLabel: 'Paese',
departmentLabel: 'Area',
emailLabel: 'Email',
messageLabel: 'Scrivi qui il tuo messaggio',
submitLabel: 'Invia messaggio',
successMessage: 'Messaggio inviato. Ti risponderemo al più presto.',
actionUrl: contactActionUrl,
successRedirect: contactSuccessRedirect,
turnstileSiteKey: contactTurnstileSiteKey,
turnstileLanguage: 'it',
turnstileHint:
'Verifica anti-spam: completa il controllo prima di inviare il messaggio.',
notifySubject: 'Richiesta contatto — sito NexStudio',
mailto: contactMailto,
mailtoFallbackSubject: 'Contatto sito',
mailtoLabels: {
name: 'Nome',
company: 'Azienda',
email: 'Email',
country: 'Paese',
department: 'Area',
defaultDepartment: 'general',
},
errors: {
turnstile: 'Completa il controllo anti-spam prima di inviare il messaggio.',
config:
'Configura `contactForm.actionUrl` (Formspree) o `contactForm.mailto` in `src/data/home/cta.ts`.',
},
fieldName: 'name',
fieldCompany: 'company',
fieldCountry: 'country',
fieldDepartment: 'department',
fieldEmail: 'email',
fieldMessage: 'message',
departmentOptions: [
{ value: 'info', label: 'Informazioni generali e commerciali' },
{ value: 'privacy', label: 'Privacy' },
{ value: 'careers', label: 'Careers / Collaborazioni' },
],
},
},
en: {
eyebrow: 'Contacts',
titleLine1: 'Automate complexity.',
titleLine2: 'Accelerate operations.',
body:
'From LLM models to global distribution in milliseconds. Use the contact form and our team will provide all the details you need.',
contactForm: {
title: 'Lets talk',
description: 'Share your details and message: the NexStudio team will reply.',
nameLabel: 'Name',
companyLabel: 'Company (optional)',
countryLabel: 'Country',
departmentLabel: 'Area',
emailLabel: 'Email',
messageLabel: 'Write your message',
submitLabel: 'Send message',
successMessage: 'Message sent. We will get back to you soon.',
actionUrl: contactActionUrl,
successRedirect: contactSuccessRedirect,
turnstileSiteKey: contactTurnstileSiteKey,
turnstileLanguage: 'en',
turnstileHint:
'Anti-spam verification: complete the challenge before sending your message.',
notifySubject: 'Contact request — NexStudio website',
mailto: contactMailto,
mailtoFallbackSubject: 'Website contact',
mailtoLabels: {
name: 'Name',
company: 'Company',
email: 'Email',
country: 'Country',
department: 'Area',
defaultDepartment: 'general',
},
errors: {
turnstile: 'Complete the anti-spam check before sending your message.',
config:
'Configure `contactForm.actionUrl` (Formspree) or `contactForm.mailto` in `src/data/home/cta.ts`.',
},
fieldName: 'name',
fieldCompany: 'company',
fieldCountry: 'country',
fieldDepartment: 'department',
fieldEmail: 'email',
fieldMessage: 'message',
departmentOptions: [
{ value: 'info', label: 'General and commercial information' },
{ value: 'privacy', label: 'Privacy' },
{ value: 'careers', label: 'Careers / Collaborations' },
],
},
},
th: {
eyebrow: 'ติดต่อ',
titleLine1: 'ทำงานซับซ้อนให้อัตโนมัติ',
titleLine2: 'เร่งความเร็วกระบวนการ',
body:
'จากโมเดล LLM สู่การกระจายระบบระดับโลกภายในไม่กี่มิลลิวินาที ใช้ฟอร์มติดต่อด้านข้าง แล้วทีม NexStudio จะตอบกลับพร้อมข้อมูลที่คุณต้องการ',
contactForm: {
title: 'คุยกับเรา',
description: 'กรอกข้อมูลและข้อความ แล้วทีม NexStudio จะติดต่อกลับ',
nameLabel: 'ชื่อ',
companyLabel: 'บริษัท (ไม่บังคับ)',
countryLabel: 'ประเทศ',
departmentLabel: 'แผนก',
emailLabel: 'อีเมล',
messageLabel: 'พิมพ์ข้อความของคุณ',
submitLabel: 'ส่งข้อความ',
successMessage: 'ส่งข้อความแล้ว เราจะติดต่อกลับโดยเร็ว',
actionUrl: contactActionUrl,
successRedirect: contactSuccessRedirect,
turnstileSiteKey: contactTurnstileSiteKey,
turnstileLanguage: 'th',
turnstileHint: 'การยืนยันป้องกันสแปม: กรุณาทำขั้นตอนให้เสร็จก่อนส่งข้อความ',
notifySubject: 'คำขอติดต่อ — เว็บไซต์ NexStudio',
mailto: contactMailto,
mailtoFallbackSubject: 'ติดต่อจากเว็บไซต์',
mailtoLabels: {
name: 'ชื่อ',
company: 'บริษัท',
email: 'อีเมล',
country: 'ประเทศ',
department: 'แผนก',
defaultDepartment: 'general',
},
errors: {
turnstile: 'กรุณายืนยันการป้องกันสแปมก่อนส่งข้อความ',
config:
'กรุณาตั้งค่า `contactForm.actionUrl` (Formspree) หรือ `contactForm.mailto` ใน `src/data/home/cta.ts`',
},
fieldName: 'name',
fieldCompany: 'company',
fieldCountry: 'country',
fieldDepartment: 'department',
fieldEmail: 'email',
fieldMessage: 'message',
departmentOptions: [
{ value: 'info', label: 'ข้อมูลทั่วไปและเชิงพาณิชย์' },
{ value: 'privacy', label: 'ความเป็นส่วนตัว' },
{ value: 'careers', label: 'Careers / Collaborations' },
],
},
},
} as const;
export const getCta = (locale: SupportedLocale) => ctaByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const cta = ctaByLocale.it;

View File

@ -1,47 +1,148 @@
import type { SupportedLocale } from './navigation';
/** FAQ — domande/risposte allineate a hero, prodotti, servizi e stack */
export const faq = {
eyebrow: 'FAQ',
title: 'Domande frequenti',
items: [
{
question: 'Che tipo di organizzazioni seguite con LexAura e MediAura?',
answer:
'Lavoriamo con studi legali e strutture sanitarie — dal professionista singolo a team e sedi multiunità — che gestiscono volumi elevati di informazione e hanno bisogno di strumenti affidabili in ambiti regolamentati. La verticalità su Legal Tech e Health Tech è parte della nostra proposta: meno genericismo, più requisiti operativi, tracciabilità e continuità del servizio.',
},
{
question: 'Lintelligenza artificiale sostituisce avvocati o medici?',
answer:
'No. I nostri prodotti sono pensati per integrarsi nei flussi professionali: sintesi, ricerca, drafting assistito, automazione della documentazione e interfacce vocali liberano tempo, ma la decisione finale e la responsabilità restano del professionista. Lobiettivo è ridurre il carico ripetitivo e offrire una base analitica solida, non sostituire il giudizio umano.',
},
{
question: 'Cosa succede dopo ladozione del software?',
answer:
'Il modello di servizio va oltre la licenza: onboarding e formazione specialistica per team legali e sanitari, supporto tecnico e consulenza operativa continuativi, aggiornamento dei motori di analisi e manutenzione evolutiva rispetto a normative e contesto di mercato. Per criticità urgenti sono previsti protocolli di intervento prioritario, così che studi e strutture non restino isolati dai propri strumenti.',
},
{
question: 'Potete collegare LexAura o MediAura ai nostri sistemi già in uso?',
answer:
'Sì, quando serve. Oltre ai prodotti standard supportiamo integrazioni tramite API e moduli personalizzati nel perimetro dei nostri SaaS, per collegare gestionali o flussi documentali preesistenti senza reinventare lintero stack informativo.',
},
{
question: 'Come gestite sicurezza, disponibilità e dati sensibili?',
answer:
'Progettiamo su architetture cloudnative ad alta disponibilità, con attenzione a crittografia, protezione di perimetro (es. WAF, mitigazione DDoS) e continuità operativa. Il trattamento di informazioni legali e sanitarie è affrontato con criteri di integrità dei dati e conformità alle normative applicabili, inclusa la chiarezza su dove risiedono i dati e sui requisiti di due diligence che i settori regolamentati si attendono.',
},
{
question: 'Lavorate solo in loco o anche con clienti in Italia e allestero?',
answer:
'Siamo una software company con base operativa a Bangkok e collaboriamo con clienti a livello internazionale. Workshop e incontri possono essere online o in presenza in base al progetto; supporto e comunicazioni operative in italiano, inglese, tedesco, francese, spagnolo o tailandese, con orari concordati.',
},
{
question: 'Diritto duso e proprietà del software: come le regolate?',
answer:
'Dipende dal contratto: in genere cediamo lesclusiva sul deliverable a saldo, con clausole esplicite su componenti open source e librerie di terze parti. Per licenze SaaS, permessi duso e responsabilità sono definiti nei termini commerciali e nel contratto quadro.',
},
{
question: 'Qual è il modo più rapido per parlarne con voi?',
answer:
'Il punto di ingresso principale è la chat dal pulsante «Chattiamo» in header: prima vedi se il canale è con operatore umano, solo chatbot o misto, poi lasci email (e facoltativamente il nome) per ricevere un link o un codice monouso e aprire la sessione senza perdite di tempo. In chat operatore umano e chatbot collaborano per orientamento iniziale, requisiti e passi successivi. Per il supporto via web ci interfacciamo in italiano, inglese, tedesco, francese, spagnolo o tailandese. Per materiale sensibile useremo sempre i canali concordati in fase di rapporto commerciale. Se ci si sente più a proprio agio usando un form di contatto, in fondo alla homepage nella sezione Contatti trovate il modulo a destra del testo introduttivo.',
},
] as const,
const faqByLocale = {
it: {
eyebrow: 'FAQ',
title: 'Domande frequenti',
items: [
{
question: 'Che tipo di organizzazioni seguite con LexAura e MediAura?',
answer:
'Lavoriamo con studi legali e strutture sanitarie — dal professionista singolo a team e sedi multiunità — che gestiscono volumi elevati di informazione e hanno bisogno di strumenti affidabili in ambiti regolamentati. La verticalità su Legal Tech e Health Tech è parte della nostra proposta: meno genericismo, più requisiti operativi, tracciabilità e continuità del servizio.',
},
{
question: 'Lintelligenza artificiale sostituisce avvocati o medici?',
answer:
'No. I nostri prodotti sono pensati per integrarsi nei flussi professionali: sintesi, ricerca, drafting assistito, automazione della documentazione e interfacce vocali liberano tempo, ma la decisione finale e la responsabilità restano del professionista. Lobiettivo è ridurre il carico ripetitivo e offrire una base analitica solida, non sostituire il giudizio umano.',
},
{
question: 'Cosa succede dopo ladozione del software?',
answer:
'Il modello di servizio va oltre la licenza: onboarding e formazione specialistica per team legali e sanitari, supporto tecnico e consulenza operativa continuativi, aggiornamento dei motori di analisi e manutenzione evolutiva rispetto a normative e contesto di mercato. Per criticità urgenti sono previsti protocolli di intervento prioritario, così che studi e strutture non restino isolati dai propri strumenti.',
},
{
question: 'Potete collegare LexAura o MediAura ai nostri sistemi già in uso?',
answer:
'Sì, quando serve. Oltre ai prodotti standard supportiamo integrazioni tramite API e moduli personalizzati nel perimetro dei nostri SaaS, per collegare gestionali o flussi documentali preesistenti senza reinventare lintero stack informativo.',
},
{
question: 'Come gestite sicurezza, disponibilità e dati sensibili?',
answer:
'Progettiamo su architetture cloudnative ad alta disponibilità, con attenzione a crittografia, protezione di perimetro (es. WAF, mitigazione DDoS) e continuità operativa. Il trattamento di informazioni legali e sanitarie è affrontato con criteri di integrità dei dati e conformità alle normative applicabili, inclusa la chiarezza su dove risiedono i dati e sui requisiti di due diligence che i settori regolamentati si attendono.',
},
{
question: 'Lavorate solo in loco o anche con clienti in Italia e allestero?',
answer:
'Siamo una software company con base operativa a Bangkok e collaboriamo con clienti a livello internazionale. Workshop e incontri possono essere online o in presenza in base al progetto; supporto e comunicazioni operative in italiano, inglese, tedesco, francese, spagnolo o tailandese, con orari concordati.',
},
{
question: 'Diritto duso e proprietà del software: come le regolate?',
answer:
'Dipende dal contratto: in genere cediamo lesclusiva sul deliverable a saldo, con clausole esplicite su componenti open source e librerie di terze parti. Per licenze SaaS, permessi duso e responsabilità sono definiti nei termini commerciali e nel contratto quadro.',
},
{
question: 'Qual è il modo più rapido per parlarne con voi?',
answer:
'Il punto di ingresso principale è la chat dal pulsante «Chattiamo» in header: prima vedi se il canale è con operatore umano, solo chatbot o misto, poi lasci email (e facoltativamente il nome) per ricevere un link o un codice monouso e aprire la sessione senza perdite di tempo. In chat operatore umano e chatbot collaborano per orientamento iniziale, requisiti e passi successivi. Per il supporto via web ci interfacciamo in italiano, inglese, tedesco, francese, spagnolo o tailandese. Per materiale sensibile useremo sempre i canali concordati in fase di rapporto commerciale. Se ci si sente più a proprio agio usando un form di contatto, in fondo alla homepage nella sezione Contatti trovate il modulo a destra del testo introduttivo.',
},
] as const,
},
en: {
eyebrow: 'FAQ',
title: 'Frequently asked questions',
items: [
{
question: 'What organizations do you support with LexAura and MediAura?',
answer:
'We work with law firms and healthcare organizations, from solo professionals to multi-site teams, that manage high information volumes and need reliable tools in regulated environments.',
},
{
question: 'Does AI replace lawyers or doctors?',
answer:
'No. Our products support professional workflows with summarization, research, assisted drafting, and automation. Final decisions and responsibility always remain with the professional.',
},
{
question: 'What happens after software adoption?',
answer:
'Our service model goes beyond licensing: specialist onboarding and training, continuous technical support and consulting, and ongoing engine updates aligned with regulations and market context.',
},
{
question: 'Can you integrate LexAura or MediAura with our existing systems?',
answer:
'Yes, when needed. We support API integrations and custom modules within our SaaS perimeter to connect existing management or document flows.',
},
{
question: 'How do you handle security, availability, and sensitive data?',
answer:
'We design for high-availability cloud-native architectures, encryption, perimeter protection, and operational continuity, with clear data-governance practices aligned with applicable regulations.',
},
{
question: 'Do you work only locally or also with international clients?',
answer:
'We are based in Bangkok and collaborate internationally. Workshops can be remote or on-site depending on the project.',
},
{
question: 'How do you handle software usage rights and ownership?',
answer:
'This depends on the contract. In many engagements, exclusive rights on deliverables are transferred after final payment, with explicit clauses for open source and third-party components.',
},
{
question: 'What is the fastest way to talk with your team?',
answer:
'The primary entry point is the chat button in the header. You provide your email to receive a one-time link or code, then start the session without delay.',
},
] as const,
},
th: {
eyebrow: 'คำถามที่พบบ่อย',
title: 'คำถามที่พบบ่อย',
items: [
{
question: 'องค์กรแบบไหนที่เหมาะกับ LexAura และ MediAura?',
answer:
'เราทำงานกับสำนักงานกฎหมายและองค์กรสุขภาพ ตั้งแต่ผู้เชี่ยวชาญเดี่ยวไปจนถึงทีมหลายสาขา ที่ต้องจัดการข้อมูลจำนวนมากในบริบทที่มีข้อกำกับสูง',
},
{
question: 'AI จะแทนที่ทนายหรือแพทย์หรือไม่?',
answer:
'ไม่ใช่ เป้าหมายของเราคือช่วยงานเชิงวิเคราะห์ งานเอกสาร และเวิร์กโฟลว์ซ้ำ ๆ โดยการตัดสินใจสุดท้ายยังคงเป็นหน้าที่ของผู้เชี่ยวชาญ',
},
{
question: 'หลังเริ่มใช้งานซอฟต์แวร์แล้วจะเกิดอะไรขึ้น?',
answer:
'เรามีบริการหลังการใช้งานครบวงจร ทั้ง onboarding การอบรมเฉพาะทาง การสนับสนุนทางเทคนิค และการอัปเดตระบบอย่างต่อเนื่อง',
},
{
question: 'สามารถเชื่อม LexAura หรือ MediAura กับระบบเดิมของเราได้ไหม?',
answer:
'ได้ เรารองรับการเชื่อมต่อผ่าน API และโมดูลแบบกำหนดเองภายในขอบเขต SaaS ของเรา',
},
{
question: 'จัดการความปลอดภัย ความพร้อมใช้งาน และข้อมูลอ่อนไหวอย่างไร?',
answer:
'เราใช้สถาปัตยกรรมคลาวด์ที่พร้อมใช้งานสูง การเข้ารหัส การป้องกันระดับเครือข่าย และแนวปฏิบัติด้านการกำกับข้อมูลให้สอดคล้องข้อกำกับที่เกี่ยวข้อง',
},
{
question: 'รับงานเฉพาะในพื้นที่หรือรับลูกค้าต่างประเทศด้วย?',
answer:
'สำนักงานใหญ่ปฏิบัติการของเราอยู่ที่กรุงเทพฯ และทำงานกับลูกค้าต่างประเทศได้ ทั้งออนไลน์และออนไซต์ตามความเหมาะสมของโครงการ',
},
{
question: 'สิทธิการใช้งานและความเป็นเจ้าของซอฟต์แวร์กำหนดอย่างไร?',
answer:
'ขึ้นกับสัญญา โดยทั่วไปจะระบุเงื่อนไขการใช้งาน ความรับผิดชอบ และสิทธิในชิ้นงานอย่างชัดเจน รวมถึงส่วนประกอบโอเพนซอร์สและ third-party',
},
{
question: 'ถ้าต้องการคุยเร็วที่สุดควรติดต่อช่องทางไหน?',
answer:
'กดปุ่มแชทบนส่วนหัวเว็บไซต์ ใส่อีเมลเพื่อรับลิงก์หรือโค้ดแบบใช้ครั้งเดียว แล้วเริ่มเซสชันได้ทันที',
},
] as const,
},
} as const;
export const getFaq = (locale: SupportedLocale) => faqByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const faq = faqByLocale.it;

View File

@ -1,87 +1,178 @@
import type { SupportedLocale } from './navigation';
/** Footer — griglia 4 colonne (link); newsletter in blocco unico sotto, allineata a sinistra */
export const footer = {
brandName: 'NexStudio',
tagline: 'Ingegneria del software e IA per settori regolamentati.',
legalLine: 'P.IVA e note legali: da inserire.',
columnTitles: {
sections: 'Sezioni',
legal: 'Normative',
social: 'Reti sociali',
company: 'Azienda',
} as const,
/** Colonna 1 — ancore sulla homepage */
homeNav: [
{ label: 'Prodotti', href: '/#prodotti' },
{ label: 'Servizi', href: '/#servizi' },
{ label: 'Stack', href: '/#stack' },
{ label: 'FAQ', href: '/#faq' },
] as const,
/** Pannello preferenze (CMP): non è un documento legale; tenuto fuori dalla colonna Normative */
cookiePreferences: {
label: 'Impostazioni cookie',
hint: 'Preferenze di consenso sul tuo browser (non è un testo legale).',
} as const,
/** Colonna 2 — documenti legali (testi bozza nelle rispettive pagine) */
legalColumnNav: [
{ label: 'Condizioni duso', href: '/terms' },
{ label: 'Informativa cookie', href: '/cookies' },
{ label: 'Privacy', href: '/privacy' },
{ label: 'Diritti privacy (GDPR)', href: '/gdpr' },
] as const,
/**
* Colonna 3 — social (sostituire gli URL con i profili reali).
* Lasciare `href` vuoto per mostrare solo etichetta disabilitata finché non configurato.
*/
socialLinks: [
{ label: 'Facebook', href: '' },
{ label: 'LinkedIn', href: '' },
{ label: 'Twitter / X', href: '' },
{ label: 'GitHub', href: '' },
] as const,
/** Colonna 4 — pagine istituzionali e compliance */
companyNav: [
{ label: 'Chi siamo', href: '/about' },
{ label: 'Codice etico', href: '/codice-etico' },
{ label: 'Modello organizzativo', href: '/modello-organizzativo' },
{ label: 'Dove siamo', href: '/dove-siamo' },
] as const,
/**
* Newsletter — invio via POST a un endpoint esterno.
*
* Opzione A (consigliata per partire): Formspree
* 1. Crea account su https://formspree.io e un nuovo form.
* 2. Copia lURL tipo `https://formspree.io/f/abcdefgh` in `actionUrl`.
* 3. Opzionale: `successRedirect` = URL assoluta della pagina di ringraziamento
* (es. `https://tuodominio.com/newsletter/grazie` — vedi `src/pages/newsletter/grazie.astro`).
* 4. In Formspree collega linvio a email / Slack / Google Sheets.
*
* Opzione B: Brevo, Mailchimp, Buttondown, ConvertKit — usano spesso un URL
* dedicato o un embed; adatta `fieldName` al nome campo richiesto dalla doc.
*
* Opzione C: Worker Cloudflare + lista in KV/D1 (più controllo, più codice).
*/
newsletter: {
title: 'Newsletter',
description: 'Aggiornamenti su prodotti e roadmap.',
placeholder: 'La tua email',
buttonLabel: 'Iscriviti',
/** Formspree: `https://formspree.io/f/xxxx` — vuoto = modulo disabilitato */
actionUrl: '',
/** Nome campo email richiesto dal provider (Formspree: di solito `email`) */
fieldName: 'email',
/**
* Formspree: redirect dopo invio (`_next`). Deve essere URL **assoluta**
* (es. produzione). Vuoto = pagina di ringrazio predefinita Formspree.
*/
successRedirect: '',
/** Formspree: oggetto della mail di notifica (`_subject`) */
notifySubject: 'Nuova iscrizione newsletter — NexStudio',
const footerByLocale = {
it: {
brandName: 'NexStudio',
tagline: 'Ingegneria del software e IA per settori regolamentati.',
legalLine: 'P.IVA e note legali: da inserire.',
columnTitles: {
sections: 'Sezioni',
legal: 'Normative',
social: 'Reti sociali',
company: 'Azienda',
} as const,
homeNav: [
{ label: 'Prodotti', href: '/#prodotti' },
{ label: 'Servizi', href: '/#servizi' },
{ label: 'Stack', href: '/#stack' },
{ label: 'FAQ', href: '/#faq' },
] as const,
cookiePreferences: {
label: 'Impostazioni cookie',
hint: 'Preferenze di consenso sul tuo browser.',
} as const,
legalColumnNav: [
{ label: 'Condizioni duso', href: '/terms' },
{ label: 'Informativa cookie', href: '/cookies' },
{ label: 'Privacy', href: '/privacy' },
{ label: 'Diritti privacy (GDPR)', href: '/gdpr' },
] as const,
socialLinks: [
{ label: 'Facebook', href: '' },
{ label: 'LinkedIn', href: '' },
{ label: 'Twitter / X', href: '' },
{ label: 'GitHub', href: '' },
] as const,
companyNav: [
{ label: 'Chi siamo', href: '/about' },
{ label: 'Codice etico', href: '/codice-etico' },
{ label: 'Modello organizzativo', href: '/modello-organizzativo' },
{ label: 'Dove siamo', href: '/dove-siamo' },
] as const,
newsletter: {
title: 'Newsletter',
description: 'Aggiornamenti su prodotti e roadmap.',
placeholder: 'La tua email',
buttonLabel: 'Iscriviti',
actionUrl: '/api/newsletter/subscribe',
fieldName: 'email',
successRedirect: '/newsletter/grazie',
notifySubject: 'Nuova iscrizione newsletter — NexStudio',
},
labels: {
cookieTitle: 'Impostazioni cookie',
socialPlaceholderTitle: 'URL da configurare in footer.ts',
companyPlaceholderTitle: 'Collegamento in arrivo',
newsletterComingSoon:
'Iscrizione in arrivo: configura lendpoint nel progetto per attivare il modulo.',
},
},
en: {
brandName: 'NexStudio',
tagline: 'Software engineering and AI for regulated industries.',
legalLine: 'VAT number and legal notes: to be added.',
columnTitles: {
sections: 'Sections',
legal: 'Legal',
social: 'Social',
company: 'Company',
} as const,
homeNav: [
{ label: 'Products', href: '/#prodotti' },
{ label: 'Services', href: '/#servizi' },
{ label: 'Stack', href: '/#stack' },
{ label: 'FAQ', href: '/#faq' },
] as const,
cookiePreferences: {
label: 'Cookie settings',
hint: 'Consent preferences in your browser.',
} as const,
legalColumnNav: [
{ label: 'Terms of use', href: '/terms' },
{ label: 'Cookie policy', href: '/cookies' },
{ label: 'Privacy', href: '/privacy' },
{ label: 'Privacy rights (GDPR)', href: '/gdpr' },
] as const,
socialLinks: [
{ label: 'Facebook', href: '' },
{ label: 'LinkedIn', href: '' },
{ label: 'Twitter / X', href: '' },
{ label: 'GitHub', href: '' },
] as const,
companyNav: [
{ label: 'About us', href: '/about' },
{ label: 'Code of ethics', href: '/codice-etico' },
{ label: 'Organizational model', href: '/modello-organizzativo' },
{ label: 'Where we are', href: '/dove-siamo' },
] as const,
newsletter: {
title: 'Newsletter',
description: 'Updates on products and roadmap.',
placeholder: 'Your email',
buttonLabel: 'Subscribe',
actionUrl: '/api/newsletter/subscribe',
fieldName: 'email',
successRedirect: '/en/newsletter/grazie',
notifySubject: 'New newsletter signup — NexStudio',
},
labels: {
cookieTitle: 'Cookie settings',
socialPlaceholderTitle: 'URL to configure in footer.ts',
companyPlaceholderTitle: 'Coming soon',
newsletterComingSoon:
'Newsletter signups coming soon: configure the endpoint in the project to enable this form.',
},
},
th: {
brandName: 'NexStudio',
tagline: 'วิศวกรรมซอฟต์แวร์และ AI สำหรับอุตสาหกรรมที่มีข้อกำกับ',
legalLine: 'เลขประจำตัวผู้เสียภาษีและข้อมูลทางกฎหมาย: รอเพิ่มภายหลัง',
columnTitles: {
sections: 'ส่วนต่าง ๆ',
legal: 'กฎหมาย',
social: 'โซเชียล',
company: 'บริษัท',
} as const,
homeNav: [
{ label: 'ผลิตภัณฑ์', href: '/#prodotti' },
{ label: 'บริการ', href: '/#servizi' },
{ label: 'สแตก', href: '/#stack' },
{ label: 'คำถามที่พบบ่อย', href: '/#faq' },
] as const,
cookiePreferences: {
label: 'ตั้งค่าคุกกี้',
hint: 'การตั้งค่าความยินยอมในเบราว์เซอร์ของคุณ',
} as const,
legalColumnNav: [
{ label: 'เงื่อนไขการใช้งาน', href: '/terms' },
{ label: 'นโยบายคุกกี้', href: '/cookies' },
{ label: 'ความเป็นส่วนตัว', href: '/privacy' },
{ label: 'สิทธิความเป็นส่วนตัว (GDPR)', href: '/gdpr' },
] as const,
socialLinks: [
{ label: 'Facebook', href: '' },
{ label: 'LinkedIn', href: '' },
{ label: 'Twitter / X', href: '' },
{ label: 'GitHub', href: '' },
] as const,
companyNav: [
{ label: 'เกี่ยวกับเรา', href: '/about' },
{ label: 'จรรยาบรรณ', href: '/codice-etico' },
{ label: 'โมเดลองค์กร', href: '/modello-organizzativo' },
{ label: 'ที่ตั้ง', href: '/dove-siamo' },
] as const,
newsletter: {
title: 'จดหมายข่าว',
description: 'อัปเดตเกี่ยวกับผลิตภัณฑ์และโรดแมป',
placeholder: 'อีเมลของคุณ',
buttonLabel: 'สมัครรับข่าวสาร',
actionUrl: '/api/newsletter/subscribe',
fieldName: 'email',
successRedirect: '/th/newsletter/grazie',
notifySubject: 'มีผู้สมัครจดหมายข่าวใหม่ — NexStudio',
},
labels: {
cookieTitle: 'ตั้งค่าคุกกี้',
socialPlaceholderTitle: 'ต้องตั้งค่า URL ใน footer.ts',
companyPlaceholderTitle: 'กำลังจะเปิดใช้งาน',
newsletterComingSoon:
'ระบบสมัครจดหมายข่าวกำลังจะเปิดใช้งาน: ตั้งค่า endpoint ในโปรเจกต์เพื่อเปิดฟอร์มนี้',
},
},
} as const;
export const getFooter = (locale: SupportedLocale) => footerByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const footer = footerByLocale.it;

View File

@ -1,29 +1,85 @@
import type { SupportedLocale } from './navigation';
/** Sezione hero */
export const hero = {
badge: 'Restituiamo valore al tuo tempo',
/** Parte prima dellevidenziazione nel titolo */
titleBefore: "Ingegneria del software per l'era dell'",
/** Segmento con gradiente nel titolo */
titleHighlight: 'Intelligenza Artificiale',
titleAfter: '.',
subtitle:
'Sviluppiamo soluzioni SaaS ad alta complessità per settori regolamentati.',
/** Paragrafi introduttivi */
paragraphs: [
"Ci occupiamo di progettazione di ecosistemi digitali evoluti. La nostra specializzazione risiede nell'integrazione di modelli di Intelligenza Artificiale all'interno di flussi di lavoro professionali dove precisione, sicurezza e affidabilità sono requisiti non negoziabili.",
'Il nostro approccio si concentra sulla trasformazione di dati complessi in strumenti operativi fluidi, attraverso lo sviluppo di algoritmi proprietari, interfacce vocali e architetture cloud scalabili.',
] as const,
businessUnitsHeading: 'Le nostre Business Unit:',
businessUnitsIntro:
'Operiamo attraverso divisioni specializzate per garantire la massima verticalità e competenza di dominio:',
businessUnits: [
{
name: 'Legal Tech',
description: "Soluzioni per l'automazione e l'analisi normativa.",
},
{
name: 'Health Tech',
description: 'Sistemi avanzati per la gestione e lautomazione sanitaria.',
},
] as const,
const heroByLocale = {
it: {
badge: 'Restituiamo valore al tuo tempo',
titleBefore: "Ingegneria del software per l'era dell'",
titleHighlight: 'Intelligenza Artificiale',
titleAfter: '.',
subtitle:
'Sviluppiamo soluzioni SaaS ad alta complessità per settori regolamentati.',
paragraphs: [
"Ci occupiamo di progettazione di ecosistemi digitali evoluti. La nostra specializzazione risiede nell'integrazione di modelli di Intelligenza Artificiale all'interno di flussi di lavoro professionali dove precisione, sicurezza e affidabilità sono requisiti non negoziabili.",
'Il nostro approccio si concentra sulla trasformazione di dati complessi in strumenti operativi fluidi, attraverso lo sviluppo di algoritmi proprietari, interfacce vocali e architetture cloud scalabili.',
] as const,
businessUnitsHeading: 'Le nostre Business Unit:',
businessUnitsIntro:
'Operiamo attraverso divisioni specializzate per garantire la massima verticalità e competenza di dominio:',
businessUnits: [
{
name: 'Legal Tech',
description: "Soluzioni per l'automazione e l'analisi normativa.",
},
{
name: 'Health Tech',
description: 'Sistemi avanzati per la gestione e lautomazione sanitaria.',
},
] as const,
},
en: {
badge: 'We give your time back',
titleBefore: "Software engineering for the era of ",
titleHighlight: 'Artificial Intelligence',
titleAfter: '.',
subtitle:
'We build high-complexity SaaS solutions for regulated industries.',
paragraphs: [
'We design advanced digital ecosystems. Our specialization is integrating AI models into professional workflows where accuracy, security, and reliability are non-negotiable requirements.',
'Our approach turns complex data into smooth operational tools through proprietary algorithms, voice interfaces, and scalable cloud-native architectures.',
] as const,
businessUnitsHeading: 'Our Business Units:',
businessUnitsIntro:
'We operate through specialized divisions to ensure deep domain expertise and vertical focus:',
businessUnits: [
{
name: 'Legal Tech',
description: 'Solutions for legal automation and regulatory analysis.',
},
{
name: 'Health Tech',
description:
'Advanced systems for healthcare workflow management and automation.',
},
] as const,
},
th: {
badge: 'คืนคุณค่าให้กับเวลาของคุณ',
titleBefore: 'วิศวกรรมซอฟต์แวร์สำหรับยุคของ',
titleHighlight: 'ปัญญาประดิษฐ์',
titleAfter: '.',
subtitle: 'เราพัฒนาโซลูชัน SaaS ที่ซับซ้อนสูงสำหรับอุตสาหกรรมที่มีข้อกำกับ',
paragraphs: [
'เราออกแบบระบบนิเวศดิจิทัลขั้นสูง โดยเชี่ยวชาญในการผสานโมเดล AI เข้ากับเวิร์กโฟลว์ระดับมืออาชีพที่ต้องการความแม่นยำ ความปลอดภัย และความน่าเชื่อถือ',
'แนวทางของเราคือเปลี่ยนข้อมูลที่ซับซ้อนให้เป็นเครื่องมือการทำงานที่ลื่นไหล ผ่านอัลกอริทึมเฉพาะทาง อินเทอร์เฟซเสียง และสถาปัตยกรรมคลาวด์ที่ขยายได้',
] as const,
businessUnitsHeading: 'หน่วยธุรกิจของเรา:',
businessUnitsIntro:
'เราดำเนินงานผ่านทีมเฉพาะทางเพื่อให้ได้ความเชี่ยวชาญเชิงลึกในแต่ละโดเมน:',
businessUnits: [
{
name: 'Legal Tech',
description: 'โซลูชันสำหรับงานกฎหมายอัตโนมัติและการวิเคราะห์ข้อกำกับ',
},
{
name: 'Health Tech',
description: 'ระบบขั้นสูงสำหรับการจัดการและทำงานอัตโนมัติในภาคสุขภาพ',
},
] as const,
},
} as const;
export const getHero = (locale: SupportedLocale) => heroByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const hero = heroByLocale.it;

View File

@ -3,13 +3,24 @@
* Modifica i file in questa cartella per aggiornare testi senza toccare il markup.
*/
export { siteMeta } from './site-meta';
export { getSiteMeta } from './site-meta';
export { chat } from './chat';
export { getChat } from './chat';
export { navigation } from './navigation';
export { getNavigation } from './navigation';
export { hero } from './hero';
export { getHero } from './hero';
export { stats } from './stats';
export { getStats } from './stats';
export { services } from './services';
export { getServices } from './services';
export { stackSection } from './stack';
export { getStackSection } from './stack';
export { productsSection } from './products';
export { getProductsSection } from './products';
export { faq } from './faq';
export { getFaq } from './faq';
export { cta } from './cta';
export { getCta } from './cta';
export { footer } from './footer';
export { getFooter } from './footer';

View File

@ -1,19 +1,77 @@
import { chat } from './chat';
/** Header — CTA unico: ingresso chat live (config in `chat.ts`) */
export const navigation = {
brandName: 'NexStudio',
items: [
{ label: 'Prodotti', href: '/#prodotti' },
{ label: 'Servizi', href: '/#servizi' },
{ label: 'Stack', href: '/#stack' },
{ label: 'FAQ', href: '/#faq' },
{ label: 'Contattaci', href: '/#contatti' },
] as const,
cta: {
label: 'Chattiamo',
linkTitle: chat.linkTitle,
ariaLabel:
'Richiedi accesso alla chat NexStudio: stato operatore e chatbot, identificazione con email (link o codice) prima della sessione.',
export type SupportedLocale = 'it' | 'en' | 'th';
const navigationByLocale = {
it: {
brandName: 'NexStudio',
items: [
{ label: 'Prodotti', href: '/#prodotti' },
{ label: 'Servizi', href: '/#servizi' },
{ label: 'Stack', href: '/#stack' },
{ label: 'FAQ', href: '/#faq' },
{ label: 'Contattaci', href: '/#contatti' },
] as const,
cta: {
label: 'Chattiamo',
linkTitle: chat.linkTitle,
ariaLabel:
'Richiedi accesso alla chat NexStudio: stato operatore e chatbot, identificazione con email (link o codice) prima della sessione.',
},
labels: {
mainNavAria: 'Principale',
mobileNavAria: 'Navigazione mobile',
languageSelectorAria: 'Seleziona lingua',
openMenuAria: 'Apri menu',
},
},
en: {
brandName: 'NexStudio',
items: [
{ label: 'Products', href: '/#prodotti' },
{ label: 'Services', href: '/#servizi' },
{ label: 'Stack', href: '/#stack' },
{ label: 'FAQ', href: '/#faq' },
{ label: 'Contact', href: '/#contatti' },
] as const,
cta: {
label: 'Lets chat',
linkTitle: 'Open NexStudio chat access request.',
ariaLabel:
'Request access to NexStudio chat: operator and chatbot availability, email verification link or code before session start.',
},
labels: {
mainNavAria: 'Main navigation',
mobileNavAria: 'Mobile navigation',
languageSelectorAria: 'Select language',
openMenuAria: 'Open menu',
},
},
th: {
brandName: 'NexStudio',
items: [
{ label: 'ผลิตภัณฑ์', href: '/#prodotti' },
{ label: 'บริการ', href: '/#servizi' },
{ label: 'สแตก', href: '/#stack' },
{ label: 'คำถามที่พบบ่อย', href: '/#faq' },
{ label: 'ติดต่อเรา', href: '/#contatti' },
] as const,
cta: {
label: 'เริ่มแชท',
linkTitle: 'เปิดคำขอเข้าใช้งานแชทของ NexStudio',
ariaLabel:
'ขอเข้าใช้งานแชท NexStudio: ตรวจสอบสถานะผู้ดูแลและแชทบอต พร้อมยืนยันตัวตนด้วยอีเมลก่อนเริ่มใช้งาน',
},
labels: {
mainNavAria: 'เมนูหลัก',
mobileNavAria: 'เมนูบนมือถือ',
languageSelectorAria: 'เลือกภาษา',
openMenuAria: 'เปิดเมนู',
},
},
} as const;
export const getNavigation = (locale: SupportedLocale) => navigationByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const navigation = navigationByLocale.it;

View File

@ -1,55 +1,173 @@
import type { SupportedLocale } from './navigation';
/** Sezione prodotti — LexAura (Legal Tech), MediAura (Health Tech) */
export const productsSection = {
eyebrow: 'Prodotti',
title: 'LexAura e MediAura',
lead:
'Due piattaforme SaaS verticali per professionisti del diritto e della sanità: AI integrata dove precisione e conformità sono fondamentali.',
items: [
{
id: 'lexaura',
name: 'LexAura',
badge: 'Legal Tech',
tagline: 'Il supporto analitico per leccellenza legale.',
focus: 'Gestione dati, memorie e contrattualistica.',
body:
'LexAura è la piattaforma SaaS di NexStudio dedicata ai professionisti del diritto. Sviluppata per ottimizzare la gestione di carichi informativi elevati, LexAura utilizza lIntelligenza Artificiale per lincrocio dinamico di leggi, sentenze e normative, garantendo una base analitica solida e tempestiva.',
highlights: [
{
title: 'Sintesi normativa',
text: 'Analisi accelerata dei precedenti, delle fonti e dei contesti.',
},
{
title: 'Drafting assistito',
text: 'Supporto alla stesura di memorie difensive e contratti complessi.',
},
{
title: 'Workflow integration',
text: 'Automazione dei processi documentali per lo studio legale moderno.',
},
] as const,
},
{
id: 'mediaura',
name: 'MediAura',
badge: 'Health Tech',
tagline: 'Lautomazione intelligente al servizio della medicina.',
focus: 'Flussi di lavoro, comandi vocali, scalabilità.',
body:
'MediAura è il sistema SaaS progettato da NexStudio per abbattere il carico burocratico in ambito sanitario. Dallo studio professionale alla struttura ospedaliera, il software coordina i flussi operativi attraverso un alto grado di automazione e interazione naturale.',
highlights: [
{
title: 'Voice-first interface',
text: 'Gestione dei flussi e della refertazione tramite comandi vocali avanzati.',
},
{
title: 'Scalabilità operativa',
text: 'Unarchitettura che si adatta alle dimensioni della struttura, dal singolo medico alla gestione di reparti complessi.',
},
{
title: 'Ottimizzazione del flusso',
text: 'Automazione delle attività di routine per restituire centralità al rapporto medico-paziente.',
},
] as const,
},
] as const,
const productsByLocale = {
it: {
eyebrow: 'Prodotti',
title: 'LexAura e MediAura',
lead:
'Due piattaforme SaaS verticali per professionisti del diritto e della sanità: AI integrata dove precisione e conformità sono fondamentali.',
items: [
{
id: 'lexaura',
name: 'LexAura',
badge: 'Legal Tech',
tagline: 'Il supporto analitico per leccellenza legale.',
focus: 'Gestione dati, memorie e contrattualistica.',
body:
'LexAura è la piattaforma SaaS di NexStudio dedicata ai professionisti del diritto. Sviluppata per ottimizzare la gestione di carichi informativi elevati, LexAura utilizza lIntelligenza Artificiale per lincrocio dinamico di leggi, sentenze e normative, garantendo una base analitica solida e tempestiva.',
highlights: [
{
title: 'Sintesi normativa',
text: 'Analisi accelerata dei precedenti, delle fonti e dei contesti.',
},
{
title: 'Drafting assistito',
text: 'Supporto alla stesura di memorie difensive e contratti complessi.',
},
{
title: 'Workflow integration',
text: 'Automazione dei processi documentali per lo studio legale moderno.',
},
] as const,
},
{
id: 'mediaura',
name: 'MediAura',
badge: 'Health Tech',
tagline: 'Lautomazione intelligente al servizio della medicina.',
focus: 'Flussi di lavoro, comandi vocali, scalabilità.',
body:
'MediAura è il sistema SaaS progettato da NexStudio per abbattere il carico burocratico in ambito sanitario. Dallo studio professionale alla struttura ospedaliera, il software coordina i flussi operativi attraverso un alto grado di automazione e interazione naturale.',
highlights: [
{
title: 'Voice-first interface',
text: 'Gestione dei flussi e della refertazione tramite comandi vocali avanzati.',
},
{
title: 'Scalabilità operativa',
text: 'Unarchitettura che si adatta alle dimensioni della struttura, dal singolo medico alla gestione di reparti complessi.',
},
{
title: 'Ottimizzazione del flusso',
text: 'Automazione delle attività di routine per restituire centralità al rapporto medico-paziente.',
},
] as const,
},
] as const,
},
en: {
eyebrow: 'Products',
title: 'LexAura and MediAura',
lead:
'Two vertical SaaS platforms for legal and healthcare professionals: AI integrated where precision and compliance are essential.',
items: [
{
id: 'lexaura',
name: 'LexAura',
badge: 'Legal Tech',
tagline: 'Analytical support for legal excellence.',
focus: 'Data management, briefs, and contracts.',
body:
'LexAura is NexStudios SaaS platform for legal professionals. Built to optimize high information workloads, LexAura uses Artificial Intelligence for dynamic cross-referencing of laws, judgments, and regulations, delivering a solid and timely analytical foundation.',
highlights: [
{
title: 'Regulatory synthesis',
text: 'Accelerated analysis of precedents, sources, and contexts.',
},
{
title: 'Assisted drafting',
text: 'Support for drafting defense briefs and complex contracts.',
},
{
title: 'Workflow integration',
text: 'Document process automation for the modern law firm.',
},
] as const,
},
{
id: 'mediaura',
name: 'MediAura',
badge: 'Health Tech',
tagline: 'Intelligent automation in service of medicine.',
focus: 'Workflows, voice commands, scalability.',
body:
'MediAura is the SaaS system designed by NexStudio to reduce bureaucratic load in healthcare. From private practice to hospital settings, the software coordinates operational flows through high automation and natural interaction.',
highlights: [
{
title: 'Voice-first interface',
text: 'Manage workflows and reporting through advanced voice commands.',
},
{
title: 'Operational scalability',
text: 'An architecture that adapts from a single physician to complex multi-department organizations.',
},
{
title: 'Flow optimization',
text: 'Automation of routine tasks to put the doctorpatient relationship back at the center.',
},
] as const,
},
] as const,
},
th: {
eyebrow: 'ผลิตภัณฑ์',
title: 'LexAura และ MediAura',
lead:
'แพลตฟอร์ม SaaS เฉพาะทางสองระบบสำหรับมืออาชีพด้านกฎหมายและสุขภาพ: ผสาน AI ในจุดที่ต้องการความแม่นยำและการปฏิบัติตามข้อกำกับ',
items: [
{
id: 'lexaura',
name: 'LexAura',
badge: 'Legal Tech',
tagline: 'เครื่องมือวิเคราะห์เพื่อความเป็นเลิศทางกฎหมาย',
focus: 'การจัดการข้อมูล คำแถลง และสัญญา',
body:
'LexAura คือแพลตฟอร์ม SaaS ของ NexStudio สำหรับมืออาชีพด้านกฎหมาย พัฒนาเพื่อจัดการปริมาณข้อมูลจำนวนมาก โดยใช้ปัญญาประดิษฐ์ในการเชื่อมโยงกฎหมาย คำพิพากษา และข้อกำกับอย่างไดนามิก เพื่อฐานวิเคราะห์ที่แข็งแรงและทันเวลา',
highlights: [
{
title: 'การสรุปเชิงกฎหมาย',
text: 'วิเคราะห์บรรทัดฐาน แหล่งข้อมูล และบริบทได้รวดเร็วขึ้น',
},
{
title: 'การร่างเอกสารแบบมีผู้ช่วย',
text: 'สนับสนุนการร่างคำแถลงและสัญญาที่ซับซ้อน',
},
{
title: 'การผสานเวิร์กโฟลว์',
text: 'ระบบงานเอกสารอัตโนมัติสำหรับสำนักงานกฎหมายสมัยใหม่',
},
] as const,
},
{
id: 'mediaura',
name: 'MediAura',
badge: 'Health Tech',
tagline: 'ระบบอัตโนมัติอัจฉริยะเพื่อการแพทย์',
focus: 'เวิร์กโฟลว์ คำสั่งเสียง ความสามารถในการขยาย',
body:
'MediAura คือระบบ SaaS ที่ NexStudio ออกแบบเพื่อลดภาระงานธุรการในภาคสุขภาพ ตั้งแต่คลินิกส่วนตัวถึงโรงพยาบาล ซอฟต์แวร์ช่วยประสานงานปฏิบัติการด้วยระบบอัตโนมัติสูงและการโต้ตอบที่เป็นธรรมชาติ',
highlights: [
{
title: 'อินเทอร์เฟซแบบ Voice-first',
text: 'จัดการเวิร์กโฟลว์และรายงานผลผ่านคำสั่งเสียงขั้นสูง',
},
{
title: 'ขยายได้ตามการดำเนินงาน',
text: 'สถาปัตยกรรมที่ปรับได้ตั้งแต่วิทยาแพทย์คนเดียวถึงแผนกที่ซับซ้อน',
},
{
title: 'เพิ่มประสิทธิภาพเวิร์กโฟลว์',
text: 'ทำให้งานประจำอัตโนมัติ เพื่อคืนความสำคัญให้ความสัมพันธ์แพทย์–ผู้ป่วย',
},
] as const,
},
] as const,
},
} as const;
export const getProductsSection = (locale: SupportedLocale) =>
productsByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const productsSection = productsByLocale.it;

View File

@ -2,51 +2,153 @@
* Servizi (griglia bento).
* layout: wide = 2 colonne, tall = 2 righe + elenco puntato
*/
export const services = {
eyebrow: 'I nostri servizi',
title: 'Implementazione, evoluzione e supporto',
lead:
'In NexStudio, il nostro impegno non si esaurisce con la fornitura della licenza software. Il nostro modello di servizio è progettato per garantire che ogni professionista e struttura possa estrarre il massimo valore dalle potenzialità dellIntelligenza Artificiale integrata in LexAura e MediAura.',
cards: [
{
layout: 'tall' as const,
/** Sfera geodetica vettoriale (SVG) in basso a destra */
decoration: 'wireframe-globe' as const,
title: 'Onboarding e formazione specialistica',
body:
'Ladozione di un sistema basato su IA richiede un passaggio metodologico, non solo tecnico. Offriamo programmi di formazione dedicati per team legali e medici, finalizzati a:',
bullets: [
'Ottimizzare linterazione con gli algoritmi di ricerca e sintesi.',
'Padroneggiare i comandi vocali e lautomazione dei flussi documentali.',
'Ridurre drasticamente la curva di apprendimento tecnologico.',
],
},
{
layout: 'wide' as const,
title: 'Supporto Tecnico e Consulenza Operativa',
body:
'Forniamo unassistenza continua e di alto profilo per garantire la costante continuità operativa dei vostri sistemi. NexStudio non agisce come un semplice fornitore di supporto tecnico, ma come un partner strategico che affianca lo studio o la struttura sanitaria nellottimizzazione dei processi interni attraverso luso dei nostri SaaS.',
callout: {
title: 'Intervento «Breaking Glass»',
import type { SupportedLocale } from './navigation';
const servicesByLocale = {
it: {
eyebrow: 'I nostri servizi',
title: 'Implementazione, evoluzione e supporto',
lead:
'In NexStudio, il nostro impegno non si esaurisce con la fornitura della licenza software. Il nostro modello di servizio è progettato per garantire che ogni professionista e struttura possa estrarre il massimo valore dalle potenzialità dellIntelligenza Artificiale integrata in LexAura e MediAura.',
cards: [
{
layout: 'tall' as const,
decoration: 'wireframe-globe' as const,
title: 'Onboarding e formazione specialistica',
body:
'Per le situazioni di estrema urgenza o criticità sistemica, garantiamo protocolli di accesso rapido e intervento prioritario. Questa procedura di emergenza permette di superare i normali flussi di assistenza per risolvere istantaneamente anomalie che potrebbero impattare sulla continuità del servizio, assicurando che il professionista non resti mai isolato dai propri dati o strumenti decisionali.',
'Ladozione di un sistema basato su IA richiede un passaggio metodologico, non solo tecnico. Offriamo programmi di formazione dedicati per team legali e medici, finalizzati a:',
bullets: [
'Ottimizzare linterazione con gli algoritmi di ricerca e sintesi.',
'Padroneggiare i comandi vocali e lautomazione dei flussi documentali.',
'Ridurre drasticamente la curva di apprendimento tecnologico.',
],
},
},
{
layout: 'default' as const,
title: 'Estensioni e integrazioni personalizzate',
body:
'Sebbene LexAura e MediAura siano prodotti finiti e pronti alluso, comprendiamo che strutture complesse possano avere esigenze di interconnessione specifiche.',
bullets: [
'API integration: supportiamo lintegrazione dei nostri SaaS con sistemi gestionali preesistenti.',
'Custom modules: progettiamo estensioni su misura che aggiungono funzionalità specifiche allinterno del perimetro dei nostri prodotti, per rispondere a flussi di lavoro unici.',
],
},
{
layout: 'default' as const,
title: 'Aggiornamento e manutenzione evolutiva',
body:
'Il mondo dellIA e delle normative (legali e sanitarie) è in costante mutamento. Il nostro servizio include laggiornamento continuo dei motori di analisi, garantendo che il software sia sempre allineato con le ultime tecnologie e le evoluzioni del mercato di riferimento.',
},
],
{
layout: 'wide' as const,
title: 'Supporto Tecnico e Consulenza Operativa',
body:
'Forniamo unassistenza continua e di alto profilo per garantire la costante continuità operativa dei vostri sistemi. NexStudio non agisce come un semplice fornitore di supporto tecnico, ma come un partner strategico che affianca lo studio o la struttura sanitaria nellottimizzazione dei processi interni attraverso luso dei nostri SaaS.',
callout: {
title: 'Intervento «Breaking Glass»',
body:
'Per le situazioni di estrema urgenza o criticità sistemica, garantiamo protocolli di accesso rapido e intervento prioritario. Questa procedura di emergenza permette di superare i normali flussi di assistenza per risolvere istantaneamente anomalie che potrebbero impattare sulla continuità del servizio, assicurando che il professionista non resti mai isolato dai propri dati o strumenti decisionali.',
},
},
{
layout: 'default' as const,
title: 'Estensioni e integrazioni personalizzate',
body:
'Sebbene LexAura e MediAura siano prodotti finiti e pronti alluso, comprendiamo che strutture complesse possano avere esigenze di interconnessione specifiche.',
bullets: [
'API integration: supportiamo lintegrazione dei nostri SaaS con sistemi gestionali preesistenti.',
'Custom modules: progettiamo estensioni su misura che aggiungono funzionalità specifiche allinterno del perimetro dei nostri prodotti, per rispondere a flussi di lavoro unici.',
],
},
{
layout: 'default' as const,
title: 'Aggiornamento e manutenzione evolutiva',
body:
'Il mondo dellIA e delle normative (legali e sanitarie) è in costante mutamento. Il nostro servizio include laggiornamento continuo dei motori di analisi, garantendo che il software sia sempre allineato con le ultime tecnologie e le evoluzioni del mercato di riferimento.',
},
],
},
en: {
eyebrow: 'Our services',
title: 'Implementation, evolution, and support',
lead:
'At NexStudio, our commitment goes beyond software licensing. Our service model helps each professional and organization unlock the full value of AI integrated into LexAura and MediAura.',
cards: [
{
layout: 'tall' as const,
decoration: 'wireframe-globe' as const,
title: 'Onboarding and specialist training',
body:
'Adopting AI-powered systems is a methodological shift, not only a technical one. We provide dedicated programs for legal and medical teams to:',
bullets: [
'Improve interaction with search and summarization algorithms.',
'Master voice commands and document workflow automation.',
'Significantly reduce the technology learning curve.',
],
},
{
layout: 'wide' as const,
title: 'Technical support and operational consulting',
body:
'We provide continuous, high-level assistance to ensure operational continuity. NexStudio is not just a support vendor, but a strategic partner for legal firms and healthcare organizations.',
callout: {
title: '“Breaking Glass” intervention',
body:
'For critical urgency or systemic incidents, we offer rapid-access and priority intervention protocols. This emergency procedure bypasses standard support flows to restore continuity as fast as possible.',
},
},
{
layout: 'default' as const,
title: 'Custom extensions and integrations',
body:
'Although LexAura and MediAura are complete, ready-to-use products, complex organizations may still require specific interoperability needs.',
bullets: [
'API integration: we integrate our SaaS with your existing management systems.',
'Custom modules: we build tailored extensions inside our product perimeter to support unique workflows.',
],
},
{
layout: 'default' as const,
title: 'Evolutionary updates and maintenance',
body:
'AI and regulated frameworks evolve continuously. Our service includes ongoing analysis-engine updates to keep software aligned with new technologies and market requirements.',
},
],
},
th: {
eyebrow: 'บริการของเรา',
title: 'การติดตั้ง การพัฒนา และการสนับสนุน',
lead:
'ที่ NexStudio ความมุ่งมั่นของเราไม่ได้จบแค่การให้สิทธิ์ใช้งานซอฟต์แวร์ โมเดลบริการของเราช่วยให้ทีมงานดึงศักยภาพสูงสุดจาก AI ที่ผสานใน LexAura และ MediAura',
cards: [
{
layout: 'tall' as const,
decoration: 'wireframe-globe' as const,
title: 'การเริ่มใช้งานและการอบรมเฉพาะทาง',
body:
'การนำระบบ AI มาใช้ต้องอาศัยการเปลี่ยนผ่านเชิงวิธีการ ไม่ใช่แค่เทคนิค เรามีโปรแกรมอบรมสำหรับทีมกฎหมายและการแพทย์เพื่อ:',
bullets: [
'เพิ่มประสิทธิภาพการใช้งานอัลกอริทึมค้นหาและสรุปผล',
'ใช้งานคำสั่งเสียงและระบบงานเอกสารอัตโนมัติได้อย่างคล่องตัว',
'ลดช่วงเวลาเรียนรู้เทคโนโลยีอย่างมีนัยสำคัญ',
],
},
{
layout: 'wide' as const,
title: 'สนับสนุนทางเทคนิคและที่ปรึกษาการปฏิบัติงาน',
body:
'เราให้การสนับสนุนอย่างต่อเนื่องเพื่อคงความต่อเนื่องในการดำเนินงาน NexStudio ไม่ได้เป็นเพียงผู้ให้บริการซัพพอร์ต แต่เป็นพาร์ตเนอร์เชิงกลยุทธ์สำหรับองค์กร',
callout: {
title: 'การแทรกแซงแบบ “Breaking Glass”',
body:
'เมื่อเกิดเหตุเร่งด่วนหรือความผิดปกติระดับวิกฤต เรามีโปรโตคอลเข้าถึงด่วนและจัดลำดับความสำคัญสูงสุด เพื่อแก้ปัญหาได้ทันทีและรักษาความต่อเนื่องของบริการ',
},
},
{
layout: 'default' as const,
title: 'ส่วนขยายและการเชื่อมต่อแบบกำหนดเอง',
body:
'แม้ LexAura และ MediAura จะพร้อมใช้งานทันที แต่บางองค์กรอาจต้องการการเชื่อมต่อเฉพาะทางกับระบบเดิม',
bullets: [
'API integration: เชื่อม SaaS ของเราเข้ากับระบบจัดการที่ใช้อยู่แล้ว',
'Custom modules: ออกแบบส่วนขยายเฉพาะงานภายในขอบเขตผลิตภัณฑ์ของเรา',
],
},
{
layout: 'default' as const,
title: 'อัปเดตและบำรุงรักษาเชิงพัฒนา',
body:
'โลกของ AI และข้อกำกับเปลี่ยนแปลงตลอดเวลา บริการของเรารวมถึงการอัปเดตเอนจินวิเคราะห์อย่างต่อเนื่องเพื่อให้ซอฟต์แวร์ทันสมัยและสอดคล้องบริบทตลาด',
},
],
},
} as const;
export const getServices = (locale: SupportedLocale) => servicesByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const services = servicesByLocale.it;

View File

@ -1,6 +1,25 @@
import type { SupportedLocale } from './navigation';
/** Meta pagina principale — usato da BaseLayout */
export const siteMeta = {
title: 'NexStudio — Ingegneria del software e IA per settori regolamentati',
description:
'NexStudio: software company con sede a Bangkok. SaaS ad alta complessità, integrazione di IA in flussi professionali, Legal Tech e Health Tech.',
const siteMetaByLocale = {
it: {
title: 'NexStudio — Ingegneria del software e IA per settori regolamentati',
description:
'NexStudio: software company con sede a Bangkok. SaaS ad alta complessità, integrazione di IA in flussi professionali, Legal Tech e Health Tech.',
},
en: {
title: 'NexStudio — Software engineering and AI for regulated industries',
description:
'NexStudio is a software company based in Bangkok. We build high-complexity SaaS products and integrate AI into professional workflows across Legal Tech and Health Tech.',
},
th: {
title: 'NexStudio — วิศวกรรมซอฟต์แวร์และ AI สำหรับอุตสาหกรรมที่มีข้อกำกับ',
description:
'NexStudio คือบริษัทซอฟต์แวร์ในกรุงเทพฯ พัฒนา SaaS ความซับซ้อนสูง และผสาน AI เข้ากับเวิร์กโฟลว์ระดับมืออาชีพในสาย Legal Tech และ Health Tech',
},
} as const;
export const getSiteMeta = (locale: SupportedLocale) => siteMetaByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const siteMeta = siteMetaByLocale.it;

View File

@ -1,9 +1,12 @@
import type { SupportedLocale } from './navigation';
/** Sezione stack — infrastruttura Cloudflare e sicurezza */
export const stackSection = {
const stackIt = {
eyebrow: 'Lo stack tecnologico',
title: 'Infrastruttura e sicurezza',
lead:
'In NexStudio, la qualità dellesperienza utente è inscindibile dalla solidità dellinfrastruttura. Per questo motivo ci affidiamo a partner globali ad alta efficienza che garantiscono i più elevati standard di disponibilità e protezione dei dati.',
infrastructureHeading: 'Infrastruttura',
cloudflare: {
title: 'Ecosistema Cloudflare',
intro:
@ -145,3 +148,168 @@ export const stackSection = {
] as const,
},
} as const;
const stackByLocale = {
it: stackIt,
en: {
...stackIt,
eyebrow: 'Technology stack',
title: 'Infrastructure and security',
lead:
'At NexStudio, user experience quality is inseparable from infrastructure resilience. That is why we rely on high-efficiency global partners that provide strong availability and data protection standards.',
infrastructureHeading: 'Infrastructure',
cloudflare: {
...stackIt.cloudflare,
title: 'Cloudflare ecosystem',
intro:
'"Cloudflare helps accelerate performance, secure app availability, and optimize web experiences globally."',
points: [
{
title: 'Performance without compromise',
body:
'Access to our services remains low-latency worldwide, ensuring fluid usage even during complex data-processing operations.',
},
{
title: 'Advanced security',
body:
'We apply modern protection layers including end-to-end encryption, DDoS mitigation, and Web Application Firewall controls.',
},
{
title: 'Compliance and data integrity',
body:
'We use geographic data-control capabilities to align legal and medical data processing with local and international privacy requirements.',
},
] as const,
},
continuity: {
title: 'Continuity and scalability',
body:
'We provide professional-grade SLA uptime. Our cloud-native architecture scales instantly for workload peaks, large offices, and multi-site organizations.',
},
whyThisChoice: {
title: 'Why NexStudio',
points: [
'Regulated vertical focus: we design for Legal Tech and Health Tech, where traceability, privacy, and service continuity are core product requirements.',
'AI in real professional workflows: we embed models and automation with attention to accuracy, auditability, and human oversight.',
'End-to-end responsibility: architecture, development, and evolution stay under one ownership model for clearer priorities and delivery.',
] as const,
},
cloudflareBadge: {
...stackIt.cloudflareBadge,
label: 'Cloudflare ecosystem',
slogan: '"Helping build a better Internet."',
},
awsBadge: {
...stackIt.awsBadge,
label: 'AWS ecosystem',
slogan: '"The worlds most comprehensive and broadly adopted cloud platform."',
},
infrastructureCarousel: {
...stackIt.infrastructureCarousel,
ariaLabel: 'NexStudio infrastructure and technology partners',
items: [
{
...stackIt.infrastructureCarousel.items[0],
label: 'Cloudflare ecosystem',
slogan: '"Helping build a better Internet."',
},
{
...stackIt.infrastructureCarousel.items[1],
label: 'AWS ecosystem',
slogan: '"The worlds most comprehensive and broadly adopted cloud platform."',
},
{
...stackIt.infrastructureCarousel.items[2],
label: 'GitHub ecosystem',
},
{
...stackIt.infrastructureCarousel.items[3],
label: 'OpenAI ecosystem',
},
] as const,
},
},
th: {
...stackIt,
eyebrow: 'เทคโนโลยีสแตก',
title: 'โครงสร้างพื้นฐานและความปลอดภัย',
lead:
'ที่ NexStudio คุณภาพประสบการณ์ผู้ใช้แยกไม่ออกจากความมั่นคงของโครงสร้างพื้นฐาน เราจึงเลือกพาร์ตเนอร์ระดับโลกที่มีประสิทธิภาพสูงเพื่อให้ได้มาตรฐานด้านความพร้อมใช้งานและความปลอดภัยของข้อมูล',
infrastructureHeading: 'โครงสร้างพื้นฐาน',
cloudflare: {
...stackIt.cloudflare,
title: 'ระบบนิเวศ Cloudflare',
intro:
'"Cloudflare ช่วยเร่งประสิทธิภาพ รับประกันความพร้อมใช้งานของแอป และยกระดับประสบการณ์เว็บทั่วโลก"',
points: [
{
title: 'ประสิทธิภาพที่ไม่ประนีประนอม',
body:
'เข้าถึงบริการของเราได้อย่างหน่วงต่ำจากทุกที่ทั่วโลก แม้ในงานประมวลผลข้อมูลที่ซับซ้อน',
},
{
title: 'ความปลอดภัยขั้นสูง',
body:
'เราใช้กลไกป้องกันสมัยใหม่ เช่น การเข้ารหัส end-to-end การป้องกัน DDoS และ Web Application Firewall',
},
{
title: 'การปฏิบัติตามข้อกำกับและความถูกต้องของข้อมูล',
body:
'เราใช้ความสามารถในการกำกับตำแหน่งข้อมูลเพื่อให้การจัดการข้อมูลอ่อนไหวด้านกฎหมายและการแพทย์สอดคล้องกับข้อกำกับความเป็นส่วนตัว',
},
] as const,
},
continuity: {
title: 'ความต่อเนื่องและการขยายตัว',
body:
'เรารับประกัน uptime ระดับมืออาชีพตาม SLA และสถาปัตยกรรม cloud-native ที่ขยายได้ทันทีเมื่อมีโหลดสูง',
},
whyThisChoice: {
title: 'ทำไมต้อง NexStudio',
points: [
'เชี่ยวชาญอุตสาหกรรมที่มีข้อกำกับ: Legal Tech และ Health Tech',
'ผสาน AI เข้ากับงานจริง โดยคำนึงถึงความแม่นยำ การตรวจสอบย้อนหลัง และการกำกับโดยมนุษย์',
'รับผิดชอบแบบ end-to-end: สถาปัตยกรรม การพัฒนา และการต่อยอดอยู่ภายใต้ทีมเดียว',
] as const,
},
cloudflareBadge: {
...stackIt.cloudflareBadge,
label: 'ระบบนิเวศ Cloudflare',
slogan: '"ช่วยกันสร้างอินเทอร์เน็ตที่ดีกว่า"',
},
awsBadge: {
...stackIt.awsBadge,
label: 'ระบบนิเวศ AWS',
slogan: '"แพลตฟอร์มคลาวด์ที่ครอบคลุมและได้รับการใช้งานอย่างกว้างขวางที่สุดในโลก"',
},
infrastructureCarousel: {
...stackIt.infrastructureCarousel,
ariaLabel: 'พาร์ตเนอร์โครงสร้างพื้นฐานและเทคโนโลยีของ NexStudio',
items: [
{
...stackIt.infrastructureCarousel.items[0],
label: 'ระบบนิเวศ Cloudflare',
slogan: '"ช่วยกันสร้างอินเทอร์เน็ตที่ดีกว่า"',
},
{
...stackIt.infrastructureCarousel.items[1],
label: 'ระบบนิเวศ AWS',
slogan: '"แพลตฟอร์มคลาวด์ที่ครอบคลุมและได้รับการใช้งานอย่างกว้างขวางที่สุดในโลก"',
},
{
...stackIt.infrastructureCarousel.items[2],
label: 'ระบบนิเวศ GitHub',
},
{
...stackIt.infrastructureCarousel.items[3],
label: 'ระบบนิเวศ OpenAI',
},
] as const,
},
},
} as const;
export const getStackSection = (locale: SupportedLocale) => stackByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const stackSection = stackByLocale.it;

View File

@ -1,20 +1,67 @@
import type { SupportedLocale } from './navigation';
/** Fascia metriche — merge performance Edge + AI + compliance settoriale (Legal/Health) */
export const stats = {
items: [
{
value: '< 30ms',
caption:
'Latenza globale: elaborazione istantanea dei dati sullarchitettura Cloudflare Edge, per risposte in tempo reale dove servono clinica e forense.',
},
{
value: '99.9%',
caption:
'Affidabilità dellanalisi predittiva e diagnostica con modelli LLM di ultima generazione; automazione intelligente dei workflow complessi e integrazione con un ecosistema curato di SaaS verticali certificati per professionisti.',
},
{
value: '100%',
caption:
'GDPR e HIPAA compliant: gestione di documenti medici e legali a norma; protezione Zero-Trust e crittografia end-to-end su dati sensibili.',
},
] as const,
const statsByLocale = {
it: {
items: [
{
value: '< 30ms',
caption:
'Latenza globale: elaborazione istantanea dei dati sullarchitettura Cloudflare Edge, per risposte in tempo reale dove servono clinica e forense.',
},
{
value: '99.9%',
caption:
'Affidabilità dellanalisi predittiva e diagnostica con modelli LLM di ultima generazione; automazione intelligente dei workflow complessi e integrazione con un ecosistema curato di SaaS verticali certificati per professionisti.',
},
{
value: '100%',
caption:
'GDPR e HIPAA compliant: gestione di documenti medici e legali a norma; protezione Zero-Trust e crittografia end-to-end su dati sensibili.',
},
] as const,
},
en: {
items: [
{
value: '< 30ms',
caption:
'Global latency: instant data processing on Cloudflare Edge architecture, delivering real-time responses for clinical and forensic use cases.',
},
{
value: '99.9%',
caption:
'Predictive and diagnostic analysis reliability with state-of-the-art LLM models; intelligent automation of complex workflows and integration with a curated ecosystem of certified vertical SaaS for professionals.',
},
{
value: '100%',
caption:
'GDPR and HIPAA compliant: handling of medical and legal documents in compliance; Zero-Trust protection and end-to-end encryption for sensitive data.',
},
] as const,
},
th: {
items: [
{
value: '< 30ms',
caption:
'ความหน่วงระดับโลก: การประมวลผลข้อมูลทันใจบนสถาปัตยกรรม Cloudflare Edge เพื่อการตอบสนองเรียลไทม์ในงานคลินิกและนิติเวช',
},
{
value: '99.9%',
caption:
'ความน่าเชื่อถือในการวิเคราะห์เชิงพยากรณ์และวินิจฉัยด้วยโมเดล LLM ล่าสุด; ระบบอัตโนมัติอัจฉริยะสำหรับเวิร์กโฟลว์ซับซ้อนพร้อมระบบนิเวศ SaaS เฉพาะทางที่ผ่านการรับรองสำหรับมืออาชีพ',
},
{
value: '100%',
caption:
'สอดคล้องกับ GDPR และ HIPAA: การจัดการเอกสารทางการแพทย์และกฎหมายตามข้อกำหนด; การป้องกันแบบ Zero-Trust และการเข้ารหัส end-to-end สำหรับข้อมูลอ่อนไหว',
},
] as const,
},
} as const;
export const getStats = (locale: SupportedLocale) => statsByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const stats = statsByLocale.it;

File diff suppressed because it is too large Load Diff

208
src/data/news.ts Normal file
View File

@ -0,0 +1,208 @@
/**
* Pagina Novità / News / ข่าว — contenuti localizzati.
*/
import type { SupportedLocale } from './home/navigation';
export type NewsBullet = {
title?: string;
text: string;
};
export type NewsSection = {
title: string;
paragraphs?: readonly string[];
bullets?: readonly NewsBullet[];
};
type NewsLocaleBundle = {
page: {
title: string;
description: string;
heading: string;
lead: string;
};
statusBanner: string;
sections: readonly NewsSection[];
contact: {
before: string;
linkLabel: string;
href: string;
after: string;
};
};
const newsByLocale: Record<SupportedLocale, NewsLocaleBundle> = {
it: {
page: {
title: 'Novità — NexStudio',
description: 'Novità, release e articoli da NexStudio.',
heading: 'Novità',
lead:
'Aggiornamenti ufficiali su piattaforma, compliance, infrastruttura e contenuti editoriali.',
},
statusBanner:
'Stato editoriale: canale attivo. Le prossime pubblicazioni verranno aggiornate su questa pagina con frequenza periodica.',
sections: [
{
title: 'Programma editoriale',
bullets: [
{
title: 'Release tecniche:',
text: 'aggiornamenti su funzionalità, integrazioni e miglioramenti di stabilità.',
},
{
title: 'Compliance:',
text: 'novità su policy, sicurezza applicativa e adempimenti normativi.',
},
{
title: 'Case e risultati:',
text: 'note operative su adozione, scalabilità e impatti di business.',
},
],
},
{
title: 'Canali di distribuzione',
bullets: [
{
text: 'Questa pagina resta il punto di riferimento ufficiale per annunci e changelog sintetici.',
},
{
text: 'La newsletter è attiva dal footer del sito (iscrizione con conferma email).',
},
{
text: 'I contenuti long-form potranno essere pubblicati anche su canali esterni, con rimando qui.',
},
],
},
{
title: 'Archivio',
paragraphs: [
'In questa fase il sito è in pre-lancio operativo. Il primo blocco di news verrà pubblicato con il go-live ufficiale.',
],
},
],
contact: {
before:
'Per richieste stampa, partnership o approfondimenti tecnici è possibile usare il form nella sezione ',
linkLabel: 'Contatti',
href: '/#contatti',
after: '.',
},
},
en: {
page: {
title: 'News — NexStudio',
description: 'News, releases, and articles from NexStudio.',
heading: 'News',
lead:
'Official updates on platform, compliance, infrastructure, and editorial content.',
},
statusBanner:
'Editorial status: channel active. Upcoming publications will be updated on this page on a periodic basis.',
sections: [
{
title: 'Editorial programme',
bullets: [
{
title: 'Technical releases:',
text: 'updates on features, integrations, and stability improvements.',
},
{
title: 'Compliance:',
text: 'news on policies, application security, and regulatory requirements.',
},
{
title: 'Cases and results:',
text: 'operational notes on adoption, scalability, and business impact.',
},
],
},
{
title: 'Distribution channels',
bullets: [
{
text: 'This page remains the official reference point for announcements and concise changelogs.',
},
{
text: 'The newsletter is available from the site footer (subscription with email confirmation).',
},
{
text: 'Long-form content may also be published on external channels, with a link back here.',
},
],
},
{
title: 'Archive',
paragraphs: [
'At this stage the site is in pre-launch operations. The first block of news will be published with the official go-live.',
],
},
],
contact: {
before:
'For press requests, partnerships, or technical deep-dives, you can use the form in the ',
linkLabel: 'Contacts',
href: '/#contatti',
after: ' section.',
},
},
th: {
page: {
title: 'ข่าว — NexStudio',
description: 'ข่าว รีลีส และบทความจาก NexStudio',
heading: 'ข่าว',
lead:
'อัปเดตอย่างเป็นทางการเกี่ยวกับแพลตฟอร์ม การปฏิบัติตามกฎ โครงสร้างพื้นฐาน และเนื้อหาบรรณาธิการ',
},
statusBanner:
'สถานะบรรณาธิการ: ช่องทางใช้งานอยู่ สิ่งที่จะเผยแพร่ต่อไปจะอัปเดตบนหน้านี้เป็นระยะ',
sections: [
{
title: 'แผนบรรณาธิการ',
bullets: [
{
title: 'รีลีสทางเทคนิค:',
text: 'อัปเดตฟีเจอร์ การผสานระบบ และการปรับปรุงความเสถียร',
},
{
title: 'การปฏิบัติตามกฎ:',
text: 'ข่าวเกี่ยวกับนโยบาย ความปลอดภัยของแอปพลิเคชัน และข้อกำหนดด้านกฎระเบียบ',
},
{
title: 'กรณีศึกษาและผลลัพธ์:',
text: 'บันทึกเชิงปฏิบัติเกี่ยวกับการนำไปใช้ ความสามารถในการขยายผล และผลกระทบทางธุรกิจ',
},
],
},
{
title: 'ช่องทางการเผยแพร่',
bullets: [
{
text: 'หน้านี้ยังคงเป็นจุดอ้างอิงอย่างเป็นทางการสำหรับประกาศและ changelog แบบสรุป',
},
{
text: 'จดหมายข่าวเปิดใช้งานจากส่วนท้ายของเว็บไซต์ (สมัครพร้อมยืนยันอีเมล)',
},
{
text: 'เนื้อหายาวอาจเผยแพร่บนช่องทางภายนอกด้วย โดยมีลิงก์กลับมายังที่นี่',
},
],
},
{
title: 'คลังข่าว',
paragraphs: [
'ในระยะนี้เว็บไซต์อยู่ในช่วงก่อนเปิดใช้งานอย่างเป็นทางการ ชุดข่าวชุดแรกจะเผยแพร่พร้อมการเปิดตัวอย่างเป็นทางการ',
],
},
],
contact: {
before:
'สำหรับคำขอสื่อ ความร่วมมือ หรือข้อมูลเชิงลึกทางเทคนิค สามารถใช้แบบฟอร์มในส่วน ',
linkLabel: 'ติดต่อ',
href: '/#contatti',
after: ' ได้',
},
},
};
export const getNews = (locale: SupportedLocale) => newsByLocale[locale];

View File

@ -2,159 +2,516 @@
* Informativa privacy del portale NexStudio e dei prodotti SaaS LexAura / MediAura.
* Questo file centralizza i contenuti della pagina /privacy per facilitare revisioni legali.
*/
export const privacyDocument = {
version: '1.0',
inVigoreDal: '22 aprile 2026',
ultimoAggiornamento: '22 aprile 2026',
} as const;
export const privacyCrossReferences = {
lead:
"L'informativa distingue il portale istituzionale NexStudio dai servizi SaaS LexAura e MediAura, che rispondono a esigenze operative e livelli di rischio differenti; le informative privacy specifiche dei singoli SaaS, pubblicate nei rispettivi siti/portali applicativi, restano il riferimento di dettaglio per l'uso del prodotto.",
} as const;
export const privacyRiferimenti: readonly { label: string; href: string }[] = [
{ label: "Condizioni d'uso", href: '/terms' },
{ label: 'Cookie policy', href: '/cookies' },
{ label: 'Diritti GDPR', href: '/gdpr' },
] as const;
import type { SupportedLocale } from './home/navigation';
export type PrivacySection = {
id: string;
title: string;
paragraphs: readonly string[];
bullets?: readonly string[];
/** Paragrafi dopo lelenco puntato (es. nota DPO in §2). */
paragraphsAfterBullets?: readonly string[];
};
export const privacySections: readonly PrivacySection[] = [
{
id: 'ambito',
title: '1. Ambito della presente informativa',
paragraphs: [
"La presente informativa descrive il quadro generale con cui NexStudio tratta i dati personali raccolti tramite il sito istituzionale pubblico e i canali corporate (contatti, richieste demo, onboarding commerciale) collegati anche ai prodotti LexAura (Legal Tech) e MediAura (Health Tech).",
"Per l'uso operativo dei SaaS (area riservata, tenant cliente, funzionalita' applicative), il livello di dettaglio e il ruolo privacy possono variare: NexStudio puo' operare come titolare, contitolare o responsabile del trattamento a seconda del servizio concretamente attivato e degli accordi contrattuali sottoscritti con il cliente business.",
"In caso di conflitto tra questa pagina e un Data Processing Agreement (DPA), un contratto quadro o condizioni speciali firmate, prevalgono i documenti contrattuali specifici per il servizio acquistato.",
],
const privacyByLocale = {
it: {
document: {
version: '1.1',
inVigoreDal: '[DATA]',
ultimoAggiornamento: '[DATA]',
},
crossReferences: {
lead:
"L'informativa distingue il portale istituzionale NexStudio dai servizi SaaS LexAura e MediAura; le informative privacy specifiche dei singoli SaaS, pubblicate nei rispettivi portali applicativi, restano il riferimento di dettaglio per l'uso del prodotto.",
},
riferimenti: [
{ label: "Condizioni d'uso", href: '/terms' },
{ label: 'Cookie policy', href: '/cookies' },
{ label: 'Diritti GDPR', href: '/gdpr' },
] as const,
sections: [
{
id: 'ambito',
title: '1. Ambito della presente informativa',
paragraphs: [
'La presente informativa descrive il quadro generale con cui NexStudio S.r.l. tratta i dati personali raccolti tramite il sito istituzionale pubblico e i canali corporate (collegati anche ai prodotti LexAura e MediAura).',
"Per l'uso operativo dei SaaS, il livello di dettaglio e il ruolo privacy possono variare: NexStudio può operare come titolare, contitolare o responsabile del trattamento a seconda del servizio e degli accordi contrattuali.",
'In caso di conflitto tra questa pagina e un Data Processing Agreement (DPA), un contratto quadro o condizioni speciali firmate, prevalgono i documenti contrattuali specifici per il servizio acquistato.',
],
},
{
id: 'titolare',
title: '2. Identità del titolare e punti di contatto',
paragraphs: [
'Titolare del trattamento: NexStudio S.r.l., con sede legale in Italia, [INDIRIZZO_COMPLETO], P.IVA [DA_INSERIRE].',
'Canali per richieste privacy:',
],
bullets: [
'Email: privacy@nexstudio.com',
'Form di contatto su nexstudio.ai',
'Per i clienti SaaS: canali autenticati/ticket previsti nei rispettivi portali (LexAura, MediAura)',
],
paragraphsAfterBullets: [
'Il Responsabile della Protezione dei Dati (DPO) è contattabile agli stessi recapiti.',
],
},
{
id: 'categorie-dati',
title: '3. Categorie di dati trattati',
paragraphs: [
'Le categorie di dati variano in funzione del canale utilizzato e dei moduli/feature attivi.',
],
bullets: [
'Portale NexStudio: dati di navigazione (log tecnici, IP, user agent), dati di contatto inviati volontariamente (nome, email, azienda, contenuto del messaggio), preferenze cookie e metriche aggregate.',
'LexAura (Legal Tech): dati di account e profilo utente, metadati di workspace, documenti giuridici caricati o generati, richieste testuali ai motori di analisi/sintesi, log di audit su accessi e operazioni.',
'MediAura (Health Tech): dati di account e ruoli, metadati organizzativi, documentazione sanitaria o amministrativa, input vocali e relative trascrizioni, log tecnici e di sicurezza.',
'Categorie particolari ex art. 9 GDPR: possono emergere in MediAura (e, in specifici casi, in LexAura) quando i clienti caricano contenuti che includono dati sanitari o altri dati sensibili; il trattamento avviene solo nei limiti del servizio e delle istruzioni ricevute dal cliente titolare.',
],
},
{
id: 'finalita-basi',
title: '4. Finalità e basi giuridiche del trattamento',
paragraphs: [],
bullets: [
'Gestione del sito istituzionale, sicurezza e continuità operativa (base: legittimo interesse alla sicurezza, prevenzione abusi e mantenimento del servizio).',
"Gestione richieste pre-contrattuali, demo, contatti commerciali e supporto (base: misure precontrattuali su richiesta dell'interessato; legittimo interesse nella gestione B2B).",
'Erogazione dei SaaS LexAura e MediAura, autenticazione utenti, gestione tenant e assistenza tecnica (base: esecuzione del contratto con il cliente business).',
'Conformità normativa, adempimenti amministrativi/fiscali, gestione contenziosi e difesa di diritti (base: obbligo legale e legittimo interesse alla tutela in giudizio).',
'Comunicazioni marketing/newsletter, ove attive (base: consenso, salvo eccezioni di soft spam consentite dalla normativa applicabile).',
'Miglioramento del servizio e sviluppo funzionalità IA: solo se previsto dal contratto o da istruzioni documentate; in assenza di tale base i dati non sono utilizzati per training generalista.',
],
},
{
id: 'ruoli-saas',
title: '5. Ruoli privacy specifici per LexAura e MediAura',
paragraphs: [
'Per i servizi SaaS B2B, il cliente determina normalmente finalità e mezzi del trattamento relativi ai dati caricati nel proprio ambiente applicativo, agendo come titolare del trattamento. NexStudio agisce di regola come responsabile del trattamento per conto del cliente, con nomina contrattuale e istruzioni documentate.',
'Qualora NexStudio determini autonomamente finalità specifiche (es. prevenzione frodi, sicurezza infrastrutturale, metriche aggregate non riconducibili al singolo interessato), agisce come titolare autonomo per tali trattamenti.',
'Le informative privacy dedicate di LexAura e MediAura, pubblicate nei rispettivi siti o ambienti applicativi, approfondiscono categorie dati, flussi, tempi di retention e basi giuridiche specifiche del singolo prodotto.',
],
},
{
id: 'conservazione',
title: '6. Tempi di conservazione',
paragraphs: [],
bullets: [
"Dati di contatto dal sito: 24 mesi dall'ultima interazione.",
'Dati di account SaaS: per tutta la durata del rapporto contrattuale + 30 giorni dalla cancellazione.',
'Log tecnici e di sicurezza: massimo 30 giorni.',
'Dati di fatturazione: 10 anni come richiesto dalla normativa fiscale italiana.',
'Dati sanitari: secondo le istruzioni del titolare (cliente) e gli obblighi di legge.',
'Dati analitici aggregati: indefinitamente in forma anonima.',
],
},
{
id: 'destinatari',
title: '7. Destinatari e categorie di soggetti autorizzati',
paragraphs: [
'I dati possono essere trattati da personale NexStudio autorizzato e istruito, nonché da fornitori qualificati che operano come responsabili/sub-responsabili (es. hosting cloud, servizi infrastrutturali, monitoraggio sicurezza, strumenti di comunicazione).',
"L'elenco aggiornato dei responsabili e sub-responsabili rilevanti per i clienti SaaS è messo a disposizione nei canali contrattuali o su richiesta.",
],
},
{
id: 'trasferimenti',
title: '8. Trasferimenti internazionali di dati',
paragraphs: [
"In ragione dell'uso di fornitori cloud globali, alcuni trattamenti possono comportare trasferimenti verso Paesi extra SEE/UE. Quando richiesto dalla normativa, NexStudio adotta garanzie adeguate (es. clausole contrattuali standard, misure supplementari tecniche/organizzative, valutazioni di impatto sul trasferimento) e limita il trasferimento ai soli dati necessari.",
'I dati sono preferibilmente trattati nello Spazio Economico Europeo. Cloudflare Inc. aderisce al Data Privacy Framework UE-USA e adotta Clausole Contrattuali Standard (SCC).',
],
},
{
id: 'sicurezza',
title: '9. Misure di sicurezza e governance',
paragraphs: [
'NexStudio adotta misure tecniche e organizzative proporzionate al rischio:',
],
bullets: [
'Controlli di accesso basati su ruoli, autenticazione, segregazione logica degli ambienti cliente e principio del minimo privilegio.',
'Cifratura dei dati in transito (TLS 1.3) e a riposo; gestione sicura delle chiavi.',
'Logging, monitoraggio, audit trail e procedure di gestione incidenti.',
'Procedure di backup, continuità operativa e disaster recovery.',
],
},
{
id: 'diritti',
title: '10. Diritti degli interessati e modalità di esercizio',
paragraphs: [
"Gli interessati possono esercitare i diritti previsti dalla normativa applicabile (accesso, rettifica, cancellazione, limitazione, opposizione, portabilità, revoca del consenso, reclamo all'autorità competente).",
'Per dati trattati nel contesto dei SaaS, la richiesta va di norma indirizzata prima al cliente titolare (es. studio legale o struttura sanitaria). NexStudio fornisce supporto al cliente titolare secondo contratto.',
'Per dati trattati da NexStudio come titolare (es. contatti dal sito, log di sicurezza propri), le richieste possono essere inviate a privacy@nexstudio.com o tramite il form su nexstudio.ai.',
'Autorità di controllo competente: Garante per la protezione dei dati personali (www.garanteprivacy.it).',
],
},
{
id: 'cookie-tracking',
title: '11. Cookie, analytics e tracciamenti',
paragraphs: [
'Le informazioni su cookie tecnici, eventuali cookie analitici e strumenti di tracciamento sono descritte nella Cookie Policy, da leggere congiuntamente alla presente informativa.',
],
},
{
id: 'minori',
title: '12. Trattamento dati di minori',
paragraphs: [
"I servizi NexStudio sono progettati per un'utenza professionale e business. Non sono rivolti intenzionalmente a minori.",
],
},
{
id: 'aggiornamenti',
title: '13. Aggiornamenti della policy',
paragraphs: [
'NexStudio può aggiornare questa informativa per adeguamenti normativi, evoluzione dei servizi, modifiche organizzative o introduzione di nuove funzionalità. Le modifiche rilevanti saranno pubblicate su questa pagina con aggiornamento di versione e data.',
],
},
] as const satisfies readonly PrivacySection[],
page: {
title: 'Privacy — NexStudio',
description:
'Informativa privacy quadro NexStudio S.r.l.: sito istituzionale, contatti corporate, rimandi ai dettagli privacy dei SaaS LexAura (Legal Tech) e MediAura (Health Tech).',
heading: 'Informativa sulla privacy',
lead:
"Informativa quadro sul trattamento dei dati personali per il portale istituzionale NexStudio S.r.l., con sede legale in Italia, [INDIRIZZO_COMPLETO], e per i servizi SaaS LexAura e MediAura. L'informativa distingue il portale istituzionale NexStudio dai servizi SaaS LexAura e MediAura; le informative privacy specifiche dei singoli SaaS, pubblicate nei rispettivi portali applicativi, restano il riferimento di dettaglio per l'uso del prodotto.",
versionLabel: 'Versione',
inForceFromLabel: 'In vigore dal',
lastUpdateLabel: 'Ultimo aggiornamento',
relatedDocsLabel: 'Documenti connessi',
indexLabel: 'Indice',
},
},
{
id: 'titolare',
title: "2. Identita' del titolare e punti di contatto",
paragraphs: [
"Titolare del trattamento: NexStudio (sede operativa a Bangkok, Thailandia). I dati anagrafici societari completi e il recapito privacy ufficiale sono indicati nei contatti istituzionali e nei documenti contrattuali applicabili.",
"Canali per richieste privacy: sezione contatti del sito, recapito email privacy dedicato ove attivo, ed eventuali canali autenticati/ticket previsti per i clienti SaaS. Se nominato, il DPO/Privacy Lead e' indicato con i relativi contatti.",
],
en: {
document: {
version: '1.1',
inVigoreDal: '[DATA]',
ultimoAggiornamento: '[DATA]',
},
crossReferences: {
lead:
'This notice distinguishes the NexStudio institutional portal from the LexAura and MediAura SaaS services; the product-specific privacy notices published on the respective application portals remain the detailed reference for product use.',
},
riferimenti: [
{ label: 'Terms of use', href: '/terms' },
{ label: 'Cookie policy', href: '/cookies' },
{ label: 'GDPR rights', href: '/gdpr' },
] as const,
sections: [
{
id: 'ambito',
title: '1. Scope of this notice',
paragraphs: [
'This notice describes the general framework under which NexStudio S.r.l. processes personal data collected through the public institutional website and corporate channels (also linked to the LexAura and MediAura products).',
'For operational use of the SaaS products, the level of detail and the privacy role may vary: NexStudio may act as controller, joint controller, or processor depending on the service and contractual agreements.',
'In case of conflict between this page and a Data Processing Agreement (DPA), a framework agreement, or signed special terms, the contractual documents specific to the purchased service prevail.',
],
},
{
id: 'titolare',
title: '2. Controller identity and contact points',
paragraphs: [
'Data controller: NexStudio S.r.l., registered office in Italy, [INDIRIZZO_COMPLETO], VAT [DA_INSERIRE].',
'Channels for privacy requests:',
],
bullets: [
'Email: privacy@nexstudio.com',
'Contact form on nexstudio.ai',
'For SaaS customers: authenticated channels/tickets provided in the respective portals (LexAura, MediAura)',
],
paragraphsAfterBullets: [
'The Data Protection Officer (DPO) can be contacted at the same addresses.',
],
},
{
id: 'categorie-dati',
title: '3. Categories of data processed',
paragraphs: [
'Data categories vary depending on the channel used and the active modules/features.',
],
bullets: [
'NexStudio portal: browsing data (technical logs, IP, user agent), contact data submitted voluntarily (name, email, company, message content), cookie preferences, and aggregate metrics.',
'LexAura (Legal Tech): account and user profile data, workspace metadata, legal documents uploaded or generated, text requests to analysis/summarization engines, audit logs of access and operations.',
'MediAura (Health Tech): account and role data, organizational metadata, health or administrative documentation, voice inputs and related transcripts, technical and security logs.',
'Special categories under Art. 9 GDPR: may arise in MediAura (and, in specific cases, in LexAura) when customers upload content including health data or other sensitive data; processing occurs only within the limits of the service and the instructions received from the customer controller.',
],
},
{
id: 'finalita-basi',
title: '4. Purposes and legal bases of processing',
paragraphs: [],
bullets: [
'Operation of the institutional website, security, and operational continuity (basis: legitimate interest in security, abuse prevention, and service maintenance).',
'Handling of pre-contractual requests, demos, commercial contacts, and support (basis: pre-contractual measures at the data subjects request; legitimate interest in B2B management).',
'Provision of LexAura and MediAura SaaS, user authentication, tenant management, and technical support (basis: performance of the contract with the business customer).',
'Regulatory compliance, administrative/tax obligations, dispute management, and defense of rights (basis: legal obligation and legitimate interest in legal protection).',
'Marketing/newsletter communications, where active (basis: consent, subject to soft-spam exceptions permitted by applicable law).',
'Service improvement and AI feature development: only if provided for by contract or documented instructions; absent such a basis, data are not used for general-purpose training.',
],
},
{
id: 'ruoli-saas',
title: '5. Privacy roles specific to LexAura and MediAura',
paragraphs: [
'For B2B SaaS services, the customer normally determines the purposes and means of processing relating to data uploaded into its application environment, acting as controller. NexStudio generally acts as processor on behalf of the customer, under contractual appointment and documented instructions.',
'Where NexStudio independently determines specific purposes (e.g. fraud prevention, infrastructure security, aggregate metrics not attributable to an individual data subject), it acts as an independent controller for such processing.',
'The dedicated LexAura and MediAura privacy notices, published on the respective sites or application environments, provide further detail on data categories, flows, retention periods, and legal bases specific to each product.',
],
},
{
id: 'conservazione',
title: '6. Retention periods',
paragraphs: [],
bullets: [
'Website contact data: 24 months from the last interaction.',
'SaaS account data: for the entire duration of the contractual relationship + 30 days from deletion.',
'Technical and security logs: maximum 30 days.',
'Billing data: 10 years as required by Italian tax law.',
'Health data: according to the controller (customer) instructions and legal obligations.',
'Aggregate analytics data: indefinitely in anonymous form.',
],
},
{
id: 'destinatari',
title: '7. Recipients and categories of authorized parties',
paragraphs: [
'Data may be processed by authorized and instructed NexStudio personnel, as well as by qualified suppliers acting as processors/sub-processors (e.g. cloud hosting, infrastructure services, security monitoring, communication tools).',
'The up-to-date list of processors and sub-processors relevant to SaaS customers is made available through contractual channels or upon request.',
],
},
{
id: 'trasferimenti',
title: '8. International data transfers',
paragraphs: [
'Due to the use of global cloud providers, some processing may involve transfers to countries outside the EEA/EU. Where required by law, NexStudio adopts appropriate safeguards (e.g. standard contractual clauses, supplementary technical/organizational measures, transfer impact assessments) and limits transfers to necessary data only.',
'Data are preferably processed within the European Economic Area. Cloudflare Inc. adheres to the EU-US Data Privacy Framework and adopts Standard Contractual Clauses (SCCs).',
],
},
{
id: 'sicurezza',
title: '9. Security measures and governance',
paragraphs: [
'NexStudio adopts technical and organizational measures proportionate to the risk:',
],
bullets: [
'Role-based access controls, authentication, logical segregation of customer environments, and the principle of least privilege.',
'Encryption of data in transit (TLS 1.3) and at rest; secure key management.',
'Logging, monitoring, audit trails, and incident management procedures.',
'Backup, operational continuity, and disaster recovery procedures.',
],
},
{
id: 'diritti',
title: '10. Data subject rights and how to exercise them',
paragraphs: [
'Data subjects may exercise the rights provided by applicable law (access, rectification, erasure, restriction, objection, portability, withdrawal of consent, complaint to the competent authority).',
'For data processed in the SaaS context, the request should normally be addressed first to the customer controller (e.g. law firm or healthcare organization). NexStudio supports the customer controller under contract.',
'For data processed by NexStudio as controller (e.g. website contacts, its own security logs), requests may be sent to privacy@nexstudio.com or via the form on nexstudio.ai.',
'Competent supervisory authority: Garante per la protezione dei dati personali (www.garanteprivacy.it).',
],
},
{
id: 'cookie-tracking',
title: '11. Cookies, analytics, and tracking',
paragraphs: [
'Information on technical cookies, any analytics cookies, and tracking tools is set out in the Cookie Policy, which should be read together with this notice.',
],
},
{
id: 'minori',
title: '12. Processing of childrens data',
paragraphs: [
'NexStudio services are designed for professional and business users. They are not intentionally directed at children.',
],
},
{
id: 'aggiornamenti',
title: '13. Policy updates',
paragraphs: [
'NexStudio may update this notice for regulatory adjustments, service evolution, organizational changes, or introduction of new features. Material changes will be published on this page with an updated version and date.',
],
},
] as const satisfies readonly PrivacySection[],
page: {
title: 'Privacy — NexStudio',
description:
'NexStudio S.r.l. framework privacy notice: institutional website, corporate contacts, and references to LexAura (Legal Tech) and MediAura (Health Tech) SaaS privacy details.',
heading: 'Privacy notice',
lead:
'Framework notice on the processing of personal data for the institutional portal of NexStudio S.r.l., registered office in Italy, [INDIRIZZO_COMPLETO], and for the LexAura and MediAura SaaS services. This notice distinguishes the NexStudio institutional portal from the LexAura and MediAura SaaS services; the product-specific privacy notices published on the respective application portals remain the detailed reference for product use.',
versionLabel: 'Version',
inForceFromLabel: 'In force from',
lastUpdateLabel: 'Last updated',
relatedDocsLabel: 'Related documents',
indexLabel: 'Contents',
},
},
{
id: 'categorie-dati',
title: '3. Categorie di dati trattati',
paragraphs: [
"Le categorie di dati variano in funzione del canale utilizzato e dei moduli/feature attivi. Di seguito il perimetro tipico a livello corporate; i dettagli di prodotto sono disciplinati nelle privacy dedicate dei singoli SaaS.",
],
bullets: [
"Portale NexStudio: dati di navigazione (log tecnici, IP, user agent), dati di contatto inviati volontariamente (nome, email, azienda, contenuto del messaggio), preferenze cookie e metriche aggregate.",
"LexAura (Legal Tech): dati di account e profilo utente, metadati di workspace, documenti giuridici caricati o generati, richieste testuali ai motori di analisi/sintesi, log di audit su accessi e operazioni.",
"MediAura (Health Tech): dati di account e ruoli, metadati organizzativi di struttura/reparto, documentazione sanitaria o amministrativa trattata nel servizio, input vocali e relative trascrizioni ove la funzione sia attivata, log tecnici e di sicurezza.",
"Categorie particolari ex art. 9 GDPR: possono emergere in MediAura (e, in specifici casi, in LexAura) quando i clienti caricano contenuti che includono dati sanitari o altri dati sensibili; il trattamento avviene solo nei limiti del servizio e delle istruzioni ricevute dal cliente titolare.",
],
th: {
document: {
version: '1.1',
inVigoreDal: '[DATA]',
ultimoAggiornamento: '[DATA]',
},
crossReferences: {
lead:
'ประกาศนี้แยกพอร์ทัลสถาบัน NexStudio จากบริการ SaaS LexAura และ MediAura ประกาศความเป็นส่วนตัวเฉพาะผลิตภัณฑ์ที่เผยแพร่บนพอร์ทัลแอปพลิเคชันที่เกี่ยวข้องยังคงเป็นเอกสารอ้างอิงรายละเอียดสำหรับการใช้ผลิตภัณฑ์',
},
riferimenti: [
{ label: 'เงื่อนไขการใช้งาน', href: '/terms' },
{ label: 'นโยบายคุกกี้', href: '/cookies' },
{ label: 'สิทธิ GDPR', href: '/gdpr' },
] as const,
sections: [
{
id: 'ambito',
title: '1. ขอบเขตของประกาศฉบับนี้',
paragraphs: [
'ประกาศฉบับนี้อธิบายกรอบทั่วไปที่ NexStudio S.r.l. ประมวลผลข้อมูลส่วนบุคคลที่เก็บรวบรวมผ่านเว็บไซต์สถาบันสาธารณะและช่องทางองค์กร (ซึ่งเชื่อมโยงกับผลิตภัณฑ์ LexAura และ MediAura ด้วย)',
'สำหรับการใช้งาน SaaS ในทางปฏิบัติ ระดับรายละเอียดและบทบาทความเป็นส่วนตัวอาจแตกต่างกัน: NexStudio อาจทำหน้าที่เป็นผู้ควบคุม ผู้ควบคุมร่วม หรือผู้ประมวลผล ขึ้นอยู่กับบริการและข้อตกลงตามสัญญา',
'หากเกิดความขัดแย้งระหว่างหน้านี้กับ Data Processing Agreement (DPA) สัญญากรอบ หรือเงื่อนไขพิเศษที่ลงนามแล้ว ให้ยึดเอกสารสัญญาเฉพาะของบริการที่ซื้อ',
],
},
{
id: 'titolare',
title: '2. ตัวตนของผู้ควบคุมและจุดติดต่อ',
paragraphs: [
'ผู้ควบคุมข้อมูล: NexStudio S.r.l. สำนักงานจดทะเบียนในอิตาลี [INDIRIZZO_COMPLETO] หมายเลขภาษีมูลค่าเพิ่ม [DA_INSERIRE]',
'ช่องทางสำหรับคำขอความเป็นส่วนตัว:',
],
bullets: [
'อีเมล: privacy@nexstudio.com',
'แบบฟอร์มติดต่อบน nexstudio.ai',
'สำหรับลูกค้า SaaS: ช่องทางที่ผ่านการยืนยันตัวตน/ตั๋วในพอร์ทัลที่เกี่ยวข้อง (LexAura, MediAura)',
],
paragraphsAfterBullets: [
'เจ้าหน้าที่คุ้มครองข้อมูล (DPO) สามารถติดต่อได้ที่ที่อยู่เดียวกัน',
],
},
{
id: 'categorie-dati',
title: '3. ประเภทข้อมูลที่ประมวลผล',
paragraphs: [
'ประเภทข้อมูลแตกต่างกันตามช่องทางที่ใช้และโมดูล/ฟีเจอร์ที่เปิดใช้งาน',
],
bullets: [
'พอร์ทัล NexStudio: ข้อมูลการท่องเว็บ (ล็อกทางเทคนิค IP user agent) ข้อมูลติดต่อที่ส่งโดยสมัครใจ (ชื่อ อีเมล บริษัท เนื้อหาข้อความ) การตั้งค่าคุกกี้ และเมตริกรวม',
'LexAura (Legal Tech): ข้อมูลบัญชีและโปรไฟล์ผู้ใช้ เมทาดาทาของ workspace เอกสารกฎหมายที่อัปโหลดหรือสร้าง คำขอข้อความไปยังเอนจินวิเคราะห์/สรุป ล็อกการตรวจสอบการเข้าถึงและการดำเนินการ',
'MediAura (Health Tech): ข้อมูลบัญชีและบทบาท เมทาดาทาองค์กร เอกสารด้านสุขภาพหรือธุรการ อินพุตเสียงและใบถอดเสียงที่เกี่ยวข้อง ล็อกทางเทคนิคและความปลอดภัย',
'ประเภทพิเศษตามมาตรา 9 GDPR: อาจเกิดขึ้นใน MediAura (และในบางกรณีใน LexAura) เมื่อลูกค้าอัปโหลดเนื้อหาที่มีข้อมูลสุขภาพหรือข้อมูลอ่อนไหวอื่น การประมวลผลเกิดขึ้นเฉพาะในขอบเขตของบริการและตามคำสั่งที่ได้รับจากลูกค้าผู้ควบคุม',
],
},
{
id: 'finalita-basi',
title: '4. วัตถุประสงค์และฐานทางกฎหมายของการประมวลผล',
paragraphs: [],
bullets: [
'การจัดการเว็บไซต์สถาบัน ความปลอดภัย และความต่อเนื่องในการดำเนินงาน (ฐาน: ประโยชน์โดยชอบด้วยกฎหมายด้านความปลอดภัย การป้องกันการใช้ในทางที่ผิด และการรักษบริการ)',
'การจัดการคำขอก่อนสัญญา การสาธิต การติดต่อเชิงพาณิชย์ และการสนับสนุน (ฐาน: มาตรการก่อนสัญญาตามคำขอของเจ้าของข้อมูล; ประโยชน์โดยชอบด้วยกฎหมายในการจัดการ B2B)',
'การให้บริการ SaaS LexAura และ MediAura การยืนยันตัวตนผู้ใช้ การจัดการเทแนนท์ และการสนับสนุนทางเทคนิค (ฐาน: การปฏิบัติตามสัญญากับลูกค้าธุรกิจ)',
'การปฏิบัติตามข้อกำกับ ภาระผูกพันด้านการบริหาร/ภาษี การจัดการข้อพิพาท และการป้องกันสิทธิ (ฐาน: ข้อผูกพันทางกฎหมายและประโยชน์โดยชอบด้วยกฎหมายในการคุ้มครองทางคดี)',
'การสื่อสารการตลาด/จดหมายข่าว หากเปิดใช้งาน (ฐาน: ความยินยอม เว้นแต่ข้อยกเว้น soft spam ที่กฎหมายอนุญาต)',
'การปรับปรุงบริการและการพัฒนาฟีเจอร์ AI: เฉพาะเมื่อสัญญาหรือคำสั่งที่เป็นลายลักษณ์อักษรกำหนดไว้ หากไม่มีฐานดังกล่าว จะไม่ใช้ข้อมูลเพื่อการฝึกโมเดลทั่วไป',
],
},
{
id: 'ruoli-saas',
title: '5. บทบาทความเป็นส่วนตัวเฉพาะสำหรับ LexAura และ MediAura',
paragraphs: [
'สำหรับบริการ SaaS แบบ B2B โดยปกติลูกค้าเป็นผู้กำหนดวัตถุประสงค์และวิธีการประมวลผลที่เกี่ยวข้องกับข้อมูลที่อัปโหลดในสภาพแวดล้อมแอปพลิเคชันของตน โดยทำหน้าที่เป็นผู้ควบคุม NexStudio โดยทั่วไปทำหน้าที่เป็นผู้ประมวลผลในนามของลูกค้า ตามการแต่งตั้งตามสัญญาและคำสั่งที่เป็นลายลักษณ์อักษร',
'หาก NexStudio กำหนดวัตถุประสงค์เฉพาะด้วยตนเอง (เช่น การป้องกันการฉ้อโกง ความปลอดภัยโครงสร้างพื้นฐาน เมตริกรวมที่ไม่สามารถโยงไปยังเจ้าของข้อมูลรายบุคคล) จะทำหน้าที่เป็นผู้ควบคุมอิสระสำหรับการประมวลผลดังกล่าว',
'ประกาศความเป็นส่วนตัวเฉพาะของ LexAura และ MediAura ที่เผยแพร่บนไซต์หรือสภาพแวดล้อมแอปพลิเคชันที่เกี่ยวข้อง จะให้รายละเอียดเพิ่มเติมเกี่ยวกับประเภทข้อมูล กระแสข้อมูล ระยะเวลาเก็บรักษา และฐานทางกฎหมายเฉพาะของแต่ละผลิตภัณฑ์',
],
},
{
id: 'conservazione',
title: '6. ระยะเวลาเก็บรักษา',
paragraphs: [],
bullets: [
'ข้อมูลติดต่อจากเว็บไซต์: 24 เดือนนับจากการโต้ตอบครั้งล่าสุด',
'ข้อมูลบัญชี SaaS: ตลอดระยะเวลาความสัมพันธ์ตามสัญญา + 30 วันนับจากการลบ',
'ล็อกทางเทคนิคและความปลอดภัย: สูงสุด 30 วัน',
'ข้อมูลการเรียกเก็บเงิน: 10 ปีตามที่กฎหมายภาษีอิตาลีกำหนด',
'ข้อมูลสุขภาพ: ตามคำสั่งของผู้ควบคุม (ลูกค้า) และข้อผูกพันทางกฎหมาย',
'ข้อมูลวิเคราะห์แบบรวม: ไม่มีกำหนดในรูปแบบนิรนาม',
],
},
{
id: 'destinatari',
title: '7. ผู้รับและประเภทของผู้ที่ได้รับอนุญาต',
paragraphs: [
'ข้อมูลอาจถูกประมวลผลโดยบุคลากร NexStudio ที่ได้รับอนุญาตและคำแนะนำ รวมถึงผู้ให้บริการที่มีคุณสมบัติซึ่งทำหน้าที่เป็นผู้ประมวลผล/ผู้ประมวลผลช่วง (เช่น การโฮสต์คลาวด์ บริการโครงสร้างพื้นฐาน การตรวจสอบความปลอดภัย เครื่องมือสื่อสาร)',
'รายชื่อผู้ประมวลผลและผู้ประมวลผลช่วงที่เกี่ยวข้องกับลูกค้า SaaS ที่เป็นปัจจุบัน จะจัดให้ผ่านช่องทางตามสัญญาหรือตามคำขอ',
],
},
{
id: 'trasferimenti',
title: '8. การโอนข้อมูลระหว่างประเทศ',
paragraphs: [
'เนื่องจากการใช้ผู้ให้บริการคลาวด์ระดับโลก การประมวลผลบางส่วนอาจเกี่ยวข้องกับการโอนไปยังประเทศนอก EEA/EU เมื่อกฎหมายกำหนด NexStudio จะใช้การคุ้มครองที่เหมาะสม (เช่น ข้อสัญญามาตรฐาน มาตรการทางเทคนิค/องค์กรเสริม การประเมินผลกระทบการโอน) และจำกัดการโอนเฉพาะข้อมูลที่จำเป็น',
'ข้อมูลจะถูกประมวลผลในเขตเศรษฐกิจยุโรปเป็นหลัก Cloudflare Inc. ปฏิบัติตาม EU-US Data Privacy Framework และใช้ Standard Contractual Clauses (SCC)',
],
},
{
id: 'sicurezza',
title: '9. มาตรการความปลอดภัยและการกำกับดูแล',
paragraphs: [
'NexStudio ใช้มาตรการทางเทคนิคและองค์กรที่สัดส่วนกับความเสี่ยง:',
],
bullets: [
'การควบคุมการเข้าถึงตามบทบาท การยืนยันตัวตน การแยกสภาพแวดล้อมลูกค้าเชิงตรรกะ และหลักการสิทธิ์น้อยที่สุด',
'การเข้ารหัสข้อมูลระหว่างส่ง (TLS 1.3) และขณะเก็บรักษา การจัดการคีย์อย่างปลอดภัย',
'การบันทึกล็อก การตรวจสอบ เส้นทางการตรวจสอบ และกระบวนการจัดการเหตุการณ์',
'กระบวนการสำรองข้อมูล ความต่อเนื่องในการดำเนินงาน และการกู้คืนจากภัยพิบัติ',
],
},
{
id: 'diritti',
title: '10. สิทธิของเจ้าของข้อมูลและวิธีการใช้สิทธิ',
paragraphs: [
'เจ้าของข้อมูลอาจใช้สิทธิตามกฎหมายที่ใช้บังคับ (เข้าถึง แก้ไข ลบ จำกัด คัดค้าน โอนย้าย ถอนความยินยอม ร้องเรียนต่อหน่วยงานที่มีอำนาจ)',
'สำหรับข้อมูลที่ประมวลผลในบริบท SaaS โดยปกติควรร้องขอไปยังลูกค้าผู้ควบคุมก่อน (เช่น สำนักงานกฎหมายหรือองค์กรด้านสุขภาพ) NexStudio สนับสนุนลูกค้าผู้ควบคุมตามสัญญา',
'สำหรับข้อมูลที่ NexStudio ประมวลผลในฐานะผู้ควบคุม (เช่น ข้อมูลติดต่อจากเว็บไซต์ ล็อกความปลอดภัยของตนเอง) สามารถส่งคำขอไปที่ privacy@nexstudio.com หรือผ่านแบบฟอร์มบน nexstudio.ai',
'หน่วยงานกำกับดูแลที่มีอำนาจ: Garante per la protezione dei dati personali (www.garanteprivacy.it)',
],
},
{
id: 'cookie-tracking',
title: '11. คุกกี้ การวิเคราะห์ และการติดตาม',
paragraphs: [
'ข้อมูลเกี่ยวกับคุกกี้ทางเทคนิค คุกกี้เชิงวิเคราะห์ (หากมี) และเครื่องมือติดตาม ระบุไว้ในนโยบายคุกกี้ ซึ่งควรอ่านร่วมกับประกาศฉบับนี้',
],
},
{
id: 'minori',
title: '12. การประมวลผลข้อมูลของผู้เยาว์',
paragraphs: [
'บริการของ NexStudio ออกแบบมาสำหรับผู้ใช้ระดับมืออาชีพและธุรกิจ ไม่ได้มุ่งเป้าไปที่ผู้เยาว์โดยเจตนา',
],
},
{
id: 'aggiornamenti',
title: '13. การอัปเดตนโยบาย',
paragraphs: [
'NexStudio อาจอัปเดตประกาศนี้เพื่อปรับตามข้อกำกับ การพัฒนาบริการ การเปลี่ยนแปลงองค์กร หรือการนำฟีเจอร์ใหม่มาใช้ การเปลี่ยนแปลงที่มีนัยสำคัญจะเผยแพร่บนหน้านี้พร้อมอัปเดตเวอร์ชันและวันที่',
],
},
] as const satisfies readonly PrivacySection[],
page: {
title: 'Privacy — NexStudio',
description:
'ประกาศความเป็นส่วนตัวกรอบของ NexStudio S.r.l.: เว็บไซต์สถาบัน การติดต่อองค์กร และการอ้างอิงรายละเอียดความเป็นส่วนตัวของ SaaS LexAura (Legal Tech) และ MediAura (Health Tech)',
heading: 'ประกาศความเป็นส่วนตัว',
lead:
'ประกาศกรอบเกี่ยวกับการประมวลผลข้อมูลส่วนบุคคลสำหรับพอร์ทัลสถาบันของ NexStudio S.r.l. สำนักงานจดทะเบียนในอิตาลี [INDIRIZZO_COMPLETO] และสำหรับบริการ SaaS LexAura และ MediAura ประกาศนี้แยกพอร์ทัลสถาบัน NexStudio จากบริการ SaaS LexAura และ MediAura ประกาศความเป็นส่วนตัวเฉพาะผลิตภัณฑ์ที่เผยแพร่บนพอร์ทัลแอปพลิเคชันที่เกี่ยวข้องยังคงเป็นเอกสารอ้างอิงรายละเอียดสำหรับการใช้ผลิตภัณฑ์',
versionLabel: 'เวอร์ชัน',
inForceFromLabel: 'มีผลตั้งแต่',
lastUpdateLabel: 'อัปเดตล่าสุด',
relatedDocsLabel: 'เอกสารที่เกี่ยวข้อง',
indexLabel: 'สารบัญ',
},
},
{
id: 'finalita-basi',
title: "4. Finalita' e basi giuridiche del trattamento",
paragraphs: [
"NexStudio tratta i dati personali per finalita' determinate e legittime, applicando la base giuridica adeguata al singolo trattamento.",
],
bullets: [
"Gestione del sito istituzionale, sicurezza e continuita' operativa (base: legittimo interesse alla sicurezza, prevenzione abusi e mantenimento del servizio).",
"Gestione richieste pre-contrattuali, demo, contatti commerciali e supporto (base: misure precontrattuali su richiesta dell'interessato; legittimo interesse nella gestione B2B).",
"Erogazione dei SaaS LexAura e MediAura, autenticazione utenti, gestione tenant e assistenza tecnica (base: esecuzione del contratto con il cliente business).",
"Conformita' normativa, adempimenti amministrativi/fiscali, gestione contenziosi e difesa di diritti (base: obbligo legale e legittimo interesse alla tutela in giudizio).",
"Comunicazioni marketing/newsletter, ove attive (base: consenso, salvo eccezioni di soft spam consentite dalla normativa applicabile).",
"Attivita' di miglioramento del servizio, quality assurance e sviluppo funzionalita' IA con dati del cliente solo se previsto dal contratto o da istruzioni documentate; in assenza di tale base i dati non sono utilizzati per training generalista.",
],
},
{
id: 'ruoli-saas',
title: '5. Ruoli privacy specifici per LexAura e MediAura',
paragraphs: [
"Per i servizi SaaS B2B, il cliente (studio legale, impresa, struttura sanitaria, professionista) determina normalmente finalita' e mezzi del trattamento relativi ai dati caricati nel proprio ambiente applicativo, agendo come titolare del trattamento.",
"NexStudio agisce di regola come responsabile del trattamento per conto del cliente, con nomina contrattuale e istruzioni documentate, limitando l'accesso ai dati al personale autorizzato che ne abbia necessita' operativa (supporto, manutenzione, sicurezza).",
"Qualora NexStudio determini autonomamente finalita' specifiche (es. prevenzione frodi, sicurezza infrastrutturale, metriche aggregate non riconducibili al singolo interessato), agisce come titolare autonomo per tali trattamenti.",
"Le informative privacy dedicate di LexAura e MediAura, pubblicate nei rispettivi siti o ambienti applicativi, approfondiscono categorie dati, flussi, tempi di retention e basi giuridiche specifiche del singolo prodotto.",
],
},
{
id: 'conservazione',
title: '6. Tempi di conservazione',
paragraphs: [
"I tempi di conservazione sono definiti in base a principio di minimizzazione, obblighi di legge e ciclo di vita del servizio. Durate puntuali possono essere regolate da contratto, configurazioni del cliente e policy interne.",
],
bullets: [
"Dati di contatto dal sito: per il tempo necessario alla gestione della richiesta e, se pertinente, per il successivo follow-up commerciale entro termini ragionevoli.",
"Dati di account SaaS: per tutta la durata del rapporto contrattuale e per il periodo tecnico-amministrativo necessario a chiusura, backup, audit e adempimenti post-contratto.",
"Log tecnici e di sicurezza: retention proporzionata a monitoraggio, incident response, prevenzione abusi e dimostrazione di conformita'.",
"Dati contrattuali/amministrativi: conservazione secondo i termini imposti dalla normativa fiscale, civilistica e regolatoria applicabile.",
],
},
{
id: 'destinatari',
title: '7. Destinatari e categorie di soggetti autorizzati',
paragraphs: [
"I dati possono essere trattati da personale NexStudio autorizzato e istruito, nonche' da fornitori qualificati che operano come responsabili/sub-responsabili (es. hosting cloud, servizi infrastrutturali, monitoraggio sicurezza, strumenti di comunicazione e assistenza).",
"L'elenco aggiornato dei responsabili e sub-responsabili rilevanti per i clienti SaaS e' messo a disposizione nei canali contrattuali o su richiesta, secondo quanto previsto dagli accordi in essere.",
],
},
{
id: 'trasferimenti',
title: '8. Trasferimenti internazionali di dati',
paragraphs: [
"In ragione della struttura operativa internazionale e dell'uso di fornitori cloud globali, alcuni trattamenti possono comportare trasferimenti verso Paesi extra SEE/UE.",
"Quando richiesto dalla normativa, NexStudio adotta garanzie adeguate (es. clausole contrattuali standard, misure supplementari tecniche/organizzative, valutazioni di impatto sul trasferimento) e limita il trasferimento ai soli dati necessari.",
],
},
{
id: 'sicurezza',
title: '9. Misure di sicurezza e governance',
paragraphs: [
"NexStudio adotta misure tecniche e organizzative proporzionate al rischio, con particolare attenzione ai contesti Legal Tech e Health Tech dove integrita', riservatezza e tracciabilita' sono requisiti essenziali.",
],
bullets: [
"Controlli di accesso basati su ruoli, autenticazione, segregazione logica degli ambienti cliente e principio del minimo privilegio.",
"Cifratura dei dati in transito e, ove previsto dall'architettura, a riposo; gestione sicura delle chiavi e dei segreti applicativi.",
"Logging, monitoraggio, audit trail e procedure di gestione incidenti con escalation interna e tracciamento delle azioni correttive.",
"Procedure di backup, continuita' operativa e disaster recovery in funzione del piano di servizio sottoscritto.",
],
},
{
id: 'diritti',
title: "10. Diritti degli interessati e modalita' di esercizio",
paragraphs: [
"Gli interessati possono esercitare i diritti previsti dalla normativa applicabile (accesso, rettifica, cancellazione, limitazione, opposizione, portabilita', revoca del consenso ove prestato, reclamo all'autorita' competente).",
"Per dati trattati nel contesto dei SaaS, la richiesta va di norma indirizzata prima al cliente titolare (es. studio legale o struttura sanitaria) che determina finalita' e mezzi del trattamento. NexStudio fornisce supporto al cliente titolare secondo contratto.",
"Per dati trattati da NexStudio come titolare (es. contatti dal sito, log di sicurezza propri), le richieste possono essere inviate ai recapiti privacy indicati in questa pagina o nella sezione contatti del sito.",
],
},
{
id: 'cookie-tracking',
title: '11. Cookie, analytics e tracciamenti',
paragraphs: [
"Le informazioni su cookie tecnici, eventuali cookie analitici e strumenti di tracciamento sono descritte nella Cookie Policy, da leggere congiuntamente alla presente informativa.",
"La gestione delle preferenze cookie avviene tramite banner/strumenti dedicati, con possibilita' di revisione successiva delle scelte quando tecnicamente disponibile.",
],
},
{
id: 'minori',
title: '12. Trattamento dati di minori',
paragraphs: [
"I servizi NexStudio sono progettati per un'utenza professionale e business. Non sono rivolti intenzionalmente a minori e non viene perseguita la raccolta volontaria di dati di minori al di fuori dei casi strettamente necessari e leciti nel contesto sanitario gestito dal cliente titolare.",
"Se viene rilevata una raccolta non appropriata di dati riferibili a minori al di fuori delle basi giuridiche consentite, NexStudio adotta le misure necessarie per la cancellazione o la regolarizzazione del trattamento, coordinandosi con il cliente titolare ove necessario.",
],
},
{
id: 'aggiornamenti',
title: '13. Aggiornamenti della policy',
paragraphs: [
"NexStudio puo' aggiornare questa informativa per adeguamenti normativi, evoluzione dei servizi, modifiche organizzative o introduzione di nuove funzionalita'.",
"Le modifiche rilevanti saranno pubblicate su questa pagina con aggiornamento di versione e data. Per i clienti SaaS, ulteriori notifiche possono essere fornite tramite canali contrattuali o in-app, quando previsto.",
],
},
] as const;
} as const;
export const getPrivacy = (locale: SupportedLocale) => privacyByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const privacyDocument = privacyByLocale.it.document;
export const privacyCrossReferences = privacyByLocale.it.crossReferences;
export const privacyRiferimenti = privacyByLocale.it.riferimenti;
export const privacySections = privacyByLocale.it.sections;

View File

@ -3,10 +3,7 @@
* I contratti B2B su LexAura, MediAura o servizi professionali restano strumenti separati.
* Aggiornare versione e data in sync con la pagina (box in testa).
*/
export const termsDocument = {
version: '1.0',
inVigoreDal: '22 aprile 2026',
} as const;
import type { SupportedLocale } from './home/navigation';
export type TermsSection = {
id: string;
@ -15,96 +12,343 @@ export type TermsSection = {
bullets?: readonly string[];
};
export const termsSections: readonly TermsSection[] = [
{
id: 'premessa',
title: '1. Premessa e ambito',
paragraphs: [
"Le presenti condizioni duso regolano laccesso e la consultazione del sito web di NexStudio (di seguito «NexStudio», «noi») e dei contenuti informativi resi disponibili online. La sede operativa di riferimento è a Bangkok, Thailandia; i servizi software commerciali (inclusi prodotti e soluzioni per Legal Tech e Health Tech, ad es. le linee LexAura e MediAura) sono regolati da condizioni generali, ordini, offerte o accordi specifici sottoscritti con i clienti: in caso di conflitto tra il presente testo e un contratto B2B sottoscritto, prevalgono i documenti specifici del servizio e gli accordi contrattuali applicabili.",
"Per il trattamento dei dati personali e i cookie si rimanda rispettivamente allinformativa privacy e alla cookie policy, citate più sotto.",
],
const termsByLocale = {
it: {
document: {
version: '1.1',
inVigoreDal: '[DATA]',
},
crossReferences: {
leadNote:
'Per LexAura e MediAura, le informative privacy dedicate pubblicate sui rispettivi portali SaaS restano il riferimento di dettaglio del singolo prodotto.',
},
riferimenti: [
{ label: 'Informativa privacy', href: '/privacy' },
{ label: 'Cookie policy', href: '/cookies' },
{ label: 'Diritti GDPR', href: '/gdpr' },
{ label: 'Codice etico', href: '/codice-etico' },
] as const,
sections: [
{
id: 'premessa',
title: '1. Premessa e ambito',
paragraphs: [
"Le presenti condizioni duso regolano laccesso e la consultazione del sito web di NexStudio S.r.l. (di seguito «NexStudio», «noi»), con sede legale in Italia, [INDIRIZZO_COMPLETO]. I servizi software commerciali (inclusi i prodotti LexAura e MediAura) sono regolati da condizioni generali, ordini, offerte o accordi specifici sottoscritti con i clienti: in caso di conflitto tra il presente testo e un contratto B2B sottoscritto, prevalgono i documenti specifici del servizio e gli accordi contrattuali applicabili.",
'Per il trattamento dei dati personali e i cookie si rimanda rispettivamente allinformativa privacy e alla cookie policy, citate più sotto.',
],
},
{
id: 'accettazione',
title: '2. Accettazione e requisiti',
paragraphs: [
'Lutilizzo del sito (navigazione oltre la mera apertura della home, invio di moduli ove presenti, uso di strumenti di contatto) implica la conoscenza e laccettazione delle presenti condizioni e della versione in vigore della privacy policy e, ove applicabile, della cookie policy.',
'Lutente dichiara di avere letà e la capacità legale per vincolarsi, o di agire sotto la responsabilità di un rappresentante legale idoneo.',
],
},
{
id: 'servizi',
title: '3. Contenuti e servizi informativi',
paragraphs: [
'Il sito ha finalità promozionali e informative su NexStudio, sui prodotti e sullorganizzazione. Le descrizioni, schermate, esempi e roadmap non costituiscono offerta vincolante né promessa di feature: restano subordinati a contrattualizzazione, disponibilità tecnica e condizioni di mercato.',
'NexStudio si impegna a mantenere le informazioni ragionevolmente aggiornate, senza obbligo di esaustività o di aggiornamento in tempo reale. Può modificare, sospendere o limitare laccesso a sezioni del sito per manutenzione, sicurezza o evoluzione del prodotto.',
],
},
{
id: 'uso-lecito',
title: '4. Uso lecito del sito',
paragraphs: [
'Lutente si impegna a usare il sito in conformità a legge, buona fede e condotta professionale, in particolare a:',
],
bullets: [
'non introdurre malware, né tentare di eludere misure di sicurezza, né effettuare attività che sovraccaricano in modo abnorme o automatizzato non autorizzato linfrastruttura;',
'non copiare o sfruttare illecitamente i contenuti (cfr. sotto proprietà intellettuale) né impersonare NexStudio o terzi;',
'non usare canali di contatto per contenuti illeciti, diffamatori, spam o al di fuori delle finalità richieste (commerciale, supporto, segnalazioni, ecc.).',
],
},
{
id: 'proprieta',
title: '5. Proprietà intellettuale',
paragraphs: [
'Testi, logotipi, elementi grafici, layout, nomi di prodotto (inclusi LexAura e MediAura) e, ove presenti, software dimostrativo o materiali scaricabili, sono protetti dalle norme sul diritto dautore e dalle norme sui marchi applicabili. È vietata la riproduzione, distribuzione o modifica non espressamente autorizzata, salvo uso consentito per legge (citazione, breve estratto con attribuzione) o espressa deroga scritta.',
],
},
{
id: 'terze-parti',
title: '6. Link, servizi di terzi e strumenti incorporati',
paragraphs: [
'Il sito può contenere collegamenti a siti o servizi di terzi. NexStudio non controlla tali risorse e non risponde dei loro contenuti o politiche; laccesso avviene sotto la responsabilità dellutente, che dovrà leggere le condizioni e linformativa privacy del terzo.',
],
},
{
id: 'responsabilita',
title: '7. Limitazione di responsabilità',
paragraphs: [
'Nella misura massima consentita dalla legge applicabile, il sito e i suoi contenuti sono forniti «così come sono» e «secondo disponibilità», senza garanzie implicite di adeguatezza a un fine particolare, non interruzione o assenza di errori. Il materiale informativo non sostituisce consulenza legale, sanitaria o professionale: decisioni e responsabilità restano a carico del visitatore o del professionista e dei propri clienti.',
'NexStudio non risponde per danni indiretti, consequenziali, mancati guadagni, perdita di dati, interruzioni, azioni di terzi o fatti oltre il controllo ragionevole, salvo quanto non possa essere escluso o limitato per legge imperativa.',
],
},
{
id: 'sospensione',
title: '8. Sospensione, violazione e forza maggiore',
paragraphs: [
'NexStudio può sospendere, limitare o interrompere laccesso in caso di violazione delle presenti condizioni, rischio per la sicurezza, ordini di autorità, o forza maggiore. La mancata esecuzione in tali casi non costituisce inadempimento a titolo di danno.',
],
},
{
id: 'legge',
title: '9. Legge applicabile e foro competente',
paragraphs: [
'Salvo disposizioni inderogabili a tutela del consumatore, le presenti condizioni e ogni contestazione inerente al sito informativo sono regolate dalla legge italiana. Il foro competente è Padova.',
],
},
{
id: 'contatti-termini',
title: '10. Contatti in merito a queste condizioni',
paragraphs: [
'Per domande sulle presenti condizioni duso del sito, utilizzare i canali indicati nella sezione contatti del sito. Per trattamento dei dati personali e diritti: privacy@nexstudio.com o il form su nexstudio.ai.',
],
},
] as const satisfies readonly TermsSection[],
page: {
title: 'Condizioni duso — NexStudio',
description:
'Condizioni duso del sito web NexStudio S.r.l.: accettazione, utilizzo lecito, responsabilità, legge italiana e foro di Padova. LexAura e MediAura, servizi B2B per contratto a parte.',
heading: 'Condizioni duso',
lead:
"Si applicano alla navigazione e all'uso del sito informativo pubblico di NexStudio S.r.l., con sede legale in Italia, [INDIRIZZO_COMPLETO]. I contratti d'abbonamento o d'acquisto sui prodotti software restano retti da offerte, ordini o accordi separati. Per LexAura e MediAura, le informative privacy dedicate pubblicate sui rispettivi portali SaaS restano il riferimento di dettaglio del singolo prodotto.",
versionLabel: 'Versione',
inForceFromLabel: 'In vigore dal',
lastUpdateLabel: 'Ultimo aggiornamento',
relatedDocsLabel: 'Documenti connessi',
indexLabel: 'Indice',
},
},
{
id: 'accettazione',
title: '2. Accettazione e requisiti',
paragraphs: [
"Lutilizzo del sito (navigazione oltre la mera apertura della home, invio di moduli ove presenti, uso di strumenti di contatto) implica la conoscenza e laccettazione delle presenti condizioni e della versione in vigore della privacy policy e, ove applicabile, della cookie policy.",
"Lutente dichiara di avere letà e la capacità legale per vincolarsi, o di agire sotto la responsabilità di un rappresentante legale idoneo.",
],
en: {
document: {
version: '1.1',
inVigoreDal: '[DATA]',
},
crossReferences: {
leadNote:
'For LexAura and MediAura, the dedicated privacy notices published on the respective SaaS portals remain the detailed reference for each product.',
},
riferimenti: [
{ label: 'Privacy notice', href: '/privacy' },
{ label: 'Cookie policy', href: '/cookies' },
{ label: 'GDPR rights', href: '/gdpr' },
{ label: 'Code of ethics', href: '/codice-etico' },
] as const,
sections: [
{
id: 'premessa',
title: '1. Preamble and scope',
paragraphs: [
'These terms of use govern access to and consultation of the website of NexStudio S.r.l. (hereinafter “NexStudio”, “we”), with registered office in Italy, [INDIRIZZO_COMPLETO]. Commercial software services (including the LexAura and MediAura products) are governed by general terms, orders, offers, or specific agreements entered into with customers: in case of conflict between this text and a signed B2B contract, the service-specific documents and applicable contractual agreements prevail.',
'For the processing of personal data and cookies, reference is made respectively to the privacy notice and the cookie policy cited below.',
],
},
{
id: 'accettazione',
title: '2. Acceptance and requirements',
paragraphs: [
'Use of the site (browsing beyond merely opening the home page, submitting forms where present, using contact tools) implies knowledge and acceptance of these terms and of the privacy policy in force and, where applicable, the cookie policy.',
'The user declares that they have the age and legal capacity to be bound, or that they act under the responsibility of a suitable legal representative.',
],
},
{
id: 'servizi',
title: '3. Informational content and services',
paragraphs: [
'The site has promotional and informational purposes regarding NexStudio, its products, and its organization. Descriptions, screenshots, examples, and roadmaps do not constitute a binding offer or a promise of features: they remain subject to contracting, technical availability, and market conditions.',
'NexStudio undertakes to keep information reasonably up to date, without any obligation of exhaustiveness or real-time updates. It may modify, suspend, or restrict access to sections of the site for maintenance, security, or product evolution.',
],
},
{
id: 'uso-lecito',
title: '4. Lawful use of the site',
paragraphs: [
'The user undertakes to use the site in accordance with law, good faith, and professional conduct, in particular to:',
],
bullets: [
'not introduce malware, attempt to circumvent security measures, or carry out activities that abnormally overload or involve unauthorized automation of the infrastructure;',
'not copy or unlawfully exploit content (see intellectual property below) or impersonate NexStudio or third parties;',
'not use contact channels for unlawful, defamatory, or spam content, or outside the requested purposes (commercial, support, reports, etc.).',
],
},
{
id: 'proprieta',
title: '5. Intellectual property',
paragraphs: [
'Texts, logos, graphic elements, layout, product names (including LexAura and MediAura) and, where present, demo software or downloadable materials are protected by applicable copyright and trademark laws. Reproduction, distribution, or modification not expressly authorized is prohibited, except for use permitted by law (citation, short excerpt with attribution) or an express written waiver.',
],
},
{
id: 'terze-parti',
title: '6. Links, third-party services, and embedded tools',
paragraphs: [
'The site may contain links to third-party sites or services. NexStudio does not control such resources and is not responsible for their content or policies; access is at the users responsibility, who must read the third partys terms and privacy notice.',
],
},
{
id: 'responsabilita',
title: '7. Limitation of liability',
paragraphs: [
'To the maximum extent permitted by applicable law, the site and its contents are provided “as is” and “as available”, without implied warranties of fitness for a particular purpose, uninterrupted availability, or absence of errors. Informational material does not replace legal, healthcare, or professional advice: decisions and responsibility remain with the visitor or the professional and their own clients.',
'NexStudio is not liable for indirect, consequential damages, lost profits, data loss, interruptions, third-party actions, or events beyond reasonable control, except where exclusion or limitation is not permitted by mandatory law.',
],
},
{
id: 'sospensione',
title: '8. Suspension, breach, and force majeure',
paragraphs: [
'NexStudio may suspend, restrict, or interrupt access in case of breach of these terms, security risk, orders of authorities, or force majeure. Non-performance in such cases does not constitute a breach giving rise to damages.',
],
},
{
id: 'legge',
title: '9. Governing law and competent court',
paragraphs: [
'Save for mandatory provisions protecting consumers, these terms and any dispute relating to the informational site are governed by Italian law. The competent court is Padua.',
],
},
{
id: 'contatti-termini',
title: '10. Contacts regarding these terms',
paragraphs: [
'For questions about these website terms of use, use the channels indicated in the contacts section of the site. For personal data processing and rights: privacy@nexstudio.com or the form on nexstudio.ai.',
],
},
] as const satisfies readonly TermsSection[],
page: {
title: 'Terms of use — NexStudio',
description:
'Terms of use of the NexStudio S.r.l. website: acceptance, lawful use, liability, Italian law and Padua court. LexAura and MediAura B2B services under separate contract.',
heading: 'Terms of use',
lead:
'These terms apply to browsing and use of the public informational website of NexStudio S.r.l., registered office in Italy, [INDIRIZZO_COMPLETO]. Subscription or purchase contracts for software products remain governed by separate offers, orders, or agreements. For LexAura and MediAura, the dedicated privacy notices published on the respective SaaS portals remain the detailed reference for each product.',
versionLabel: 'Version',
inForceFromLabel: 'In force from',
lastUpdateLabel: 'Last updated',
relatedDocsLabel: 'Related documents',
indexLabel: 'Contents',
},
},
{
id: 'servizi',
title: '3. Contenuti e servizi informativi',
paragraphs: [
"Il sito ha finalità promozionali e informative su NexStudio, sui prodotti e sullorganizzazione. Le descrizioni, schermate, esempi e roadmap non costituiscono offerta vincolante né promessa di feature: restano subordinati a contrattualizzazione, disponibilità tecnica e condizioni di mercato.",
"NexStudio si impegna a mantenere le informazioni ragionevolmente aggiornate, senza obbligo di esaustività o di aggiornamento in tempo reale. Può modificare, sospendere o limitare laccesso a sezioni del sito per manutenzione, sicurezza o evoluzione del prodotto.",
],
th: {
document: {
version: '1.1',
inVigoreDal: '[DATA]',
},
crossReferences: {
leadNote:
'สำหรับ LexAura และ MediAura ประกาศความเป็นส่วนตัวเฉพาะที่เผยแพร่บนพอร์ทัล SaaS ที่เกี่ยวข้องยังคงเป็นเอกสารอ้างอิงรายละเอียดของแต่ละผลิตภัณฑ์',
},
riferimenti: [
{ label: 'ประกาศความเป็นส่วนตัว', href: '/privacy' },
{ label: 'นโยบายคุกกี้', href: '/cookies' },
{ label: 'สิทธิ GDPR', href: '/gdpr' },
{ label: 'จรรยาบรรณ', href: '/codice-etico' },
] as const,
sections: [
{
id: 'premessa',
title: '1. คำนำและขอบเขต',
paragraphs: [
'เงื่อนไขการใช้งานฉบับนี้กำกับดูแลการเข้าถึงและการดูเว็บไซต์ของ NexStudio S.r.l. (ต่อไปนี้เรียก «NexStudio» «เรา») สำนักงานจดทะเบียนในอิตาลี [INDIRIZZO_COMPLETO] บริการซอฟต์แวร์เชิงพาณิชย์ (รวมถึงผลิตภัณฑ์ LexAura และ MediAura) อยู่ภายใต้เงื่อนไขทั่วไป คำสั่งซื้อ ข้อเสนอ หรือข้อตกลงเฉพาะที่ทำกับลูกค้า หากเกิดความขัดแย้งระหว่างข้อความนี้กับสัญญา B2B ที่ลงนามแล้ว ให้ยึดเอกสารเฉพาะของบริการและข้อตกลงตามสัญญาที่ใช้บังคับ',
'สำหรับการประมวลผลข้อมูลส่วนบุคคลและคุกกี้ ให้อ้างอิงประกาศความเป็นส่วนตัวและนโยบายคุกกี้ที่ระบุไว้ด้านล่างตามลำดับ',
],
},
{
id: 'accettazione',
title: '2. การยอมรับและคุณสมบัติ',
paragraphs: [
'การใช้เว็บไซต์ (การท่องเว็บนอกเหนือจากการเปิดหน้าแรก การส่งแบบฟอร์มหากมี การใช้เครื่องมือติดต่อ) ถือว่าทราบและยอมรับเงื่อนไขฉบับนี้ รวมถึงนโยบายความเป็นส่วนตัวที่ใช้บังคับ และนโยบายคุกกี้หากเกี่ยวข้อง',
'ผู้ใช้รับรองว่ามีอายุและความสามารถทางกฎหมายที่จะผูกพัน หรือดำเนินการภายใต้ความรับผิดชอบของตัวแทนทางกฎหมายที่เหมาะสม',
],
},
{
id: 'servizi',
title: '3. เนื้อหาและบริการข้อมูล',
paragraphs: [
'เว็บไซต์มีวัตถุประสงค์เพื่อส่งเสริมและให้ข้อมูลเกี่ยวกับ NexStudio ผลิตภัณฑ์ และองค์กร คำอธิบาย ภาพหน้าจอ ตัวอย่าง และ roadmap ไม่ถือเป็นข้อเสนอที่มีผลผูกพันหรือคำมั่นสัญญาฟีเจอร์: ยังขึ้นอยู่กับการทำสัญญา ความพร้อมทางเทคนิค และเงื่อนไขตลาด',
'NexStudio มุ่งมั่นรักษาข้อมูลให้เป็นปัจจุบันอย่างสมเหตุสมผล โดยไม่มีข้อผูกพันว่าต้องครบถ้วนหรืออัปเดตแบบเรียลไทม์ อาจแก้ไข ระงับ หรือจำกัดการเข้าถึงส่วนต่างๆ ของเว็บไซต์เพื่อบำรุงรักษา ความปลอดภัย หรือการพัฒนาผลิตภัณฑ์',
],
},
{
id: 'uso-lecito',
title: '4. การใช้เว็บไซต์โดยชอบด้วยกฎหมาย',
paragraphs: [
'ผู้ใช้ตกลงใช้เว็บไซต์ให้สอดคล้องกับกฎหมาย เจตนาดี และพฤติกรรมระดับมืออาชีพ โดยเฉพาะอย่างยิ่ง:',
],
bullets: [
'ไม่นำมัลแวร์เข้ามา ไม่พยายามหลีกเลี่ยงมาตรการความปลอดภัย และไม่ดำเนินกิจกรรมที่ทำให้โครงสร้างพื้นฐานรับภาระเกินปกติหรือใช้ระบบอัตโนมัติโดยไม่ได้รับอนุญาต;',
'ไม่คัดลอกหรือใช้ประโยชน์เนื้อหาโดยมิชอบ (ดูทรัพย์สินทางปัญญาด้านล่าง) และไม่แอบอ้างเป็น NexStudio หรือบุคคลที่สาม;',
'ไม่ใช้ช่องทางติดต่อเพื่อเนื้อหาที่ผิดกฎหมาย หมิ่นประมาท สแปม หรือนอกเหนือวัตถุประสงค์ที่ร้องขอ (เชิงพาณิชย์ การสนับสนุน การรายงาน ฯลฯ)',
],
},
{
id: 'proprieta',
title: '5. ทรัพย์สินทางปัญญา',
paragraphs: [
'ข้อความ โลโก้ องค์ประกอบกราฟิก เลย์เอาต์ ชื่อผลิตภัณฑ์ (รวมถึง LexAura และ MediAura) และซอฟต์แวร์สาธิตหรือวัสดุที่ดาวน์โหลดได้ (หากมี) ได้รับการคุ้มครองตามกฎหมายลิขสิทธิ์และเครื่องหมายการค้าที่ใช้บังคับ ห้ามทำซ้ำ แจกจ่าย หรือแก้ไขโดยไม่ได้รับอนุญาตอย่างชัดเจน เว้นแต่การใช้ที่กฎหมายอนุญาต (การอ้างอิง ข้อความสั้นพร้อมระบุแหล่งที่มา) หรือการผ่อนผันเป็นลายลักษณ์อักษร',
],
},
{
id: 'terze-parti',
title: '6. ลิงก์ บริการของบุคคลที่สาม และเครื่องมือที่ฝังไว้',
paragraphs: [
'เว็บไซต์อาจมีลิงก์ไปยังไซต์หรือบริการของบุคคลที่สาม NexStudio ไม่ได้ควบคุมทรัพยากรดังกล่าวและไม่รับผิดชอบต่อเนื้อหาหรือนโยบายของบุคคลนั้น การเข้าถึงเป็นความรับผิดชอบของผู้ใช้ ซึ่งต้องอ่านเงื่อนไขและประกาศความเป็นส่วนตัวของบุคคลที่สาม',
],
},
{
id: 'responsabilita',
title: '7. ข้อจำกัดความรับผิด',
paragraphs: [
'ในขอบเขตสูงสุดที่กฎหมายที่ใช้บังคับอนุญาต เว็บไซต์และเนื้อหาถูกจัดให้ «ตามสภาพ» และ «ตามความพร้อมใช้งาน» โดยไม่มีการรับประกันโดยนัยว่าเหมาะสมกับวัตถุประสงค์เฉพาะ ไม่มีการรับประกันว่าจะไม่ขัดข้อง หรือไม่มีข้อผิดพลาด เอกสารข้อมูลไม่ทดแทนคำปรึกษาด้านกฎหมาย สุขภาพ หรือวิชาชีพ: การตัดสินใจและความรับผิดชอบยังคงอยู่กับผู้เยี่ยมชมหรือผู้ประกอบวิชาชีพและลูกค้าของตน',
'NexStudio ไม่รับผิดต่อความเสียหายทางอ้อม ความเสียหายที่เป็นผลสืบเนื่อง กำไรที่สูญเสีย การสูญเสียข้อมูล การขัดข้อง การกระทำของบุคคลที่สาม หรือเหตุการณ์ที่อยู่นอกการควบคุมอย่างสมเหตุสมผล เว้นแต่สิ่งที่กฎหมายบังคับไม่อนุญาตให้ยกเว้นหรือจำกัด',
],
},
{
id: 'sospensione',
title: '8. การระงับ การละเมิด และเหตุสุดวิสัย',
paragraphs: [
'NexStudio อาจระงับ จำกัด หรือยุติการเข้าถึงในกรณีที่มีการละเมิดเงื่อนไขฉบับนี้ ความเสี่ยงด้านความปลอดภัย คำสั่งของหน่วยงาน หรือเหตุสุดวิสัย การไม่ปฏิบัติตามในกรณีดังกล่าวไม่ถือเป็นการผิดสัญญาที่ก่อให้เกิดค่าเสียหาย',
],
},
{
id: 'legge',
title: '9. กฎหมายที่ใช้บังคับและศาลที่มีอำนาจ',
paragraphs: [
'เว้นแต่บทบัญญัติที่ไม่อาจยกเว้นได้เพื่อคุ้มครองผู้บริโภค เงื่อนไขฉบับนี้และข้อพิพาทใดๆ ที่เกี่ยวกับเว็บไซต์ข้อมูลนี้อยู่ภายใต้กฎหมายอิตาลี ศาลที่มีอำนาจคือ Padova',
],
},
{
id: 'contatti-termini',
title: '10. การติดต่อเกี่ยวกับเงื่อนไขเหล่านี้',
paragraphs: [
'สำหรับคำถามเกี่ยวกับเงื่อนไขการใช้งานเว็บไซต์ฉบับนี้ ให้ใช้ช่องทางที่ระบุในส่วนติดต่อของเว็บไซต์ สำหรับการประมวลผลข้อมูลส่วนบุคคลและสิทธิ: privacy@nexstudio.com หรือแบบฟอร์มบน nexstudio.ai',
],
},
] as const satisfies readonly TermsSection[],
page: {
title: 'เงื่อนไขการใช้งาน — NexStudio',
description:
'เงื่อนไขการใช้งานเว็บไซต์ NexStudio S.r.l.: การยอมรับ การใช้โดยชอบ ความรับผิด กฎหมายอิตาลี และศาล Padova บริการ B2B LexAura และ MediAura อยู่ภายใต้สัญญาแยกต่างหาก',
heading: 'เงื่อนไขการใช้งาน',
lead:
'เงื่อนไขเหล่านี้ใช้กับการท่องเว็บและการใช้เว็บไซต์ข้อมูลสาธารณะของ NexStudio S.r.l. สำนักงานจดทะเบียนในอิตาลี [INDIRIZZO_COMPLETO] สัญญาสมัครสมาชิกหรือซื้อผลิตภัณฑ์ซอฟต์แวร์ยังคงอยู่ภายใต้ข้อเสนอ คำสั่งซื้อ หรือข้อตกลงแยกต่างหาก สำหรับ LexAura และ MediAura ประกาศความเป็นส่วนตัวเฉพาะที่เผยแพร่บนพอร์ทัล SaaS ที่เกี่ยวข้องยังคงเป็นเอกสารอ้างอิงรายละเอียดของแต่ละผลิตภัณฑ์',
versionLabel: 'เวอร์ชัน',
inForceFromLabel: 'มีผลตั้งแต่',
lastUpdateLabel: 'อัปเดตล่าสุด',
relatedDocsLabel: 'เอกสารที่เกี่ยวข้อง',
indexLabel: 'สารบัญ',
},
},
{
id: 'uso-lecito',
title: '4. Uso lecito del sito',
paragraphs: [
"Lutente si impegna a usare il sito in conformità a legge, buona fede e condotta professionale, in particolare a:",
],
bullets: [
'non introdurre malware, né tentare di eludere misure di sicurezza, né effettuare attività che sovraccaricano in modo abnorme o automatizzato non autorizzato linfrastruttura;',
'non copiare o sfruttare illecitamente i contenuti (cfr. sotto proprietà intellettuale) né impersonare NexStudio o terzi;',
"non usare canali di contatto per contenuti illeciti, diffamatori, spam o al di fuori delle finalità richieste (commerciale, supporto, segnalazioni, ecc.).",
],
},
{
id: 'proprieta',
title: '5. Proprietà intellettuale',
paragraphs: [
"Testi, logotipi, elementi grafici, layout, nomi di prodotto (inclusi LexAura e MediAura) e, ove presenti, software dimostrativo o materiali scaricabili, sono protetti dalle norme sul diritto dautore e dalle norme sui marchi applicabili. È vietata la riproduzione, distribuzione o modifica non espressamente autorizzata, salvo uso consentito per legge (citazione, breve estratto con attribuzione) o espressa deroga scritta.",
],
},
{
id: 'terze-parti',
title: '6. Link, servizi di terzi e strumenti incorporati',
paragraphs: [
"Il sito può contenere collegamenti a siti o servizi di terzi. NexStudio non controlla tali risorse e non risponde dei loro contenuti o politiche; laccesso avviene sotto la responsabilità dellutente, che dovrà leggere le condizioni e linformativa privacy del terzo.",
],
},
{
id: 'responsabilita',
title: '7. Limitazione di responsabilità',
paragraphs: [
"Nella misura massima consentita dalla legge applicabile, il sito e i suoi contenuti sono forniti «così come sono» e «secondo disponibilità», senza garanzie implicite di adeguatezza a un fine particolare, non interruzione o assenza di errori. Il materiale informativo non sostituisce consulenza legale, sanitaria o professionale: decisioni e responsabilità restano a carico del visitatore o del professionista e dei propri clienti.",
"NexStudio non risponde per danni indiretti, consequenziali, mancati guadagni, perdita di dati, interruzioni, azioni di terzi o fatti oltre il controllo ragionevole, salvo quanto non possa essere escluso o limitato per legge imperativa.",
],
},
{
id: 'sospensione',
title: '8. Sospensione, violazione e forza maggiore',
paragraphs: [
"NexStudio può sospendere, limitare o interrompere laccesso in caso di violazione delle presenti condizioni, rischio per la sicurezza, ordini di autorità, o forza maggiore. La mancata esecuzione in tali casi non costituisce inadempimento a titolo di danno.",
],
},
{
id: 'legge',
title: '9. Legge applicabile e foro (da confermare con il legale)',
paragraphs: [
"Salvo disposizioni inderogabili, le presenti condizioni e ogni contestazione inerente al sito informativo sono regolate dalla legge indicata qui sotto, con foro competente ove ammesso (da definire in base alla entità operativa, ai clienti e al mercato: es. Tailandia, o altra scelta con parere legale).",
],
},
{
id: 'contatti-termini',
title: '10. Contatti in merito a queste condizioni',
paragraphs: [
"Per domande sulle presenti condizioni duso del sito, utilizzare i contatti e i canali indicati nel sito. Per trattamento dei dati personali e diritti, seguire linformativa privacy; per cookie, la cookie policy.",
],
},
] as const;
export const termsRiferimenti: readonly { label: string; href: string }[] = [
{ label: 'Informativa privacy', href: '/privacy' },
{ label: 'Cookie policy', href: '/cookies' },
{ label: 'Diritti GDPR', href: '/gdpr' },
{ label: 'Codice etico', href: '/codice-etico' },
] as const;
export const termsCrossReferences = {
leadNote:
'I documenti collegati vanno allineati alla stessa data di revisione ove si tratti di un pacchetto unico di pubblicazione; per LexAura e MediAura, le informative privacy dedicate pubblicate sui portali SaaS restano il riferimento di dettaglio del singolo prodotto.',
} as const;
export const getTerms = (locale: SupportedLocale) => termsByLocale[locale];
/** Retro-compatibilità: fallback italiano. */
export const termsDocument = termsByLocale.it.document;
export const termsCrossReferences = termsByLocale.it.crossReferences;
export const termsRiferimenti = termsByLocale.it.riferimenti;
export const termsSections = termsByLocale.it.sections;

View File

@ -1,5 +1,6 @@
---
import '../styles/global.css';
import globalStylesheetHref from '../styles/global.css?url';
import CookieConsent from '../components/CookieConsent.astro';
import ChatGateModal from '../components/ChatGateModal.astro';
@ -15,17 +16,29 @@ const {
const pathname = Astro.url.pathname;
const localeMatch = pathname.match(/^\/(en|th)(\/|$)/);
const htmlLang = localeMatch?.[1] ?? 'it';
const canonicalUrl = Astro.url.toString();
---
<!doctype html>
<html lang={htmlLang} class="bg-nx-bg">
<html lang={htmlLang} class="bg-nx-bg" translate="no">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content={description} />
<meta name="google" content="notranslate" />
<link rel="canonical" href={canonicalUrl} />
<meta name="theme-color" content="#030712" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="NexStudio" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:url" content={canonicalUrl} />
<meta name="twitter:card" content="summary" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<link rel="stylesheet" href={globalStylesheetHref} />
<title>{title}</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
@ -34,7 +47,7 @@ const htmlLang = localeMatch?.[1] ?? 'it';
rel="stylesheet"
/>
</head>
<body class="min-h-screen">
<body class="notranslate min-h-screen">
<slot />
<CookieConsent />
<ChatGateModal />

View File

@ -1,6 +1,7 @@
---
import BaseLayout from './BaseLayout.astro';
import SiteFooter from '../components/SiteFooter.astro';
import { getNavigation } from '../data/home';
interface Props {
title: string;
@ -29,7 +30,8 @@ const printDate = new Intl.DateTimeFormat('it-IT', {
const printSource = Astro.url.href;
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = localeMatch?.[1] ?? 'it';
const currentLocale = (localeMatch?.[1] ?? 'it') as 'it' | 'en' | 'th';
const navigation = getNavigation(currentLocale);
const strippedPath = path.replace(/^\/(en|th)(?=\/|$)/, '') || '/';
const localeHref = (locale: 'it' | 'en' | 'th') => {
if (locale === 'it') return strippedPath;
@ -71,7 +73,7 @@ const langMeta = {
<details class="relative" data-lang-dropdown>
<summary
class="inline-flex h-10 cursor-pointer list-none items-center gap-2 rounded-lg border border-nx-border/80 bg-nx-bg/50 px-3 text-xs font-medium text-nx-fg transition-colors hover:border-sky-500/35 hover:text-white"
aria-label="Seleziona lingua"
aria-label={navigation.labels.languageSelectorAria}
>
<img src={langMeta[currentLocale].flagSrc} alt="" class="h-3.5 w-5 rounded-[2px] object-cover" loading="lazy" decoding="async" />
<span>{langMeta[currentLocale].code}</span>
@ -129,7 +131,7 @@ const langMeta = {
</details>
<a
class="text-sm font-medium text-sky-400/90 hover:text-sky-300"
href={localize('/#contatti')}>Contattaci</a
href={localize('/#contatti')}>{navigation.items[4]?.label ?? 'Contattaci'}</a
>
</div>
</div>

47
src/lib/email.ts Normal file
View File

@ -0,0 +1,47 @@
/**
* Outbound email via Cloudflare Email Service (`send_email` binding).
*/
import { env as workerEnv } from 'cloudflare:workers';
export type RuntimeEnv = Record<string, unknown>;
type EmailBinding = {
send: (message: {
to: string;
from: string;
subject: string;
text?: string;
html?: string;
replyTo?: string;
}) => Promise<{ messageId: string }>;
};
export function getEmailBinding(runtimeEnv?: RuntimeEnv): EmailBinding | null {
const env = (runtimeEnv ?? (workerEnv as unknown as RuntimeEnv)) as RuntimeEnv;
const binding = env.EMAIL;
if (binding && typeof binding === 'object' && 'send' in binding) {
return binding as EmailBinding;
}
return null;
}
export async function sendCloudflareEmail(
email: EmailBinding,
options: {
fromEmail: string;
toEmail: string;
subject: string;
text: string;
html?: string;
replyTo?: string;
},
): Promise<void> {
await email.send({
from: options.fromEmail,
to: options.toEmail,
subject: options.subject,
text: options.text,
...(options.html ? { html: options.html } : {}),
...(options.replyTo ? { replyTo: options.replyTo } : {}),
});
}

109
src/lib/newsletter.ts Normal file
View File

@ -0,0 +1,109 @@
/**
* Shared helpers for newsletter API routes (Cloudflare Worker / Astro).
*/
import { env as workerEnv } from 'cloudflare:workers';
export type RuntimeEnv = Record<string, unknown>;
export type SubscriberStatus = 'pending' | 'active' | 'unsubscribed';
export type NewsletterSubscriber = {
id: number;
email: string;
status: SubscriberStatus;
locale: string;
confirm_token: string | null;
unsubscribe_token: string;
source: string;
created_at: string;
confirmed_at: string | null;
unsubscribed_at: string | null;
};
/** Astro v6+: use `cloudflare:workers` env (locals.runtime.env was removed). */
export function getRuntimeEnv(_locals?: unknown): RuntimeEnv {
return workerEnv as unknown as RuntimeEnv;
}
export function readEnv(name: string, runtimeEnv: RuntimeEnv): string {
const runtimeValue = runtimeEnv[name];
if (typeof runtimeValue === 'string' && runtimeValue.trim().length > 0) {
return runtimeValue.trim();
}
const staticValue = import.meta.env[name];
return typeof staticValue === 'string' ? staticValue.trim() : '';
}
export function getNewsletterDb(runtimeEnv: RuntimeEnv): D1Database | null {
const db = runtimeEnv.NEWSLETTER_DB;
if (db && typeof db === 'object' && 'prepare' in db) {
return db as D1Database;
}
return null;
}
export function sanitizeRedirect(candidate: string, requestUrl: URL): string {
if (!candidate) return '';
try {
const u = new URL(candidate, requestUrl);
if (u.origin !== requestUrl.origin) return '';
return u.pathname + u.search + u.hash;
} catch {
return '';
}
}
export function required(value: FormDataEntryValue | null): string {
return typeof value === 'string' ? value.trim() : '';
}
export function isValidEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) && email.length <= 254;
}
export function normalizeEmail(email: string): string {
return email.trim().toLowerCase();
}
export function createToken(): string {
const bytes = new Uint8Array(32);
crypto.getRandomValues(bytes);
return [...bytes].map((b) => b.toString(16).padStart(2, '0')).join('');
}
export async function verifyTurnstile(
token: string,
secret: string,
ip?: string | null,
): Promise<boolean> {
const body = new URLSearchParams();
body.set('secret', secret);
body.set('response', token);
if (ip) body.set('remoteip', ip);
const response = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
method: 'POST',
body,
});
if (!response.ok) return false;
const payload = (await response.json()) as { success?: boolean };
return payload.success === true;
}
export { getEmailBinding, sendCloudflareEmail } from './email';
export function siteOrigin(requestUrl: URL, runtimeEnv: RuntimeEnv): string {
const configured = readEnv('PUBLIC_SITE_URL', runtimeEnv) || readEnv('SITE', runtimeEnv);
if (configured) {
try {
return new URL(configured).origin;
} catch {
/* fall through */
}
}
return requestUrl.origin;
}
export function absoluteUrl(origin: string, path: string): string {
return new URL(path, origin).toString();
}

View File

@ -1,83 +1,40 @@
---
import SubpageLayout from '../layouts/SubpageLayout.astro';
import type { SupportedLocale } from '../data/home/navigation';
import { getAbout } from '../data/about';
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = (localeMatch?.[1] ?? 'it') as SupportedLocale;
const about = getAbout(currentLocale);
const { page, intro, sections } = about;
---
<SubpageLayout
title="Chi siamo — NexStudio"
description="Missione, valori e come lavoriamo con studi legali e strutture sanitarie: qualità, verticalità e impegno nel tempo."
heading="Chi siamo"
lead="Siamo un team internazionale — tra Bangkok e lItalia — che unisce ingegneria del software, intelligenza artificiale applicata e conoscenza dei domini regolamentati, per strumenti professionali usabili e sostenibili nel tempo."
title={page.title}
description={page.description}
heading={page.heading}
lead={page.lead}
>
<p>
Ci impegniamo a rendere la tecnologia invisibile nel lavoro quotidiano. Progettiamo
<strong>software che assume su di sé</strong> processi strutturati, controlli e
complessità computazionale, così che chi lavora su norme, pazienti o documentazione
possa concentrarsi su <strong>giudizio, responsabilità e relazione</strong> — non su
menu, formati o incertezze operative. Nascondere la complessità sotto
<strong>interfaccia essenziale, tempi reattivi e tracciabilità</strong> è al centro di
come disegniamo il prodotto, non un optional estetico.
</p>
<p>{intro}</p>
<h2>La nostra missione</h2>
<p>
Operiamo dove un errore pesa: <strong>Legal Tech e Health Tech</strong>. Qui servono
precisione, tracciamento delle scelte e rispetto dei vincoli normativi, non
sperimenti generici. La nostra missione è fornire <strong>piattaforme SaaS mature</strong>
— come <strong>LexAura</strong> e <strong>MediAura</strong> — che integrino modelli
dintelligenza artificiale in flussi già vissuti dallo studio o dalla struttura, con
continuità tra automazione, revisione umana e responsabilità riconoscibile.
</p>
<h2>Perché possiamo meritare la tua fiducia</h2>
<p>
La fiducia non si ottiene con un motto, ma con <strong>ingegneria, chiarezza e
continuità</strong>. Significa architetture provate, aggiornamenti regolari dei
modelli danalisi, assistenza e formazione laddove ladozione chiede metodo oltre
al click. Significa anche onestà sui confini: lIA nelle nostre offerte è pensata
per <strong>estendere e accelerare</strong> il lavoro professionale, lasciando a
avvocato, medico o organizzazione <strong>la decisione finale e la
documentazione</strong> che la norma richiede.
</p>
<h2>Come lavoriamo, in sintesi</h2>
<ul>
<li>
<strong>Verticalità e dominio.</strong> Less genericismo, più requisiti operativi
(normative, flussi, audit) incorporati fin dalla progettazione.
</li>
<li>
<strong>Qualità e sicurezza come priorità, non retorica.</strong> Ingegneria, test
e infrastrutture coerenti con lesposizione a dati sensibili.
</li>
<li>
<strong>Supporto e evoluzione.</strong> Il rapporto con il cliente non finisce
allabbonamento: manutenzione evolutiva, assistenza e procedure chiare in caso di
urgenze operative, dove serve.
</li>
<li>
<strong>Comunicazione trasparente.</strong> Documentazione, informativa privacy
(dove obbligo) e <strong>aspettative reciproche</strong> esplicite, perché
lonboarding non diventi il primo ostacolo.
</li>
</ul>
<h2>Team e cultura</h2>
<p>
Siamo developer, product e specialisti di dominio in un <strong>modello
distribuito</strong>: fusi orari diversi e linguaggi comuni, un solo obiettivo
prodotto. Ci muoviamo con <strong>iterazioni rapide</strong> laddove serve
innovazione, e con <strong>governance</strong> laddove un cambiamento impatta
validazione, conformità o continuità del servizio. La diversità del gruppo
(competenze, contesti) è per noi vantaggio competitivo: riduce lassunto implicito
e costringe a rendere esplicito ciò che il software promette, e a chi.
</p>
<h2>Impegno nel tempo</h2>
<p>
Il settore che ci sceglie non cerca un prototipo, ma <strong>un percorso
sostenibile</strong>: allineamento normativo, adozione in team, affiancamento quando
qualcosa non gira. Costruiamo e manteniamo il prodotto con quella lente, perché
nelle vostre professioni la <strong>continuità</strong> non è comodità, è
requisito.
</p>
{
sections.map((s) => (
<>
<h2>{s.title}</h2>
{s.paragraphs?.map((p) => (
<p>{p}</p>
))}
{s.bullets && s.bullets.length > 0 ? (
<ul>
{s.bullets.map((b) => (
<li>
<strong>{b.title}</strong> {b.text}
</li>
))}
</ul>
) : null}
</>
))
}
</SubpageLayout>

109
src/pages/allegati.astro Normal file
View File

@ -0,0 +1,109 @@
---
import SubpageLayout from '../layouts/SubpageLayout.astro';
import { getAllegatiCodiceEtico } from '../data/allegati-codice-etico';
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = (localeMatch?.[1] ?? 'it') as 'it' | 'en' | 'th';
const a = getAllegatiCodiceEtico(currentLocale);
const { page, allegati } = a;
const link = 'text-sky-400/90 underline-offset-2 hover:text-sky-300 hover:underline';
const isLabeledFields = (item: unknown): item is { label: string; fields: string[] } =>
typeof item === 'object' && item !== null && 'label' in item && 'fields' in item;
const isLabeledText = (item: unknown): item is { label: string; text: string } =>
typeof item === 'object' && item !== null && 'label' in item && 'text' in item;
---
<SubpageLayout
title={page.title}
description={page.description}
heading={page.heading}
lead={page.lead}
>
<p class="mb-8 text-sm">
<a class={link} href={`/${currentLocale === 'it' ? '' : currentLocale + '/'}codice-etico`}>
{page.backLabel}
</a>
</p>
<div class="space-y-16">
{
allegati.map((allegato) => (
<section id={allegato.id} class="scroll-mt-8">
<h2 class="border-b border-nx-border pb-2 text-xl font-bold text-nx-fg">
{allegato.heading}
</h2>
<p class="mt-1 text-sm text-nx-muted">{allegato.description}</p>
{allegato.intro ? (
<p class="mt-4 text-sm italic text-nx-muted/90">{allegato.intro}</p>
) : null}
<div class="mt-6 space-y-6">
{
allegato.sections.map((section) => (
<div>
{section.title ? (
<h3 class="mb-3 text-base font-semibold text-nx-fg">{section.title}</h3>
) : null}
{section.intro ? (
<p class="mb-3 text-sm text-nx-muted">{section.intro}</p>
) : null}
{Array.isArray(section.content) ? (
section.content.length > 0 && typeof section.content[0] === 'string' ? (
<ul class="list-disc space-y-1.5 pl-5 text-sm leading-relaxed sm:text-base">
{(section.content as string[]).map((item) => (
<li>{item}</li>
))}
</ul>
) : section.content.length > 0 && isLabeledFields(section.content[0]) ? (
<dl class="space-y-3">
{(section.content as { label: string; fields: string[] }[]).map((item) => (
<div class="border-l-2 border-nx-border pl-4">
<dt class="text-sm font-medium text-nx-fg">{item.label}</dt>
<dd class="mt-0.5 font-mono text-sm text-nx-muted">
{item.fields.join(' | ')}
</dd>
</div>
))}
</dl>
) : section.content.length > 0 && isLabeledText(section.content[0]) ? (
<dl class="space-y-3">
{(section.content as { label: string; text: string }[]).map((item) => (
<div class="border-l-2 border-nx-border pl-4">
<dt class="text-sm font-semibold text-nx-fg">{item.label}</dt>
<dd class="mt-0.5 text-sm leading-relaxed text-nx-muted">{item.text}</dd>
</div>
))}
</dl>
) : (
<p class="text-sm text-nx-muted">{String(section.content)}</p>
)
) : (
<p class="text-sm leading-relaxed sm:text-base">{String(section.content)}</p>
)}
{section.note ? (
<p class="mt-3 border-l-2 border-sky-500/30 pl-4 text-sm leading-relaxed text-nx-muted">
{section.note}
</p>
) : null}
</div>
))
}
</div>
{allegato.footer ? (
<p class="mt-8 border-t border-nx-border pt-4 text-sm text-nx-fg">
{allegato.footer}
</p>
) : null}
</section>
))
}
</div>
</SubpageLayout>

258
src/pages/api/contact.ts Normal file
View File

@ -0,0 +1,258 @@
import type { APIRoute } from 'astro';
import { env as workerEnv } from 'cloudflare:workers';
import { getEmailBinding, sendCloudflareEmail } from '../../lib/email';
export const prerender = false;
type RuntimeEnv = Record<string, unknown>;
function getRuntimeEnv(): RuntimeEnv {
return workerEnv as unknown as RuntimeEnv;
}
function readEnv(name: string, runtimeEnv: RuntimeEnv): string {
const runtimeValue = runtimeEnv[name];
if (typeof runtimeValue === 'string' && runtimeValue.trim().length > 0) {
return runtimeValue.trim().replace(/^['"]+|['"]+$/g, '').trim();
}
const staticValue = import.meta.env[name];
return typeof staticValue === 'string'
? staticValue.trim().replace(/^['"]+|['"]+$/g, '').trim()
: '';
}
function looksLikeEmail(value: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
}
function sanitizeRedirect(candidate: string, requestUrl: URL): string {
if (!candidate) return '';
try {
const u = new URL(candidate, requestUrl);
return u.toString();
} catch {
return '';
}
}
function required(value: FormDataEntryValue | null): string {
return typeof value === 'string' ? value.trim() : '';
}
function wantsJson(request: Request): boolean {
return (request.headers.get('Accept') || '').includes('application/json');
}
const USER_SEND_ERROR =
'Impossibile inviare il messaggio in questo momento. Riprova più tardi.';
function fail(request: Request, message: string, status: number): Response {
if (wantsJson(request)) {
return Response.json({ ok: false, error: message }, { status });
}
return new Response(message, { status });
}
function succeed(request: Request, redirectTarget: string): Response {
if (wantsJson(request)) {
return Response.json({ ok: true, redirect: redirectTarget });
}
return Response.redirect(redirectTarget, 303);
}
function emailErrorCode(error: unknown): string {
if (error && typeof error === 'object' && 'code' in error) {
return String((error as { code?: unknown }).code || '');
}
return '';
}
function userEmailErrorMessage(error: unknown): string {
const code = emailErrorCode(error);
switch (code) {
case 'E_RATE_LIMIT_EXCEEDED':
case 'E_DAILY_LIMIT_EXCEEDED':
return 'Troppe richieste al momento. Riprova tra poco.';
default:
return USER_SEND_ERROR;
}
}
async function verifyTurnstile(token: string, secret: string, ip?: string | null): Promise<boolean> {
const body = new URLSearchParams();
body.set('secret', secret);
body.set('response', token);
if (ip) body.set('remoteip', ip);
const response = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
method: 'POST',
body,
});
if (!response.ok) return false;
const payload = (await response.json()) as { success?: boolean };
return payload.success === true;
}
/**
* Destinatario per area. Override solo se l'indirizzo è valido;
* altrimenti usa sempre CONTACT_FORM_TO_EMAIL (evita secret INFO/PRIVACY malformati).
*/
function resolveDepartmentRecipient(
runtimeEnv: RuntimeEnv,
defaultRecipient: string,
department: string,
): string {
const normalized = department.trim().toLowerCase();
if (!normalized || normalized === 'info') {
return defaultRecipient;
}
const override =
normalized === 'privacy'
? readEnv('CONTACT_FORM_TO_EMAIL_PRIVACY', runtimeEnv)
: normalized === 'careers'
? readEnv('CONTACT_FORM_TO_EMAIL_CAREERS', runtimeEnv)
: '';
return looksLikeEmail(override) ? override : defaultRecipient;
}
export const POST: APIRoute = async ({ request, url }) => {
try {
const runtimeEnv = getRuntimeEnv();
const mode = readEnv('CONTACT_FORM_MODE', runtimeEnv).toLowerCase() || 'dev';
// Preferisci il mittente newsletter (secret già usato con successo) sul Variable del form.
const fromEmail =
readEnv('NEWSLETTER_FROM_EMAIL', runtimeEnv) ||
readEnv('CONTACT_FORM_FROM_EMAIL', runtimeEnv);
const toEmail =
readEnv('CONTACT_FORM_TO_EMAIL', runtimeEnv) ||
readEnv('NEWSLETTER_NOTIFY_TO_EMAIL', runtimeEnv);
const turnstileSecret = readEnv('CONTACT_TURNSTILE_SECRET_KEY', runtimeEnv);
const form = await request.formData();
// Honeypot rimosso dal form (Turnstile basta). Se un client legacy lo manda, ignoralo.
const redirectTarget =
sanitizeRedirect(required(form.get('_next')), url) ||
sanitizeRedirect(readEnv('PUBLIC_CONTACT_FORM_SUCCESS_REDIRECT', runtimeEnv), url) ||
sanitizeRedirect(request.headers.get('referer') ?? '/', url) ||
new URL('/#contatti', url).toString();
const name = required(form.get('name'));
const company = required(form.get('company'));
const email = required(form.get('email'));
const country = required(form.get('country'));
const department = required(form.get('department')) || 'info';
const message = required(form.get('message'));
if (!name || !email || !country || !message) {
return fail(request, 'Compila tutti i campi obbligatori.', 400);
}
const turnstileToken = required(form.get('cf-turnstile-response'));
if (turnstileSecret) {
const isHuman = await verifyTurnstile(
turnstileToken,
turnstileSecret,
request.headers.get('CF-Connecting-IP'),
);
if (!isHuman) {
return fail(
request,
'Completa il controllo anti-spam e riprova.',
400,
);
}
}
const subject = required(form.get('_subject')) || 'Richiesta contatto - sito NexStudio';
const text = [
`Nome: ${name}`,
company ? `Azienda: ${company}` : '',
`Email: ${email}`,
`Paese: ${country}`,
`Reparto: ${department}`,
'',
message,
]
.filter(Boolean)
.join('\n');
if (mode === 'live') {
const emailBinding = getEmailBinding(runtimeEnv);
if (!emailBinding || !fromEmail || !toEmail) {
const missing = [
!emailBinding ? 'EMAIL binding' : '',
!fromEmail ? 'CONTACT_FORM_FROM_EMAIL|NEWSLETTER_FROM_EMAIL' : '',
!toEmail ? 'CONTACT_FORM_TO_EMAIL|NEWSLETTER_NOTIFY_TO_EMAIL' : '',
].filter(Boolean);
console.error('[contact] incomplete email config', { missing });
return fail(request, USER_SEND_ERROR, 500);
}
const destination = resolveDepartmentRecipient(runtimeEnv, toEmail, department);
if (!looksLikeEmail(fromEmail) || !looksLikeEmail(destination)) {
console.error('[contact] invalid from/to email shape', {
fromOk: looksLikeEmail(fromEmail),
toOk: looksLikeEmail(destination),
department,
});
return fail(request, USER_SEND_ERROR, 500);
}
try {
await sendCloudflareEmail(emailBinding, {
fromEmail,
toEmail: destination,
subject,
text,
replyTo: email,
});
} catch (error) {
console.error('[contact] email failed', {
code: emailErrorCode(error),
fromDomain: fromEmail.split('@')[1] || '',
toDomain: destination.split('@')[1] || '',
department,
error,
});
// Retry without replyTo: some payloads reject Reply-To even when From/To are valid.
try {
await sendCloudflareEmail(emailBinding, {
fromEmail,
toEmail: destination,
subject,
text,
});
} catch (retryError) {
console.error('[contact] email retry failed', {
code: emailErrorCode(retryError),
fromDomain: fromEmail.split('@')[1] || '',
toDomain: destination.split('@')[1] || '',
error: retryError,
});
return fail(request, userEmailErrorMessage(retryError), 502);
}
}
} else {
const isProdHost =
url.hostname === 'nexstudio.ai' ||
url.hostname.endsWith('.nexstudio.ai') ||
url.hostname.endsWith('.workers.dev');
console.warn('[contact] CONTACT_FORM_MODE is not live', { mode, host: url.hostname });
if (isProdHost) {
return fail(request, USER_SEND_ERROR, 503);
}
console.info('[contact:dev] Contact payload received', {
name,
company,
email,
country,
department,
subject,
});
}
return succeed(request, redirectTarget);
} catch (error) {
console.error('[contact] unexpected error', error);
return fail(request, USER_SEND_ERROR, 500);
}
};

View File

@ -0,0 +1,176 @@
import type { APIRoute } from 'astro';
import {
absoluteUrl,
getEmailBinding,
getNewsletterDb,
getRuntimeEnv,
readEnv,
sendCloudflareEmail,
siteOrigin,
type NewsletterSubscriber,
} from '../../../../lib/newsletter';
export const prerender = false;
type SendBody = {
subject?: string;
text?: string;
html?: string;
dryRun?: boolean;
};
function unauthorized(): Response {
return new Response('Unauthorized', { status: 401 });
}
function authorize(request: Request, runtimeEnv: ReturnType<typeof getRuntimeEnv>): boolean {
const expected = readEnv('NEWSLETTER_ADMIN_TOKEN', runtimeEnv);
if (!expected) return false;
const header = request.headers.get('authorization') || '';
const bearer = header.toLowerCase().startsWith('bearer ')
? header.slice(7).trim()
: '';
return bearer === expected;
}
export const POST: APIRoute = async ({ request, locals, url }) => {
const runtimeEnv = getRuntimeEnv(locals);
if (!authorize(request, runtimeEnv)) return unauthorized();
const mode = (readEnv('NEWSLETTER_MODE', runtimeEnv) || readEnv('CONTACT_FORM_MODE', runtimeEnv) || 'dev').toLowerCase();
let body: SendBody;
try {
body = (await request.json()) as SendBody;
} catch {
return new Response('Invalid JSON body.', { status: 400 });
}
const subject = (body.subject || '').trim();
const text = (body.text || '').trim();
const html = (body.html || '').trim();
if (!subject || !text) {
return new Response('Fields subject and text are required.', { status: 400 });
}
if (mode !== 'live') {
console.info('[newsletter:dev] Campaign send requested', {
subject,
dryRun: Boolean(body.dryRun),
textPreview: text.slice(0, 120),
});
return Response.json({
mode,
accepted: true,
sent: 0,
failed: 0,
note: 'Dev mode: campaign not sent.',
});
}
const db = getNewsletterDb(runtimeEnv);
if (!db) {
return new Response('Newsletter database binding NEWSLETTER_DB is not configured.', {
status: 500,
});
}
const emailBinding = getEmailBinding(runtimeEnv);
const fromEmail =
readEnv('NEWSLETTER_FROM_EMAIL', runtimeEnv) ||
readEnv('CONTACT_FORM_FROM_EMAIL', runtimeEnv);
if (!emailBinding || !fromEmail) {
return new Response('Missing EMAIL binding or from email for newsletter send.', {
status: 500,
});
}
const subscribersResult = await db
.prepare(
`SELECT * FROM newsletter_subscribers
WHERE status = 'active'
ORDER BY id ASC`,
)
.all<NewsletterSubscriber>();
const subscribers = subscribersResult.results ?? [];
const campaignInsert = await db
.prepare(
`INSERT INTO newsletter_campaigns (subject, body_text, body_html, status, created_by)
VALUES (?, ?, ?, ?, ?)`,
)
.bind(subject, text, html || null, body.dryRun ? 'draft' : 'sending', 'admin-api')
.run();
const campaignId = Number(campaignInsert.meta.last_row_id);
const origin = siteOrigin(url, runtimeEnv);
if (body.dryRun) {
return Response.json({
dryRun: true,
campaignId,
recipients: subscribers.length,
sample: subscribers.slice(0, 5).map((s) => s.email),
});
}
let sent = 0;
let failed = 0;
for (const subscriber of subscribers) {
const unsubUrl = absoluteUrl(
origin,
`/api/newsletter/unsubscribe?token=${subscriber.unsubscribe_token}`,
);
const personalizedText = `${text}\n\n---\nDisiscrizione: ${unsubUrl}\n`;
const personalizedHtml = html
? `${html}<hr /><p style="font-size:12px;color:#666">Disiscrizione: <a href="${unsubUrl}">${unsubUrl}</a></p>`
: undefined;
try {
await sendCloudflareEmail(emailBinding, {
fromEmail,
toEmail: subscriber.email,
subject,
text: personalizedText,
html: personalizedHtml,
});
await db
.prepare(
`INSERT INTO newsletter_campaign_sends
(campaign_id, subscriber_id, email, status, sent_at)
VALUES (?, ?, ?, 'sent', datetime('now'))`,
)
.bind(campaignId, subscriber.id, subscriber.email)
.run();
sent += 1;
} catch (error) {
failed += 1;
const message = error instanceof Error ? error.message : String(error);
await db
.prepare(
`INSERT INTO newsletter_campaign_sends
(campaign_id, subscriber_id, email, status, error, sent_at)
VALUES (?, ?, ?, 'failed', ?, datetime('now'))`,
)
.bind(campaignId, subscriber.id, subscriber.email, message.slice(0, 500))
.run();
}
}
await db
.prepare(
`UPDATE newsletter_campaigns
SET status = ?, sent_at = datetime('now')
WHERE id = ?`,
)
.bind(failed > 0 && sent === 0 ? 'failed' : 'sent', campaignId)
.run();
return Response.json({
campaignId,
recipients: subscribers.length,
sent,
failed,
});
};

View File

@ -0,0 +1,74 @@
import type { APIRoute } from 'astro';
import {
getNewsletterDb,
getRuntimeEnv,
readEnv,
type NewsletterSubscriber,
} from '../../../../lib/newsletter';
export const prerender = false;
function unauthorized(): Response {
return new Response('Unauthorized', { status: 401 });
}
function authorize(request: Request, runtimeEnv: ReturnType<typeof getRuntimeEnv>): boolean {
const expected = readEnv('NEWSLETTER_ADMIN_TOKEN', runtimeEnv);
if (!expected) return false;
const header = request.headers.get('authorization') || '';
const bearer = header.toLowerCase().startsWith('bearer ')
? header.slice(7).trim()
: '';
const queryToken = new URL(request.url).searchParams.get('token') || '';
return bearer === expected || queryToken === expected;
}
export const GET: APIRoute = async ({ request, locals }) => {
const runtimeEnv = getRuntimeEnv(locals);
if (!authorize(request, runtimeEnv)) return unauthorized();
const mode = (readEnv('NEWSLETTER_MODE', runtimeEnv) || readEnv('CONTACT_FORM_MODE', runtimeEnv) || 'dev').toLowerCase();
if (mode !== 'live') {
return Response.json({
mode,
subscribers: [],
note: 'Dev mode: no database query performed.',
});
}
const db = getNewsletterDb(runtimeEnv);
if (!db) {
return new Response('Newsletter database binding NEWSLETTER_DB is not configured.', {
status: 500,
});
}
const status = new URL(request.url).searchParams.get('status');
let rows: NewsletterSubscriber[];
if (status === 'pending' || status === 'active' || status === 'unsubscribed') {
const result = await db
.prepare(
`SELECT id, email, status, locale, source, created_at, confirmed_at, unsubscribed_at
FROM newsletter_subscribers
WHERE status = ?
ORDER BY created_at DESC`,
)
.bind(status)
.all<NewsletterSubscriber>();
rows = result.results ?? [];
} else {
const result = await db
.prepare(
`SELECT id, email, status, locale, source, created_at, confirmed_at, unsubscribed_at
FROM newsletter_subscribers
ORDER BY created_at DESC`,
)
.all<NewsletterSubscriber>();
rows = result.results ?? [];
}
return Response.json({
count: rows.length,
subscribers: rows,
});
};

View File

@ -0,0 +1,86 @@
import type { APIRoute } from 'astro';
import {
getEmailBinding,
getNewsletterDb,
getRuntimeEnv,
readEnv,
required,
sendCloudflareEmail,
type NewsletterSubscriber,
} from '../../../lib/newsletter';
export const prerender = false;
export const GET: APIRoute = async ({ request, locals, url }) => {
const runtimeEnv = getRuntimeEnv(locals);
const mode = (readEnv('NEWSLETTER_MODE', runtimeEnv) || readEnv('CONTACT_FORM_MODE', runtimeEnv) || 'dev').toLowerCase();
const token = required(url.searchParams.get('token'));
const okPath = '/newsletter/confermata';
const failPath = '/newsletter/conferma-errore';
if (!token) {
return Response.redirect(new URL(failPath, url).toString(), 303);
}
if (mode !== 'live') {
console.info('[newsletter:dev] Confirm token received', { token: token.slice(0, 8) + '…' });
return Response.redirect(new URL(okPath, url).toString(), 303);
}
const db = getNewsletterDb(runtimeEnv);
if (!db) {
return new Response('Newsletter database binding NEWSLETTER_DB is not configured.', {
status: 500,
});
}
const row = await db
.prepare(
`SELECT * FROM newsletter_subscribers
WHERE confirm_token = ? AND status = 'pending'
LIMIT 1`,
)
.bind(token)
.first<NewsletterSubscriber>();
if (!row) {
return Response.redirect(new URL(failPath, url).toString(), 303);
}
await db
.prepare(
`UPDATE newsletter_subscribers
SET status = 'active',
confirmed_at = datetime('now'),
confirm_token = NULL
WHERE id = ?`,
)
.bind(row.id)
.run();
const emailBinding = getEmailBinding(runtimeEnv);
const fromEmail =
readEnv('NEWSLETTER_FROM_EMAIL', runtimeEnv) ||
readEnv('CONTACT_FORM_FROM_EMAIL', runtimeEnv);
const notifyTo =
readEnv('NEWSLETTER_NOTIFY_TO_EMAIL', runtimeEnv) ||
readEnv('CONTACT_FORM_TO_EMAIL', runtimeEnv);
if (emailBinding && fromEmail && notifyTo) {
try {
await sendCloudflareEmail(emailBinding, {
fromEmail,
toEmail: notifyTo,
subject: 'Iscrizione newsletter confermata — NexStudio',
text: `Lindirizzo ${row.email} ha confermato liscrizione alla newsletter.`,
});
} catch (error) {
console.warn('[newsletter] confirm notify failed', error);
}
}
return Response.redirect(
new URL(`${okPath}?unsub=${encodeURIComponent(row.unsubscribe_token)}`, request.url).toString(),
303,
);
};

View File

@ -0,0 +1,222 @@
import type { APIRoute } from 'astro';
import {
absoluteUrl,
createToken,
getEmailBinding,
getNewsletterDb,
getRuntimeEnv,
isValidEmail,
normalizeEmail,
readEnv,
required,
sanitizeRedirect,
sendCloudflareEmail,
siteOrigin,
verifyTurnstile,
type NewsletterSubscriber,
} from '../../../lib/newsletter';
export const prerender = false;
function confirmEmailCopy(
locale: string,
confirmUrl: string,
unsubscribeUrl: string,
): { subject: string; text: string } {
if (locale === 'en') {
return {
subject: 'Confirm your NexStudio newsletter subscription',
text: [
'Thanks for subscribing to the NexStudio newsletter.',
'',
'Confirm your email by opening this link:',
confirmUrl,
'',
'If you did not request this, you can ignore this message.',
'',
'Unsubscribe:',
unsubscribeUrl,
].join('\n'),
};
}
if (locale === 'th') {
return {
subject: 'ยืนยันการสมัครรับจดหมายข่าว NexStudio',
text: [
'ขอบคุณที่สมัครรับจดหมายข่าว NexStudio',
'',
'ยืนยันอีเมลของคุณโดยเปิดลิงก์นี้:',
confirmUrl,
'',
'หากคุณไม่ได้ขอสมัคร สามารถเพิกเฉยต่อข้อความนี้ได้',
'',
'ยกเลิกการสมัคร:',
unsubscribeUrl,
].join('\n'),
};
}
return {
subject: 'Conferma iscrizione newsletter NexStudio',
text: [
'Grazie per esserti iscritto alla newsletter di NexStudio.',
'',
'Conferma lindirizzo email aprendo questo link:',
confirmUrl,
'',
'Se non hai richiesto liscrizione, puoi ignorare questo messaggio.',
'',
'Per disiscriverti:',
unsubscribeUrl,
].join('\n'),
};
}
export const POST: APIRoute = async ({ request, locals, url }) => {
const runtimeEnv = getRuntimeEnv(locals);
const mode = (readEnv('NEWSLETTER_MODE', runtimeEnv) || readEnv('CONTACT_FORM_MODE', runtimeEnv) || 'dev').toLowerCase();
const db = getNewsletterDb(runtimeEnv);
const form = await request.formData();
const redirectOk =
sanitizeRedirect(required(form.get('_next')), url) ||
'/newsletter/grazie';
const redirectErr = sanitizeRedirect(required(form.get('_error')), url) || redirectOk;
// Honeypot
if (required(form.get('_gotcha'))) {
return Response.redirect(new URL(redirectOk, url).toString(), 303);
}
const emailRaw = required(form.get('email'));
const locale = required(form.get('locale')) || 'it';
const source = required(form.get('source')) || 'footer';
if (!emailRaw || !isValidEmail(emailRaw)) {
return new Response('Invalid email.', { status: 400 });
}
const email = normalizeEmail(emailRaw);
// Turnstile solo se configurato esplicitamente per la newsletter.
// Non riusare CONTACT_TURNSTILE_SECRET_KEY: il form footer non ha il widget.
const turnstileSecret = readEnv('NEWSLETTER_TURNSTILE_SECRET_KEY', runtimeEnv);
if (turnstileSecret) {
const turnstileToken = required(form.get('cf-turnstile-response'));
const isHuman = await verifyTurnstile(
turnstileToken,
turnstileSecret,
request.headers.get('CF-Connecting-IP'),
);
if (!isHuman) {
return new Response('Turnstile verification failed.', { status: 400 });
}
}
const confirmToken = createToken();
const unsubscribeToken = createToken();
const origin = siteOrigin(url, runtimeEnv);
const confirmUrl = absoluteUrl(origin, `/api/newsletter/confirm?token=${confirmToken}`);
const unsubscribeUrl = absoluteUrl(
origin,
`/api/newsletter/unsubscribe?token=${unsubscribeToken}`,
);
if (mode !== 'live') {
console.info('[newsletter:dev] Subscription request', {
email,
locale,
source,
confirmUrl,
});
return Response.redirect(new URL(redirectOk, url).toString(), 303);
}
if (!db) {
return new Response('Newsletter database binding NEWSLETTER_DB is not configured.', {
status: 500,
});
}
const emailBinding = getEmailBinding(runtimeEnv);
const fromEmail =
readEnv('NEWSLETTER_FROM_EMAIL', runtimeEnv) ||
readEnv('CONTACT_FORM_FROM_EMAIL', runtimeEnv);
const notifyTo =
readEnv('NEWSLETTER_NOTIFY_TO_EMAIL', runtimeEnv) ||
readEnv('CONTACT_FORM_TO_EMAIL', runtimeEnv);
if (!emailBinding || !fromEmail) {
return new Response(
'Live mode requires EMAIL binding (Cloudflare Email Service) and NEWSLETTER_FROM_EMAIL (or CONTACT_FORM_FROM_EMAIL).',
{ status: 500 },
);
}
const existing = await db
.prepare('SELECT * FROM newsletter_subscribers WHERE email = ? LIMIT 1')
.bind(email)
.first<NewsletterSubscriber>();
if (existing?.status === 'active') {
// Already subscribed: redirect without revealing status too loudly.
return Response.redirect(new URL(redirectOk, url).toString(), 303);
}
if (existing?.status === 'unsubscribed' || existing?.status === 'pending') {
await db
.prepare(
`UPDATE newsletter_subscribers
SET status = 'pending',
locale = ?,
source = ?,
confirm_token = ?,
unsubscribe_token = ?,
confirmed_at = NULL,
unsubscribed_at = NULL
WHERE id = ?`,
)
.bind(locale, source, confirmToken, unsubscribeToken, existing.id)
.run();
} else {
await db
.prepare(
`INSERT INTO newsletter_subscribers
(email, status, locale, confirm_token, unsubscribe_token, source)
VALUES (?, 'pending', ?, ?, ?, ?)`,
)
.bind(email, locale, confirmToken, unsubscribeToken, source)
.run();
}
const copy = confirmEmailCopy(locale, confirmUrl, unsubscribeUrl);
try {
await sendCloudflareEmail(emailBinding, {
fromEmail,
toEmail: email,
subject: copy.subject,
text: copy.text,
});
} catch (error) {
console.error('[newsletter] confirm email failed', error);
return Response.redirect(new URL(redirectErr, url).toString(), 303);
}
if (notifyTo) {
try {
await sendCloudflareEmail(emailBinding, {
fromEmail,
toEmail: notifyTo,
subject: required(form.get('_subject')) || 'Nuova richiesta newsletter — NexStudio',
text: [
`Nuova richiesta di iscrizione (pending conferma).`,
`Email: ${email}`,
`Locale: ${locale}`,
`Source: ${source}`,
].join('\n'),
});
} catch (error) {
console.warn('[newsletter] admin notify failed', error);
}
}
return Response.redirect(new URL(redirectOk, url).toString(), 303);
};

View File

@ -0,0 +1,85 @@
import type { APIRoute } from 'astro';
import {
getNewsletterDb,
getRuntimeEnv,
readEnv,
required,
type NewsletterSubscriber,
} from '../../../lib/newsletter';
export const prerender = false;
async function unsubscribeByToken(
locals: unknown,
token: string,
): Promise<'ok' | 'missing' | 'not_found' | 'no_db'> {
if (!token) return 'missing';
const runtimeEnv = getRuntimeEnv(locals);
const mode = (readEnv('NEWSLETTER_MODE', runtimeEnv) || readEnv('CONTACT_FORM_MODE', runtimeEnv) || 'dev').toLowerCase();
if (mode !== 'live') {
console.info('[newsletter:dev] Unsubscribe token received', { token: token.slice(0, 8) + '…' });
return 'ok';
}
const db = getNewsletterDb(runtimeEnv);
if (!db) return 'no_db';
const row = await db
.prepare(
`SELECT * FROM newsletter_subscribers
WHERE unsubscribe_token = ?
LIMIT 1`,
)
.bind(token)
.first<NewsletterSubscriber>();
if (!row) return 'not_found';
if (row.status !== 'unsubscribed') {
await db
.prepare(
`UPDATE newsletter_subscribers
SET status = 'unsubscribed',
unsubscribed_at = datetime('now'),
confirm_token = NULL
WHERE id = ?`,
)
.bind(row.id)
.run();
}
return 'ok';
}
export const GET: APIRoute = async ({ locals, url }) => {
const token = required(url.searchParams.get('token'));
const result = await unsubscribeByToken(locals, token);
if (result === 'no_db') {
return new Response('Newsletter database binding NEWSLETTER_DB is not configured.', {
status: 500,
});
}
const path =
result === 'ok' ? '/newsletter/disiscrizione' : '/newsletter/disiscrizione-errore';
return Response.redirect(new URL(path, url).toString(), 303);
};
export const POST: APIRoute = async ({ request, locals, url }) => {
const form = await request.formData();
const token = required(form.get('token')) || required(url.searchParams.get('token'));
const result = await unsubscribeByToken(locals, token);
if (result === 'no_db') {
return new Response('Newsletter database binding NEWSLETTER_DB is not configured.', {
status: 500,
});
}
const path =
result === 'ok' ? '/newsletter/disiscrizione' : '/newsletter/disiscrizione-errore';
return Response.redirect(new URL(path, url).toString(), 303);
};

View File

@ -1,143 +1,77 @@
---
import SubpageLayout from '../layouts/SubpageLayout.astro';
import {
getCodiceEticoPremessa,
codiceEticoConformityBlocks,
qualitaValidazioneProdotto,
getCodiceEticoProductListPhrase,
notePraticheDPIA,
codiceEticoTutelaDatiRuoli,
} from '../data/codice-etico';
import { getCodiceEtico } from '../data/codice-etico';
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = (localeMatch?.[1] ?? 'it') as 'it' | 'en' | 'th';
const c = getCodiceEtico(currentLocale);
const { page } = c;
const link = 'text-sky-400/90 underline-offset-2 hover:text-sky-300 hover:underline';
const isLabeled = (item: string | { label: string; text: string }): item is { label: string; text: string } =>
typeof item !== 'string';
---
<SubpageLayout
title="Codice etico — NexStudio"
description="Codice etico definitivo NexStudio: valori, conformità legale e sanitaria, privacy, sicurezza, AI, governance e segnalazioni."
heading="Codice etico definitivo"
lead="Per NexStudio — sede: Bangkok, Thailandia. Versione 1.0, in vigore dal 21 aprile 2026. Documento interno ed esterno: principi, obblighi e procedure operative per chi collabora con la Società."
title={page.title}
description={page.description}
heading={page.heading}
lead={page.lead}
>
<div class="[&_h3]:mt-6 [&_h3]:text-base [&_h3]:font-semibold [&_h3]:text-nx-fg">
<p class="border-l-2 border-sky-500/35 pl-4 text-sm leading-relaxed text-nx-muted">
<span class="font-medium text-nx-fg">Versione</span> 1.0
<span class="font-medium text-nx-fg">{page.versionLabel}</span> 1.0
<span class="text-nx-muted/80"> · </span>
<span class="font-medium text-nx-fg">Entrata in vigore</span>: 21 aprile 2026
<span class="font-medium text-nx-fg">{page.inForceLabel}</span>: {page.inForceDate}
</p>
<h2 id="indice" class="!mt-8">Indice sintetico</h2>
<h2 id="indice" class="!mt-8">{page.indexTitle}</h2>
<ol class="!mt-3 list-decimal space-y-1.5 pl-5 text-sm leading-relaxed sm:text-base">
<li><a class={link} href="#premessa">Premessa</a></li>
<li><a class={link} href="#ambito">Ambito di applicazione</a></li>
<li><a class={link} href="#valori">Valori e principi fondamentali</a></li>
<li>
<a class={link} href="#conformita">Conformità normativa (per perimetro: piattaforma, legale, sanitario)</a>
<ol class="mt-2 list-decimal space-y-1 pl-5 text-nx-muted/95">
{
c.index.map((item) => (
<li>
<a class={link} href="#conformita-piattaforma">4.1 Piattaforma e SaaS (orizzontale)</a>
<a class={link} href={item.href}>
{item.label}
</a>
{item.children ? (
<ol class="mt-2 list-decimal space-y-1 pl-5 text-nx-muted/95">
{item.children.map((child) => (
<li>
<a class={link} href={child.href}>
{child.label}
</a>
</li>
))}
</ol>
) : null}
</li>
<li>
<a class={link} href="#conformita-legal">4.2 Ambito legale (LexAura)</a>
</li>
<li>
<a class={link} href="#conformita-sanitario">4.3 Ambito sanitario (MediAura)</a>
</li>
</ol>
</li>
<li>
<a class={link} href="#tutela-dati">Tutela dei dati personali e dati sensibili</a>
<ol class="mt-2 list-decimal space-y-1 pl-5 text-nx-muted/95">
<li>
<a class={link} href="#tutela-dati-ruoli"
>5.1 Ruoli: pazienti, clienti, abbonati, Società</a
>
</li>
</ol>
</li>
<li>
<a class={link} href="#sicurezza">Sicurezza dellinformazione e resilienza operativa</a>
</li>
<li>
<a class={link} href="#qualita"
>Qualità del software, validazione clinica e rischio per gli utenti</a
>
</li>
<li>
<a class={link} href="#proprieta-intellettuale"
>Proprietà intellettuale e uso di componenti esterni</a
>
</li>
<li><a class={link} href="#ai-ml">Uso responsabile di AI/ML e automazione</a></li>
<li>
<a class={link} href="#conflitti">Conflitti di interesse e integrità commerciale</a>
</li>
<li>
<a class={link} href="#linee-operative"
>Linee operative: sviluppo, rilascio e gestione vulnerabilità</a
>
</li>
<li><a class={link} href="#governance">Governance, ruoli e responsabilità</a></li>
<li>
<a class={link} href="#onboarding">Onboarding, formazione e certificazione</a>
</li>
<li>
<a class={link} href="#segnalazioni"
>Segnalazioni, protezione del segnalante e investigazioni</a
>
</li>
<li><a class={link} href="#violazioni">Gestione delle violazioni e sanzioni</a></li>
<li>
<a class={link} href="#monitoraggio">Monitoraggio, audit e miglioramento continuo</a>
</li>
<li>
<a class={link} href="#documentazione">Documentazione obbligatoria e registri</a>
</li>
<li><a class={link} href="#allegati">Allegati e moduli</a></li>
<li><a class={link} href="#approvazione">Approvazione e contatti utili</a></li>
))
}
</ol>
<h2 id="premessa">1. Premessa</h2>
<p>
{getCodiceEticoPremessa()}
</p>
<h2 id="premessa">{c.premessaTitle}</h2>
<p>{c.premessa}</p>
<h2 id="ambito">2. Ambito di applicazione</h2>
<p>
Si applica a: fondatori, dirigenti, dipendenti (full-time, part-time), collaboratori esterni,
consulenti, appaltatori e fornitori. Copre tutte le attività: analisi, progettazione,
sviluppo, test, rilascio, manutenzione, supporto, marketing, vendite e ricerca.
</p>
<h2 id="ambito">{c.ambito.title}</h2>
<p>{c.ambito.text}</p>
<h2 id="valori">3. Valori e principi fondamentali</h2>
<h2 id="valori">{c.valori.title}</h2>
<ul>
<li>Integrità, onestà e trasparenza.</li>
<li>Rispetto della dignità umana, inclusione e non discriminazione.</li>
<li>Responsabilità tecnico-professionale e qualità del servizio.</li>
<li>Riservatezza e protezione dei dati personali e sensibili.</li>
<li>Sicurezza e resilienza operativa.</li>
<li>Conformità legale e regolamentare.</li>
<li>Miglioramento continuo e accountability.</li>
{c.valori.items.map((item) => <li>{item}</li>)}
</ul>
<h2 id="conformita">4. Conformità normativa specifica</h2>
<h2 id="conformita">{c.conformita.title}</h2>
<p>
I riferimenti sotto sono raggruppati: obblighi orizzontali a tutte le offerte
<strong>SaaS</strong>, riferimenti distinti per il perimetro
<strong>Legal Tech / LexAura</strong> e per <strong>Health Tech / MediAura</strong> (ciascuna
linea può estendersi o diversificare secondo prodotto e mercato, senza commistione indebita
ad altri perimetri). Riepilogo riga prodotto:
<span class="text-nx-fg">{getCodiceEticoProductListPhrase()}</span>.
{c.conformita.introBefore}
<span class="text-nx-fg">{c.productListPhrase}</span>
{c.conformita.introAfter}
</p>
{
codiceEticoConformityBlocks.map((block, i) => (
<section
class:list={['', i > 0 && 'mt-5']}
data-conformity-block={block.id}
>
<h3
id={block.anchorId}
class="scroll-mt-8 text-base font-semibold text-nx-fg"
>
c.conformityBlocks.map((block, i) => (
<section class:list={['', i > 0 && 'mt-5']} data-conformity-block={block.id}>
<h3 id={block.anchorId} class="scroll-mt-8 text-base font-semibold text-nx-fg">
{block.title}
</h3>
{block.lead ? <p class="text-sm text-nx-muted sm:text-base">{block.lead}</p> : null}
@ -149,392 +83,263 @@ const link = 'text-sky-400/90 underline-offset-2 hover:text-sky-300 hover:underl
</section>
))
}
<p class="mt-6 text-sm sm:text-base">
La Società effettua DPIA (Data Protection Impact Assessment) per trattamenti ad alto rischio e
consulta autorità o <em>regulatory counsel</em> quando necessario, con riferimenti
<strong>per ciascun trattamento</strong> al perimetro (piattaforma, legale, sanitario) ove
interessa.
</p>
<p class="mt-6 text-sm sm:text-base">{c.conformita.afterBlocks}</p>
<h2 id="tutela-dati">5. Tutela dei dati personali e dati sensibili</h2>
<p class="text-nx-muted sm:text-base">{codiceEticoTutelaDatiRuoli.intro}</p>
<h2 id="tutela-dati">{c.tutelaDati.title}</h2>
<p class="text-nx-muted sm:text-base">{c.tutelaDatiRuoli.intro}</p>
<h3
id={codiceEticoTutelaDatiRuoli.anchorId}
id={c.tutelaDatiRuoli.anchorId}
class="!mt-6 text-base font-semibold text-nx-fg"
>
{codiceEticoTutelaDatiRuoli.title}
{c.tutelaDatiRuoli.title}
</h3>
{
codiceEticoTutelaDatiRuoli.rows.map((r) => (
c.tutelaDatiRuoli.rows.map((r) => (
<p class="mt-3 sm:text-base">
<span class="font-semibold text-nx-fg">{r.label}.</span>{' '}
{r.text}
<span class="font-semibold text-nx-fg">{r.label}.</span> {r.text}
</p>
))
}
<p
class="mt-4 border-l-2 border-sky-500/30 pl-4 text-sm leading-relaxed text-nx-muted"
>
{codiceEticoTutelaDatiRuoli.note}
<p class="mt-4 border-l-2 border-sky-500/30 pl-4 text-sm leading-relaxed text-nx-muted">
{c.tutelaDatiRuoli.note}
</p>
<p class="mt-8 text-sm font-medium text-nx-fg sm:text-base">
Nella concreta operatività, la Società e i collaboratori si attengono ai principi operativi
seguenti (complementari ai ruoli sopra):
{c.tutelaDati.operationalIntro}
</p>
<ul>
<li><strong>Minimizzazione:</strong> raccogliere solo i dati necessari e pertinenti.</li>
<li>
<strong>Base giuridica:</strong> documentare la base legale per ogni trattamento (consenso,
esecuzione del contratto, obbligo legale, interesse legittimo valutato).
</li>
<li>
<strong>Informativa e consenso:</strong> fornire informative chiare e ottenere consensi
quando richiesto; gestire il consenso in modo verificabile.
</li>
<li>
<strong>Classificazione:</strong> definire categorie (PII, dati sanitari, informazioni legali
privilegiate) e applicare misure differenziate.
</li>
<li>
<strong>Conservazione e cancellazione:</strong> politiche di retention documentate;
anonimizzazione o cancellazione al termine delle finalità.
</li>
<li>
<strong>Trasferimenti internazionali:</strong> valutare basi legali (clausole contrattuali
standard, decisioni di adeguatezza, misure tecniche) per trasferimenti fuori dalla
Thailandia.
</li>
<li>
<strong>Diritti degli interessati:</strong> procedure per accesso, rettifica, cancellazione,
limitazione, portabilità e opposizione, con tempi e log documentati.
</li>
<li>
<strong>Sub-processori:</strong> contratti che impongono misure di sicurezza equivalenti,
obblighi di notifica in caso di violazione (<em>breach</em>) e divieti di ulteriori
sub-affidamenti non autorizzati.
</li>
</ul>
<h2 id="sicurezza">6. Sicurezza dellinformazione e resilienza operativa</h2>
<ul>
<li>
<strong>Security by design</strong> e <strong>privacy by design:</strong> integrazione di
sicurezza e privacy nel ciclo di vita del prodotto.
</li>
<li>
<strong>Controlli di accesso:</strong> principio del privilegio minimo, MFA obbligatorio per
accesso a risorse sensibili, gestione centralizzata delle identità.
</li>
<li>
<strong>Crittografia:</strong> cifratura a riposo e in transito con algoritmi aggiornati;
gestione sicura delle chiavi.
</li>
<li>
<strong>Logging e monitoraggio:</strong> log immutabili per accessi e operazioni su dati
sensibili e ambienti di produzione.
</li>
<li>
<strong>Vulnerability management:</strong> scansioni automatiche, penetration test
periodici, processo di patching e programma di responsible disclosure o bug bounty.
</li>
<li>
<strong>Continuità operativa e disaster recovery:</strong> piani testati regolarmente con
RTO/RPO definiti.
</li>
<li>
<strong>Gestione incidenti:</strong> playbook documentato (identificazione, containment,
eradicazione, recovery, post-mortem), ruoli e tempi di notifica interni ed esterni.
</li>
</ul>
<h2 id="qualita">7. Qualità del software, validazione clinica e rischio per gli utenti</h2>
<ul>
<li>
<strong>Standard di sviluppo:</strong> codice leggibile, test automatici, code review
obbligatorie, CI/CD sicuro.
</li>
<li>
<strong>Test e copertura:</strong> definire copertura minima per componenti critici; test di
integrazione, performance e sicurezza.
</li>
{
qualitaValidazioneProdotto.map((v) => (
c.tutelaDati.principles.map((p) => (
<li>
<strong>Validazione (per {v.id === 'legal' ? 'perimetro legale' : 'perimetro sanitario'}):</strong>{' '}
<strong>{p.label}:</strong> {p.text}
</li>
))
}
</ul>
<h2 id="sicurezza">{c.sicurezza.title}</h2>
<ul>
{
c.sicurezza.items.map((item) => (
<li>
<strong>{item.label}:</strong> {item.text}
</li>
))
}
</ul>
<h2 id="qualita">{c.qualita.title}</h2>
<ul>
{
c.qualita.itemsBefore.map((item) => (
<li>
<strong>{item.label}:</strong> {item.text}
</li>
))
}
{
c.qualitaValidazioneProdotto.map((v) => (
<li>
<strong>
{v.id === 'legal'
? c.qualita.validazioneLabelLegal
: c.qualita.validazioneLabelHealth}
:
</strong>{' '}
{v.text}
</li>
))
}
{
c.qualita.itemsAfter.map((item) => (
<li>
<strong>{item.label}:</strong> {item.text}
</li>
))
}
</ul>
<h2 id="proprieta-intellettuale">{c.proprieta.title}</h2>
<ul>
{
c.proprieta.items.map((item) =>
item.kind === 'labeled' ? (
<li>
<strong>{item.label}:</strong> {item.text}
</li>
) : (
<li>{item.text}</li>
),
)
}
</ul>
<h2 id="ai-ml">{c.ai.title}</h2>
<ul>
{
c.ai.items.map((item) =>
isLabeled(item) ? (
<li>
<strong>{item.label}:</strong> {item.text}
</li>
) : (
<li>{item}</li>
),
)
}
</ul>
<h2 id="conflitti">{c.conflitti.title}</h2>
<ul>
{c.conflitti.items.map((item) => <li>{item}</li>)}
</ul>
<h2 id="linee-operative">{c.lineeOperative.title}</h2>
<ul>
{
c.lineeOperative.items.map((item) => (
<li>
<strong>{item.label}:</strong> {item.text}
</li>
))
}
</ul>
<p class="mt-3 text-sm">
<a
class={link}
href={`/${currentLocale === 'it' ? '' : currentLocale + '/'}allegati#security-disclosure`}
>
{c.lineeOperative.disclosureLinkLabel}
</a>
</p>
<h2 id="governance">{c.governance.title}</h2>
<ul>
{
c.governance.items.map((item) => (
<li>
<strong>{item.label}:</strong> {item.text}
</li>
))
}
</ul>
<h2 id="onboarding">{c.onboarding.title}</h2>
<ul>
<li>
<strong>Classificazione del rischio:</strong> valutare impatto su salute o diritti e
applicare misure di mitigazione proporzionate.
{c.onboarding.item1Before}
{c.productListPhrase}
{c.onboarding.item1After}
</li>
{c.onboarding.itemsRest.map((item) => <li>{item}</li>)}
</ul>
<p class="mt-3 text-sm">
<a
class={link}
href={`/${currentLocale === 'it' ? '' : currentLocale + '/'}allegati#piano-formazione`}
>
{c.onboarding.planLinkLabel}
</a>
</p>
<h2 id="segnalazioni">{c.segnalazioni.title}</h2>
<ul>
<li>
<strong>{c.segnalazioni.channelsLabel}:</strong>{' '}
{c.segnalazioni.channelsBefore}{' '}
<a class={link} href="mailto:ethics@nexstudio.com">ethics@nexstudio.com</a>
{c.segnalazioni.channelsAfter}
</li>
<li>
<strong>Non sostituzione del professionista:</strong> la piattaforma fornisce supporto e
strumenti informativi; non si presenta come sostitutiva di pareri professionali senza
esplicita supervisione e avallo professionale.
<strong>{c.segnalazioni.protection.label}:</strong>{' '}
{c.segnalazioni.protection.text}
</li>
<li>
<strong>Registri ed evidenze:</strong> mantenere documentazione di test clinici, valutazioni
di rischio e approvazioni regolatorie.
<strong>{c.segnalazioni.investigations.label}:</strong>{' '}
{c.segnalazioni.investigations.text}
</li>
</ul>
<h2 id="proprieta-intellettuale">8. Proprietà intellettuale e uso di componenti esterni</h2>
<h2 id="violazioni">{c.violazioni.title}</h2>
<ul>
<li>
I contributi di dipendenti e consulenti inerenti a software, documentazione e know-how sono
di proprietà della Società, salvo diverso accordo scritto.
</li>
<li>
<strong>Open source:</strong> mantenere SBOM (Software Bill of Materials), verificare la
compatibilità delle licenze, rispettare obblighi di disclosure e aggiornamento delle patch.
</li>
<li>
Divieto di uso non autorizzato di codice o proprietà intellettuale di terzi; gestione dei
rischi di contaminazione IP.
</li>
{c.violazioni.items.map((item) => <li>{item}</li>)}
</ul>
<h2 id="ai-ml">9. Uso responsabile di AI/ML e automazione</h2>
<h2 id="monitoraggio">{c.monitoraggio.title}</h2>
<ul>
<li>Documentare dataset di training, provenienza, pre-elaborazione e limiti noti.</li>
<li>
<strong>Bias assessment:</strong> condurre analisi per identificare e mitigare pregiudizi
che possano discriminare o causare danni.
</li>
<li>
<strong>Supervisione umana:</strong> per decisioni che influenzano salute, procedimenti
legali o diritti fondamentali, prevedere supervisione umana e avvisi chiari sui limiti del
sistema.
</li>
<li>
<strong>Explainability e responsabilità:</strong> fornire informazioni comprensibili su
come i modelli supportano le decisioni e su prestazioni e affidabilità.
</li>
<li>
<strong>Monitoraggio post-release:</strong> misurare le prestazioni in produzione e
correggere drift o comportamenti inattesi.
</li>
{
c.monitoraggio.items.map((item) =>
isLabeled(item) ? (
<li>
<strong>{item.label}:</strong> {item.text}
</li>
) : (
<li>{item}</li>
),
)
}
</ul>
<h2 id="conflitti">10. Conflitti di interesse e integrità commerciale</h2>
<h2 id="documentazione">{c.documentazione.title}</h2>
<p>{c.documentazione.intro}</p>
<ul>
<li>
Dichiarazione obbligatoria di conflitti reali o potenziali (investimenti, relazioni con
fornitori o clienti).
</li>
<li>Politiche anticorruzione e anti-tangenti conformi a leggi nazionali e internazionali.</li>
<li>
Comunicazioni commerciali veritiere; non ingannare su capacità, approvazioni regolatorie o
risultati.
</li>
<li>
Trasparenza contrattuale: SLA, limitazioni di responsabilità e condizioni duso chiare e
accessibili.
</li>
{c.documentazione.items.map((item) => <li>{item}</li>)}
</ul>
<h2 id="linee-operative">11. Linee operative: sviluppo, rilascio e gestione vulnerabilità</h2>
<h2 id="allegati">{c.allegati.title}</h2>
<p class="mb-4 text-sm">
<a class={link} href={`/${currentLocale === 'it' ? '' : currentLocale + '/'}allegati`}>
{c.allegati.linkLabel}
</a>
</p>
<ul>
<li>
<strong>Sviluppo:</strong> strategia di branch, code review obbligatoria, merge solo con CI
positiva, checklist pre-release (sicurezza, privacy, compliance).
</li>
<li>
<strong>Ambiente di test:</strong> uso di dati sintetici o anonimizzati; accesso a dati
reali solo su base minima e autorizzata, in ambienti isolati.
</li>
<li>
<strong>Deployment:</strong> CI/CD standardizzato con piano di rollback e rilasci graduali
per funzionalità critiche.
</li>
<li>
<strong>Gestione vulnerabilità:</strong> canale pubblico o privato per segnalazioni (bug
bounty o indirizzo dedicato <em>security@</em>), conferma di ricezione entro 72 ore, piano di
remediation con tempi stimati.
</li>
<li>
<strong>Patch management:</strong> tempi di rilascio delle patch definiti in base alla
severità.
</li>
<li>
<strong>Documentazione di rilascio:</strong> changelog, impatti noti e raccomandazioni
operative.
</li>
{c.allegati.items.map((item) => <li>{item}</li>)}
</ul>
<h2 id="governance">12. Governance, ruoli e responsabilità</h2>
<h2 id="approvazione">{c.approvazione.title}</h2>
<ul>
<li><strong>Board / fondatori:</strong> approvano le politiche, definiscono strategia e risorse.</li>
<li><strong>CEO:</strong> responsabilità complessiva di conformità e governance.</li>
<li><strong>CTO:</strong> qualità tecnica, architettura e pratiche di sviluppo.</li>
<li>
<strong>CISO / responsabile sicurezza:</strong> sicurezza operativa, risposta agli incidenti,
gestione delle vulnerabilità.
</li>
<li>
<strong>DPO / referente privacy:</strong> conformità PDPA e GDPR, DPIA, gestione richieste
degli interessati.
</li>
<li>
<strong>Legal &amp; compliance:</strong> supervisione legale, contratti e valutazioni
regolatorie.
</li>
<li><strong>HR:</strong> formazione, codice di condotta e gestione disciplinare.</li>
<li>
<strong>Team lead / PM:</strong> applicano le policy operative e garantiscono conformità
quotidiana.
</li>
<li>
<strong>Comitato etico (consigliato):</strong> gruppo multidisciplinare per valutare casi
complessi (impatti clinici o legali, conflitti), con riunioni periodiche e su richiesta.
</li>
{
c.approvazione.items.map((item) => (
<li>
<strong>{item.label}:</strong> {item.text}
</li>
))
}
</ul>
<p class="font-medium text-nx-fg">{page.contactsTitle}</p>
<ul>
{
c.approvazione.contacts.map((contact) => (
<li>
{contact.label}:{' '}
<a class={link} href={`mailto:${contact.email}`}>
{contact.email}
</a>
{contact.note === 'anonymous' ? (
<> {page.anonymousPlatformNote}</>
) : null}
</li>
))
}
</ul>
<h2 id="onboarding">13. Onboarding, formazione e certificazione</h2>
<ul>
<li>
Onboarding obbligatorio su: sicurezza informatica, privacy (PDPA e GDPR), gestione dati
sensibili, requisiti e documentazione riferibili a ciascuna linea prodotto
(oggi: {getCodiceEticoProductListPhrase()}; estendere il curriculum quando lelenco
cresce), uso responsabile dellAI, pratiche di coding sicuro.
</li>
<li>Formazione annuale obbligatoria e formazione aggiuntiva per ruoli critici.</li>
<li>Registrazione e conservazione dei record di formazione.</li>
</ul>
<h2 id="segnalazioni">14. Segnalazioni, protezione del segnalante e investigazioni</h2>
<ul>
<li>
<strong>Canali:</strong> almeno un canale confidenziale interno (es. <a
class={link}
href="mailto:ethics@nexstudio.com">ethics@nexstudio.com</a
>), piattaforma anonima esterna o servizio terzo; contatto per escalation indipendente.
</li>
<li>
<strong>Protezione:</strong> divieto di ritorsioni; misure disciplinari per ritorsioni
comprovate.
</li>
<li>
<strong>Indagini:</strong> condotte in modo imparziale, documentato e con tempi definiti;
comunicazione dei risultati alla persona segnalante e alle parti interessate, nei limiti
della riservatezza.
</li>
</ul>
<h2 id="violazioni">15. Gestione delle violazioni e sanzioni</h2>
<ul>
<li>
Azioni proporzionate alla gravità: formazione correttiva, richiamo, riassegnazione,
sospensione, risoluzione del rapporto contrattuale, azioni legali se necessario.
</li>
<li>Registro delle infrazioni e delle azioni intraprese; diritto di difesa dellinteressato.</li>
</ul>
<h2 id="monitoraggio">16. Monitoraggio, audit e miglioramento continuo</h2>
<ul>
<li>Audit interni ed esterni regolari su sicurezza, privacy, compliance e controllo qualità.</li>
<li>
<strong>KPI (esempi):</strong> numero di incidenti, tempo medio di remediation, percentuale
di copertura dei test, percentuale di formazione completata, tempo medio di risposta alle
segnalazioni.
</li>
<li>
Revisione del Codice: almeno annuale o in caso di cambiamenti normativi, tecnologici o di
mercato.
</li>
</ul>
<h2 id="documentazione">17. Documentazione obbligatoria e registri</h2>
<p>La Società mantiene e aggiorna, tra laltro:</p>
<ul>
<li>registro dei trattamenti e DPIA;</li>
<li>SBOM e inventario dei componenti (open source);</li>
<li>log di accesso e audit trail;</li>
<li>registro incidenti e violazioni dei dati;</li>
<li>contratti con fornitori e sub-processori;</li>
<li>record di formazione e dichiarazioni di adesione al Codice.</li>
</ul>
<h2 id="allegati">18. Allegati e moduli (inclusi)</h2>
<ul>
<li>Dichiarazione personale di adesione al Codice (da firmare in onboarding).</li>
<li>Template NDA e clausole minime per fornitori e sub-processori.</li>
<li>Checklist pre-release (security e privacy).</li>
<li>Flowchart gestione incidenti e template di notifica.</li>
<li>Modello DPIA semplificato ed esempio compilato.</li>
<li>Modello informativa privacy e modulo consenso per utenti o pazienti.</li>
<li>Template SBOM.</li>
<li>Policy di retention dei dati (tempi e giustificazioni).</li>
<li>Template per valutazione dimpatto AI/ML.</li>
</ul>
<h2 id="approvazione">19. Approvazione e contatti utili</h2>
<ul>
<li>
<strong>Approvato da:</strong> organo competente / amministratore delegato (nome e cognome
alla firma).
</li>
<li>
<strong>Responsabile aggiornamenti:</strong> Legal &amp; compliance, con supporto di DPO
e CISO.
</li>
<li>
<strong>Revisione:</strong> minimo annuale o a fronte di mutamenti rilevanti.
</li>
</ul>
<p class="font-medium text-nx-fg">Contatti</p>
<ul>
<li>
DPO / referente privacy: <a class={link} href="mailto:privacy@nexstudio.com"
>privacy@nexstudio.com</a
>
</li>
<li>
Security / incident response: <a class={link} href="mailto:security@nexstudio.com"
>security@nexstudio.com</a
>
</li>
<li>
Segnalazioni confidenziali: <a class={link} href="mailto:ethics@nexstudio.com"
>ethics@nexstudio.com</a
>
(o link a piattaforma anonima dedicata)
</li>
<li>
Legal: <a class={link} href="mailto:legal@nexstudio.com">legal@nexstudio.com</a>
</li>
<li>HR: <a class={link} href="mailto:hr@nexstudio.com">hr@nexstudio.com</a></li>
</ul>
<h2 id="clausola-adesione">Clausola finale di adesione (da firmare in onboarding)</h2>
<h2 id="clausola-adesione">{c.adesione.title}</h2>
<p>
Dichiaro di aver letto e compreso il Codice Etico di <strong>NexStudio</strong> e mi impegno a
rispettarne i principi e le procedure. Mi impegno inoltre a segnalare, in buona fede, eventuali
violazioni di cui dovessi venire a conoscenza.
{c.adesione.textBefore}
<strong>NexStudio</strong>
{c.adesione.textAfter}
</p>
<p class="mt-4 text-nx-fg">
Firma: <span class="inline-block min-w-[12rem] border-b border-nx-border"> </span>
{page.signatureLabel}:{' '}
<span class="inline-block min-w-[12rem] border-b border-nx-border"> </span>
<span class="mx-3 text-nx-muted">·</span>
Data: <span class="inline-block min-w-[6rem] border-b border-nx-border"> </span>
{page.dateLabel}:{' '}
<span class="inline-block min-w-[6rem] border-b border-nx-border"> </span>
</p>
<h2 id="note-pratiche">Note pratiche e prossimi passi consigliati</h2>
<h2 id="note-pratiche">{c.notePratiche.title}</h2>
<ul>
<li>
Personalizzare il documento con il nome ufficiale della società (se diverso dal marchio
operativo), firme e riferimenti legali locali a Bangkok.
</li>
<li>
Allegare i template elencati come documenti separati e implementare i canali tecnici per
segnalazioni e bug bounty.
</li>
<li>
Predisporre un piano di formazione di 90 giorni per lonboarding e formazione ricorrente
annuale.
</li>
{notePraticheDPIA.map((t) => <li>{t}</li>)}
{c.notePratiche.items.map((item) => <li>{item}</li>)}
{c.notePraticheDPIA.map((t) => <li>{t}</li>)}
</ul>
</div>
</SubpageLayout>

View File

@ -1,461 +1,203 @@
---
import SubpageLayout from '../layouts/SubpageLayout.astro';
import { cookieConsentMeta, cookieCategories } from '../data/cookie-consent';
import { cookieConsentMeta } from '../data/cookie-consent';
import type { SupportedLocale } from '../data/home/navigation';
import { getCookies } from '../data/cookies';
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = (localeMatch?.[1] ?? 'it') as SupportedLocale;
const cookies = getCookies(currentLocale);
const { document: cookiesDocument, riferimenti, sections, page } = cookies;
const localize = (href: string) => {
if (currentLocale === 'it') return href;
if (href.startsWith(`/${currentLocale}`)) return href;
if (href.startsWith('/#')) return `/${currentLocale}${href}`;
if (href.startsWith('/')) return `/${currentLocale}${href}`;
return href;
};
const resolveHref = (href: string) =>
href.startsWith('mailto:') || href.startsWith('http') ? href : localize(href);
const link = 'text-sky-400/90 underline-offset-2 hover:text-sky-300 hover:underline';
const tbl = 'mt-4 w-full border-collapse text-left text-sm';
const th = 'border border-nx-border bg-nx-surface/60 px-3 py-2 font-semibold text-nx-fg';
const td = 'border border-nx-border px-3 py-2 align-top text-nx-muted';
const indexTitle = (title: string) => title.replace(/^\d+\.\s*/, '');
---
<SubpageLayout
title="Cookie Policy — NexStudio"
description="Cookie Policy NexStudio: categorie, base giuridica, consenso, terze parti, diritti e contatti. Sede Bangkok."
heading="Cookie Policy"
lead="Per NexStudio, con sede a Bangkok, Thailandia. Versione documento 1.0, in vigore dal 21 aprile 2026. Integra l'uso del modulo di consenso first-party descritto sotto; per il trattamento dei dati personali vedi l'informativa privacy."
title={page.title}
description={page.description}
heading={page.heading}
lead={page.lead}
>
<div class="[&_h3]:mt-6 [&_h3]:text-base [&_h3]:font-semibold [&_h3]:text-nx-fg">
<p class="border-l-2 border-sky-500/35 pl-4 text-sm leading-relaxed text-nx-muted">
<span class="font-medium text-nx-fg">Versione</span> 1.0
<span class="font-medium text-nx-fg">{page.versionLabel}</span> {cookiesDocument.version}
<span class="text-nx-muted/80"> · </span>
<span class="font-medium text-nx-fg">In vigore dal</span> 21 aprile 2026
<span class="font-medium text-nx-fg">{page.inForceFromLabel}</span> {cookiesDocument.inVigoreDal}
<span class="text-nx-muted/80"> · </span>
<span class="font-medium text-nx-fg">Versione tecnica consenso</span>{' '}
<span class="font-medium text-nx-fg">{page.consentVersionLabel}</span>{' '}
<code class="rounded bg-nx-elevated px-1.5 py-0.5 font-mono text-xs text-sky-300/90"
>v{cookieConsentMeta.policyVersion}</code
>
</p>
<h2 id="indice" class="!mt-8">Indice</h2>
<ol class="!mt-3 list-decimal space-y-1.5 pl-5 text-sm leading-relaxed sm:text-base">
<li><a class={link} href="#premessa">Premessa</a></li>
<li><a class={link} href="#definizioni">Definizioni</a></li>
<li><a class={link} href="#finalita">Finalità dei cookie e categorie</a></li>
<li><a class={link} href="#elenco">Elenco dei cookie (modello da compilare)</a></li>
<li><a class={link} href="#sanitario-legale">Base giuridica e ambito sanitario/legale</a></li>
<li><a class={link} href="#consenso">Modalità di raccolta del consenso e registro</a></li>
<li><a class={link} href="#revoca">Gestione e revoca del consenso</a></li>
<li><a class={link} href="#browser">Disabilitazione via browser e impatto</a></li>
<li><a class={link} href="#terze-parti">Cookie di terze parti e responsabilità</a></li>
<li><a class={link} href="#retention">Conservazione e criteri di retention</a></li>
<li><a class={link} href="#trasferimenti">Trasferimenti internazionali</a></li>
<li><a class={link} href="#minori">Minori età</a></li>
<li><a class={link} href="#sicurezza">Misure di sicurezza</a></li>
<li><a class={link} href="#advertising">Consenso per scopi pubblicitari e profiling</a></li>
<li><a class={link} href="#effetti">Effetti di rifiuto o accettazione</a></li>
<li><a class={link} href="#modifiche">Modifiche alla Cookie Policy</a></li>
<li><a class={link} href="#diritti">Diritti degli interessati e contatti</a></li>
<li><a class={link} href="#appendice-tabella">Registro dei cookie (appendice)</a></li>
<li><a class={link} href="#implementazione">Implementazione tecnica e strumenti</a></li>
<li><a class={link} href="#test-qc">Test e controllo qualità</a></li>
<li><a class={link} href="#banner-testi">Esempio di testi per il banner (IT / EN)</a></li>
<li><a class={link} href="#note-legali">Note legali</a></li>
<li><a class={link} href="#versioning">Versioning e storico revisioni</a></li>
</ol>
<h2 id="premessa">1. Premessa</h2>
<p>
Questa Cookie Policy descrive le tipologie di cookie e tecnologie simili utilizzate sul sito
web di <strong>NexStudio</strong> (di seguito «Società», «noi»), con sede a Bangkok,
Thailandia, e spiega come gestire le preferenze. Per il trattamento dei dati personali e per i
diritti degli interessati si rimanda all<a class={link} href="/privacy"
>Informativa privacy</a
>.
</p>
<h2 id="definizioni">2. Definizioni</h2>
<ul>
<li>
<strong>Cookie:</strong> piccolo file di testo inviato dal sito al browser e memorizzato sul
dispositivo, re-inviato al sito alle visite successive.
</li>
<li>
<strong>Cookie di prima parte:</strong> impostati dal dominio del sito che lutente visita.
</li>
<li>
<strong>Cookie di terze parti:</strong> impostati da domini diversi (fornitori esterni,
embed, CDN, strumenti di misura).
</li>
<li>
<strong>Tecnologie simili:</strong> web beacon, pixel, <em>local storage</em>, e in generale
identificatori lato client. Tecnologie invasive (es. fingerprinting) richiedono base legale
adeguata e massima trasparenza.
</li>
</ul>
<h2 id="finalita">3. Finalità dei cookie e categorie</h2>
<p>I cookie sul sito possono essere raggruppati come segue.</p>
<h3>3.1 Cookie strettamente necessari (essenziali)</h3>
<ul>
<li>
<strong>Finalità:</strong> navigazione, sicurezza, gestione sessione ove prevista,
memorizzazione delle preferenze di consenso.
</li>
<li>
<strong>Base giuridica:</strong> esecuzione di misure precontrattuali / necessità tecnica e,
ove applicabile, interesse legittimo limitato al funzionamento del servizio richiesto
dallutente.
</li>
<li>
<strong>Esempi tipici:</strong> token di sessione, cookie anti-CSRF, chiave di consenso in
<em>local storage</em> (se usata al posto di cookie HTTP).
</li>
</ul>
<h3>3.2 Cookie di preferenza / funzionali</h3>
<ul>
<li>
<strong>Finalità:</strong> lingua, regione, tema o altre impostazioni di interfaccia non
strettamente indispensabili.
</li>
<li><strong>Base giuridica:</strong> consenso, salvo trattamenti minimi e non invasivi.</li>
<li><strong>Esempi:</strong> <code class="font-mono text-xs text-sky-300/90">lang</code>, preferenze UI.</li>
</ul>
<h3>3.3 Cookie di performance / analytics</h3>
<ul>
<li>
<strong>Finalità:</strong> statistiche aggregate su visite e percorsi per migliorare il
sito.
</li>
<li>
<strong>Base giuridica:</strong> consenso, salvo misurazione strettamente anonimizzata e
non identificativa secondo orientamenti applicabili.
</li>
<li>
<strong>Esempi:</strong> <code class="font-mono text-xs text-sky-300/90">_ga</code>, <code
class="font-mono text-xs text-sky-300/90">_gid</code
> (Google Analytics), strumenti tipo Hotjar, se integrati.
</li>
</ul>
<h3>3.4 Cookie per advertising e targeting</h3>
<ul>
<li>
<strong>Finalità:</strong> pubblicità mirata, remarketing, misura delle campagne.
</li>
<li><strong>Base giuridica:</strong> consenso esplicito.</li>
<li>
<strong>Esempi:</strong> identificatori di reti pubblicitarie, pixel di retargeting, cookie
DSP.
</li>
</ul>
<h3>3.5 Cookie di terze parti e integrazioni social</h3>
<ul>
<li>
<strong>Finalità:</strong> video incorporati, pulsanti social, mappe, chat di terze parti.
</li>
<li>
<strong>Nota:</strong> il trattamento da parte di terzi è regolato dalle rispettive policy;
NexStudio non controlla cookie impostati senza adeguata configurazione e consenso.
</li>
</ul>
<h2 id="elenco">4. Elenco dei cookie utilizzati (modello da compilare)</h2>
<p>
Per ogni cookie reale indicare: nome, fornitore, categoria, finalità, durata, prima o terza
parte, base giuridica, link alla privacy del fornitore e, se utile, opt-out.
</p>
<p>
La tabella sotto è <strong>esemplificativa</strong>: va sostituita con linventario aggiornato
(ideale: hash o versione e data di aggiornamento in calce alla tabella pubblicata).
</p>
<table class={tbl}>
<thead>
<tr>
<th class={th}>Nome</th>
<th class={th}>Provider</th>
<th class={th}>Categoria</th>
<th class={th}>Durata</th>
<th class={th}>Tipo</th>
<th class={th}>Base giuridica</th>
</tr>
</thead>
<tbody>
<tr>
<td class={td}><code class="font-mono text-xs">_ga</code> (es.)</td>
<td class={td}>Google LLC</td>
<td class={td}>Analytics</td>
<td class={td}>24 mesi (tipico)</td>
<td class={td}>3rd</td>
<td class={td}>Consenso</td>
</tr>
<tr>
<td class={td}>
<code class="font-mono text-xs">{cookieConsentMeta.storageKey}</code> (non cookie HTTP)
</td>
<td class={td}>NexStudio</td>
<td class={td}>Necessario</td>
<td class={td}>Fino a revoca / aggiornamento policy</td>
<td class={td}>1st (local)</td>
<td class={td}>Necessario / consenso informato</td>
</tr>
<tr>
<td class={td}><code class="font-mono text-xs">_fbp</code> (es.)</td>
<td class={td}>Meta</td>
<td class={td}>Advertising</td>
<td class={td}>90 giorni (tipico)</td>
<td class={td}>3rd</td>
<td class={td}>Consenso</td>
</tr>
</tbody>
</table>
<h2 id="sanitario-legale">5. Base giuridica e trattamento in ambito sanitario o legale</h2>
<ul>
<li>
Per cookie che comportano trattamento di dati particolari (salute, dati giudiziari o
assimilabili) è richiesto <strong>consenso esplicito</strong> informato e misure tecniche e
organizzative aggiuntive, salvo diversa base prevista dalla legge applicabile.
</li>
<li>
Nei servizi SaaS medico-legali la raccolta tramite cookie sul sito vetrina è da
<strong>limitare e segregare</strong>; per test e analytics privilegiare dati sintetici o
anonimizzazione efficace.
</li>
</ul>
<h2 id="consenso">6. Modalità di raccolta del consenso e registro</h2>
<p>
Il sito utilizza una <strong>CMP first-party</strong> sviluppata per NexStudio, orientata ai
requisiti di trasparenza e granularità (inclusi riferimenti GDPR e PDPA tailandese come quadro
di riferimento per le descrizioni). Al primo accesso, o dopo aumento della versione della
policy tecnica, viene mostrato un banner con:
</p>
<ul>
<li><strong>Rifiuta non essenziali</strong></li>
<li><strong>Personalizza</strong> (apertura pannello categorie)</li>
<li><strong>Accetta tutto</strong></li>
</ul>
<p>
La prova della scelta è memorizzata nel browser in <em>local storage</em> alla chiave{' '}
<code class="rounded bg-nx-elevated px-1.5 py-0.5 font-mono text-xs text-sky-300/90"
>{cookieConsentMeta.storageKey}</code
>, con versione <code class="rounded bg-nx-elevated px-1.5 py-0.5 font-mono text-xs text-sky-300/90"
>v{cookieConsentMeta.policyVersion}</code
>, timestamp di aggiornamento e stato per categoria. I cookie strettamente necessari possono
restare attivi compatibilmente con informativa chiara. Per audit avanzati si può integrare in
futuro un log lato server (IP, user-agent, ID sessione) con informativa dedicata.
</p>
<p class="font-medium text-nx-fg">Categorie gestite dal modulo attuale</p>
<ul>
<p class="!mt-5 text-sm text-nx-muted sm:text-base">
{page.relatedDocsLabel}:{' '}
{
cookieCategories.map((c) => (
riferimenti.map((r, i) => (
<span>
{i > 0 && <span class="text-nx-muted/80"> · </span>}
<a class={link} href={localize(r.href)}>{r.label}</a>
</span>
))
}
</p>
<h2 id="indice" class="!mt-8">{page.indexLabel}</h2>
<ol class="!mt-3 list-decimal space-y-1.5 pl-5 text-sm leading-relaxed sm:text-base">
{
sections.map((s) => (
<li>
<strong class="text-nx-fg">{c.label}</strong>
{c.required ? ' — sempre attiva.' : ' — attiva solo se accettata.'}{' '}
{c.description}
<a class={link} href={`#${s.id}`}>{indexTitle(s.title)}</a>
</li>
))
}
</ul>
<p>
Il codice espone <code class="rounded bg-nx-elevated px-1.5 py-0.5 font-mono text-xs text-sky-300/90"
>window.NexStudioCookieConsent</code
> e levento <code class="rounded bg-nx-elevated px-1.5 py-0.5 font-mono text-xs text-sky-300/90"
>nexstudio:cookie-consent</code
> per caricare script non necessari solo dopo consenso.
</p>
</ol>
<h2 id="revoca">7. Gestione e revoca del consenso</h2>
<ul>
<li>Tramite il banner o il pannello <strong>Personalizza</strong> al primo accesso.</li>
<li>
In qualsiasi momento tramite <strong>Impostazioni cookie</strong> nel footer del sito (sotto
le colonne di navigazione).
</li>
<li>
Cancellando i dati locali del sito dalle impostazioni del browser (inclusa la voce di{' '}
<em>local storage</em> citata sopra): al successivo caricamento il banner si ripresenta.
</li>
</ul>
<p>
Dopo la revoca non devono essere impostati cookie non essenziali; alcune funzioni (es.
preferenze memorizzate, statistiche) possono risultare limitate.
</p>
{
sections.map((s) => (
<section class="mt-10" id={s.id}>
<h2 class="scroll-mt-8 text-lg font-semibold text-nx-fg sm:text-xl">{s.title}</h2>
<h2 id="browser">8. Disabilitazione via browser e impatto</h2>
<p>
È possibile bloccare o cancellare i cookie dalle impostazioni del browser (Chrome, Firefox,
Safari, Edge e altri). Disabilitando i cookie possono smettere di funzionare login, preferenze
o strumenti che ne dipendono.
</p>
{s.richParagraphs?.map((parts) => (
<p class="mt-3 text-sm leading-relaxed text-nx-muted sm:text-base">
{parts.map((part) => {
if (part.t === 'text') return part.v;
if (part.t === 'em') return <em>{part.v}</em>;
if (part.t === 'strong') return <strong>{part.v}</strong>;
if (part.external) {
return (
<a class={link} href={part.href} target="_blank" rel="noopener noreferrer">
{part.label}
</a>
);
}
return (
<a class={link} href={resolveHref(part.href)}>
{part.label}
</a>
);
})}
</p>
))}
<h2 id="terze-parti">9. Cookie di terze parti e responsabilità</h2>
<p>
Il sito può integrare strumenti di terze parti (analytics, CDN, social, gateway di pagamento,
video). Le terze parti possono impostare cookie propri e trattare dati secondo le loro policy.
</p>
<p>
<strong>Esempi di fornitori</strong> spesso usati in contesti analoghi (elenco da adattare al
sito reale): Google Analytics, Google Tag Manager, Cloudflare, Stripe, Vimeo, YouTube, Meta,
LinkedIn, Hotjar. Per ciascuno vanno indicati link a privacy/cookie policy e strumenti di
opt-out ove disponibili.
</p>
{s.paragraphs?.map((p) => (
<p class="mt-3 text-sm leading-relaxed text-nx-muted sm:text-base">{p}</p>
))}
<h2 id="retention">10. Conservazione e criteri di retention</h2>
<ul>
<li>
Le durate dei singoli cookie seguono le tabelle pubblicate e le policy dei fornitori (es.
finestra indicativa <strong>24 mesi</strong> per dati aggregati di analytics, salvo obblighi
di legge o contratto diversi — da confermare con il legale).
</li>
<li>
Prove di consenso e gestione DSR: periodi di conservazione da definire nella retention
policy (orientamento comune per documentazione di conformità: <strong
>37 anni</strong
>, da calibrare su base legale e rischio — non vincolante in questa pagina).
</li>
</ul>
{s.definitions && s.definitions.length > 0 ? (
<ul class="mt-3 list-disc space-y-2 pl-5 text-sm sm:text-base">
{s.definitions.map((d) => (
<li class="text-nx-muted">
<strong>{d.term}:</strong> {d.definition}
</li>
))}
</ul>
) : null}
<h2 id="trasferimenti">11. Trasferimenti internazionali</h2>
<p>
Alcuni fornitori possono trattare dati in Paesi terzi (es. Stati Uniti). Ove applicabile si
adottano garanzie come clausole contrattuali standard, decisioni di adeguatezza o altre basi
previste dalla legge. Per dettagli su trasferimenti specifici contattare il referente privacy
indicato sotto.
</p>
{s.subsections?.map((sub) => (
<>
<h3>{sub.title}</h3>
<p class="mt-3 text-sm leading-relaxed text-nx-muted sm:text-base">
{sub.purposeLabel ? (
<>
<strong>{sub.purposeLabel}:</strong> {sub.purpose}{' '}
</>
) : null}
{sub.legalBasisLabel ? (
<>
<strong>{sub.legalBasisLabel}:</strong> {sub.legalBasis}
</>
) : null}
</p>
</>
))}
<h2 id="minori">12. Minori età</h2>
<p>
Il sito non è rivolto alla raccolta intenzionale di dati di minori. Per utenti sotto la soglia
prevista dalla legge locale, i trattamenti non essenziali richiedono consenso del titore
della responsabilità genitoriale ove imposto dalla normativa applicabile.
</p>
{s.bullets && s.bullets.length > 0 ? (
<ul class="mt-3 list-disc space-y-2 pl-5 text-sm sm:text-base">
{s.bullets.map((b) => (
<li class="text-nx-muted">
{typeof b === 'string' ? (
b
) : 'before' in b ? (
<>
{b.before}
<strong>{b.strong}</strong>
{b.after || ''}
</>
) : (
<>
<strong>{b.strong}</strong>
{b.after || ''}
</>
)}
</li>
))}
</ul>
) : null}
<h2 id="sicurezza">13. Misure di sicurezza</h2>
<p>
Si applicano misure tecniche e organizzative appropriate: trasmissione su TLS, accesso
limitato ai log, principio di minimizzazione, monitoraggio anomalie ove in uso.
</p>
{s.paragraphsAfterBullets?.map((p) =>
typeof p === 'string' ? (
<p class="mt-3 text-sm leading-relaxed text-nx-muted sm:text-base">{p}</p>
) : (
<p class="mt-3 text-sm leading-relaxed text-nx-muted sm:text-base">
{p.map((part) => {
if (part.t === 'text') return part.v;
if (part.t === 'em') return <em>{part.v}</em>;
if (part.t === 'strong') return <strong>{part.v}</strong>;
if (part.external) {
return (
<a class={link} href={part.href} target="_blank" rel="noopener noreferrer">
{part.label}
</a>
);
}
return (
<a class={link} href={resolveHref(part.href)}>
{part.label}
</a>
);
})}
</p>
),
)}
<h2 id="advertising">14. Consenso per scopi pubblicitari e profiling</h2>
<p>
Profilazione e pubblicità comportano <strong>consenso esplicito</strong> e tracciabilità
della scelta. Ove si usino reti pubblicitarie, fornire link agli strumenti di opt-out del
fornitore (es. impostazioni annunci Google).
</p>
<h2 id="effetti">15. Effetti di rifiuto o accettazione</h2>
<ul>
<li>
<strong>Accettando</strong> le categorie opzionali si abilitano preferenze avanzate,
analytics e, se configurate, funzioni di marketing.
</li>
<li>
<strong>Rifiutando</strong> i non essenziali il sito resta navigabile con funzioni core; le
altre possono essere limitate.
</li>
</ul>
<h2 id="modifiche">16. Modifiche alla Cookie Policy</h2>
<p>
La Policy può essere aggiornata quando cambiano cookie, fornitori o norme. Sarà indicata la
data di revisione; ove richiesto dalla legge o dallimpatto del cambiamento, si richiederà un
nuovo consenso (incrementando la versione tecnica nel modulo).
</p>
<h2 id="diritti">17. Diritti degli interessati e contatti</h2>
<p>
Diritti: accesso, rettifica, cancellazione, limitazione, portabilità, opposizione e revoca del
consenso ove previsti. Per esercitarli: <a class={link} href="mailto:privacy@nexstudio.com"
>privacy@nexstudio.com</a
> (DPO o referente privacy, da formalizzare).
</p>
<h2 id="appendice-tabella">18. Registro dei cookie e informazioni tecniche (appendice)</h2>
<p>
Pubblicare qui la tabella completa con colonne: nome, provider, categoria, descrizione
tecnica, durata, 1st/3rd, link opt-out, link privacy, note (GTM, embed, CDN, ecc.).
</p>
<table class={tbl}>
<thead>
<tr>
<th class={th}>Nome</th>
<th class={th}>Provider</th>
<th class={th}>Categoria</th>
<th class={th}>Scopo / durata</th>
<th class={th}>Note</th>
</tr>
</thead>
<tbody>
<tr>
<td class={td} colspan="5">
<em>Da compilare dopo scansione del sito in staging e produzione.</em>
</td>
</tr>
</tbody>
</table>
<h2 id="implementazione">19. Implementazione tecnica e strumenti</h2>
<ul>
<li>
<strong>CMP:</strong> modulo first-party NexStudio (nessun OneTrust/Cookiebot obbligatorio
in questa build); estensibile verso CMP commerciali se richiesto da clienti o policy
aziendale.
</li>
<li>
<strong>Log consenso:</strong> attualmente payload JSON in <em>local storage</em> con
versione policy, timestamp ISO e booleani per categoria; valutare integrazione server-side
per audit e correlazione sessione.
</li>
<li>
<strong>Banner:</strong> testo breve, link a questa Cookie Policy, tre azioni come da §6.
</li>
<li>
<strong>Sviluppo:</strong> caricare script di analytics, marketing o embed solo dopo lettura
del consenso tramite API/evento sopra citati.
</li>
</ul>
<h2 id="test-qc">20. Test e controllo qualità</h2>
<ul>
<li>Scansione periodica del sito per elenco cookie (strumenti automatici + revisione manuale).</li>
<li>Allineamento tra tabella pubblicata, CMP e script effettivamente caricati.</li>
<li>Audit trimestrale delle terze parti e aggiornamento categorie/testi.</li>
</ul>
<h2 id="banner-testi">21. Esempio di testi per il banner (IT / EN)</h2>
<ul>
<li>
<strong>Italiano (allineato al sito):</strong> «Utilizziamo cookie e memorizzazione locale.
Seleziona le categorie che preferisci. I necessari restano attivi per registrare la tua
scelta. Dettagli nellinformativa cookie.» — pulsanti: <strong>Rifiuta non essenziali</strong
>, <strong>Personalizza</strong>, <strong>Accetta tutto</strong>.
</li>
<li>
<strong>English (per versione bilingue futura):</strong> «We use cookies and local storage.
Choose the categories you prefer. Necessary ones stay on to record your choice. See our
cookie policy.» — <strong>Reject non-essential</strong>, <strong>Customize</strong>, <strong
>Accept all</strong
>.
</li>
</ul>
<h2 id="note-legali">22. Note legali</h2>
<p>
Questa Cookie Policy <strong>integra ma non sostituisce</strong> lInformativa privacy. Per
aspetti di trattamento dei dati personali collegati ai cookie fare riferimento anche a{' '}
<a class={link} href="/gdpr">GDPR</a> / documenti privacy pubblicati e al counsel locale per
Thailandia, UE o altri mercati serviti.
</p>
<h2 id="versioning">23. Versioning e storico revisioni</h2>
<ul>
<li><strong>Versione 1.0</strong> — 21 aprile 2026 — Prima pubblicazione strutturata.</li>
</ul>
<h2>Appendice: checklist tecnica per il rilascio</h2>
<ul class="list-none space-y-2 pl-0 [&_li]:flex [&_li]:gap-2">
<li><span class="text-nx-muted">☐</span> Scansione cookie completa e tabella aggiornata</li>
<li><span class="text-nx-muted">☐</span> CMP e prova consenso / revoca funzionanti</li>
<li><span class="text-nx-muted">☐</span> Traduzioni banner (es. EN / TH) se richieste dal mercato</li>
<li><span class="text-nx-muted">☐</span> Script non essenziali bloccati fino a consenso</li>
<li><span class="text-nx-muted">☐</span> Tabella cookie con metadati e versioning pubblico</li>
<li><span class="text-nx-muted">☐</span> Procedure DSR e revoca collegate dallinformativa privacy</li>
<li><span class="text-nx-muted">☐</span> Link a privacy e contatti DPO/referente nel footer</li>
</ul>
{s.table ? (
<table class={tbl}>
<thead>
<tr>
{s.table.headers.map((h) => (
<th class={th}>{h}</th>
))}
</tr>
</thead>
<tbody>
<tr>
<td class={td} colspan={s.table.headers.length}>
<em>{s.table.emptyNote}</em>
</td>
</tr>
</tbody>
</table>
) : null}
</section>
))
}
</div>
</SubpageLayout>

View File

@ -1,22 +1,37 @@
---
import SubpageLayout from '../layouts/SubpageLayout.astro';
import type { SupportedLocale } from '../data/home/navigation';
import { getDoveSiamo } from '../data/dove-siamo';
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = (localeMatch?.[1] ?? 'it') as SupportedLocale;
const doveSiamo = getDoveSiamo(currentLocale);
const { page, sections } = doveSiamo;
---
<SubpageLayout
title="Dove siamo — NexStudio"
description="Sede operativa a Bangkok, Thailandia; fusi orari e lingue per lavorare con clienti in Europa e altre regioni."
heading="Dove siamo"
lead="NexStudio ha base a Bangkok, in Thailandia. Sotto, come organizziamo fusi orari e lingue rispetto a clienti in Europa e oltre."
title={page.title}
description={page.description}
heading={page.heading}
lead={page.lead}
>
<h2>Sede</h2>
<p>
<strong>Thailandia</strong> — <strong>Bangkok</strong>
</p>
<h2>Fusi orari e lingue</h2>
<p>
Il sito e il supporto via web possono essere gestiti in italiano, inglese,
tedesco, francese, spagnolo e tailandese, con orari da concordare in base
al team e al fuso rispetto ai clienti in Europa e altre regioni.
</p>
{
sections.map((s) => (
<>
<h2>{s.title}</h2>
{s.paragraphs.map((p) =>
s.locationLine ? (
<p>
<strong>{p.split(' — ')[0]}</strong>
{' — '}
<strong>{p.split(' — ')[1]}</strong>
</p>
) : (
<p>{p}</p>
),
)}
</>
))
}
</SubpageLayout>

View File

@ -0,0 +1,5 @@
---
import Page from '../allegati.astro';
---
<Page />

View File

@ -2,4 +2,4 @@
import Page from '../index.astro';
---
<Page />
<Page locale="en" />

View File

@ -0,0 +1,4 @@
---
import Page from '../../newsletter/conferma-errore.astro';
---
<Page />

View File

@ -0,0 +1,4 @@
---
import Page from '../../newsletter/confermata.astro';
---
<Page />

View File

@ -0,0 +1,4 @@
---
import Page from '../../newsletter/disiscrizione-errore.astro';
---
<Page />

View File

@ -0,0 +1,4 @@
---
import Page from '../../newsletter/disiscrizione.astro';
---
<Page />

View File

@ -1,49 +1,56 @@
---
import SubpageLayout from '../layouts/SubpageLayout.astro';
import {
gdprCrossReferences,
gdprDocument,
gdprRiferimenti,
gdprSections,
} from '../data/gdpr';
import type { SupportedLocale } from '../data/home/navigation';
import { getGdpr } from '../data/gdpr';
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = (localeMatch?.[1] ?? 'it') as SupportedLocale;
const gdpr = getGdpr(currentLocale);
const { document: gdprDocument, riferimenti, sections, page } = gdpr;
const localize = (href: string) => {
if (currentLocale === 'it') return href;
if (href.startsWith(`/${currentLocale}`)) return href;
if (href.startsWith('/#')) return `/${currentLocale}${href}`;
if (href.startsWith('/')) return `/${currentLocale}${href}`;
return href;
};
const link = 'text-sky-400/90 underline-offset-2 hover:text-sky-300 hover:underline';
const lead =
"Diritti degli interessati e modalita' di esercizio ai sensi del Regolamento (UE) 2016/679, in coerenza con l'informativa privacy quadro del portale NexStudio. " +
gdprCrossReferences.lead;
---
<SubpageLayout
title="GDPR — NexStudio"
description="Pagina GDPR NexStudio: diritti degli interessati, modalita' operative, ruoli tra portale corporate e SaaS LexAura/MediAura, coordinamento con privacy dedicate."
heading="GDPR e diritti degli interessati"
lead={lead}
title={page.title}
description={page.description}
heading={page.heading}
lead={page.lead}
>
<div>
<p class="border-l-2 border-sky-500/35 pl-4 text-sm leading-relaxed text-nx-muted">
<span class="font-medium text-nx-fg">Versione</span> {gdprDocument.version}
<span class="font-medium text-nx-fg">{page.versionLabel}</span> {gdprDocument.version}
<span class="text-nx-muted/80"> · </span>
<span class="font-medium text-nx-fg">In vigore dal</span> {gdprDocument.inVigoreDal}
<span class="font-medium text-nx-fg">{page.inForceFromLabel}</span> {gdprDocument.inVigoreDal}
<span class="text-nx-muted/80"> · </span>
<span class="font-medium text-nx-fg">Ultimo aggiornamento</span> {gdprDocument.ultimoAggiornamento}
<span class="font-medium text-nx-fg">{page.lastUpdateLabel}</span> {gdprDocument.ultimoAggiornamento}
</p>
<p class="!mt-5 text-sm text-nx-muted sm:text-base">
Documenti connessi:{' '}
{page.relatedDocsLabel}:{' '}
{
gdprRiferimenti.map((r, i) => (
riferimenti.map((r, i) => (
<span>
{i > 0 && <span class="text-nx-muted/80"> · </span>}
<a class={link} href={r.href}>{r.label}</a>
<a class={link} href={localize(r.href)}>{r.label}</a>
</span>
))
}
</p>
<h2 id="indice" class="!mt-8">Indice</h2>
<h2 id="indice" class="!mt-8">{page.indexLabel}</h2>
<ol class="!mt-3 list-decimal space-y-1.5 pl-5 text-sm leading-relaxed sm:text-base">
{
gdprSections.map((s) => (
sections.map((s) => (
<li>
<a class={link} href={`#${s.id}`}>{s.title}</a>
</li>
@ -52,7 +59,7 @@ const lead =
</ol>
{
gdprSections.map((s) => (
sections.map((s) => (
<section class="mt-10" id={s.id}>
<h2 class="scroll-mt-8 text-lg font-semibold text-nx-fg sm:text-xl">
{s.title}
@ -67,6 +74,9 @@ const lead =
))}
</ul>
) : null}
{s.paragraphsAfterBullets?.map((p) => (
<p class="mt-3 text-sm leading-relaxed text-nx-muted sm:text-base">{p}</p>
))}
</section>
))
}

View File

@ -6,21 +6,44 @@ import ContactFormInline from '../components/ContactFormInline.astro';
import CardBottomRightTexture from '../components/CardBottomRightTexture.astro';
import SiteFooter from '../components/SiteFooter.astro';
import {
siteMeta,
navigation,
hero,
stats,
services,
stackSection,
productsSection,
faq,
cta,
getSiteMeta,
getNavigation,
getHero,
getStats,
getServices,
getStackSection,
getProductsSection,
getFaq,
getCta,
} from '../data/home';
import type { SupportedLocale } from '../data/home/navigation';
const infrastructureCarouselItems = stackSection.infrastructureCarousel.items;
interface Props {
/** Locale esplicito dalle route /en e /th (evita ambiguità se la page è importata come componente). */
locale?: SupportedLocale;
}
const { locale: localeProp } = Astro.props;
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = localeMatch?.[1] ?? 'it';
const currentLocale = (localeProp ??
(localeMatch?.[1] as SupportedLocale | undefined) ??
'it') as SupportedLocale;
const navigation = getNavigation(currentLocale);
const siteMeta = getSiteMeta(currentLocale);
const hero = getHero(currentLocale);
const stats = getStats(currentLocale);
const services = getServices(currentLocale);
const stackSection = getStackSection(currentLocale);
const productsSection = getProductsSection(currentLocale);
const faq = getFaq(currentLocale);
const cta = getCta(currentLocale);
const infrastructureCarouselItems = stackSection.infrastructureCarousel.items;
const pageLabels = {
it: { focus: 'Focus' },
en: { focus: 'Focus' },
th: { focus: 'จุดเน้น' },
}[currentLocale];
const localize = (href: string) => {
if (currentLocale === 'it') return href;
if (href.startsWith(`/${currentLocale}`)) return href;
@ -64,7 +87,7 @@ const langMeta = {
<nav
class="hidden items-center gap-8 text-sm md:flex"
aria-label="Principale"
aria-label={navigation.labels.mainNavAria}
>
{navigation.items.map((item) => {
const sectionId = item.href.includes('#')
@ -87,7 +110,7 @@ const langMeta = {
<details class="relative hidden sm:block" data-lang-dropdown>
<summary
class="inline-flex h-10 cursor-pointer list-none items-center gap-2 rounded-lg border border-nx-border/80 bg-nx-bg/50 px-3 text-xs font-medium text-nx-fg transition-colors hover:border-sky-500/35 hover:text-white"
aria-label="Seleziona lingua"
aria-label={navigation.labels.languageSelectorAria}
>
<img src={langMeta[currentLocale].flagSrc} alt="" class="h-3.5 w-5 rounded-[2px] object-cover" loading="lazy" decoding="async" />
<span>{langMeta[currentLocale].code}</span>
@ -157,7 +180,7 @@ const langMeta = {
@click="mobile = !mobile"
:aria-expanded="mobile"
aria-controls="mobile-nav"
aria-label="Apri menu"
aria-label={navigation.labels.openMenuAria}
>
<svg
x-show="!mobile"
@ -191,7 +214,7 @@ const langMeta = {
x-cloak
class="border-b border-nx-border bg-black px-4 py-4 md:hidden"
>
<nav class="flex flex-col gap-3 text-sm" aria-label="Mobile">
<nav class="flex flex-col gap-3 text-sm" aria-label={navigation.labels.mobileNavAria}>
{navigation.items.map((item) => {
const sectionId = item.href.includes('#')
? item.href.split('#').pop()!
@ -356,7 +379,7 @@ const langMeta = {
pi === 0 ? 'text-sky-200/85' : 'text-emerald-200/85',
]}
>
<span class="text-nx-muted font-normal">Focus — </span>
<span class="text-nx-muted font-normal">{pageLabels.focus} — </span>
{product.focus}
</p>
<p class="mt-5 text-sm leading-relaxed text-nx-muted sm:text-base">
@ -497,7 +520,7 @@ const langMeta = {
class="relative z-[1] mt-12 rounded-2xl border border-orange-500/20 bg-gradient-to-br from-orange-950/20 to-nx-elevated/40 p-6 sm:p-8"
>
<p class="text-xs font-medium uppercase tracking-wider text-orange-200/90">
Infrastruttura
{stackSection.infrastructureHeading}
</p>
<div
class="nx-infra-carousel mt-5"

View File

@ -1,66 +1,66 @@
---
import SubpageLayout from '../layouts/SubpageLayout.astro';
import {
modelloOrganizzativo,
modelloOrganizzativoRuoli,
modelloOrganizzativoGovernance,
modelloOrganizzativoResponsabilitaSintesi,
modelloOrganizzativoControlliLean,
modelloOrganizzativoKpi,
modelloOrganizzativoDocumentazione,
modelloOrganizzativoPiano30,
modelloOrganizzativoOutsourcing,
modelloOrganizzativoNote,
modelloOrganizzativoFlussiDecisionali,
} from '../data/modello-organizzativo';
import { getModelloOrganizzativo } from '../data/modello-organizzativo';
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = (localeMatch?.[1] ?? 'it') as 'it' | 'en' | 'th';
const m = getModelloOrganizzativo(currentLocale);
const { page } = m;
const localize = (href: string) => {
if (currentLocale === 'it') return href;
if (href.startsWith(`/${currentLocale}`)) return href;
if (href.startsWith('/#')) return `/${currentLocale}${href}`;
if (href.startsWith('/')) return `/${currentLocale}${href}`;
return href;
};
const link = 'text-sky-400/90 underline-offset-2 hover:text-sky-300 hover:underline';
const tbl = 'min-w-[22rem] w-full border-collapse text-left text-sm';
const th = 'border border-nx-border bg-nx-surface/60 px-3 py-2 font-semibold text-nx-fg';
const td = 'border border-nx-border px-3 py-2 align-top text-nx-muted';
const tableWrap = 'mt-2 overflow-x-auto';
const mark = (on?: boolean) => (on ? '●' : '');
---
<SubpageLayout
title="Modello organizzativo — NexStudio"
description="Modello organizzativo NexStudio: governance, ruoli, RACI, controlli lean, KPI, piano 30 giorni — per le linee LexAura (Legal Tech) e MediAura (Health Tech)."
heading="Modello organizzativo"
lead="Ruoli, ritmi di governo, processi e controlli «lean» in una software house con SaaS a elevata regolamentazione. Complementare al codice etico. Non sostituisce statuti, modelli di compliance societari (es. 231) né pareri legali: andate in parallelo con struttura reale e consulenti."
title={page.title}
description={page.description}
heading={page.heading}
lead={page.lead}
>
<div class="[&_h3]:mt-6 [&_h3]:text-base [&_h3]:font-semibold [&_h3]:text-nx-fg">
<p class="border-l-2 border-sky-500/35 pl-4 text-sm leading-relaxed text-nx-muted">
{modelloOrganizzativo.introBox}
{m.introBox}
</p>
<p class="mt-3 text-sm leading-relaxed text-nx-muted sm:text-base">
{modelloOrganizzativo.perimetro}
{m.perimetro}
</p>
<h2 id="indice" class="!mt-8">Indice</h2>
<h2 id="indice" class="!mt-8">{page.indexTitle}</h2>
<ol class="!mt-3 list-decimal space-y-1.5 pl-5 text-sm leading-relaxed sm:text-base">
<li><a class={link} href="#ruoli">Ruoli e perimetro</a></li>
<li><a class={link} href="#governance">Governance essenziale</a></li>
<li><a class={link} href="#responsabilita">Principali responsabilità (sintesi)</a></li>
<li><a class={link} href="#raci">RACI per processi critici</a></li>
<li><a class={link} href="#flussi">Flussi decisionali rapidi</a></li>
<li><a class={link} href="#controlli">Controlli minimi obbligatori (lean)</a></li>
<li><a class={link} href="#kpi">KPI essenziali</a></li>
<li><a class={link} href="#documentazione">Documentazione minima da mantenere</a></li>
<li><a class={link} href="#piano-30">Primo piano operativo (30 giorni)</a></li>
<li><a class={link} href="#outsourcing">Outsourcing consigliato</a></li>
<li><a class={link} href="#note">Note pratiche e raccomandazioni</a></li>
{
page.index.map((item) => (
<li>
<a class={link} href={item.href}>
{item.label}
</a>
</li>
))
}
</ol>
<h2 id="ruoli">1. Ruoli e perimetro</h2>
<h2 id="ruoli">{page.sectionTitles.ruoli}</h2>
<p>
Elenco sintetico delle funzioni e delle attese di impegno. Per allineare il dominio
prodotto (Legal Tech, Health Tech) a ruoli e consiglieri, fate riferimento anche a <a
class={link}
href="/codice-etico">codice etico</a
> e, per i trattamenti di dati, a privacy policy e DPA.
{page.ruoliIntroBefore}{' '}
<a class={link} href={localize(page.codiceEticoHref)}>{page.codiceEticoLabel}</a>
{' '}
{page.ruoliIntroAfter}
</p>
<ul>
{
modelloOrganizzativoRuoli.map((r) => (
m.ruoli.map((r) => (
<li>
<strong>{r.label}:</strong> {r.text}
</li>
@ -68,217 +68,85 @@ const tableWrap = 'mt-2 overflow-x-auto';
}
</ul>
<h2 id="governance">2. Governance essenziale</h2>
<h2 id="governance">{page.sectionTitles.governance}</h2>
<ul>
{
modelloOrganizzativoGovernance.map((g) => (
m.governance.map((g) => (
<li>
<strong>{g.nome}:</strong> {g.partecipanti}. <span class="text-nx-muted/95">— {g.nota}</span>
<strong>{g.nome}:</strong> {g.partecipanti}.{' '}
<span class="text-nx-muted/95">— {g.nota}</span>
</li>
))
}
</ul>
<h2 id="responsabilita">3. Principali responsabilità (sintesi)</h2>
<h2 id="responsabilita">{page.sectionTitles.responsabilita}</h2>
<ul>
{modelloOrganizzativoResponsabilitaSintesi.map((l) => <li>{l}</li>)}
{m.responsabilitaSintesi.map((l) => <li>{l}</li>)}
</ul>
<h2 id="raci">4. RACI sintetico per processi critici</h2>
<h2 id="raci">{page.sectionTitles.raci}</h2>
<p>
Legenda: <strong>R</strong> = Responsible, <strong>A</strong> = Accountable,
<strong>C</strong> = Consulted, <strong>I</strong> = Informed.
<strong>{page.raciLegendBefore}</strong> {page.raciLegend}
</p>
<h3 class="!mt-5" id="raci-release">Rilascio in produzione</h3>
<div class={tableWrap}>
<table class={tbl}>
<thead>
<tr>
<th class={th} scope="col">Ruolo</th>
<th class={th} scope="col">R</th>
<th class={th} scope="col">A</th>
<th class={th} scope="col">C</th>
<th class={th} scope="col">I</th>
</tr>
</thead>
<tbody>
<tr>
<td class={td}>Lead engineer</td>
<td class={td}>●</td>
<td class={td}></td>
<td class={td}></td>
<td class={td}></td>
</tr>
<tr>
<td class={td}>CTO</td>
<td class={td}></td>
<td class={td}>●</td>
<td class={td}></td>
<td class={td}></td>
</tr>
<tr>
<td class={td}>Security lead, DPO, product advisor</td>
<td class={td}></td>
<td class={td}></td>
<td class={td}>●</td>
<td class={td}></td>
</tr>
<tr>
<td class={td}>CEO, customer success</td>
<td class={td}></td>
<td class={td}></td>
<td class={td}></td>
<td class={td}></td>
</tr>
</tbody>
</table>
</div>
{
m.raci.map((matrix) => (
<>
<h3 class:list={[matrix.id === 'raci-release' && '!mt-5']} id={matrix.id}>
{matrix.title}
</h3>
{'note' in matrix && matrix.note ? (
<p class="text-sm text-nx-muted sm:text-base">
{matrix.note}{' '}
<a class={link} href={localize(matrix.noteLinkHref!)}>
{matrix.noteLinkLabel}
</a>
{'noteAfter' in matrix ? matrix.noteAfter : null}
</p>
) : null}
<div class={tableWrap}>
<table class={tbl}>
<thead>
<tr>
<th class={th} scope="col">
{page.roleCol}
</th>
<th class={th} scope="col">
R
</th>
<th class={th} scope="col">
A
</th>
<th class={th} scope="col">
C
</th>
<th class={th} scope="col">
I
</th>
</tr>
</thead>
<tbody>
{matrix.rows.map((row) => (
<tr>
<td class={td}>{row.role}</td>
<td class={td}>{mark(row.r)}</td>
<td class={td}>{mark(row.a)}</td>
<td class={td}>{mark(row.c)}</td>
<td class={td}>{mark(row.i)}</td>
</tr>
))}
</tbody>
</table>
</div>
</>
))
}
<h3 id="raci-incident">Incident response (violazione dei dati o incidente P0)</h3>
<div class={tableWrap}>
<table class={tbl}>
<thead>
<tr>
<th class={th} scope="col">Ruolo</th>
<th class={th} scope="col">R</th>
<th class={th} scope="col">A</th>
<th class={th} scope="col">C</th>
<th class={th} scope="col">I</th>
</tr>
</thead>
<tbody>
<tr>
<td class={td}>Security lead</td>
<td class={td}>●</td>
<td class={td}></td>
<td class={td}></td>
<td class={td}></td>
</tr>
<tr>
<td class={td}>CEO</td>
<td class={td}></td>
<td class={td}>●</td>
<td class={td}></td>
<td class={td}></td>
</tr>
<tr>
<td class={td}>DPO, legal, CTO</td>
<td class={td}></td>
<td class={td}></td>
<td class={td}>●</td>
<td class={td}></td>
</tr>
<tr>
<td class={td}>Clienti interessati, board (se impatto elevato)</td>
<td class={td}></td>
<td class={td}></td>
<td class={td}></td>
<td class={td}>●</td>
</tr>
</tbody>
</table>
</div>
<h3 id="raci-vendor">Onboarding vendor (sub-processore)</h3>
<div class={tableWrap}>
<table class={tbl}>
<thead>
<tr>
<th class={th} scope="col">Ruolo</th>
<th class={th} scope="col">R</th>
<th class={th} scope="col">A</th>
<th class={th} scope="col">C</th>
<th class={th} scope="col">I</th>
</tr>
</thead>
<tbody>
<tr>
<td class={td}>Operations</td>
<td class={td}>●</td>
<td class={td}></td>
<td class={td}></td>
<td class={td}></td>
</tr>
<tr>
<td class={td}>Legal</td>
<td class={td}></td>
<td class={td}>●</td>
<td class={td}></td>
<td class={td}></td>
</tr>
<tr>
<td class={td}>Security lead, DPO</td>
<td class={td}></td>
<td class={td}></td>
<td class={td}>●</td>
<td class={td}></td>
</tr>
<tr>
<td class={td}>CTO, finance</td>
<td class={td}></td>
<td class={td}></td>
<td class={td}></td>
<td class={td}>●</td>
</tr>
</tbody>
</table>
</div>
<h3 id="raci-dpia">DPIA (per perimetro: LexAura, MediAura, piattaforma)</h3>
<p class="text-sm text-nx-muted sm:text-base">
La matrice RACI non sostituisce il criterio legale (chi è titolare, chi responsabile) definito
in contratti e in <a class={link} href="/codice-etico#tutela-dati-ruoli">§5.1 del codice etico</a
>. Qui: chi coordina lesercizio interno della valutazione dimpatto.
</p>
<div class={tableWrap}>
<table class={tbl}>
<thead>
<tr>
<th class={th} scope="col">Ruolo</th>
<th class={th} scope="col">R</th>
<th class={th} scope="col">A</th>
<th class={th} scope="col">C</th>
<th class={th} scope="col">I</th>
</tr>
</thead>
<tbody>
<tr>
<td class={td}>DPO</td>
<td class={td}>●</td>
<td class={td}></td>
<td class={td}></td>
<td class={td}></td>
</tr>
<tr>
<td class={td}>Legal</td>
<td class={td}></td>
<td class={td}>●</td>
<td class={td}></td>
<td class={td}></td>
</tr>
<tr>
<td class={td}>Product advisor, CTO, security lead</td>
<td class={td}></td>
<td class={td}></td>
<td class={td}>●</td>
<td class={td}></td>
</tr>
<tr>
<td class={td}>CEO</td>
<td class={td}></td>
<td class={td}></td>
<td class={td}></td>
<td class={td}>●</td>
</tr>
</tbody>
</table>
</div>
<h2 id="flussi">5. Flussi decisionali rapidi</h2>
<h2 id="flussi">{page.sectionTitles.flussi}</h2>
<ul>
{
modelloOrganizzativoFlussiDecisionali.map((f) => (
m.flussiDecisionali.map((f) => (
<li>
<strong>{f.titolo}:</strong> {f.testo}
</li>
@ -286,15 +154,15 @@ const tableWrap = 'mt-2 overflow-x-auto';
}
</ul>
<h2 id="controlli">6. Controlli minimi obbligatori (lean)</h2>
<h2 id="controlli">{page.sectionTitles.controlli}</h2>
<ul>
{modelloOrganizzativoControlliLean.map((c) => <li>{c}</li>)}
{m.controlliLean.map((c) => <li>{c}</li>)}
</ul>
<h2 id="kpi">7. KPI essenziali</h2>
<h2 id="kpi">{page.sectionTitles.kpi}</h2>
<ul>
{
modelloOrganizzativoKpi.map((k) => (
m.kpi.map((k) => (
<li>
<strong>{k.area}:</strong> {k.testo}
</li>
@ -302,15 +170,15 @@ const tableWrap = 'mt-2 overflow-x-auto';
}
</ul>
<h2 id="documentazione">8. Documentazione minima da mantenere</h2>
<h2 id="documentazione">{page.sectionTitles.documentazione}</h2>
<ul>
{modelloOrganizzativoDocumentazione.map((d) => <li>{d}</li>)}
{m.documentazione.map((d) => <li>{d}</li>)}
</ul>
<h2 id="piano-30">9. Primo piano operativo (30 giorni)</h2>
<h2 id="piano-30">{page.sectionTitles.piano30}</h2>
<ol>
{
modelloOrganizzativoPiano30.map((p) => (
m.piano30.map((p) => (
<li>
<strong>{p.quando}:</strong> {p.testo}
</li>
@ -318,10 +186,10 @@ const tableWrap = 'mt-2 overflow-x-auto';
}
</ol>
<h2 id="outsourcing">10. Outsourcing consigliato (per restare snelli)</h2>
<h2 id="outsourcing">{page.sectionTitles.outsourcing}</h2>
<ul>
{
modelloOrganizzativoOutsourcing.map((o) => (
m.outsourcing.map((o) => (
<li>
<strong>{o.titolo}:</strong> {o.testo}
</li>
@ -329,9 +197,9 @@ const tableWrap = 'mt-2 overflow-x-auto';
}
</ul>
<h2 id="note">11. Note pratiche e raccomandazioni</h2>
<h2 id="note">{page.sectionTitles.note}</h2>
<ul>
{modelloOrganizzativoNote.map((n) => <li>{n}</li>)}
{m.note.map((n) => <li>{n}</li>)}
</ul>
</div>
</SubpageLayout>

View File

@ -1,18 +1,56 @@
---
import SubpageLayout from '../layouts/SubpageLayout.astro';
import type { SupportedLocale } from '../data/home/navigation';
import { getNews } from '../data/news';
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = (localeMatch?.[1] ?? 'it') as SupportedLocale;
const news = getNews(currentLocale);
const { page, statusBanner, sections, contact } = news;
const localize = (href: string) => {
if (currentLocale === 'it') return href;
if (href.startsWith(`/${currentLocale}`)) return href;
if (href.startsWith('/#')) return `/${currentLocale}${href}`;
if (href.startsWith('/')) return `/${currentLocale}${href}`;
return href;
};
---
<SubpageLayout
title="Novità — NexStudio"
description="Novità, release e articoli da NexStudio."
heading="Novità"
lead="Hub comunicazioni: collegate qui il vostro blog CMS, oppure elencate manualmente le notizie."
title={page.title}
description={page.description}
heading={page.heading}
lead={page.lead}
>
<p>
Quando avrete contenuti da pubblicare (release di prodotto, note di
conformità, eventi), potete sostituire questa pagina con una raccolta
articoli Astro, un redirect verso Medium/Substack, o un headless CMS.
<p class="rounded-xl border border-emerald-500/30 bg-emerald-500/10 px-4 py-3 text-emerald-100">
{statusBanner}
</p>
{
sections.map((s) => (
<>
<h2>{s.title}</h2>
{s.bullets && s.bullets.length > 0 ? (
<ul>
{s.bullets.map((b) => (
<li>
{b.title ? <strong>{b.title}</strong> : null} {b.text}
</li>
))}
</ul>
) : null}
{s.paragraphs?.map((p) => (
<p class="text-nx-muted">{p}</p>
))}
</>
))
}
<p>
{contact.before}
<a href={localize(contact.href)} class="text-sky-300 hover:text-sky-200">{contact.linkLabel}</a>
{contact.after}
</p>
<h2>Archivio</h2>
<p class="text-nx-muted">Nessuna news pubblicata per il momento.</p>
</SubpageLayout>

View File

@ -0,0 +1,18 @@
---
import SubpageLayout from '../../layouts/SubpageLayout.astro';
---
<SubpageLayout
title="Conferma non riuscita — Newsletter NexStudio"
description="Il link di conferma newsletter non è valido o è già stato usato."
heading="Conferma non riuscita"
lead="Il link di conferma non è valido, è scaduto o è già stato utilizzato."
>
<p>
Puoi riprovare iscrivendoti di nuovo dal form newsletter nel footer del
sito: riceverai un nuovo messaggio di conferma.
</p>
<p>
<a class="text-sky-400/90 hover:text-sky-300" href="/">Torna alla home</a>
</p>
</SubpageLayout>

View File

@ -0,0 +1,34 @@
---
import SubpageLayout from '../../layouts/SubpageLayout.astro';
const unsubToken = Astro.url.searchParams.get('unsub')?.trim() ?? '';
const unsubHref = unsubToken
? `/api/newsletter/unsubscribe?token=${encodeURIComponent(unsubToken)}`
: '';
---
<SubpageLayout
title="Iscrizione confermata — Newsletter NexStudio"
description="La tua iscrizione alla newsletter NexStudio è confermata."
heading="Iscrizione confermata"
lead="Il tuo indirizzo email è ora attivo per ricevere aggiornamenti su prodotti e roadmap NexStudio."
>
<p>
Puoi disiscriverti in qualsiasi momento dal link presente in fondo a ogni
messaggio della newsletter.
</p>
{
unsubHref ? (
<p>
Oppure disiscriviti subito:{' '}
<a class="text-sky-400/90 hover:text-sky-300" href={unsubHref}>
Annulla iscrizione
</a>
.
</p>
) : null
}
<p>
<a class="text-sky-400/90 hover:text-sky-300" href="/">Torna alla home</a>
</p>
</SubpageLayout>

View File

@ -0,0 +1,20 @@
---
import SubpageLayout from '../../layouts/SubpageLayout.astro';
---
<SubpageLayout
title="Disiscrizione non riuscita — Newsletter NexStudio"
description="Il link di disiscrizione newsletter non è valido."
heading="Disiscrizione non riuscita"
lead="Il link di disiscrizione non è valido o è incompleto."
>
<p>
Se continui a ricevere messaggi indesiderati, scrivi a
<a class="text-sky-400/90 hover:text-sky-300" href="mailto:privacy@nexstudio.com"
>privacy@nexstudio.com</a
>.
</p>
<p>
<a class="text-sky-400/90 hover:text-sky-300" href="/">Torna alla home</a>
</p>
</SubpageLayout>

View File

@ -0,0 +1,18 @@
---
import SubpageLayout from '../../layouts/SubpageLayout.astro';
---
<SubpageLayout
title="Disiscrizione — Newsletter NexStudio"
description="Disiscrizione dalla newsletter NexStudio completata."
heading="Disiscrizione completata"
lead="Non riceverai più la newsletter di NexStudio su questo indirizzo."
>
<p>
Se ti sei disiscritto per errore, puoi iscriverti di nuovo dal footer del
sito e confermare lemail.
</p>
<p>
<a class="text-sky-400/90 hover:text-sky-300" href="/">Torna alla home</a>
</p>
</SubpageLayout>

View File

@ -4,13 +4,14 @@ import SubpageLayout from '../../layouts/SubpageLayout.astro';
<SubpageLayout
title="Grazie — Newsletter NexStudio"
description="Iscrizione alla newsletter ricevuta."
heading="Grazie"
lead="La richiesta di iscrizione è stata inviata. Controlla la posta se è prevista una conferma (double opt-in)."
description="Richiesta di iscrizione alla newsletter ricevuta."
heading="Controlla la tua email"
lead="Abbiamo ricevuto la richiesta di iscrizione. Per completarla, apri il messaggio di conferma e clicca sul link (double opt-in)."
>
<p>
Se usi un servizio con conferma via email, apri il messaggio e segui il
link per completare liscrizione.
Se non trovi lemail, controlla anche la cartella spam o promozioni. Il link
di conferma scade solo se viene generata una nuova richiesta per lo stesso
indirizzo.
</p>
<p>
<a class="text-sky-400/90 hover:text-sky-300" href="/">Torna alla home</a>

View File

@ -1,50 +1,57 @@
---
import SubpageLayout from '../layouts/SubpageLayout.astro';
import {
privacyCrossReferences,
privacyDocument,
privacyRiferimenti,
privacySections,
} from '../data/privacy';
import type { SupportedLocale } from '../data/home/navigation';
import { getPrivacy } from '../data/privacy';
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = (localeMatch?.[1] ?? 'it') as SupportedLocale;
const privacy = getPrivacy(currentLocale);
const { document: privacyDocument, riferimenti, sections, page } = privacy;
const localize = (href: string) => {
if (currentLocale === 'it') return href;
if (href.startsWith(`/${currentLocale}`)) return href;
if (href.startsWith('/#')) return `/${currentLocale}${href}`;
if (href.startsWith('/')) return `/${currentLocale}${href}`;
return href;
};
const link = 'text-sky-400/90 underline-offset-2 hover:text-sky-300 hover:underline';
const lead =
"Informativa quadro sul trattamento dei dati personali per il portale istituzionale NexStudio (sede operativa: Bangkok, Thailandia) e per le interazioni corporate con i servizi SaaS LexAura e MediAura. " +
privacyCrossReferences.lead;
---
<SubpageLayout
title="Privacy — NexStudio"
description="Informativa privacy quadro NexStudio: sito istituzionale, contatti corporate, rimandi ai dettagli privacy dei SaaS LexAura (Legal Tech) e MediAura (Health Tech)."
heading="Informativa sulla privacy"
lead={lead}
title={page.title}
description={page.description}
heading={page.heading}
lead={page.lead}
>
<div>
<p class="border-l-2 border-sky-500/35 pl-4 text-sm leading-relaxed text-nx-muted">
<span class="font-medium text-nx-fg">Versione</span> {privacyDocument.version}
<span class="font-medium text-nx-fg">{page.versionLabel}</span> {privacyDocument.version}
<span class="text-nx-muted/80"> · </span>
<span class="font-medium text-nx-fg">In vigore dal</span> {privacyDocument.inVigoreDal}
<span class="font-medium text-nx-fg">{page.inForceFromLabel}</span> {privacyDocument.inVigoreDal}
<span class="text-nx-muted/80"> · </span>
<span class="font-medium text-nx-fg">Ultimo aggiornamento</span>{' '}
<span class="font-medium text-nx-fg">{page.lastUpdateLabel}</span>{' '}
{privacyDocument.ultimoAggiornamento}
</p>
<p class="!mt-5 text-sm text-nx-muted sm:text-base">
Documenti connessi:{' '}
{page.relatedDocsLabel}:{' '}
{
privacyRiferimenti.map((r, i) => (
riferimenti.map((r, i) => (
<span>
{i > 0 && <span class="text-nx-muted/80"> · </span>}
<a class={link} href={r.href}>{r.label}</a>
<a class={link} href={localize(r.href)}>{r.label}</a>
</span>
))
}
</p>
<h2 id="indice" class="!mt-8">Indice</h2>
<h2 id="indice" class="!mt-8">{page.indexLabel}</h2>
<ol class="!mt-3 list-decimal space-y-1.5 pl-5 text-sm leading-relaxed sm:text-base">
{
privacySections.map((s) => (
sections.map((s) => (
<li>
<a class={link} href={`#${s.id}`}>{s.title}</a>
</li>
@ -53,7 +60,7 @@ const lead =
</ol>
{
privacySections.map((s) => (
sections.map((s) => (
<section class="mt-10" id={s.id}>
<h2
class="scroll-mt-8 text-lg font-semibold text-nx-fg sm:text-xl"
@ -70,6 +77,9 @@ const lead =
))}
</ul>
) : null}
{s.paragraphsAfterBullets?.map((p) => (
<p class="mt-3 text-sm leading-relaxed text-nx-muted sm:text-base">{p}</p>
))}
</section>
))
}

View File

@ -1,42 +1,54 @@
---
import SubpageLayout from '../layouts/SubpageLayout.astro';
import { termsDocument, termsSections, termsRiferimenti, termsCrossReferences } from '../data/terms';
import type { SupportedLocale } from '../data/home/navigation';
import { getTerms } from '../data/terms';
const path = Astro.url.pathname;
const localeMatch = path.match(/^\/(en|th)(\/|$)/);
const currentLocale = (localeMatch?.[1] ?? 'it') as SupportedLocale;
const terms = getTerms(currentLocale);
const { document: termsDocument, riferimenti, sections, page } = terms;
const localize = (href: string) => {
if (currentLocale === 'it') return href;
if (href.startsWith(`/${currentLocale}`)) return href;
if (href.startsWith('/#')) return `/${currentLocale}${href}`;
if (href.startsWith('/')) return `/${currentLocale}${href}`;
return href;
};
const link = 'text-sky-400/90 underline-offset-2 hover:text-sky-300 hover:underline';
const lead =
"Si applicano alla navigazione e all'uso del sito informativo pubblico di NexStudio (sede: Bangkok, Thailandia). I contratti d'abbonamento o d'acquisto sui prodotti software restano retti da offerte, ordini o accordi separati. " +
termsCrossReferences.leadNote;
---
<SubpageLayout
title="Condizioni duso — NexStudio"
description="Condizioni duso del sito web NexStudio: accettazione, utilizzo lecito, responsabilità, legge applicabile. Bangkok, LexAura e MediAura, servizi B2B per contratto a parte."
heading="Condizioni duso"
lead={lead}
title={page.title}
description={page.description}
heading={page.heading}
lead={page.lead}
>
<div>
<p class="border-l-2 border-sky-500/35 pl-4 text-sm leading-relaxed text-nx-muted">
<span class="font-medium text-nx-fg">Versione</span> {termsDocument.version}
<span class="font-medium text-nx-fg">{page.versionLabel}</span> {termsDocument.version}
<span class="text-nx-muted/80"> · </span>
<span class="font-medium text-nx-fg">In vigore dal</span> {termsDocument.inVigoreDal}
<span class="font-medium text-nx-fg">{page.inForceFromLabel}</span> {termsDocument.inVigoreDal}
</p>
<p class="!mt-5 text-sm text-nx-muted sm:text-base">
Documenti connessi:{' '}
{page.relatedDocsLabel}:{' '}
{
termsRiferimenti.map((r, i) => (
riferimenti.map((r, i) => (
<span>
{i > 0 && <span class="text-nx-muted/80"> · </span>}
<a class={link} href={r.href}>{r.label}</a>
<a class={link} href={localize(r.href)}>{r.label}</a>
</span>
))
}
</p>
<h2 id="indice" class="!mt-8">Indice</h2>
<h2 id="indice" class="!mt-8">{page.indexLabel}</h2>
<ol class="!mt-3 list-decimal space-y-1.5 pl-5 text-sm leading-relaxed sm:text-base">
{
termsSections.map((s) => (
sections.map((s) => (
<li>
<a class={link} href={`#${s.id}`}>{s.title}</a>
</li>
@ -45,7 +57,7 @@ const lead =
</ol>
{
termsSections.map((s) => (
sections.map((s) => (
<section class="mt-10" id={s.id}>
<h2
class="scroll-mt-8 text-lg font-semibold text-nx-fg sm:text-xl"

View File

@ -0,0 +1,5 @@
---
import Page from '../allegati.astro';
---
<Page />

View File

@ -2,4 +2,4 @@
import Page from '../index.astro';
---
<Page />
<Page locale="th" />

View File

@ -0,0 +1,4 @@
---
import Page from '../../newsletter/conferma-errore.astro';
---
<Page />

View File

@ -0,0 +1,4 @@
---
import Page from '../../newsletter/confermata.astro';
---
<Page />

View File

@ -0,0 +1,4 @@
---
import Page from '../../newsletter/disiscrizione-errore.astro';
---
<Page />

View File

@ -0,0 +1,4 @@
---
import Page from '../../newsletter/disiscrizione.astro';
---
<Page />

View File

@ -1,15 +1,26 @@
/** Modulo contatti in pagina (#contatti): mailto o POST Formspree. */
/** Modulo contatti in pagina (#contatti): mailto o POST /api/contact. */
interface ContactFormCfg {
actionUrl: string;
mailto: string;
turnstileSiteKey: string;
notifySubject: string;
successRedirect: string;
successMessage: string;
fieldName: string;
fieldCompany: string;
fieldCountry: string;
fieldDepartment: string;
fieldEmail: string;
fieldMessage: string;
turnstileError: string;
configError: string;
mailtoFallbackSubject: string;
mailtoLabelName: string;
mailtoLabelCompany: string;
mailtoLabelEmail: string;
mailtoLabelCountry: string;
mailtoLabelDepartment: string;
mailtoDefaultDepartment: string;
}
function readCfg(): ContactFormCfg {
@ -22,24 +33,78 @@ function readCfg(): ContactFormCfg {
turnstileSiteKey: '',
notifySubject: '',
successRedirect: '',
successMessage: 'Message sent.',
fieldName: 'name',
fieldCompany: 'company',
fieldCountry: 'country',
fieldDepartment: 'department',
fieldEmail: 'email',
fieldMessage: 'message',
turnstileError: 'Complete anti-spam verification before sending.',
configError: 'Contact form endpoint is not configured.',
mailtoFallbackSubject: 'Website contact',
mailtoLabelName: 'Name',
mailtoLabelCompany: 'Company',
mailtoLabelEmail: 'Email',
mailtoLabelCountry: 'Country',
mailtoLabelDepartment: 'Department',
mailtoDefaultDepartment: 'general',
};
}
return JSON.parse(raw) as ContactFormCfg;
}
function showError(message: string): void {
const ok = document.getElementById('nx-contact-form-success');
if (ok) ok.classList.add('hidden');
const err = document.getElementById('nx-contact-form-error');
if (!err) return;
err.textContent = message;
err.classList.remove('hidden');
}
function showSuccess(message: string): void {
const err = document.getElementById('nx-contact-form-error');
if (err) err.classList.add('hidden');
const ok = document.getElementById('nx-contact-form-success');
if (!ok) return;
ok.textContent = message;
ok.classList.remove('hidden');
}
/** Dopo un POST fallito il token Turnstile è già consumato: ne serve uno nuovo. */
function resetTurnstile(form: HTMLFormElement): void {
const widget = form.querySelector<HTMLElement>('.cf-turnstile');
const turnstile = (
window as Window & {
turnstile?: { reset: (target?: string | HTMLElement) => void };
}
).turnstile;
if (turnstile?.reset) {
try {
if (widget) turnstile.reset(widget);
else turnstile.reset();
return;
} catch {
// fall through
}
}
const tokenInput = form.querySelector<HTMLInputElement>(
'input[name="cf-turnstile-response"]',
);
if (tokenInput) tokenInput.value = '';
}
function bind(): void {
const form = document.getElementById('nx-contact-form') as HTMLFormElement | null;
if (!form) return;
form.addEventListener('submit', (e) => {
form.addEventListener('submit', async (e) => {
const cfg = readCfg();
const err = document.getElementById('nx-contact-form-error');
const ok = document.getElementById('nx-contact-form-success');
if (err) err.classList.add('hidden');
if (ok) ok.classList.add('hidden');
const hasTurnstile =
typeof cfg.turnstileSiteKey === 'string' && cfg.turnstileSiteKey.length > 0;
@ -49,39 +114,69 @@ function bind(): void {
)?.value;
if (!token) {
e.preventDefault();
if (err) {
err.textContent =
'Completa il controllo anti-spam prima di inviare il messaggio.';
err.classList.remove('hidden');
}
showError(cfg.turnstileError);
return;
}
}
if (cfg.actionUrl) {
e.preventDefault();
const submitBtn = form.querySelector<HTMLButtonElement>('button[type="submit"]');
if (submitBtn) submitBtn.disabled = true;
try {
const res = await fetch(cfg.actionUrl, {
method: 'POST',
headers: { Accept: 'application/json' },
body: new FormData(form),
});
const data = (await res.json().catch(() => null)) as
| { ok?: boolean; error?: string; redirect?: string }
| null;
if (!res.ok) {
showError(
(data && typeof data.error === 'string' && data.error) ||
'Invio non riuscito. Riprova tra poco.',
);
if (hasTurnstile) resetTurnstile(form);
return;
}
form.reset();
if (hasTurnstile) resetTurnstile(form);
showSuccess(
cfg.successMessage || 'Messaggio inviato. Ti risponderemo al più presto.',
);
form.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
} catch {
showError('Errore di rete. Controlla la connessione e riprova.');
if (hasTurnstile) resetTurnstile(form);
} finally {
if (submitBtn) submitBtn.disabled = false;
}
return;
}
e.preventDefault();
const fd = new FormData(form);
const name = String(fd.get(cfg.fieldName) ?? '').trim();
const company = String(fd.get(cfg.fieldCompany) ?? '').trim();
const email = String(fd.get(cfg.fieldEmail) ?? '').trim();
const country = String(fd.get(cfg.fieldCountry) ?? '').trim();
const department = String(fd.get(cfg.fieldDepartment) ?? '').trim();
const message = String(fd.get(cfg.fieldMessage) ?? '').trim();
if (cfg.mailto) {
const subject = encodeURIComponent(cfg.notifySubject || 'Contatto sito');
const companyLine = company ? `\nAzienda: ${company}` : '';
const subject = encodeURIComponent(
cfg.notifySubject || cfg.mailtoFallbackSubject || 'Website contact',
);
const companyLine = company
? `\n${cfg.mailtoLabelCompany}: ${company}`
: '';
const body = encodeURIComponent(
`Nome: ${name}${companyLine}\nEmail: ${email}\nPaese: ${country}\n\n${message}`,
`${cfg.mailtoLabelName}: ${name}${companyLine}\n${cfg.mailtoLabelEmail}: ${email}\n${cfg.mailtoLabelCountry}: ${country}\n${cfg.mailtoLabelDepartment}: ${department || cfg.mailtoDefaultDepartment}\n\n${message}`,
);
window.location.href = `mailto:${cfg.mailto}?subject=${subject}&body=${body}`;
return;
}
if (err) {
err.textContent =
'Configura `contactForm.actionUrl` (Formspree) o `contactForm.mailto` in `src/data/home/cta.ts`.';
err.classList.remove('hidden');
}
showError(cfg.configError);
});
}

View File

@ -75,8 +75,11 @@ function measureHeaderAndSync(): void {
}
function bind(): void {
const path = window.location.pathname;
if (path !== '/' && path !== '/index.html') return;
const path = window.location.pathname.replace(/\/$/, '') || '/';
// Home IT (`/`) e home localizzate (`/en`, `/th`).
if (path !== '/' && path !== '/index.html' && path !== '/en' && path !== '/th') {
return;
}
const header = document.querySelector('header');
measureHeaderAndSync();

View File

@ -36,14 +36,15 @@
}
}
/* Canvas particelle: dentro .nx-grid-bg, sopra lo sfondo della sezione */
/* Canvas particelle: fisso al viewport (non alto quanto tutto il documento) */
.nx-particle-layer {
position: absolute;
position: fixed;
inset: 0;
z-index: 0;
display: block;
width: 100%;
height: 100%;
pointer-events: none;
}
/* Subtle grid (Vercel-like) + vignette */
@ -128,11 +129,16 @@
/* Spine: prolunga i flussi hero verso la fascia “Prodotti” (dietro al contenuto) */
.nx-dataflow-spine {
position: absolute;
inset: 0;
top: 0;
left: 0;
right: 0;
/* Solo fascia hero → prodotti: evita SVG filtrato alto quanto tutto il main */
height: min(160vh, 2200px);
z-index: 0;
pointer-events: none;
overflow: hidden;
opacity: 0.46;
contain: paint;
mask-image: linear-gradient(
to bottom,
transparent 0,

View File

@ -9,5 +9,22 @@
},
"observability": {
"enabled": true
}
},
"vars": {
"CONTACT_FORM_FROM_EMAIL": "no-reply@nexstudio.ai",
"CONTACT_FORM_TO_EMAIL": "javaxman69@gmail.com"
},
"d1_databases": [
{
"binding": "NEWSLETTER_DB",
"database_name": "nexstudio-newsletter",
"database_id": "f2b30e34-6e63-4df8-8f92-f0a0dad08cda",
"migrations_dir": "migrations"
}
],
"send_email": [
{
"name": "EMAIL"
}
]
}