diff --git a/.astro/content-assets.mjs b/.astro/content-assets.mjs new file mode 100644 index 0000000..2b8b823 --- /dev/null +++ b/.astro/content-assets.mjs @@ -0,0 +1 @@ +export default new Map(); \ No newline at end of file diff --git a/.astro/content-modules.mjs b/.astro/content-modules.mjs new file mode 100644 index 0000000..2b8b823 --- /dev/null +++ b/.astro/content-modules.mjs @@ -0,0 +1 @@ +export default new Map(); \ No newline at end of file diff --git a/.astro/content.d.ts b/.astro/content.d.ts new file mode 100644 index 0000000..c0082cc --- /dev/null +++ b/.astro/content.d.ts @@ -0,0 +1,199 @@ +declare module 'astro:content' { + export interface RenderResult { + Content: import('astro/runtime/server/index.js').AstroComponentFactory; + headings: import('astro').MarkdownHeading[]; + remarkPluginFrontmatter: Record; + } + interface Render { + '.md': Promise; + } + + export interface RenderedContent { + html: string; + metadata?: { + imagePaths: Array; + [key: string]: unknown; + }; + } +} + +declare module 'astro:content' { + type Flatten = T extends { [K: string]: infer U } ? U : never; + + export type CollectionKey = keyof AnyEntryMap; + export type CollectionEntry = Flatten; + + export type ContentCollectionKey = keyof ContentEntryMap; + export type DataCollectionKey = keyof DataEntryMap; + + type AllValuesOf = T extends any ? T[keyof T] : never; + type ValidContentEntrySlug = AllValuesOf< + ContentEntryMap[C] + >['slug']; + + export type ReferenceDataEntry< + C extends CollectionKey, + E extends keyof DataEntryMap[C] = string, + > = { + collection: C; + id: E; + }; + export type ReferenceContentEntry< + C extends keyof ContentEntryMap, + E extends ValidContentEntrySlug | (string & {}) = string, + > = { + collection: C; + slug: E; + }; + export type ReferenceLiveEntry = { + collection: C; + id: string; + }; + + /** @deprecated Use `getEntry` instead. */ + export function getEntryBySlug< + C extends keyof ContentEntryMap, + E extends ValidContentEntrySlug | (string & {}), + >( + collection: C, + // Note that this has to accept a regular string too, for SSR + entrySlug: E, + ): E extends ValidContentEntrySlug + ? Promise> + : Promise | undefined>; + + /** @deprecated Use `getEntry` instead. */ + export function getDataEntryById( + collection: C, + entryId: E, + ): Promise>; + + export function getCollection>( + collection: C, + filter?: (entry: CollectionEntry) => entry is E, + ): Promise; + export function getCollection( + collection: C, + filter?: (entry: CollectionEntry) => unknown, + ): Promise[]>; + + export function getLiveCollection( + collection: C, + filter?: LiveLoaderCollectionFilterType, + ): Promise< + import('astro').LiveDataCollectionResult, LiveLoaderErrorType> + >; + + export function getEntry< + C extends keyof ContentEntryMap, + E extends ValidContentEntrySlug | (string & {}), + >( + entry: ReferenceContentEntry, + ): E extends ValidContentEntrySlug + ? Promise> + : Promise | undefined>; + export function getEntry< + C extends keyof DataEntryMap, + E extends keyof DataEntryMap[C] | (string & {}), + >( + entry: ReferenceDataEntry, + ): E extends keyof DataEntryMap[C] + ? Promise + : Promise | undefined>; + export function getEntry< + C extends keyof ContentEntryMap, + E extends ValidContentEntrySlug | (string & {}), + >( + collection: C, + slug: E, + ): E extends ValidContentEntrySlug + ? Promise> + : Promise | undefined>; + export function getEntry< + C extends keyof DataEntryMap, + E extends keyof DataEntryMap[C] | (string & {}), + >( + collection: C, + id: E, + ): E extends keyof DataEntryMap[C] + ? string extends keyof DataEntryMap[C] + ? Promise | undefined + : Promise + : Promise | undefined>; + export function getLiveEntry( + collection: C, + filter: string | LiveLoaderEntryFilterType, + ): Promise, LiveLoaderErrorType>>; + + /** Resolve an array of entry references from the same collection */ + export function getEntries( + entries: ReferenceContentEntry>[], + ): Promise[]>; + export function getEntries( + entries: ReferenceDataEntry[], + ): Promise[]>; + + export function render( + entry: AnyEntryMap[C][string], + ): Promise; + + export function reference( + collection: C, + ): import('astro/zod').ZodEffects< + import('astro/zod').ZodString, + C extends keyof ContentEntryMap + ? ReferenceContentEntry> + : ReferenceDataEntry + >; + // Allow generic `string` to avoid excessive type errors in the config + // if `dev` is not running to update as you edit. + // Invalid collection names will be caught at build time. + export function reference( + collection: C, + ): import('astro/zod').ZodEffects; + + type ReturnTypeOrOriginal = T extends (...args: any[]) => infer R ? R : T; + type InferEntrySchema = import('astro/zod').infer< + ReturnTypeOrOriginal['schema']> + >; + + type ContentEntryMap = { + + }; + + type DataEntryMap = { + + }; + + type AnyEntryMap = ContentEntryMap & DataEntryMap; + + type ExtractLoaderTypes = T extends import('astro/loaders').LiveLoader< + infer TData, + infer TEntryFilter, + infer TCollectionFilter, + infer TError + > + ? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError } + : { data: never; entryFilter: never; collectionFilter: never; error: never }; + type ExtractDataType = ExtractLoaderTypes['data']; + type ExtractEntryFilterType = ExtractLoaderTypes['entryFilter']; + type ExtractCollectionFilterType = ExtractLoaderTypes['collectionFilter']; + type ExtractErrorType = ExtractLoaderTypes['error']; + + type LiveLoaderDataType = + LiveContentConfig['collections'][C]['schema'] extends undefined + ? ExtractDataType + : import('astro/zod').infer< + Exclude + >; + type LiveLoaderEntryFilterType = + ExtractEntryFilterType; + type LiveLoaderCollectionFilterType = + ExtractCollectionFilterType; + type LiveLoaderErrorType = ExtractErrorType< + LiveContentConfig['collections'][C]['loader'] + >; + + export type ContentConfig = typeof import("../src/content.config.mjs"); + export type LiveContentConfig = never; +} diff --git a/.astro/data-store.json b/.astro/data-store.json new file mode 100644 index 0000000..11cb064 --- /dev/null +++ b/.astro/data-store.json @@ -0,0 +1 @@ +[["Map",1,2],"meta::meta",["Map",3,4,5,6],"astro-version","5.15.3","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"site\":\"https://valtrix.systems\",\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":{\"type\":\"shiki\",\"excludeLangs\":[\"math\"]},\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true,\"allowedDomains\":[]},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false,\"staticImportMetaEnv\":false,\"chromeDevtoolsWorkspace\":false,\"failOnPrerenderConflict\":false},\"legacy\":{\"collections\":false}}"] \ No newline at end of file diff --git a/.astro/settings.json b/.astro/settings.json new file mode 100644 index 0000000..e50932f --- /dev/null +++ b/.astro/settings.json @@ -0,0 +1,5 @@ +{ + "_variables": { + "lastUpdateCheck": 1762086631503 + } +} \ No newline at end of file diff --git a/.astro/types.d.ts b/.astro/types.d.ts new file mode 100644 index 0000000..f964fe0 --- /dev/null +++ b/.astro/types.d.ts @@ -0,0 +1 @@ +/// diff --git a/.containerignore b/.containerignore new file mode 100644 index 0000000..3ee66e9 --- /dev/null +++ b/.containerignore @@ -0,0 +1,9 @@ +node_modules +.astro +.vscode +.git +npm-debug.log* +yarn-error.log* +.DS_Store +# We rebuild inside the image, no need to send dist +/dist diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..edbc419 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules +package-lock.json +dist +.env +.DS_Store +.vscode \ No newline at end of file diff --git a/.stylelintrc.json b/.stylelintrc.json new file mode 100644 index 0000000..9c7076b --- /dev/null +++ b/.stylelintrc.json @@ -0,0 +1,7 @@ +{ + "rules": { + "at-rule-no-unknown": [true, { + "ignoreAtRules": ["tailwind", "apply", "variants", "responsive", "screen"] + }] + } +} diff --git a/Containerfile b/Containerfile new file mode 100644 index 0000000..d41f523 --- /dev/null +++ b/Containerfile @@ -0,0 +1,25 @@ +# Multi-stage build for Astro static site +FROM node:22-alpine AS builder +WORKDIR /app + +# Install deps +COPY package*.json ./ +RUN npm ci || npm install + +# Copy sources and build static output +COPY . . +RUN npm run build + +# ---- Runtime stage ---- +FROM node:22-alpine AS runtime +WORKDIR /app +ENV NODE_ENV=production +ENV PORT=3000 +ENV WEB_ROOT=/app/dist +ENV TZ=Europe/Berlin +COPY --from=builder /app/dist /app/dist +COPY server.mjs /app/server.mjs +# Drop root: use the pre-created node user +USER node +#EXPOSE 3000 +CMD ["node", "/app/server.mjs"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..5842a9a --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# Valtrix – Corporate Website (Astro + Tailwind) v4 + +- Dark Hero standard, Case Studies integriert +- Logo: `public/logo-valtrix.png` + +## Quickstart +```bash +npm ci +npm run dev +npm run build +``` + +## Container (Podman) + +Container bauen und starten: + +```bash +# Image bauen (nutzt Containerfile im Repo) +podman build -t valtrix-site . + +# Container starten (localhost:8080 → Container:3000) +podman run --rm -p 8080:3000 valtrix-site +``` + +Optional: als Pod via `podman play kube` (setzt lokales Image `localhost/valtrix-site:latest` voraus): + +```bash +# Taggen für den lokalen Registry-Namespace +podman tag valtrix-site localhost/valtrix-site:latest + +# Pod aus YAML starten +podman play kube deploy/podman-kube.yaml +``` + +Hinweise: +- Das Image ist zweistufig: Build in Node 22 Alpine, Runtime in NGINX Alpine. +- Die Seite ist rein statisch (Astro output: static) und benötigt keinen Server‑Side‑Code. diff --git a/astro.config.mjs b/astro.config.mjs new file mode 100644 index 0000000..7723ae0 --- /dev/null +++ b/astro.config.mjs @@ -0,0 +1,2 @@ +import { defineConfig } from 'astro/config'; +export default defineConfig({ site: 'https://valtrix.systems' }); diff --git a/deploy/podman-kube.yaml b/deploy/podman-kube.yaml new file mode 100644 index 0000000..19eb0fa --- /dev/null +++ b/deploy/podman-kube.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Pod +metadata: + name: valtrix-site +spec: + containers: + - name: web + image: localhost/valtrix-site:latest + ports: + - containerPort: 3000 + resources: {} + # Read-only root filesystem is fine for static serving + securityContext: + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + restartPolicy: Never diff --git a/package.json b/package.json new file mode 100644 index 0000000..b8074a3 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "valtrix-site", + "version": "0.4.0", + "private": true, + "type": "module", + "scripts": { + "dev": "astro dev", + "build": "astro build", + "preview": "astro preview" + }, + "dependencies": { + "astro": "^5.15.3" + }, + "devDependencies": { + "autoprefixer": "^10.4.20", + "postcss": "^8.4.47", + "tailwindcss": "^3.4.14" + }, + "engines": { + "node": ">=22" + } +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..99417eb --- /dev/null +++ b/postcss.config.js @@ -0,0 +1 @@ +export default { plugins: { tailwindcss: {}, autoprefixer: {} } }; diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..deefbd2 --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/logo-valtrix.png b/public/logo-valtrix.png new file mode 100644 index 0000000..d6fcdec Binary files /dev/null and b/public/logo-valtrix.png differ diff --git a/public/styles.css b/public/styles.css new file mode 100644 index 0000000..caa6d85 --- /dev/null +++ b/public/styles.css @@ -0,0 +1 @@ +@import "/_astro/src/styles/globals.css"; diff --git a/server.mjs b/server.mjs new file mode 100644 index 0000000..2a4a971 --- /dev/null +++ b/server.mjs @@ -0,0 +1,98 @@ +import http from 'http'; +import fs from 'fs'; +import path from 'path'; +import url from 'url'; + +const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); +const ROOT = process.env.WEB_ROOT || path.resolve(__dirname, 'dist'); +const PORT = Number(process.env.PORT || 3000); + +const MIME = { + '.html': 'text/html; charset=utf-8', + '.css': 'text/css; charset=utf-8', + '.js': 'application/javascript; charset=utf-8', + '.mjs': 'application/javascript; charset=utf-8', + '.json': 'application/json; charset=utf-8', + '.svg': 'image/svg+xml', + '.png': 'image/png', + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.webp': 'image/webp', + '.gif': 'image/gif', + '.ico': 'image/x-icon', + '.txt': 'text/plain; charset=utf-8', + '.map': 'application/json; charset=utf-8', + '.woff': 'font/woff', + '.woff2': 'font/woff2', + '.ttf': 'font/ttf', + '.otf': 'font/otf', + '.eot': 'application/vnd.ms-fontobject', + '.mp4': 'video/mp4', + '.webm': 'video/webm', + '.wasm': 'application/wasm' +}; + +function send(res, status, headers, stream) { + res.writeHead(status, headers); + if (stream) stream.pipe(res); else res.end(); +} + +function isUnder(p, root) { + const rel = path.relative(root, p); + return !!rel && !rel.startsWith('..') && !path.isAbsolute(rel); +} + +const server = http.createServer((req, res) => { + try { + const parsed = url.parse(req.url || '/'); + let pathname = decodeURIComponent(parsed.pathname || '/'); + + // Hard normalize, prevent traversal + pathname = path.normalize(pathname).replace(/^([/\\])*|\/+$/g, '/'); + let fp = path.join(ROOT, pathname); + + // If path is a directory, serve index.html + if (fs.existsSync(fp) && fs.statSync(fp).isDirectory()) { + fp = path.join(fp, 'index.html'); + } + + // If it doesn't exist and path didn't include .html, try appending index.html + if (!fs.existsSync(fp) && !path.extname(fp)) { + fp = path.join(fp, 'index.html'); + } + + // Verify containment in ROOT + if (!isUnder(fp, ROOT) && path.resolve(fp) !== path.resolve(ROOT, 'index.html')) { + return send(res, 403, { 'content-type': 'text/plain; charset=utf-8' }, null); + } + + if (!fs.existsSync(fp) || fs.statSync(fp).isDirectory()) { + return send(res, 404, { 'content-type': 'text/plain; charset=utf-8' }, null); + } + + const ext = path.extname(fp).toLowerCase(); + const type = MIME[ext] || 'application/octet-stream'; + + const headers = { 'content-type': type }; + + // Caching: long cache for assets, no-cache for html + if (ext === '.html') { + headers['cache-control'] = 'no-cache'; + } else if (fp.includes('/_astro/') || fp.startsWith(path.join(ROOT, 'assets'))) { + headers['cache-control'] = 'public, max-age=31536000, immutable'; + } else { + headers['cache-control'] = 'public, max-age=3600'; + } + + const stream = fs.createReadStream(fp); + stream.on('open', () => send(res, 200, headers, stream)); + stream.on('error', () => send(res, 500, { 'content-type': 'text/plain; charset=utf-8' }, null)); + } catch (err) { + send(res, 500, { 'content-type': 'text/plain; charset=utf-8' }, null); + } +}); + +server.listen(PORT, '0.0.0.0', () => { + // eslint-disable-next-line no-console + console.log(`Static server listening on :${PORT}, root: ${ROOT}`); +}); diff --git a/src/layouts/Base.astro b/src/layouts/Base.astro new file mode 100644 index 0000000..2bb097e --- /dev/null +++ b/src/layouts/Base.astro @@ -0,0 +1,56 @@ +--- +import "../styles/globals.css"; +const { title = "Valtrix – Struktur schafft Wert", description = "Cloud, Security & AI Consulting aus Berlin", path = "/" } = Astro.props; +--- + + + + + + {title} + + + + + + + + + +
+ +
+
+ +
+
+
+ Valtrix · Precision. Power. Purpose. +
+ © {new Date().getFullYear()} Valtrix · Impressum · Datenschutz +
+ + + diff --git a/src/pages/cases.astro b/src/pages/cases.astro new file mode 100644 index 0000000..e6ee3ea --- /dev/null +++ b/src/pages/cases.astro @@ -0,0 +1,19 @@ +--- +import Base from "../layouts/Base.astro"; +const items = [ + { slug: "cloud-migration", title: "Sichere Cloud-Migration (AWS/Azure)", summary: "Landing Zone, Zero-Trust, Pipeline & Observability in 6 Wochen." }, + { slug: "k8s-hardening", title: "Kubernetes Hardening & GitOps", summary: "CIS Benchmarks, OPA Policies-as-Code, ArgoCD Rollouts." } +]; +--- + +

Referenzen

+
+ {items.map(i => ( + +

{i.title}

+

{i.summary}

+ Case ansehen +
+ ))} +
+ diff --git a/src/pages/cases/cloud-migration.astro b/src/pages/cases/cloud-migration.astro new file mode 100644 index 0000000..4a0251d --- /dev/null +++ b/src/pages/cases/cloud-migration.astro @@ -0,0 +1,36 @@ +--- +import Base from "../../layouts/Base.astro"; +--- + +

Case Study: Sichere Cloud-Migration

+

Enterprise-Migration von On-Prem nach Cloud mit Fokus auf Sicherheit & Compliance.

+ +
+
+

Ziel

+

Sichere AWS/Azure Landing Zone, segmentiertes Netzwerk, zentralisiertes IAM.

+
+
+

Vorgehen

+

Guardrails, IaC (Terraform/OpenTofu), GitOps, Observability, Cost Controls.

+
+
+

Ergebnis

+

Schnelleres Deployment, Auditfähigkeit, reduzierte Risiken & Kosten.

+
+
+ +
+

Kennzahlen

+
    +
  • 6 Wochen bis produktionsreifer Basis
  • +
  • ~40% weniger manuelle Deployments durch CI/CD
  • +
  • 100% Infrastruktur als Code & Policies-as-Code
  • +
+
+ +
+
Ähnliches Projekt geplant?
+ Jetzt anfragen +
+ diff --git a/src/pages/cases/k8s-hardening.astro b/src/pages/cases/k8s-hardening.astro new file mode 100644 index 0000000..bd39528 --- /dev/null +++ b/src/pages/cases/k8s-hardening.astro @@ -0,0 +1,48 @@ +--- +import Base from "../../layouts/Base.astro"; +--- + +

Case Study: Kubernetes Hardening & GitOps

+

Produktionsreifes K8s‑Setup mit Härtung nach CIS, durchgängigen Policies‑as‑Code und GitOps‑Deployments.

+ +
+
+

Ziel

+

Sicheres Cluster‑Baseline: identitätszentrierte Zugriffe, Netzwerk‑Segmentierung, saubere Supply Chain.

+
+
+

Vorgehen

+

CIS Benchmarks, OPA/Gatekeeper oder Kyverno Policies, Pod Security Standards, signierte Artefakte & GitOps (Argo CD).

+
+
+

Ergebnis

+

Reproduzierbare Deployments, Policy‑konforme Releases, Auditfähigkeit und reduzierte Angriffsfläche.

+
+
+ +
+

Kennzahlen

+
    +
  • 100% deklarative Deployments (GitOps, Pull‑basiert)
  • +
  • ~80% der CIS‑Kontrollen automatisiert überprüfbar
  • +
  • Policy‑Breaks verhindern riskante Manifeste vor dem Rollout
  • +
  • Signierte Container‑Images & SBOM für Kernservices
  • +
+
+ +
+
+

GitOps Setup

+

Mehrstufige Environments (dev/stage/prod) mit Branch/Tag‑Strategie, Kustomize‑Overlays und PR‑basierten Changes.

+
+
+

Cluster Hardening

+

RBAC Least‑Privilege, NetworkPolicies default‑deny, Pod Security, Image‑Scanning, Secrets‑Management.

+
+
+ +
+
Ähnliches Projekt geplant?
+ Jetzt anfragen +
+ diff --git a/src/pages/datenschutz.astro b/src/pages/datenschutz.astro new file mode 100644 index 0000000..272c8f6 --- /dev/null +++ b/src/pages/datenschutz.astro @@ -0,0 +1,62 @@ +--- +import Base from "../layouts/Base.astro"; +const updated = new Date().toLocaleDateString('de-DE', { year: 'numeric', month: 'long', day: '2-digit' }); +--- + +

Datenschutzhinweise

+ +

Diese Hinweise informieren darüber, wie wir personenbezogene Daten beim Besuch dieser Website verarbeiten. Die Website ist als statische Seite umgesetzt; es werden keine Tracking‑ oder Marketing‑Cookies eingesetzt und keine externen Ressourcen (z. B. Google Fonts, Analytics, Formdienste) geladen.

+ +
+

1. Verantwortlicher

+

Verantwortlicher im Sinne der DSGVO ist: Valtrix. Anschrift siehe Impressum. E‑Mail: kontakt@valtrix.systems

+
+ +
+

2. Hosting & Bereitstellung

+

Diese Website wird als statische Anwendung ausgeliefert. Die Auslieferung erfolgt über Infrastruktur innerhalb der EU.

+
+ +
+

3. Server‑Logfiles

+

Beim Aufruf der Webseite werden durch den zuständigen Server/Proxy systembedingt Zugriffsdaten verarbeitet (z. B. IP‑Adresse, Datum/Uhrzeit, angeforderte Ressource/URL, Referrer, User‑Agent, Statuscode). Die Verarbeitung dient dem Betrieb, der Sicherheit und Fehleranalyse.

+
    +
  • Rechtsgrundlage: Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse am sicheren Betrieb)
  • +
  • Speicherdauer: Protokolle werden regelmäßig rotiert und gelöscht, sofern keine sicherheitsrelevante Auswertung erforderlich ist.
  • +
+
+ +
+

4. Cookies & externe Inhalte

+

Wir verwenden keine Tracking‑ oder Marketing‑Cookies. Es werden keine externen Inhalte, Schriftarten oder Skripte von Drittanbietern nachgeladen.

+
+ +
+

5. Kontaktaufnahme

+

Bei Kontakt per E‑Mail oder über die bereitgestellte mailto‑Funktion verarbeiten wir die mitgeteilten Daten (z. B. Name, E‑Mail, Nachricht) zur Bearbeitung der Anfrage.

+
    +
  • Rechtsgrundlage: Art. 6 Abs. 1 lit. b DSGVO (vorvertragliche/vertragliche Kommunikation) bzw. lit. f (allgemeine Anfragen)
  • +
  • Speicherdauer: Wir löschen Anfragen, sobald sie erledigt sind, es sei denn, gesetzliche Aufbewahrungspflichten verlangen eine längere Speicherung.
  • +
+
+ +
+

6. Empfänger & Auftragsverarbeiter

+

Zur Bereitstellung der Website können wir technische Dienstleister (z. B. Hosting/Operations) einsetzen. Diese verarbeiten Daten ausschließlich nach unserer Weisung (Art. 28 DSGVO). Eine Übermittlung in Drittländer findet nicht statt, sofern nicht ausdrücklich angegeben.

+
+ +
+

7. Ihre Rechte

+

Sie haben gegenüber uns Rechte auf Auskunft (Art. 15 DSGVO), Berichtigung (Art. 16), Löschung (Art. 17), Einschränkung (Art. 18), Datenübertragbarkeit (Art. 20) sowie Widerspruch (Art. 21). Zudem besteht ein Beschwerderecht bei einer Datenschutz‑Aufsichtsbehörde (Art. 77).

+
+ +
+

8. Sicherheit

+

Wir treffen technische und organisatorische Maßnahmen gemäß Art. 32 DSGVO (u. a. TLS‑Verschlüsselung, Härtung der Infrastruktur, geringste Privilegien). Die Website lädt keine Ressourcen von Dritten.

+
+ +
+

9. Aktualität dieser Hinweise

+

Stand: {updated}. Wir passen diese Hinweise an, wenn sich Technik, Rechtslage oder unser Angebot ändern.

+
+ diff --git a/src/pages/impressum.astro b/src/pages/impressum.astro new file mode 100644 index 0000000..f129e77 --- /dev/null +++ b/src/pages/impressum.astro @@ -0,0 +1,11 @@ +--- +import Base from "../layouts/Base.astro"; +--- + +

Impressum

+

VALTRIX GmbH i.G.

+

Am Wassergraben 4, 10179 Berlin

+

Amtsgericht Charlottenburg. Handelregister: HRB-XXXX

+

Umsatzsteuer-ID: XXXXXXX

+

Platzhalter für Unternehmensangaben (Sitz, Register, Vertretungsberechtigte, Kontakt).

+ diff --git a/src/pages/index.astro b/src/pages/index.astro new file mode 100644 index 0000000..b1ade09 --- /dev/null +++ b/src/pages/index.astro @@ -0,0 +1,73 @@ +--- +import Base from "../layouts/Base.astro"; +const benefits = [ + { title: "Zero-Trust & Hardening", text: "Security-by-Design, CIS/BSI-Guidelines, automatisierte Policies." }, + { title: "Cloud Foundations", text: "Landing Zones, Identity, Observability – sauber & skalierbar." }, + { title: "Automatisierung", text: "IaC/CI/CD, GitOps, wiederholbar & auditfähig." } +]; +const features = [ + { title: "Cloud & DevOps", text: "Plan, Build & Run – effizient, sicher, skalierbar." }, + { title: "Security & Compliance", text: "BSI/ISO-aligned, Zero-Trust, Audits, Policies-as-Code." }, + { title: "AI & Automation", text: "RAG, Agenten & Process Automation mit messbarem Outcome." } +]; +--- + +
+
+

+ Security-first Cloud Consulting. +

+

+ Wir verbinden Struktur und Wert: sichere Cloud-Architekturen, Automatisierung und Compliance – pragmatisch und messbar. +

+ +
+
DSGVO / EU-Hosting
+
BSI/ISO-orientiert
+
Auditfähig
+
+
+
+ +
+ {benefits.map(b => ( +
+

{b.title}

+

{b.text}

+
+ ))} +
+ +
+

Leistungen

+
+ {features.map(f => ( +
+

{f.title}

+

{f.text}

+ Mehr erfahren +
+ ))} +
+
+ +
+

Unser Ansatz

+
    +
  1. Assess: Risiko & Reifegrad → Quick-Scan.
  2. +
  3. Design: Zielarchitektur & Guardrails.
  4. +
  5. Build: IaC, Pipelines, Policies-as-Code.
  6. +
  7. Run: Monitoring, Audits, Kostentransparenz.
  8. +
+
+ +
+
+
Sichere Cloud – pragmatisch umgesetzt.
+ Projekt anfragen +
+
+ diff --git a/src/pages/kontakt.astro b/src/pages/kontakt.astro new file mode 100644 index 0000000..aa2b1c1 --- /dev/null +++ b/src/pages/kontakt.astro @@ -0,0 +1,27 @@ +--- +import Base from "../layouts/Base.astro"; +--- + +

Kontakt

+
+ + + + +
+

Wir verwenden keine externen Dienste. Ihre Anfrage wird in Ihrem E‑Mail‑Programm geöffnet.

+ + diff --git a/src/pages/leistungen.astro b/src/pages/leistungen.astro new file mode 100644 index 0000000..50602eb --- /dev/null +++ b/src/pages/leistungen.astro @@ -0,0 +1,20 @@ +--- +import Base from "../layouts/Base.astro"; +--- + +

Leistungen

+
+
+

Security & Compliance

+

Zero-Trust, IAM, Hardening, Audits, BSI/ISO 27001-orientierte Prozesse, Policies-as-Code.

+
+
+

Cloud & DevOps

+

Landing Zones, GitOps, Observability, Kostensteuerung – reproduzierbar & skalierbar.

+
+
+

AI & Automation

+

RAG/Agenten, Automatisierung von Betriebsprozessen, sichere Integration in bestehende Plattformen.

+
+
+ diff --git a/src/pages/sicherheit.astro b/src/pages/sicherheit.astro new file mode 100644 index 0000000..304914d --- /dev/null +++ b/src/pages/sicherheit.astro @@ -0,0 +1,11 @@ +--- +import Base from "../layouts/Base.astro"; +--- + +

Sicherheit & Compliance

+
    +
  • Datensouverän: EU-Hosting, DSGVO-konform
  • +
  • BSI/ISO 27001-orientierte Prozesse
  • +
  • Security-by-Design, regelmäßige Audits
  • +
+ diff --git a/src/pages/ueber-uns.astro b/src/pages/ueber-uns.astro new file mode 100644 index 0000000..b2d71c7 --- /dev/null +++ b/src/pages/ueber-uns.astro @@ -0,0 +1,7 @@ +--- +import Base from "../layouts/Base.astro"; +--- + +

Über uns

+

Wir sind ein Team aus Cloud-, Security- und AI-Engineers. Wir bauen robuste Systeme – pragmatisch und messbar.

+ diff --git a/src/styles/globals.css b/src/styles/globals.css new file mode 100644 index 0000000..95ca897 --- /dev/null +++ b/src/styles/globals.css @@ -0,0 +1,57 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* Typography & base */ +body { @apply font-sans text-text; } +.fade-in { animation: fadeIn .6s ease-out both; } +.rise-in { animation: riseIn .7s ease-out both; } +@keyframes fadeIn { from { opacity:0 } to { opacity:1 } } +@keyframes riseIn { from{ opacity:0; transform: translateY(12px) } to { opacity:1; transform:translateY(0) } } + +/* Light/Dark surface defaults */ +[data-theme="dark"] body { background:#0B1220; color:#E5E7EB } +.card { background:#ffffff; } +[data-theme="dark"] .card { background:#0F1B2F; border-color:#1F2A44 } +[data-theme="dark"] a { color:#A5D8FF } + +/* Subtle aurora glow background (no external assets) */ +body::before, +body::after { + content: ""; + position: fixed; + inset: 0; + pointer-events: none; + z-index: -1; +} + +/* Glow blobs */ +body::before { + background: + radial-gradient(closest-side at 20% 10%, rgba(0,184,217,0.15), transparent 60%), + radial-gradient(closest-side at 80% 0%, rgba(13,71,161,0.10), transparent 60%); + filter: blur(40px); +} +[data-theme="dark"] body::before { + background: + radial-gradient(closest-side at 15% 10%, rgba(0,184,217,0.20), transparent 60%), + radial-gradient(closest-side at 85% 0%, rgba(13,71,161,0.18), transparent 60%); +} + +/* Aurora sweep + subtle vignette */ +body::after { + background: + radial-gradient(120% 80% at 50% 8%, rgba(13,71,161,0.10), transparent 50%), + conic-gradient(from 200deg at 70% 25%, rgba(0,184,217,0.10), transparent 25%, rgba(13,71,161,0.06), transparent 55%, rgba(0,184,217,0.08), transparent 80%); +} +[data-theme="dark"] body::after { + background: + radial-gradient(140% 90% at 50% 2%, rgba(13,71,161,0.20), transparent 55%), + conic-gradient(from 200deg at 70% 25%, rgba(0,184,217,0.16), transparent 25%, rgba(13,71,161,0.10), transparent 55%, rgba(0,184,217,0.12), transparent 80%); +} + +/* Dark mode readability overrides */ +[data-theme="dark"] .text-textMuted { color: #94A3B8; } +[data-theme="dark"] .text-primary { color: #60A5FA; } +[data-theme="dark"] .border-primary { border-color: #60A5FA; } +[data-theme="dark"] .bg-graybg { background-color: #0F1B2F; } diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..d973c26 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,17 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ["./src/**/*.{astro,html,md,mdx,js,jsx,ts,tsx,vue}"], + theme: { + extend: { + colors: { + primary: "#0D47A1", + accent: "#00B8D9", + graybg: "#F3F4F6", + text: "#111827", + textMuted: "#374151" + }, + borderRadius: { brand: "8px" } + } + }, + plugins: [] +};