feat(moritzgrafde): rewrite personal website with Vue 3, Vite, and Tailwind CSS
- Replace legacy Hugo static site generator and hugo-goa theme with a modern Vue 3 + Vite + TypeScript application - Implement Clean Enterprise design system using Tailwind CSS with light/dark mode support - Position website around Cloud Platform & Infrastructure Leadership (GCP & AWS central platforms, Kubernetes at scale, enterprise spin-off governance, and systems depth) without chronological resumes - Add 6 comprehensive capability dossiers and verified credential badges (CKA, PSM II, M.Sc.) - Implement interactive skill matrix with real-time text search and category filtering - Add engineering philosophy section, contact channels with one-click email copying, and Impressum modal - Configure multi-stage Dockerfile (Node builder to Nginx runtime) with custom gzip, caching, and security headers - Update build scripts (Makefile, build.sh), .gitignore, and AGENTS.md documentation across the monorepo
@@ -75,7 +75,7 @@ This repository is divided into distinct domains, each with its own `AGENTS.md`
|
||||
* **Purpose**: Personal homepage and portfolio for Moritz Graf (`https://moritzgraf.de`).
|
||||
* **Manifest**: `k8s/web/moritzgrafde.yaml` (Namespace: `web`).
|
||||
* **Image**: `registry.haumdaucher.de/moritzgrafde:latest`.
|
||||
* **Key Rules**: Static Hugo website using `hugo-goa` theme served via Nginx. Migrated from standalone `moritzgraf` repository into this monorepo.
|
||||
* **Key Rules**: Executive portfolio built with Vue 3, Vite, TypeScript, and Tailwind CSS served via Nginx in Docker. Migrated from legacy Hugo to a modern reactive platform showcase.
|
||||
* **Read**: [projects/moritzgrafde/AGENTS.md](./projects/moritzgrafde/AGENTS.md)
|
||||
|
||||
## Global Constraints
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
**/.DS_Store
|
||||
.hugo_build.lock
|
||||
node_modules/
|
||||
dist/
|
||||
.DS_Store
|
||||
*.local
|
||||
*.tsbuildinfo
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
@@ -1,31 +1,37 @@
|
||||
# AGENTS.md — Moritz Graf Personal Website (`moritzgrafde`)
|
||||
|
||||
> [!NOTE]
|
||||
> **Domain**: Personal portfolio and homepage for Moritz Graf.
|
||||
> **Domain**: Personal executive portfolio and capabilities showcase for Moritz Graf.
|
||||
> **Live Production URL**: `https://moritzgraf.de`
|
||||
> **Kubernetes Manifest**: `k8s/web/moritzgrafde.yaml` (Namespace: `web`)
|
||||
> **Container Image**: `registry.haumdaucher.de/moritzgrafde:latest`
|
||||
> **Source Location**: `projects/moritzgrafde/` (Migrated from standalone `moritzgraf` repo)
|
||||
> **Source Location**: `projects/moritzgrafde/`
|
||||
|
||||
---
|
||||
|
||||
## 🏛️ Architecture & Build Pipeline
|
||||
## 🏛️ Architecture & Tech Stack
|
||||
|
||||
1. **Stack**:
|
||||
- Static site generated with [Hugo](https://gohugo.io/) using the `hugo-goa` theme.
|
||||
- Served via lightweight `nginx` container (`Dockerfile`).
|
||||
- The compiled static site is maintained in `public/`.
|
||||
- **Frontend Framework**: Vue 3 (Composition API, `<script setup lang="ts">`).
|
||||
- **Build Tooling**: Vite 6 + TypeScript (`vue-tsc`).
|
||||
- **Styling**: Tailwind CSS v4 with Clean Enterprise / Executive design system (light/dark dual mode).
|
||||
- **Icons**: Lucide Vue Next.
|
||||
- **Serving Runtime**: Nginx (`nginx:alpine`) configured with gzip compression, SPA history routing fallback, security headers, and asset caching.
|
||||
|
||||
2. **Container Build & Architecture**:
|
||||
- The target Kubernetes node `haumdaucher` runs on **`linux/amd64`**.
|
||||
- Build command:
|
||||
- Multi-stage Dockerfile (`node:22-alpine` builder → `nginx:alpine` runtime).
|
||||
- Target architecture: **`linux/amd64`**.
|
||||
- Build commands:
|
||||
```bash
|
||||
docker build --platform linux/amd64 -t registry.haumdaucher.de/moritzgrafde:latest .
|
||||
docker push registry.haumdaucher.de/moritzgrafde:latest
|
||||
cd projects/moritzgrafde
|
||||
make compile # Local static bundle build to dist/
|
||||
make build # Docker multi-stage build (linux/amd64)
|
||||
make push # Push to registry.haumdaucher.de
|
||||
make deploy # Rollout restart on Kubernetes
|
||||
```
|
||||
|
||||
3. **Deployment**:
|
||||
- Deployed to the Kubernetes cluster in namespace `web`.
|
||||
3. **Kubernetes Deployment**:
|
||||
- Deployed to the Kubernetes cluster in namespace `web` (`k8s/web/moritzgrafde.yaml`).
|
||||
- Ingress controller: Traefik, Let's Encrypt SSL via `cert-manager`.
|
||||
- Rollout restart:
|
||||
```bash
|
||||
@@ -37,9 +43,38 @@
|
||||
|
||||
## 📁 Repository Structure
|
||||
|
||||
- `content/`: Hugo content markdown files.
|
||||
- `themes/hugo-goa/`: Hugo Goa theme layouts and partials.
|
||||
- `public/`: Pre-rendered production assets and HTML ready for the Nginx container.
|
||||
- `Dockerfile`: Minimal Nginx container copying `./public/` to `/usr/share/nginx/html/`.
|
||||
- `build.sh`: Convenience build, push, and redeploy script.
|
||||
- `config.toml`: Hugo site configuration.
|
||||
```
|
||||
projects/moritzgrafde/
|
||||
├── Dockerfile # Multi-stage container build (Node builder -> Nginx runtime)
|
||||
├── Makefile # Standard make targets (compile, dev, build, push, deploy)
|
||||
├── build.sh # Automated build, push, and Kubernetes redeploy script
|
||||
├── nginx.conf # Nginx configuration (gzip, caching, security headers)
|
||||
├── package.json # Dependencies and build scripts
|
||||
├── vite.config.ts # Vite configuration with Vue and Tailwind v4 plugins
|
||||
├── tsconfig.json # TypeScript configuration with @/* path aliases
|
||||
├── index.html # HTML entrypoint with metadata and Google Fonts
|
||||
├── public/
|
||||
│ └── headshot.jpg # Portrait image (drop-in replaceable)
|
||||
└── src/
|
||||
├── main.ts # Vue app initialization
|
||||
├── App.vue # Top-level application layout
|
||||
├── style.css # Tailwind CSS v4 entrypoint & base styles
|
||||
├── types.ts # TypeScript interfaces for portfolio data models
|
||||
├── data/
|
||||
│ └── portfolioData.ts # Single source of truth for capabilities, skills, and links
|
||||
└── components/
|
||||
├── Navbar.vue # Sticky header with navigation & dark mode switcher
|
||||
├── Hero.vue # Executive hero with portrait, credentials & CTAs
|
||||
├── ImpactStats.vue # Scale & impact metrics (Multi-Cloud, Spin-Off, K8s, 12+ Years)
|
||||
├── CapabilityDossiers.vue # 6 deep capability pillars
|
||||
├── SkillMatrix.vue # Interactive skill search & category filter
|
||||
├── EngineeringPhilosophy.vue # Declarative Everything, Self-Service, Systems Thinking
|
||||
├── ContactSection.vue # Direct email copy button & verified professional links
|
||||
└── ImpressumModal.vue # Legal notice & privacy modal
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🖼️ Updating the Portrait Image
|
||||
|
||||
To update the portrait image, simply replace `projects/moritzgrafde/public/headshot.jpg` with the new photo (JPEG/PNG) and run `make compile && make build && make push && make deploy`.
|
||||
|
||||
@@ -1,2 +1,19 @@
|
||||
FROM nginx
|
||||
COPY ./public/ /usr/share/nginx/html/
|
||||
# Build stage
|
||||
FROM node:22-alpine AS builder
|
||||
WORKDIR /app
|
||||
|
||||
# Copy dependency definitions and install
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
|
||||
# Copy source files and compile static bundle
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# Production runtime stage
|
||||
FROM nginx:alpine
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
@@ -5,12 +5,15 @@ PLATFORM ?= linux/amd64
|
||||
|
||||
IMAGE ?= $(REGISTRY)/$(IMAGE_NAME):$(TAG)
|
||||
|
||||
.PHONY: compile build tag push deploy all
|
||||
.PHONY: compile dev build tag push deploy all
|
||||
|
||||
all: compile build tag push deploy
|
||||
|
||||
compile:
|
||||
hugo
|
||||
npm run build
|
||||
|
||||
dev:
|
||||
npm run dev
|
||||
|
||||
build:
|
||||
docker build --platform $(PLATFORM) -t $(IMAGE_NAME):$(TAG) .
|
||||
|
||||
@@ -1,16 +1,31 @@
|
||||
# website as docker image
|
||||
# Moritz Graf Personal Website
|
||||
|
||||
Build container and push to registry
|
||||
Personal executive portfolio and capabilities showcase for **Moritz Graf** (`https://moritzgraf.de`).
|
||||
|
||||
Built with **Vue 3**, **Vite**, **TypeScript**, and **Tailwind CSS**. Deployed via GitOps to private Kubernetes infrastructure.
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
build.sh
|
||||
cat build.sh # to execute commands seperately
|
||||
# Install dependencies
|
||||
npm install
|
||||
|
||||
# Start development server
|
||||
npm run dev
|
||||
|
||||
# Typecheck and build static production bundle to dist/
|
||||
npm run build
|
||||
```
|
||||
|
||||
Then ssh to the server and redeploy container:
|
||||
## Production Build & Deploy
|
||||
|
||||
```bash
|
||||
ssh moritzgraf.de
|
||||
cd /home/docker/container/
|
||||
docker-compose-fullupgrade moritzgrafde
|
||||
# Build static site and container image
|
||||
make build
|
||||
|
||||
# Push to private registry
|
||||
make push
|
||||
|
||||
# Trigger rolling restart on Kubernetes
|
||||
make deploy
|
||||
```
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
title: "{{ replace .TranslationBaseName "-" " " | title }}"
|
||||
date: {{ .Date }}
|
||||
draft: true
|
||||
---
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
CONTAINER_NAME="moritzgrafde"
|
||||
DEPLOYMENT="moritzgrafde"
|
||||
NAMESPACE="web"
|
||||
|
||||
hugo
|
||||
npm run build
|
||||
|
||||
docker build --platform linux/amd64 -t ${CONTAINER_NAME}:latest -t registry.haumdaucher.de/${CONTAINER_NAME}:latest .
|
||||
docker push registry.haumdaucher.de/${CONTAINER_NAME}:latest
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
## Basic Configuration
|
||||
|
||||
baseurl = "https://moritzgraf.de"
|
||||
builddrafts = false
|
||||
canonifyurls = false
|
||||
languageCode = "en-US"
|
||||
|
||||
contentdir = "content"
|
||||
layoutdir = "layouts"
|
||||
publishdir = "public"
|
||||
|
||||
author = "Moritz Graf"
|
||||
title = "Moritz Graf"
|
||||
|
||||
theme = "hugo-goa"
|
||||
|
||||
## Hugo Built-in Features
|
||||
#disqusShortname = "https-shenoybr-github-io-hugo-goa-demo" # Comment out to disable Disqus.
|
||||
#googleAnalytics = "XYZ"
|
||||
#enableRobotsTXT = true
|
||||
|
||||
## Site Settings
|
||||
[params]
|
||||
author = "Moritz Graf"
|
||||
intro = "- - - - - - - - -"
|
||||
description = "- - - - - - - - -"
|
||||
authorimage = "headshot.jpg"
|
||||
dateformat = "2 Jan 2006"
|
||||
|
||||
## Site Meta Settings
|
||||
[params.meta]
|
||||
description = "Online business card - Moritz Graf"
|
||||
keywords = "monitoring,ansible,configuration management,icinga2,grafana,ELK-Stack,prometheus,kubernetes,k3s,cncf,grafana,boto3,aws,terraform,terragrunt,atlantis,serverless,packer,loki,kustomize,jenkins,dependabot,oauth"
|
||||
|
||||
## Social Accounts
|
||||
[params.social]
|
||||
github = "iptizer"
|
||||
#instagram = "<username>"
|
||||
linkedin = "moritz-graf-92abaa91"
|
||||
twitter = "TheIPtizer"
|
||||
#facebook = "<username>"
|
||||
#google = "<username>"
|
||||
stackoverflow = "7779723"
|
||||
xing = "Moritz_Graf4"
|
||||
email = "contact-moritz@moritzgraf.de"
|
||||
|
||||
## Extras
|
||||
[params.extra]
|
||||
copyright = "© 2021. Moritz Graf. [Some Rights Reserved](http://creativecommons.org/licenses/by/3.0/)."
|
||||
poweredby = false
|
||||
highlightjs = true
|
||||
|
||||
## Main Menu
|
||||
# [[menu.main]]
|
||||
# name = "blog"
|
||||
# weight = 100
|
||||
# identifier = "blog"
|
||||
# url = "http://iptize.it"
|
||||
#[[menu.main]]
|
||||
# name = "about"
|
||||
# identifier = "about"
|
||||
# weight = 200
|
||||
# url = "/about/"
|
||||
#[[menu.main]]
|
||||
# name = "coderag"
|
||||
# identifier = "coderag"
|
||||
# weight = 300
|
||||
# url = "/coderag/"
|
||||
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Moritz Graf — Cloud Platform & Infrastructure Leader</title>
|
||||
<meta name="description" content="Moritz Graf — Cloud Platform & Infrastructure Leader specializing in GCP & AWS Central Platforms, Kubernetes at scale, Infrastructure as Code, and Enterprise Spin-off Governance." />
|
||||
<meta name="keywords" content="Moritz Graf, Cloud Platform Engineer, GCP, AWS, Kubernetes, CKA, PSM II, Terraform, Terragrunt, Platform Engineering, DevOps, GitOps, Central Platform, Regensburg, Germany" />
|
||||
<meta name="author" content="Moritz Graf" />
|
||||
|
||||
<!-- Open Graph / Meta -->
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://moritzgraf.de/" />
|
||||
<meta property="og:title" content="Moritz Graf — Cloud Platform & Infrastructure Leader" />
|
||||
<meta property="og:description" content="Specializing in GCP & AWS Central Platforms, Kubernetes at scale, IaC, and Enterprise Spin-off Governance. Grounded in low-level systems & maker craft." />
|
||||
<meta property="og:image" content="https://moritzgraf.de/headshot.jpg" />
|
||||
|
||||
<link rel="icon" type="image/jpeg" href="/headshot.jpg" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
|
||||
</head>
|
||||
<body class="bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-slate-100 transition-colors duration-200 antialiased font-sans">
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,30 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_proxied any;
|
||||
gzip_types text/plain text/css text/xml application/javascript application/json application/xml+rss image/svg+xml;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# Cache static assets
|
||||
location ~* \.(?:css|js|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc|woff2|woff)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "moritzgrafde",
|
||||
"private": true,
|
||||
"version": "2.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc -b && vite build",
|
||||
"preview": "vite preview",
|
||||
"typecheck": "vue-tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"lucide-vue-next": "^0.468.0",
|
||||
"vue": "^3.5.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.0.9",
|
||||
"@types/node": "^22.10.2",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"tailwindcss": "^4.0.9",
|
||||
"typescript": "^5.7.2",
|
||||
"vite": "^6.2.0",
|
||||
"vue-tsc": "^2.2.0"
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
|
||||
<meta name="description" content="Online business card - Moritz Graf">
|
||||
<meta name="keywords" content="monitoring,ansible,configuration management,icinga2,grafana,ELK-Stack,prometheus,kubernetes,k3s,cncf,grafana,boto3,aws,terraform,terragrunt,atlantis,serverless,packer,loki,kustomize,jenkins,dependabot,oauth">
|
||||
|
||||
<base href="https://moritzgraf.de/">
|
||||
|
||||
<title>Moritz Graf</title>
|
||||
|
||||
<meta name="generator" content="Hugo 0.160.1">
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato:300,400|Roboto+Slab:400,700|Roboto:300,300i,400,400i,500,500i,700,700i">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="https://moritzgraf.de/css/main.css">
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
</head>
|
||||
<body lang="en-US">
|
||||
<div class="container">
|
||||
|
||||
|
||||
<section id="error-pane" class="row text-center error">
|
||||
<h1 class="text-center">404</h1>
|
||||
<p>Sorry! That page does not exist. It may have been removed or moved to a new section on the site. Click <a href="/">here</a> go back to the home page.</p>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
<footer class="row text-center footer">
|
||||
<hr />
|
||||
|
||||
<h6 class="text-center copyright">© 2021. Moritz Graf. <a href="http://creativecommons.org/licenses/by/3.0/">Some Rights Reserved</a>.</h6>
|
||||
|
||||
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
hljs.initHighlightingOnLoad();
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.3 KiB |
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<TileColor>#ffc40d</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
@@ -1,89 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
|
||||
<meta name="description" content="Online business card - Moritz Graf">
|
||||
<meta name="keywords" content="monitoring,ansible,configuration management,icinga2,grafana,ELK-Stack,prometheus,kubernetes,k3s,cncf,grafana,boto3,aws,terraform,terragrunt,atlantis,serverless,packer,loki,kustomize,jenkins,dependabot,oauth">
|
||||
|
||||
<base href="https://moritzgraf.de/">
|
||||
|
||||
<title>Moritz Graf</title>
|
||||
|
||||
<meta name="generator" content="Hugo 0.160.1">
|
||||
|
||||
|
||||
<link href="https://moritzgraf.de/categories/index.xml" rel="alternate" type="application/rss+xml" title="Moritz Graf" />
|
||||
<link href="https://moritzgraf.de/categories/index.xml" rel="feed" type="application/rss+xml" title="Moritz Graf" />
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato:300,400|Roboto+Slab:400,700|Roboto:300,300i,400,400i,500,500i,700,700i">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="https://moritzgraf.de/css/main.css">
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
</head>
|
||||
<body lang="en-US">
|
||||
<div class="container">
|
||||
|
||||
|
||||
<section id="list-pane" class="row list">
|
||||
<ul >
|
||||
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="menu-pane" class="row menu text-center">
|
||||
|
||||
<h4 class="text-center"><a class="menu-item" href="https://moritzgraf.de/">home</a></h4>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
<footer class="row text-center footer">
|
||||
<hr />
|
||||
|
||||
<h6 class="text-center copyright">© 2021. Moritz Graf. <a href="http://creativecommons.org/licenses/by/3.0/">Some Rights Reserved</a>.</h6>
|
||||
|
||||
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
hljs.initHighlightingOnLoad();
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>Categories on Moritz Graf</title>
|
||||
<link>https://moritzgraf.de/categories/</link>
|
||||
<description>Recent content in Categories on Moritz Graf</description>
|
||||
<generator>Hugo</generator>
|
||||
<language>en-US</language>
|
||||
<atom:link href="https://moritzgraf.de/categories/index.xml" rel="self" type="application/rss+xml" />
|
||||
</channel>
|
||||
</rss>
|
||||
@@ -1,257 +0,0 @@
|
||||
@-ms-viewport
|
||||
{
|
||||
width: device-width;
|
||||
}
|
||||
@-o-viewport
|
||||
{
|
||||
width: device-width;
|
||||
}
|
||||
@viewport
|
||||
{
|
||||
width: device-width;
|
||||
}
|
||||
a,
|
||||
a:hover,
|
||||
a:visited,
|
||||
a:link
|
||||
{
|
||||
text-decoration: none;
|
||||
|
||||
color: #526d7a;
|
||||
}
|
||||
body
|
||||
{
|
||||
font-family: 'Roboto Slab', 'Roboto', 'Lato', sans-serif;
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
|
||||
width: auto;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
margin: 0;
|
||||
|
||||
background-color: #fcfcfc;
|
||||
}
|
||||
html
|
||||
{
|
||||
position: relative;
|
||||
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
.author
|
||||
{
|
||||
color: #444;
|
||||
}
|
||||
.author h1,
|
||||
.author h2,
|
||||
.author h3,
|
||||
.author h4,
|
||||
.author h5,
|
||||
.author h6,
|
||||
.author p
|
||||
{
|
||||
font-family: 'Roboto Slab', serif;
|
||||
}
|
||||
.category
|
||||
{
|
||||
color: #966 !important;
|
||||
}
|
||||
.container
|
||||
{
|
||||
overflow: auto;
|
||||
|
||||
max-width: 680px;
|
||||
padding: 0 15px;
|
||||
}
|
||||
.content
|
||||
{
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
div.content
|
||||
{
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 18px;
|
||||
font-weight: 300;
|
||||
|
||||
color: #777;
|
||||
}
|
||||
.copyright
|
||||
{
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 200;
|
||||
}
|
||||
.description
|
||||
{
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-weight: 200;
|
||||
color: #bbb;
|
||||
}
|
||||
.error
|
||||
{
|
||||
font-family: 'Roboto', sans-serif;
|
||||
|
||||
margin-top: 100px;
|
||||
}
|
||||
.error h1
|
||||
{
|
||||
font-size: 80px;
|
||||
|
||||
color: #bbb;
|
||||
}
|
||||
.error p
|
||||
{
|
||||
margin-top: 150px;
|
||||
|
||||
color: #777;
|
||||
}
|
||||
.excerpt
|
||||
{
|
||||
font-family: 'Lato', sans-serif;
|
||||
font-size: 18px;
|
||||
font-weight: 200;
|
||||
|
||||
color: #555;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.footer
|
||||
{
|
||||
margin-top: 10px;
|
||||
|
||||
color: #bbb;
|
||||
}
|
||||
.header
|
||||
{
|
||||
margin-top: 60px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.headshot
|
||||
{
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
.info
|
||||
{
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.intro
|
||||
{
|
||||
font-family: 'Roboto Slab', serif;
|
||||
font-weight: 200;
|
||||
color: #777;
|
||||
}
|
||||
h3.intro
|
||||
{
|
||||
margin-top: 0px
|
||||
}
|
||||
.list
|
||||
{
|
||||
font-family: 'Roboto', sans-serif;
|
||||
|
||||
margin-top: 50px;
|
||||
}
|
||||
.list li
|
||||
{
|
||||
list-style: none;
|
||||
}
|
||||
.list-entry
|
||||
{
|
||||
font-family: 'Roboto Slab', serif;
|
||||
font-size: 25px;
|
||||
font-weight: 400;
|
||||
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.list-entry-link
|
||||
{
|
||||
padding: 0;
|
||||
}
|
||||
.main-menu
|
||||
{
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.menu
|
||||
{
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.menu-item
|
||||
{
|
||||
font-family: 'Lato', sans-serif;
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
|
||||
text-decoration: none;
|
||||
|
||||
color: #966 !important;
|
||||
}
|
||||
.meta
|
||||
{
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 200;
|
||||
|
||||
color: #bbb;
|
||||
}
|
||||
a.meta
|
||||
{
|
||||
color: #966;
|
||||
}
|
||||
h6.meta
|
||||
{
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 200;
|
||||
|
||||
color: #bbb;
|
||||
}
|
||||
section.meta {
|
||||
margin-top:
|
||||
}
|
||||
.powered
|
||||
{
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
}
|
||||
.social {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.social,
|
||||
.social a
|
||||
{
|
||||
color: #526d7a;
|
||||
}
|
||||
.title
|
||||
{
|
||||
font-family: 'Roboto Slab', serif;
|
||||
font-weight: 400;
|
||||
|
||||
color: #555;
|
||||
}
|
||||
header.title
|
||||
{
|
||||
margin-top: 50px;
|
||||
padding: 0 15px;
|
||||
}
|
||||
@media (max-width: 991px)
|
||||
{
|
||||
.header
|
||||
{
|
||||
margin-top: 50px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.list
|
||||
{
|
||||
font-family: 'Roboto', sans-serif;
|
||||
|
||||
margin-top: 30px;
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 269 B |
|
Before Width: | Height: | Size: 436 B |
|
Before Width: | Height: | Size: 874 KiB After Width: | Height: | Size: 874 KiB |
|
Before Width: | Height: | Size: 888 KiB |
@@ -1,127 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
|
||||
<meta name="description" content="Online business card - Moritz Graf">
|
||||
<meta name="keywords" content="monitoring,ansible,configuration management,icinga2,grafana,ELK-Stack,prometheus,kubernetes,k3s,cncf,grafana,boto3,aws,terraform,terragrunt,atlantis,serverless,packer,loki,kustomize,jenkins,dependabot,oauth">
|
||||
|
||||
<base href="https://moritzgraf.de/">
|
||||
|
||||
<title>Moritz Graf</title>
|
||||
|
||||
<meta name="generator" content="Hugo 0.160.1">
|
||||
|
||||
|
||||
<link href="https://moritzgraf.de/index.xml" rel="alternate" type="application/rss+xml" title="Moritz Graf" />
|
||||
<link href="https://moritzgraf.de/index.xml" rel="feed" type="application/rss+xml" title="Moritz Graf" />
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato:300,400|Roboto+Slab:400,700|Roboto:300,300i,400,400i,500,500i,700,700i">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="https://moritzgraf.de/css/main.css">
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
</head>
|
||||
<body lang="en-US">
|
||||
<div class="container">
|
||||
|
||||
|
||||
<header class="row text-center header">
|
||||
<img src="img/headshot.jpg" alt="Author Image" class="img-circle text-center headshot">
|
||||
<h1 class="author">Moritz Graf</h1>
|
||||
</header>
|
||||
|
||||
|
||||
<section id="info-pane" class="row text-center info">
|
||||
|
||||
<h3 class="intro"><hr>
|
||||
</h3>
|
||||
|
||||
|
||||
<h4 class="description"><hr>
|
||||
</h4>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<section id="social-pane" class="row text-center social">
|
||||
|
||||
<a href="https://twitter.com/TheIPtizer"><i class="fa fa-twitter" aria-hidden="true"></i></a>
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="https://github.com/iptizer"><i class="fa fa-github" aria-hidden="true"></i></a>
|
||||
|
||||
|
||||
|
||||
<a href="https://linkedin.com/in/moritz-graf-92abaa91"><i class="fa fa-linkedin" aria-hidden="true"></i></a>
|
||||
|
||||
|
||||
<a href="https://stackoverflow.com/users/7779723"><i class="fa fa-stack-overflow" aria-hidden="true"></i></a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="mailto:contact-moritz@moritzgraf.de"><i class="fa fa-envelope-o" aria-hidden="true"></i></a>
|
||||
|
||||
|
||||
<a href="https://www.xing.com/profile/Moritz_Graf4"><i class="fa fa-xing" aria-hidden="true"></i></a>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<section id="main-menu-pane" class="row text-center main-menu">
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
<footer class="row text-center footer">
|
||||
<hr />
|
||||
|
||||
<h6 class="text-center copyright">© 2021. Moritz Graf. <a href="http://creativecommons.org/licenses/by/3.0/">Some Rights Reserved</a>.</h6>
|
||||
|
||||
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
hljs.initHighlightingOnLoad();
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>Moritz Graf</title>
|
||||
<link>https://moritzgraf.de/</link>
|
||||
<description>Recent content on Moritz Graf</description>
|
||||
<generator>Hugo</generator>
|
||||
<language>en-US</language>
|
||||
<atom:link href="https://moritzgraf.de/index.xml" rel="self" type="application/rss+xml" />
|
||||
</channel>
|
||||
</rss>
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright 2014-2015 Twitter, Inc.
|
||||
// Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
if (navigator.userAgent.match(/IEMobile\/10\.0/)) {
|
||||
var msViewportStyle = document.createElement('style')
|
||||
msViewportStyle.appendChild(
|
||||
document.createTextNode(
|
||||
'@-ms-viewport{width:auto!important}'
|
||||
)
|
||||
)
|
||||
document.querySelector('head').appendChild(msViewportStyle)
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"name": "hugo-goa",
|
||||
"icons": [
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="152.000000pt" height="152.000000pt" viewBox="0 0 152.000000 152.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.11, written by Peter Selinger 2001-2013
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,152.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M719 1506 c-177 -61 -281 -271 -231 -465 14 -57 55 -145 71 -155 5
|
||||
-3 12 -27 16 -53 3 -27 9 -62 11 -79 6 -36 -58 -232 -90 -279 -25 -37 -62 -72
|
||||
-98 -91 -25 -13 -28 -12 -47 12 -12 15 -21 32 -22 38 0 6 -2 20 -5 31 -2 11
|
||||
-5 32 -6 47 -2 29 -13 27 -42 -9 -10 -13 -23 -22 -30 -20 -14 5 -35 -22 -36
|
||||
-48 -1 -19 -2 -19 -9 -1 -7 18 -9 17 -35 -10 -97 -103 -111 -239 -31 -314 26
|
||||
-24 149 -70 175 -65 94 20 146 37 173 57 18 13 44 30 59 37 15 8 35 30 44 50
|
||||
9 20 23 39 30 44 7 4 25 30 40 57 14 27 32 51 40 54 13 5 14 11 -30 -143 -22
|
||||
-74 -21 -134 2 -154 20 -18 84 -47 105 -47 7 0 21 6 31 13 16 12 19 31 22 123
|
||||
5 170 6 175 42 189 30 13 31 13 46 -26 9 -22 16 -48 16 -58 0 -21 67 -128 98
|
||||
-155 38 -33 130 -73 159 -69 16 2 49 4 74 3 37 0 51 5 79 30 18 16 36 30 40
|
||||
30 3 0 13 12 22 28 22 37 17 116 -10 167 -18 34 -23 34 -22 0 1 -10 -33 -69
|
||||
-46 -82 -18 -19 -52 -16 -80 6 l-25 20 36 17 c19 9 42 28 51 43 9 15 32 36 52
|
||||
46 74 40 89 123 39 222 l-24 46 -7 -39 c-8 -43 -31 -84 -46 -84 -6 0 -10 7
|
||||
-10 15 0 24 -16 17 -17 -7 -3 -30 -27 -46 -64 -42 -74 9 -129 56 -182 155 -22
|
||||
42 -24 55 -20 150 4 112 5 129 15 144 62 103 82 176 75 281 -10 145 -82 266
|
||||
-191 319 -64 31 -152 40 -207 21z m167 -1062 c2 -16 6 -39 9 -51 7 -29 -13
|
||||
-40 -20 -11 -9 36 -13 82 -8 91 7 12 13 3 19 -29z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
||||
<url>
|
||||
<loc>https://moritzgraf.de/categories/</loc>
|
||||
</url><url>
|
||||
<loc>https://moritzgraf.de/</loc>
|
||||
</url><url>
|
||||
<loc>https://moritzgraf.de/tags/</loc>
|
||||
</url>
|
||||
</urlset>
|
||||
@@ -1,89 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
|
||||
<meta name="description" content="Online business card - Moritz Graf">
|
||||
<meta name="keywords" content="monitoring,ansible,configuration management,icinga2,grafana,ELK-Stack,prometheus,kubernetes,k3s,cncf,grafana,boto3,aws,terraform,terragrunt,atlantis,serverless,packer,loki,kustomize,jenkins,dependabot,oauth">
|
||||
|
||||
<base href="https://moritzgraf.de/">
|
||||
|
||||
<title>Moritz Graf</title>
|
||||
|
||||
<meta name="generator" content="Hugo 0.160.1">
|
||||
|
||||
|
||||
<link href="https://moritzgraf.de/tags/index.xml" rel="alternate" type="application/rss+xml" title="Moritz Graf" />
|
||||
<link href="https://moritzgraf.de/tags/index.xml" rel="feed" type="application/rss+xml" title="Moritz Graf" />
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato:300,400|Roboto+Slab:400,700|Roboto:300,300i,400,400i,500,500i,700,700i">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="https://moritzgraf.de/css/main.css">
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
</head>
|
||||
<body lang="en-US">
|
||||
<div class="container">
|
||||
|
||||
|
||||
<section id="list-pane" class="row list">
|
||||
<ul >
|
||||
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="menu-pane" class="row menu text-center">
|
||||
|
||||
<h4 class="text-center"><a class="menu-item" href="https://moritzgraf.de/">home</a></h4>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
<footer class="row text-center footer">
|
||||
<hr />
|
||||
|
||||
<h6 class="text-center copyright">© 2021. Moritz Graf. <a href="http://creativecommons.org/licenses/by/3.0/">Some Rights Reserved</a>.</h6>
|
||||
|
||||
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
hljs.initHighlightingOnLoad();
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>Tags on Moritz Graf</title>
|
||||
<link>https://moritzgraf.de/tags/</link>
|
||||
<description>Recent content in Tags on Moritz Graf</description>
|
||||
<generator>Hugo</generator>
|
||||
<language>en-US</language>
|
||||
<atom:link href="https://moritzgraf.de/tags/index.xml" rel="self" type="application/rss+xml" />
|
||||
</channel>
|
||||
</rss>
|
||||
@@ -0,0 +1,47 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import Navbar from '@/components/Navbar.vue'
|
||||
import Hero from '@/components/Hero.vue'
|
||||
import ImpactStats from '@/components/ImpactStats.vue'
|
||||
import CapabilityDossiers from '@/components/CapabilityDossiers.vue'
|
||||
import SkillMatrix from '@/components/SkillMatrix.vue'
|
||||
import EngineeringPhilosophy from '@/components/EngineeringPhilosophy.vue'
|
||||
import ContactSection from '@/components/ContactSection.vue'
|
||||
import ImpressumModal from '@/components/ImpressumModal.vue'
|
||||
|
||||
const isImpressumOpen = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="min-h-screen flex flex-col bg-slate-50 dark:bg-slate-950 text-slate-900 dark:text-slate-100 selection:bg-blue-500/20 selection:text-blue-700 dark:selection:text-blue-300">
|
||||
<!-- Sticky Navigation -->
|
||||
<Navbar />
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="flex-grow">
|
||||
<!-- Hero Section -->
|
||||
<Hero />
|
||||
|
||||
<!-- Impact Stats Banner -->
|
||||
<ImpactStats />
|
||||
|
||||
<!-- Deep Capability Dossiers (6 Pillars) -->
|
||||
<CapabilityDossiers />
|
||||
|
||||
<!-- Interactive Skill Matrix & Filter -->
|
||||
<SkillMatrix />
|
||||
|
||||
<!-- Architectural & Leadership Principles -->
|
||||
<EngineeringPhilosophy />
|
||||
|
||||
<!-- Contact & Connect -->
|
||||
<ContactSection @open-impressum="isImpressumOpen = true" />
|
||||
</main>
|
||||
|
||||
<!-- Impressum Modal -->
|
||||
<ImpressumModal
|
||||
:is-open="isImpressumOpen"
|
||||
@close="isImpressumOpen = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,115 @@
|
||||
<script setup lang="ts">
|
||||
import { capabilityDossiers } from '@/data/portfolioData'
|
||||
import {
|
||||
Cloud,
|
||||
Server,
|
||||
Database,
|
||||
Code2,
|
||||
Users,
|
||||
Cpu,
|
||||
CheckCircle2,
|
||||
Sparkles
|
||||
} from 'lucide-vue-next'
|
||||
|
||||
const getDossierIcon = (iconName: string) => {
|
||||
switch (iconName) {
|
||||
case 'Cloud': return Cloud
|
||||
case 'Server': return Server
|
||||
case 'Database': return Database
|
||||
case 'Code2': return Code2
|
||||
case 'Users': return Users
|
||||
case 'Cpu': return Cpu
|
||||
default: return Cloud
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section id="capabilities" class="py-16 md:py-24 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<!-- Section Header -->
|
||||
<div class="text-left max-w-3xl mb-12 sm:mb-16">
|
||||
<div class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-semibold uppercase tracking-wider bg-blue-50 text-blue-700 dark:bg-blue-950/60 dark:text-blue-300 border border-blue-200/80 dark:border-blue-800/80 mb-3">
|
||||
<Sparkles class="w-3.5 h-3.5" />
|
||||
<span>Core Competencies & Scope</span>
|
||||
</div>
|
||||
<h2 class="text-3xl sm:text-4xl font-extrabold text-slate-900 dark:text-white tracking-tight">
|
||||
Capability Dossiers
|
||||
</h2>
|
||||
<p class="mt-3 text-base sm:text-lg text-slate-600 dark:text-slate-300">
|
||||
A deep architectural breakdown of what I build, operate, and lead — spanning enterprise multi-cloud platforms, Kubernetes orchestration, corporate carve-outs, and low-level systems engineering.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Dossiers Grid: 6 Pillars -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<div
|
||||
v-for="dossier in capabilityDossiers"
|
||||
:key="dossier.id"
|
||||
class="flex flex-col justify-between p-6 sm:p-7 rounded-2xl bg-white dark:bg-slate-900 border border-slate-200/90 dark:border-slate-800/90 shadow-xs hover:shadow-lg hover:border-blue-400 dark:hover:border-blue-500/50 transition-all duration-300 group text-left"
|
||||
>
|
||||
<div>
|
||||
<!-- Card Header & Icon -->
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<span class="text-xs font-semibold uppercase tracking-wider text-blue-600 dark:text-blue-400">
|
||||
{{ dossier.category }}
|
||||
</span>
|
||||
<div class="p-2.5 rounded-xl bg-slate-100 dark:bg-slate-800 text-slate-700 dark:text-slate-200 group-hover:bg-blue-600 group-hover:text-white transition-colors">
|
||||
<component :is="getDossierIcon(dossier.icon)" class="w-5 h-5" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Title & Tagline -->
|
||||
<h3 class="text-xl font-bold text-slate-900 dark:text-white mb-2 group-hover:text-blue-600 dark:group-hover:text-blue-400 transition-colors">
|
||||
{{ dossier.title }}
|
||||
</h3>
|
||||
<p class="text-xs font-medium text-slate-500 dark:text-slate-400 italic mb-4">
|
||||
"{{ dossier.tagline }}"
|
||||
</p>
|
||||
|
||||
<!-- Overview -->
|
||||
<p class="text-xs sm:text-sm text-slate-600 dark:text-slate-300 leading-relaxed mb-6">
|
||||
{{ dossier.overview }}
|
||||
</p>
|
||||
|
||||
<!-- Key Scope Areas -->
|
||||
<div class="space-y-2.5 mb-6">
|
||||
<div class="text-[11px] font-bold uppercase tracking-wider text-slate-400 dark:text-slate-500">
|
||||
Key Focus Areas
|
||||
</div>
|
||||
<ul class="space-y-2">
|
||||
<li
|
||||
v-for="(area, aIdx) in dossier.keyAreas"
|
||||
:key="aIdx"
|
||||
class="flex items-start gap-2 text-xs text-slate-700 dark:text-slate-300"
|
||||
>
|
||||
<CheckCircle2 class="w-3.5 h-3.5 text-blue-500 mt-0.5 shrink-0" />
|
||||
<span class="leading-snug">{{ area }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<!-- Impact Box -->
|
||||
<div class="p-3 rounded-xl bg-slate-50 dark:bg-slate-800/60 border border-slate-100 dark:border-slate-800 text-[11px] text-slate-600 dark:text-slate-300 mb-5">
|
||||
<span class="font-semibold text-slate-800 dark:text-slate-200">Outcome:</span> {{ dossier.impactNote }}
|
||||
</div>
|
||||
|
||||
<!-- Technologies Badges -->
|
||||
<div class="pt-3 border-t border-slate-100 dark:border-slate-800">
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<span
|
||||
v-for="tech in dossier.technologies"
|
||||
:key="tech"
|
||||
class="px-2 py-0.5 rounded-md text-[11px] font-mono font-medium bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-300 border border-slate-200/60 dark:border-slate-700/60"
|
||||
>
|
||||
{{ tech }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
@@ -0,0 +1,133 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { personalInfo, socialLinks } from '@/data/portfolioData'
|
||||
import { Mail, Linkedin, Github, Copy, Check, ExternalLink, ShieldCheck } from 'lucide-vue-next'
|
||||
|
||||
defineEmits<{
|
||||
(e: 'openImpressum'): void
|
||||
}>()
|
||||
|
||||
const copied = ref(false)
|
||||
|
||||
const copyEmail = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(personalInfo.email)
|
||||
copied.value = true
|
||||
setTimeout(() => {
|
||||
copied.value = false
|
||||
}, 2000)
|
||||
} catch (err) {
|
||||
console.error('Failed to copy', err)
|
||||
}
|
||||
}
|
||||
|
||||
const getSocialIcon = (platform: string) => {
|
||||
switch (platform) {
|
||||
case 'LinkedIn': return Linkedin
|
||||
case 'GitHub': return Github
|
||||
case 'Email': return Mail
|
||||
default: return ExternalLink
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section id="contact" class="py-16 md:py-24 border-t border-slate-200/80 dark:border-slate-800/80 bg-slate-100/60 dark:bg-slate-900/40">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
|
||||
<div class="max-w-3xl mx-auto text-center space-y-4 mb-12">
|
||||
<div class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-semibold uppercase tracking-wider bg-blue-50 text-blue-700 dark:bg-blue-950/60 dark:text-blue-300 border border-blue-200/80 dark:border-blue-800/80">
|
||||
<Mail class="w-3.5 h-3.5" />
|
||||
<span>Connect & Exchange</span>
|
||||
</div>
|
||||
<h2 class="text-3xl sm:text-4xl font-extrabold text-slate-900 dark:text-white tracking-tight">
|
||||
Let’s Talk Cloud Platforms, Systems & Scale
|
||||
</h2>
|
||||
<p class="text-base sm:text-lg text-slate-600 dark:text-slate-300">
|
||||
Whether you want to discuss cloud platform engineering, Kubernetes at scale, enterprise carve-outs, or maker hardware craft: my inbox is open.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Contact Cards -->
|
||||
<div class="max-w-2xl mx-auto grid grid-cols-1 sm:grid-cols-2 gap-4 mb-12">
|
||||
<!-- Direct Email Card with Copy Button -->
|
||||
<div class="p-6 rounded-2xl bg-white dark:bg-slate-900 border border-slate-200/90 dark:border-slate-800/90 shadow-xs flex flex-col justify-between text-left">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<span class="text-xs font-bold uppercase tracking-wider text-blue-600 dark:text-blue-400">
|
||||
Direct Mail
|
||||
</span>
|
||||
<div class="p-2 rounded-lg bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400">
|
||||
<Mail class="w-4 h-4" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<div class="text-xs text-slate-500 dark:text-slate-400 mb-1">Send an inquiry</div>
|
||||
<a
|
||||
:href="`mailto:${personalInfo.email}`"
|
||||
class="text-sm font-semibold text-slate-900 dark:text-white hover:text-blue-600 dark:hover:text-blue-400 transition-colors break-all"
|
||||
>
|
||||
{{ personalInfo.email }}
|
||||
</a>
|
||||
</div>
|
||||
<button
|
||||
@click="copyEmail"
|
||||
class="inline-flex items-center justify-center gap-1.5 w-full py-2 px-3 rounded-lg text-xs font-semibold bg-slate-100 hover:bg-slate-200 dark:bg-slate-800 dark:hover:bg-slate-750 text-slate-700 dark:text-slate-200 transition-colors cursor-pointer"
|
||||
>
|
||||
<Check v-if="copied" class="w-3.5 h-3.5 text-emerald-500" />
|
||||
<Copy v-else class="w-3.5 h-3.5 text-slate-500" />
|
||||
<span>{{ copied ? 'Copied to Clipboard!' : 'Copy Email Address' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Professional Networks -->
|
||||
<div class="p-6 rounded-2xl bg-white dark:bg-slate-900 border border-slate-200/90 dark:border-slate-800/90 shadow-xs flex flex-col justify-between text-left">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<span class="text-xs font-bold uppercase tracking-wider text-blue-600 dark:text-blue-400">
|
||||
Public Profiles
|
||||
</span>
|
||||
<div class="p-2 rounded-lg bg-indigo-50 dark:bg-indigo-950 text-indigo-600 dark:text-indigo-400">
|
||||
<ExternalLink class="w-4 h-4" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
<a
|
||||
v-for="link in socialLinks.filter(l => l.platform !== 'Email')"
|
||||
:key="link.platform"
|
||||
:href="link.url"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="flex items-center justify-between p-2.5 rounded-lg border border-slate-200/80 dark:border-slate-800 hover:bg-slate-50 dark:hover:bg-slate-800/60 transition-all group"
|
||||
>
|
||||
<div class="flex items-center gap-2.5">
|
||||
<component :is="getSocialIcon(link.platform)" class="w-4 h-4 text-slate-500 group-hover:text-blue-600 transition-colors" />
|
||||
<span class="text-xs font-semibold text-slate-800 dark:text-slate-200">{{ link.platform }}</span>
|
||||
</div>
|
||||
<span class="text-[11px] font-mono text-slate-400 group-hover:text-blue-600 dark:group-hover:text-blue-400">
|
||||
/{{ link.handle }}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Infrastructure Tag & Impressum Trigger -->
|
||||
<div class="pt-8 border-t border-slate-200/80 dark:border-slate-800/80 flex flex-col sm:flex-row items-center justify-between gap-4 text-xs text-slate-500 dark:text-slate-400">
|
||||
<div class="flex items-center gap-2">
|
||||
<ShieldCheck class="w-4 h-4 text-emerald-500" />
|
||||
<span>Self-hosted on private Kubernetes infrastructure (haumdaucher) via GitOps</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<button
|
||||
@click="$emit('openImpressum')"
|
||||
class="hover:text-slate-800 dark:hover:text-slate-200 underline cursor-pointer"
|
||||
>
|
||||
Legal Notice (Impressum)
|
||||
</button>
|
||||
<span>·</span>
|
||||
<span>© {{ new Date().getFullYear() }} Moritz Graf</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
@@ -0,0 +1,60 @@
|
||||
<script setup lang="ts">
|
||||
import { engineeringPrinciples } from '@/data/portfolioData'
|
||||
import { FileCode, Zap, Layers, HeartHandshake, Compass } from 'lucide-vue-next'
|
||||
|
||||
const getPrincipleIcon = (name: string) => {
|
||||
switch (name) {
|
||||
case 'FileCode': return FileCode
|
||||
case 'Zap': return Zap
|
||||
case 'Layers': return Layers
|
||||
case 'HeartHandshake': return HeartHandshake
|
||||
default: return Compass
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section id="principles" class="py-16 md:py-24 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<!-- Section Header -->
|
||||
<div class="text-left max-w-3xl mb-12 sm:mb-16">
|
||||
<div class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-semibold uppercase tracking-wider bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-300 border border-slate-200 dark:border-slate-700 mb-3">
|
||||
<Compass class="w-3.5 h-3.5" />
|
||||
<span>Philosophy & Stance</span>
|
||||
</div>
|
||||
<h2 class="text-3xl sm:text-4xl font-extrabold text-slate-900 dark:text-white tracking-tight">
|
||||
Engineering Principles
|
||||
</h2>
|
||||
<p class="mt-3 text-base sm:text-lg text-slate-600 dark:text-slate-300">
|
||||
How I approach architecture, platform governance, and team leadership.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Principles Grid -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<div
|
||||
v-for="p in engineeringPrinciples"
|
||||
:key="p.title"
|
||||
class="p-7 rounded-2xl bg-white dark:bg-slate-900 border border-slate-200/90 dark:border-slate-800/90 shadow-xs hover:border-slate-300 dark:hover:border-slate-700 transition-all text-left flex flex-col justify-between"
|
||||
>
|
||||
<div>
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<div class="p-2.5 rounded-xl bg-blue-50 dark:bg-blue-950/60 text-blue-600 dark:text-blue-400">
|
||||
<component :is="getPrincipleIcon(p.icon)" class="w-5 h-5" />
|
||||
</div>
|
||||
<h3 class="text-lg font-bold text-slate-900 dark:text-white">
|
||||
{{ p.title }}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<blockquote class="text-sm font-semibold text-slate-800 dark:text-slate-200 italic border-l-2 border-blue-500 pl-3 my-3">
|
||||
"{{ p.statement }}"
|
||||
</blockquote>
|
||||
|
||||
<p class="text-xs sm:text-sm text-slate-600 dark:text-slate-400 leading-relaxed mt-4">
|
||||
{{ p.explanation }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
@@ -0,0 +1,164 @@
|
||||
<script setup lang="ts">
|
||||
import { personalInfo, credentials } from '@/data/portfolioData'
|
||||
import {
|
||||
ShieldCheck,
|
||||
Award,
|
||||
GraduationCap,
|
||||
CheckCircle2,
|
||||
Globe,
|
||||
ArrowRight,
|
||||
Terminal,
|
||||
MapPin
|
||||
} from 'lucide-vue-next'
|
||||
|
||||
const getIcon = (name: string) => {
|
||||
switch (name) {
|
||||
case 'ShieldCheck': return ShieldCheck
|
||||
case 'Award': return Award
|
||||
case 'GraduationCap': return GraduationCap
|
||||
case 'CheckCircle2': return CheckCircle2
|
||||
case 'Globe': return Globe
|
||||
default: return Award
|
||||
}
|
||||
}
|
||||
|
||||
const scrollTo = (selector: string) => {
|
||||
const el = document.querySelector(selector)
|
||||
if (el) {
|
||||
el.scrollIntoView({ behavior: 'smooth' })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section id="overview" class="relative pt-12 pb-16 md:pt-20 md:pb-24 overflow-hidden">
|
||||
<!-- Subtle Background Ambient Accents -->
|
||||
<div class="absolute -top-24 left-1/2 -translate-x-1/2 w-full max-w-7xl h-96 bg-gradient-to-b from-blue-50/70 via-indigo-50/30 to-transparent dark:from-blue-950/20 dark:via-indigo-950/10 dark:to-transparent pointer-events-none -z-10 blur-3xl"></div>
|
||||
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-12 lg:gap-8 items-center">
|
||||
|
||||
<!-- Left Column: Copy, Headlines & Badges (7 cols) -->
|
||||
<div class="lg:col-span-7 space-y-6 text-left">
|
||||
|
||||
<!-- Status Pill -->
|
||||
<div class="inline-flex items-center gap-2 px-3 py-1 rounded-full text-xs font-medium bg-emerald-50 text-emerald-700 dark:bg-emerald-950/50 dark:text-emerald-400 border border-emerald-200/80 dark:border-emerald-800/80 shadow-xs">
|
||||
<span class="relative flex h-2 w-2">
|
||||
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
|
||||
<span class="relative inline-flex rounded-full h-2 w-2 bg-emerald-500"></span>
|
||||
</span>
|
||||
<span>{{ personalInfo.availabilityBadge }}</span>
|
||||
<span class="text-slate-300 dark:text-slate-700">|</span>
|
||||
<span class="inline-flex items-center gap-1 text-slate-500 dark:text-slate-400">
|
||||
<MapPin class="w-3 h-3" />
|
||||
{{ personalInfo.location }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Main Title -->
|
||||
<div class="space-y-2">
|
||||
<h1 class="text-4xl sm:text-5xl lg:text-6xl font-extrabold text-slate-900 dark:text-white tracking-tight leading-tight">
|
||||
Moritz Graf
|
||||
</h1>
|
||||
<p class="text-xl sm:text-2xl font-semibold bg-gradient-to-r from-blue-600 via-indigo-600 to-cyan-600 dark:from-blue-400 dark:via-indigo-400 dark:to-cyan-400 bg-clip-text text-transparent">
|
||||
{{ personalInfo.title }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Value Proposition / Bio Summary -->
|
||||
<p class="text-base sm:text-lg text-slate-600 dark:text-slate-300 leading-relaxed font-normal max-w-2xl">
|
||||
{{ personalInfo.bioSummary }}
|
||||
</p>
|
||||
|
||||
<!-- Verified Credential Badges -->
|
||||
<div class="pt-2">
|
||||
<div class="text-xs uppercase tracking-wider font-semibold text-slate-400 dark:text-slate-500 mb-3">
|
||||
Verified Credentials & Degrees
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2.5">
|
||||
<div
|
||||
v-for="cred in credentials"
|
||||
:key="cred.id"
|
||||
class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium transition-all"
|
||||
:class="[
|
||||
cred.highlight
|
||||
? 'bg-blue-50 text-blue-800 border border-blue-200 dark:bg-blue-950/40 dark:text-blue-300 dark:border-blue-800'
|
||||
: 'bg-slate-100 text-slate-700 border border-slate-200 dark:bg-slate-900 dark:text-slate-300 dark:border-slate-800'
|
||||
]"
|
||||
:title="`${cred.title} — ${cred.issuer}`"
|
||||
>
|
||||
<component :is="getIcon(cred.icon)" class="w-3.5 h-3.5" :class="cred.highlight ? 'text-blue-600 dark:text-blue-400' : 'text-slate-500 dark:text-slate-400'" />
|
||||
<span class="font-semibold">{{ cred.badgeText }}</span>
|
||||
<span class="text-slate-400 dark:text-slate-500 text-[10px] hidden sm:inline">({{ cred.issuer.split(' ')[0] }})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="flex flex-wrap items-center gap-4 pt-4">
|
||||
<button
|
||||
@click="scrollTo('#capabilities')"
|
||||
class="inline-flex items-center gap-2 px-5 py-2.5 rounded-xl font-semibold text-sm bg-blue-600 text-white hover:bg-blue-700 shadow-md hover:shadow-lg transition-all cursor-pointer"
|
||||
>
|
||||
<span>Explore Capabilities</span>
|
||||
<ArrowRight class="w-4 h-4" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
@click="scrollTo('#skills')"
|
||||
class="inline-flex items-center gap-2 px-5 py-2.5 rounded-xl font-semibold text-sm bg-white dark:bg-slate-900 text-slate-700 dark:text-slate-200 border border-slate-200 dark:border-slate-800 hover:border-slate-300 dark:hover:border-slate-700 shadow-xs hover:bg-slate-50 dark:hover:bg-slate-800/80 transition-all cursor-pointer"
|
||||
>
|
||||
<Terminal class="w-4 h-4 text-slate-500" />
|
||||
<span>Skill Matrix & Tech</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Right Column: Executive Portrait & Cluster Info (5 cols) -->
|
||||
<div class="lg:col-span-5 flex justify-center lg:justify-end">
|
||||
<div class="relative w-full max-w-sm sm:max-w-md">
|
||||
|
||||
<!-- Glow background -->
|
||||
<div class="absolute inset-0 bg-gradient-to-tr from-blue-500/20 to-indigo-500/20 rounded-3xl blur-2xl -z-10 transform scale-95"></div>
|
||||
|
||||
<!-- Portrait Frame -->
|
||||
<div class="relative rounded-3xl p-2 bg-gradient-to-b from-slate-200 to-slate-100 dark:from-slate-800 dark:to-slate-900 shadow-xl border border-slate-200/80 dark:border-slate-800/80">
|
||||
<div class="relative rounded-2xl overflow-hidden bg-slate-900 aspect-square">
|
||||
<img
|
||||
src="/headshot.jpg"
|
||||
alt="Moritz Graf"
|
||||
class="w-full h-full object-cover object-center filter contrast-[1.02] hover:scale-102 transition-transform duration-500"
|
||||
/>
|
||||
|
||||
<!-- Floating Info Badge on bottom of image -->
|
||||
<div class="absolute bottom-3 inset-x-3 p-3 rounded-xl bg-slate-950/85 backdrop-blur-md border border-white/10 text-white shadow-lg text-left">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="text-xs font-semibold text-white tracking-wide">Moritz Graf</div>
|
||||
<div class="text-[11px] text-slate-300">Central Platform Engineer & Lead</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<span class="inline-block px-2 py-0.5 rounded text-[10px] font-mono font-medium bg-blue-500/20 text-blue-300 border border-blue-400/30">
|
||||
GCP · AWS · K8s
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Decorative floating badge -->
|
||||
<div class="absolute -top-4 -right-4 px-3.5 py-1.5 rounded-xl bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 shadow-lg text-xs font-mono text-slate-700 dark:text-slate-300 hidden sm:flex items-center gap-2">
|
||||
<span class="w-2 h-2 rounded-full bg-blue-500 animate-pulse"></span>
|
||||
<span>Central Platform</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
@@ -0,0 +1,43 @@
|
||||
<script setup lang="ts">
|
||||
import { impactStats } from '@/data/portfolioData'
|
||||
import { Cloud, GitFork, Server, Activity } from 'lucide-vue-next'
|
||||
|
||||
const getStatIcon = (index: number) => {
|
||||
switch (index) {
|
||||
case 0: return Cloud
|
||||
case 1: return GitFork
|
||||
case 2: return Server
|
||||
case 3: return Activity
|
||||
default: return Server
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="py-10 border-y border-slate-200/80 dark:border-slate-800/80 bg-slate-100/50 dark:bg-slate-900/30">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<div
|
||||
v-for="(stat, idx) in impactStats"
|
||||
:key="stat.label"
|
||||
class="relative p-6 rounded-2xl bg-white dark:bg-slate-900 border border-slate-200/80 dark:border-slate-800/80 shadow-xs hover:shadow-md transition-all group text-left"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<span class="text-2xl lg:text-3xl font-extrabold text-slate-900 dark:text-white tracking-tight">
|
||||
{{ stat.value }}
|
||||
</span>
|
||||
<div class="p-2.5 rounded-xl bg-blue-50 dark:bg-blue-950/60 text-blue-600 dark:text-blue-400 group-hover:scale-110 transition-transform">
|
||||
<component :is="getStatIcon(idx)" class="w-5 h-5" />
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="text-sm font-bold text-slate-900 dark:text-slate-100 mb-1">
|
||||
{{ stat.label }}
|
||||
</h3>
|
||||
<p class="text-xs text-slate-500 dark:text-slate-400 leading-relaxed font-normal">
|
||||
{{ stat.description }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
@@ -0,0 +1,79 @@
|
||||
<script setup lang="ts">
|
||||
import { X, Shield } from 'lucide-vue-next'
|
||||
|
||||
defineProps<{
|
||||
isOpen: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'close'): void
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="isOpen"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-950/70 backdrop-blur-xs"
|
||||
@click.self="emit('close')"
|
||||
>
|
||||
<div
|
||||
class="relative w-full max-w-lg p-6 sm:p-8 rounded-2xl bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 shadow-2xl text-left max-h-[90vh] overflow-y-auto"
|
||||
>
|
||||
<!-- Close Button -->
|
||||
<button
|
||||
@click="emit('close')"
|
||||
class="absolute top-4 right-4 p-2 rounded-lg text-slate-400 hover:text-slate-600 dark:hover:text-slate-200 hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors cursor-pointer"
|
||||
aria-label="Close Modal"
|
||||
>
|
||||
<X class="w-5 h-5" />
|
||||
</button>
|
||||
|
||||
<div class="flex items-center gap-2.5 mb-6">
|
||||
<div class="p-2 rounded-lg bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400">
|
||||
<Shield class="w-5 h-5" />
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-slate-900 dark:text-white">
|
||||
Legal Notice (Impressum)
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4 text-xs sm:text-sm text-slate-600 dark:text-slate-300 leading-relaxed">
|
||||
<div>
|
||||
<h4 class="font-bold text-slate-900 dark:text-white mb-1">Information according to § 5 TMG</h4>
|
||||
<p>Moritz Graf</p>
|
||||
<p>Prinz-Ludwig-Str. 4a</p>
|
||||
<p>93055 Regensburg, Germany</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 class="font-bold text-slate-900 dark:text-white mb-1">Contact</h4>
|
||||
<p>Email: <a href="mailto:contact-moritz@moritzgraf.de" class="text-blue-600 dark:text-blue-400 underline">contact-moritz@moritzgraf.de</a></p>
|
||||
<p>Website: <a href="https://moritzgraf.de" class="text-blue-600 dark:text-blue-400 underline">https://moritzgraf.de</a></p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 class="font-bold text-slate-900 dark:text-white mb-1">Responsible for Content (§ 55 Abs. 2 RStV)</h4>
|
||||
<p>Moritz Graf</p>
|
||||
<p>Prinz-Ludwig-Str. 4a, 93055 Regensburg</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 class="font-bold text-slate-900 dark:text-white mb-1">Disclaimer</h4>
|
||||
<p class="text-slate-500 dark:text-slate-400 text-xs">
|
||||
This personal website presents professional capabilities, skills, and engineering philosophies. All trademarks, service marks, and company names cited are the property of their respective owners. Self-hosted on private Kubernetes infrastructure.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 pt-4 border-t border-slate-200 dark:border-slate-800 text-right">
|
||||
<button
|
||||
@click="emit('close')"
|
||||
class="px-4 py-2 rounded-lg text-xs font-semibold bg-slate-900 text-white dark:bg-white dark:text-slate-900 hover:bg-slate-800 dark:hover:bg-slate-100 transition-colors cursor-pointer"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,149 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { Sun, Moon, Menu, X, ArrowUpRight } from 'lucide-vue-next'
|
||||
|
||||
const isDark = ref(false)
|
||||
const isMobileMenuOpen = ref(false)
|
||||
|
||||
const toggleDarkMode = () => {
|
||||
isDark.value = !isDark.value
|
||||
if (isDark.value) {
|
||||
document.documentElement.classList.add('dark')
|
||||
localStorage.setItem('theme', 'dark')
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark')
|
||||
localStorage.setItem('theme', 'light')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const savedTheme = localStorage.getItem('theme')
|
||||
if (savedTheme === 'dark' || (!savedTheme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||
isDark.value = true
|
||||
document.documentElement.classList.add('dark')
|
||||
} else {
|
||||
isDark.value = false
|
||||
document.documentElement.classList.remove('dark')
|
||||
}
|
||||
})
|
||||
|
||||
const navLinks = [
|
||||
{ name: 'Overview', href: '#overview' },
|
||||
{ name: 'Capabilities', href: '#capabilities' },
|
||||
{ name: 'Skills & Tech', href: '#skills' },
|
||||
{ name: 'Principles', href: '#principles' },
|
||||
{ name: 'Contact', href: '#contact' },
|
||||
]
|
||||
|
||||
const scrollTo = (href: string) => {
|
||||
isMobileMenuOpen.value = false
|
||||
const element = document.querySelector(href)
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: 'smooth' })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="sticky top-0 z-50 w-full border-b border-slate-200/80 dark:border-slate-800/80 bg-white/80 dark:bg-slate-950/80 backdrop-blur-md">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center justify-between h-16">
|
||||
<!-- Brand / Identity -->
|
||||
<a href="#overview" class="flex items-center space-x-3 group text-left">
|
||||
<div class="w-9 h-9 rounded-lg bg-gradient-to-tr from-blue-600 to-indigo-600 flex items-center justify-center text-white font-bold text-base shadow-sm group-hover:shadow transition-all">
|
||||
MG
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-bold text-slate-900 dark:text-white tracking-tight leading-none group-hover:text-blue-600 dark:group-hover:text-blue-400 transition-colors">
|
||||
Moritz Graf
|
||||
</div>
|
||||
<div class="text-xs text-slate-500 dark:text-slate-400 font-medium">
|
||||
Cloud Platform & Infrastructure
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<!-- Desktop Navigation -->
|
||||
<nav class="hidden md:flex items-center space-x-1 lg:space-x-2">
|
||||
<button
|
||||
v-for="link in navLinks"
|
||||
:key="link.name"
|
||||
@click="scrollTo(link.href)"
|
||||
class="px-3 py-2 rounded-md text-sm font-medium text-slate-600 dark:text-slate-300 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-slate-900 transition-colors cursor-pointer"
|
||||
>
|
||||
{{ link.name }}
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<!-- Right Side: Dark Mode Toggle & Direct Connect -->
|
||||
<div class="hidden md:flex items-center space-x-3">
|
||||
<!-- Theme switch button -->
|
||||
<button
|
||||
@click="toggleDarkMode"
|
||||
type="button"
|
||||
class="p-2 rounded-lg text-slate-500 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-slate-900 transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer"
|
||||
:title="isDark ? 'Switch to Light Mode' : 'Switch to Dark Mode'"
|
||||
aria-label="Toggle Theme"
|
||||
>
|
||||
<Sun v-if="isDark" class="w-5 h-5 text-amber-400 transition-transform rotate-0 scale-100" />
|
||||
<Moon v-else class="w-5 h-5 text-slate-600 transition-transform -rotate-90 scale-100" />
|
||||
</button>
|
||||
|
||||
<a
|
||||
href="mailto:contact-moritz@moritzgraf.de"
|
||||
class="inline-flex items-center gap-1.5 px-3.5 py-1.5 rounded-lg text-xs font-semibold bg-slate-900 text-white dark:bg-white dark:text-slate-900 hover:bg-slate-800 dark:hover:bg-slate-100 shadow-sm transition-all"
|
||||
>
|
||||
<span>Get in touch</span>
|
||||
<ArrowUpRight class="w-3.5 h-3.5" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu Trigger -->
|
||||
<div class="flex md:hidden items-center space-x-2">
|
||||
<button
|
||||
@click="toggleDarkMode"
|
||||
type="button"
|
||||
class="p-2 rounded-lg text-slate-500 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-900"
|
||||
aria-label="Toggle Theme"
|
||||
>
|
||||
<Sun v-if="isDark" class="w-5 h-5 text-amber-400" />
|
||||
<Moon v-else class="w-5 h-5 text-slate-600" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
@click="isMobileMenuOpen = !isMobileMenuOpen"
|
||||
type="button"
|
||||
class="p-2 rounded-lg text-slate-600 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-900"
|
||||
aria-label="Toggle Navigation Menu"
|
||||
>
|
||||
<X v-if="isMobileMenuOpen" class="w-6 h-6" />
|
||||
<Menu v-else class="w-6 h-6" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Dropdown Menu -->
|
||||
<div
|
||||
v-if="isMobileMenuOpen"
|
||||
class="md:hidden border-b border-slate-200 dark:border-slate-800 bg-white/95 dark:bg-slate-950/95 backdrop-blur-md px-4 pt-2 pb-4 space-y-1"
|
||||
>
|
||||
<button
|
||||
v-for="link in navLinks"
|
||||
:key="link.name"
|
||||
@click="scrollTo(link.href)"
|
||||
class="block w-full text-left px-3 py-2 rounded-md text-base font-medium text-slate-700 dark:text-slate-200 hover:bg-slate-100 dark:hover:bg-slate-900"
|
||||
>
|
||||
{{ link.name }}
|
||||
</button>
|
||||
<div class="pt-2 border-t border-slate-100 dark:border-slate-800 mt-2">
|
||||
<a
|
||||
href="mailto:contact-moritz@moritzgraf.de"
|
||||
class="block text-center w-full px-4 py-2 rounded-lg text-sm font-semibold bg-blue-600 text-white hover:bg-blue-700 shadow-sm"
|
||||
>
|
||||
Contact Moritz
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
@@ -0,0 +1,202 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { skills } from '@/data/portfolioData'
|
||||
import type { SkillCategory } from '@/types'
|
||||
import { Search, X, Layers, Star } from 'lucide-vue-next'
|
||||
|
||||
const searchQuery = ref('')
|
||||
const selectedCategory = ref<SkillCategory>('all')
|
||||
|
||||
const categories: { id: SkillCategory; label: string }[] = [
|
||||
{ id: 'all', label: 'All Technologies' },
|
||||
{ id: 'cloud', label: 'Cloud & Multi-Cloud' },
|
||||
{ id: 'k8s', label: 'Kubernetes & CNCF' },
|
||||
{ id: 'iac', label: 'IaC & Automation' },
|
||||
{ id: 'systems', label: 'Systems & Networks' },
|
||||
{ id: 'data', label: 'Data & Observability' },
|
||||
{ id: 'leadership', label: 'Leadership & Agile' },
|
||||
{ id: 'iot', label: 'Hardware & IoT' },
|
||||
]
|
||||
|
||||
const filteredSkills = computed(() => {
|
||||
const query = searchQuery.value.toLowerCase().trim()
|
||||
return skills.filter((skill) => {
|
||||
const matchesCategory =
|
||||
selectedCategory.value === 'all' || skill.category === selectedCategory.value
|
||||
|
||||
const matchesSearch =
|
||||
!query ||
|
||||
skill.name.toLowerCase().includes(query) ||
|
||||
skill.description.toLowerCase().includes(query) ||
|
||||
skill.tags.some((tag) => tag.toLowerCase().includes(query))
|
||||
|
||||
return matchesCategory && matchesSearch
|
||||
})
|
||||
})
|
||||
|
||||
const clearSearch = () => {
|
||||
searchQuery.value = ''
|
||||
selectedCategory.value = 'all'
|
||||
}
|
||||
|
||||
const getCategoryBadgeClass = (category: string) => {
|
||||
switch (category) {
|
||||
case 'cloud':
|
||||
return 'bg-sky-50 text-sky-700 border-sky-200 dark:bg-sky-950/50 dark:text-sky-300 dark:border-sky-800'
|
||||
case 'k8s':
|
||||
return 'bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-950/50 dark:text-blue-300 dark:border-blue-800'
|
||||
case 'iac':
|
||||
return 'bg-indigo-50 text-indigo-700 border-indigo-200 dark:bg-indigo-950/50 dark:text-indigo-300 dark:border-indigo-800'
|
||||
case 'systems':
|
||||
return 'bg-amber-50 text-amber-700 border-amber-200 dark:bg-amber-950/50 dark:text-amber-300 dark:border-amber-800'
|
||||
case 'data':
|
||||
return 'bg-emerald-50 text-emerald-700 border-emerald-200 dark:bg-emerald-950/50 dark:text-emerald-300 dark:border-emerald-800'
|
||||
case 'leadership':
|
||||
return 'bg-purple-50 text-purple-700 border-purple-200 dark:bg-purple-950/50 dark:text-purple-300 dark:border-purple-800'
|
||||
case 'iot':
|
||||
return 'bg-rose-50 text-rose-700 border-rose-200 dark:bg-rose-950/50 dark:text-rose-300 dark:border-rose-800'
|
||||
default:
|
||||
return 'bg-slate-50 text-slate-700 border-slate-200 dark:bg-slate-900 dark:text-slate-300 dark:border-slate-800'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section id="skills" class="py-16 md:py-24 border-t border-slate-200/80 dark:border-slate-800/80 bg-slate-50/50 dark:bg-slate-900/20">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
|
||||
<!-- Section Header -->
|
||||
<div class="text-left max-w-3xl mb-10">
|
||||
<div class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-semibold uppercase tracking-wider bg-indigo-50 text-indigo-700 dark:bg-indigo-950/60 dark:text-indigo-300 border border-indigo-200/80 dark:border-indigo-800/80 mb-3">
|
||||
<Layers class="w-3.5 h-3.5" />
|
||||
<span>Interactive Skill Matrix</span>
|
||||
</div>
|
||||
<h2 class="text-3xl sm:text-4xl font-extrabold text-slate-900 dark:text-white tracking-tight">
|
||||
Tools, Technologies & Applied Craft
|
||||
</h2>
|
||||
<p class="mt-3 text-base sm:text-lg text-slate-600 dark:text-slate-300">
|
||||
Search or filter across hyperscalers, container runtimes, telecom protocols, and hardware nodes.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Controls: Search & Category Pills -->
|
||||
<div class="space-y-4 mb-8">
|
||||
<!-- Live Search Bar -->
|
||||
<div class="relative max-w-xl">
|
||||
<div class="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-slate-400">
|
||||
<Search class="w-4 h-4" />
|
||||
</div>
|
||||
<input
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
placeholder="Search technologies, tools or concepts (e.g. GCP, Istio, BGP, Ansible)..."
|
||||
class="block w-full pl-10 pr-10 py-2.5 rounded-xl border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-900 text-slate-900 dark:text-white placeholder-slate-400 dark:placeholder-slate-500 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all shadow-xs"
|
||||
/>
|
||||
<button
|
||||
v-if="searchQuery"
|
||||
@click="searchQuery = ''"
|
||||
class="absolute inset-y-0 right-0 pr-3.5 flex items-center text-slate-400 hover:text-slate-600 dark:hover:text-slate-200 cursor-pointer"
|
||||
>
|
||||
<X class="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Category Pills -->
|
||||
<div class="flex flex-wrap gap-2 pt-2">
|
||||
<button
|
||||
v-for="cat in categories"
|
||||
:key="cat.id"
|
||||
@click="selectedCategory = cat.id"
|
||||
class="px-3 py-1.5 rounded-lg text-xs font-semibold transition-all cursor-pointer"
|
||||
:class="[
|
||||
selectedCategory === cat.id
|
||||
? 'bg-blue-600 text-white shadow-xs'
|
||||
: 'bg-white dark:bg-slate-800 text-slate-600 dark:text-slate-300 border border-slate-200 dark:border-slate-700 hover:bg-slate-50 dark:hover:bg-slate-750'
|
||||
]"
|
||||
>
|
||||
{{ cat.label }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Result Counter -->
|
||||
<div class="flex items-center justify-between text-xs text-slate-500 dark:text-slate-400 mb-6">
|
||||
<div>
|
||||
Showing <span class="font-semibold text-slate-800 dark:text-slate-200">{{ filteredSkills.length }}</span> technologies
|
||||
</div>
|
||||
<button
|
||||
v-if="searchQuery || selectedCategory !== 'all'"
|
||||
@click="clearSearch"
|
||||
class="text-blue-600 dark:text-blue-400 hover:underline cursor-pointer"
|
||||
>
|
||||
Reset filters
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Skills Cards Grid -->
|
||||
<div v-if="filteredSkills.length > 0" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
||||
<div
|
||||
v-for="skill in filteredSkills"
|
||||
:key="skill.name"
|
||||
class="p-4 rounded-xl bg-white dark:bg-slate-900 border border-slate-200/80 dark:border-slate-800/80 shadow-xs hover:border-slate-300 dark:hover:border-slate-700 transition-all text-left flex flex-col justify-between"
|
||||
>
|
||||
<div>
|
||||
<div class="flex items-start justify-between gap-2 mb-2">
|
||||
<h4 class="font-bold text-sm text-slate-900 dark:text-white">
|
||||
{{ skill.name }}
|
||||
</h4>
|
||||
<span
|
||||
v-if="skill.featured"
|
||||
class="inline-flex items-center gap-1 text-[10px] font-semibold text-amber-600 dark:text-amber-400"
|
||||
title="Core Competency"
|
||||
>
|
||||
<Star class="w-3 h-3 fill-amber-500 text-amber-500" />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p class="text-xs text-slate-600 dark:text-slate-300 leading-relaxed mb-3">
|
||||
{{ skill.description }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<!-- Category and tags -->
|
||||
<div class="pt-2 border-t border-slate-100 dark:border-slate-800/80 flex flex-wrap gap-1">
|
||||
<span
|
||||
class="px-1.5 py-0.5 rounded text-[10px] font-mono border"
|
||||
:class="getCategoryBadgeClass(skill.category)"
|
||||
>
|
||||
{{ skill.category }}
|
||||
</span>
|
||||
<span
|
||||
v-for="tag in skill.tags"
|
||||
:key="tag"
|
||||
class="px-1.5 py-0.5 rounded text-[10px] font-mono bg-slate-100 dark:bg-slate-800 text-slate-500 dark:text-slate-400"
|
||||
>
|
||||
#{{ tag }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Empty State -->
|
||||
<div
|
||||
v-else
|
||||
class="text-center py-12 px-4 rounded-2xl bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800"
|
||||
>
|
||||
<p class="text-slate-600 dark:text-slate-300 font-medium text-sm">
|
||||
No technologies match "{{ searchQuery }}" in this category.
|
||||
</p>
|
||||
<button
|
||||
@click="clearSearch"
|
||||
class="mt-3 px-4 py-2 rounded-lg text-xs font-semibold bg-blue-600 text-white hover:bg-blue-700 cursor-pointer"
|
||||
>
|
||||
Clear filters & search
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
@@ -0,0 +1,290 @@
|
||||
import type {
|
||||
Credential,
|
||||
StatItem,
|
||||
CapabilityDossier,
|
||||
SkillItem,
|
||||
Principle,
|
||||
SocialLink
|
||||
} from '@/types'
|
||||
|
||||
export const personalInfo = {
|
||||
name: 'Moritz Graf',
|
||||
title: 'Cloud Platform & Infrastructure Leader',
|
||||
tagline: 'Architecting resilient multi-cloud foundations, scaling Kubernetes platforms, and steering enterprise carve-out migrations.',
|
||||
bioSummary: 'Central Platform Engineer and Engineering Leader with over a decade of production operations experience. I specialize in building autonomous developer platforms on Google Cloud and AWS, operating Kubernetes at scale, and orchestrating complex enterprise cloud spin-offs. Grounded in low-level systems engineering, network routing (BGP, SIP), and maker craft.',
|
||||
location: 'Regensburg, Germany',
|
||||
email: 'contact-moritz@moritzgraf.de',
|
||||
headshotUrl: '/headshot.jpg',
|
||||
availabilityBadge: 'Active in Central Platform Engineering',
|
||||
credentialsSummary: 'CKA · PSM II · M.Sc. Computer Science'
|
||||
}
|
||||
|
||||
export const credentials: Credential[] = [
|
||||
{
|
||||
id: 'cka',
|
||||
title: 'Certified Kubernetes Administrator (CKA)',
|
||||
issuer: 'Cloud Native Computing Foundation (CNCF)',
|
||||
badgeText: 'CKA Certified',
|
||||
icon: 'ShieldCheck',
|
||||
highlight: true
|
||||
},
|
||||
{
|
||||
id: 'psm2',
|
||||
title: 'Professional Scrum Master II (PSM II)',
|
||||
issuer: 'Scrum.org',
|
||||
badgeText: 'PSM II Advanced',
|
||||
icon: 'Award',
|
||||
highlight: true
|
||||
},
|
||||
{
|
||||
id: 'msc',
|
||||
title: 'M.Sc. Computer Science (Technical Systems)',
|
||||
issuer: 'OTH Regensburg',
|
||||
badgeText: 'M.Sc. Degree',
|
||||
icon: 'GraduationCap'
|
||||
},
|
||||
{
|
||||
id: 'psm1',
|
||||
title: 'Professional Scrum Master I (PSM I)',
|
||||
issuer: 'Scrum.org',
|
||||
badgeText: 'PSM I',
|
||||
icon: 'Award'
|
||||
},
|
||||
{
|
||||
id: 'itil',
|
||||
title: 'ITIL Foundation (v3)',
|
||||
issuer: 'AXELOS',
|
||||
badgeText: 'ITIL Certified',
|
||||
icon: 'CheckCircle2'
|
||||
},
|
||||
{
|
||||
id: 'lang',
|
||||
title: 'UNIcert III Technical English (C1)',
|
||||
issuer: 'Language Center Regensburg',
|
||||
badgeText: 'C1 Fluent',
|
||||
icon: 'Globe'
|
||||
}
|
||||
]
|
||||
|
||||
export const impactStats: StatItem[] = [
|
||||
{
|
||||
value: 'Multi-Cloud',
|
||||
label: 'Hyperscaler Mastery',
|
||||
description: 'Central Platform on GCP & enterprise analytics platforms on AWS'
|
||||
},
|
||||
{
|
||||
value: 'Spin-Off',
|
||||
label: 'Carve-Out Leadership',
|
||||
description: 'Technical cloud separation & migration from Continental to AUMOVIO'
|
||||
},
|
||||
{
|
||||
value: 'K8s at Scale',
|
||||
label: 'CKA-Certified GitOps',
|
||||
description: 'Bare-metal & cloud clusters managed via Kubespray, Helm, & Istio'
|
||||
},
|
||||
{
|
||||
value: '12+ Years',
|
||||
label: 'Infrastructure Depth',
|
||||
description: 'Continuous production operations, high-availability & on-call leadership'
|
||||
}
|
||||
]
|
||||
|
||||
export const capabilityDossiers: CapabilityDossier[] = [
|
||||
{
|
||||
id: 'cloud-platforms',
|
||||
title: 'Enterprise Multi-Cloud Platforms',
|
||||
category: 'Cloud Architecture & Governance',
|
||||
tagline: 'Resilient, secure, and self-service landing zones across Google Cloud and AWS.',
|
||||
overview: 'Designing and maintaining centralized enterprise cloud foundations that balance developer velocity with enterprise-grade guardrails. Leading central platform strategy for Google Cloud (GCP) and enterprise analytics platforms on AWS.',
|
||||
icon: 'Cloud',
|
||||
keyAreas: [
|
||||
'GCP Organization & Landing Zones architecture with fine-grained IAM & VPC Service Controls',
|
||||
'AWS multi-account governance (Control Tower, SCPs, Transit Gateway, Direct Connect)',
|
||||
'Automated tenant onboarding with self-service project/account vending machines',
|
||||
'Cost governance, FinOps visibility, resource tagging, and budget alerting automation'
|
||||
],
|
||||
technologies: ['Google Cloud (GCP)', 'AWS', 'IAM', 'VPC Service Controls', 'Transit Gateway', 'Organizations', 'FinOps'],
|
||||
impactNote: 'Powers centralized platform capabilities across hundreds of projects and thousands of internal users.'
|
||||
},
|
||||
{
|
||||
id: 'kubernetes-cncf',
|
||||
title: 'Kubernetes & Cloud-Native Ecosystem',
|
||||
category: 'Container Orchestration & GitOps',
|
||||
tagline: 'CKA-certified cluster architecture, service meshes, and GitOps delivery pipelines.',
|
||||
overview: 'Deep hands-on expertise operating production Kubernetes clusters on bare-metal and in cloud environments. Specializing in immutable cluster provisioning, zero-trust service mesh networking, and declarative GitOps pipelines.',
|
||||
icon: 'Server',
|
||||
keyAreas: [
|
||||
'Production cluster lifecycle provisioning via Kubespray, Ansible, and managed cloud engines',
|
||||
'Service mesh routing, mutual TLS (mTLS), and traffic shaping with Istio and Traefik Ingress',
|
||||
'Declarative GitOps deployment workflows using ArgoCD, Flux, and Kustomize overlays',
|
||||
'Full-stack cluster observability with Prometheus, Thanos, Loki, and custom Grafana dashboards'
|
||||
],
|
||||
technologies: ['Kubernetes (CKA)', 'Kubespray', 'Istio', 'Traefik', 'Helm', 'Kustomize', 'ArgoCD', 'Prometheus', 'Grafana'],
|
||||
impactNote: 'Maintains zero-downtime microservice environments with automated certificate lifecycle management.'
|
||||
},
|
||||
{
|
||||
id: 'data-platforms',
|
||||
title: 'Enterprise Data Lake & Analytics Enablement',
|
||||
category: 'Data Engineering & Service Ownership',
|
||||
tagline: 'Product & Service Ownership of global enterprise data lake platforms.',
|
||||
overview: 'Led the Continental.datalake platform as Global Team Lead, Service Owner, and Product Owner. Bridging business data requirements with rock-solid infrastructure to deliver self-service analytics for data scientists and engineers.',
|
||||
icon: 'Database',
|
||||
keyAreas: [
|
||||
'Global Service & Product Ownership of an enterprise data lake serving hundreds of data workloads',
|
||||
'Scalable analytical storage pipelines leveraging Amazon S3, Redshift, Glue, and Matillion ETL',
|
||||
'Data governance, cataloging, compliance, and granular data access control policies',
|
||||
'High-throughput telemetry and metrics processing with real-time alerting'
|
||||
],
|
||||
technologies: ['Amazon Redshift', 'S3 Data Lake', 'AWS Glue', 'Matillion', 'Kafka', 'Python Data Tooling', 'Athena'],
|
||||
impactNote: 'Delivered mission-critical data lake services supporting global enterprise analytics and machine learning initiatives.'
|
||||
},
|
||||
{
|
||||
id: 'iac-automation',
|
||||
title: 'Declarative Infrastructure as Code (IaC)',
|
||||
category: 'Automation & Continuous Delivery',
|
||||
tagline: '100% Everything as Code: repeatable, testable, and drift-resistant infrastructure.',
|
||||
overview: 'Pioneer of GitOps and automated infrastructure workflows. Managing multi-region infrastructure exclusively through declarative manifests, pull-request driven automation, and automated validation tests.',
|
||||
icon: 'Code2',
|
||||
keyAreas: [
|
||||
'Modular Terraform and Terragrunt architectures with DRY module reuse and remote state locking',
|
||||
'Automated PR workflows with Atlantis, Renovatebot, and pre-commit security validation',
|
||||
'Configuration management and server templating using Ansible and Packer',
|
||||
'Robust CI/CD pipelines in Jenkins and GitHub Actions with automated linting and security scanning'
|
||||
],
|
||||
technologies: ['Terraform', 'Terragrunt', 'Atlantis', 'Ansible', 'GitHub Actions', 'Jenkins', 'Packer', 'Renovatebot'],
|
||||
impactNote: 'Eliminated manual infrastructure changes, enforcing peer review and automated compliance across all environments.'
|
||||
},
|
||||
{
|
||||
id: 'leadership-governance',
|
||||
title: 'Engineering Leadership & Enterprise Spin-Offs',
|
||||
category: 'People, Culture & Organizational Transformation',
|
||||
tagline: 'Steering high-stakes cloud carve-outs and cultivating high-trust engineering teams.',
|
||||
overview: 'Experienced team lead and PSM II Scrum Master with a proven track record of guiding engineering teams through structural corporate carve-outs. Steered the cloud infrastructure separation during the spin-off from Continental to AUMOVIO.',
|
||||
icon: 'Users',
|
||||
keyAreas: [
|
||||
'Technical leadership of the cloud platform carve-out and migration from Continental to AUMOVIO',
|
||||
'Serving as Global Team Lead and Agile Coach (PSM II) fostering psychological safety and autonomy',
|
||||
'Founding and hosting technical exchange forums (DevOps Office Hours, Architecture Syncs)',
|
||||
'Mentoring junior and mid-level platform engineers in infrastructure best practices'
|
||||
],
|
||||
technologies: ['Scrum (PSM II)', 'Carve-Out Strategy', 'Service Management', 'Team Mentorship', 'DevOps Advocacy', 'Stakeholder Management'],
|
||||
impactNote: 'Executed seamless platform transition during enterprise spin-off without operational downtime.'
|
||||
},
|
||||
{
|
||||
id: 'systems-networking-iot',
|
||||
title: 'Systems Depth, Telecom Roots & Maker Craft',
|
||||
category: 'Low-Level Engineering & Physical Hardware',
|
||||
tagline: 'From BGP and VoIP SIP protocols to physical IoT sensors and microcontroller labs.',
|
||||
overview: 'Unlike purely conceptual architects, my foundation is built on deep telecommunications engineering, Linux kernel performance tuning, and hands-on physical hardware hacking. Understanding the metal makes cloud abstractions trivial.',
|
||||
icon: 'Cpu',
|
||||
keyAreas: [
|
||||
'Telecom infrastructure: SIP redirect servers, dynamic least-cost routing, ISDN, and Cisco ASR platforms',
|
||||
'Enterprise networking: BGP routing and troubleshooting, Fortinet firewalls, DMZ segmentation',
|
||||
'Deep Linux performance engineering and kernel diagnostics using atop, eBPF, and system tools',
|
||||
'Physical IoT and hardware tinkering: ESPHome firmware, BLE sensor mesh, and home automation nodes'
|
||||
],
|
||||
technologies: ['BGP', 'Cisco ASR', 'VoIP (SIP/ISDN)', 'Linux Internals (atop)', 'ESPHome', 'ESP32 / BLE', 'Fortinet', 'Python / Bash'],
|
||||
impactNote: 'Applies deep understanding of physical networking and hardware constraints to diagnose complex distributed systems.'
|
||||
}
|
||||
]
|
||||
|
||||
export const skills: SkillItem[] = [
|
||||
// Cloud & Multi-Cloud
|
||||
{ name: 'Google Cloud (GCP)', category: 'cloud', tags: ['Central Platform', 'Landing Zones', 'IAM', 'VPC Service Controls'], description: 'Leading central enterprise platform architecture and organizational governance.', featured: true },
|
||||
{ name: 'Amazon Web Services (AWS)', category: 'cloud', tags: ['Enterprise Scale', 'Control Tower', 'EKS', 'S3', 'Redshift'], description: 'Multi-account AWS landing zones, analytics architectures, and automation.', featured: true },
|
||||
{ name: 'Multi-Cloud Architecture', category: 'cloud', tags: ['Enterprise Strategy', 'Hybrid Cloud', 'Zero-Trust'], description: 'Designing unified identity and network boundaries across disparate providers.' },
|
||||
{ name: 'FinOps & Cost Governance', category: 'cloud', tags: ['Budgets', 'Tagging Enforcement', 'Cost Optimization'], description: 'Algorithmic cost allocation, rightsizing, and spend visibility.' },
|
||||
|
||||
// Kubernetes & CNCF
|
||||
{ name: 'Kubernetes (CKA)', category: 'k8s', tags: ['Certified', 'Cluster Architecture', 'Production Operations'], description: 'Certified Kubernetes Administrator with bare-metal and cloud cluster expertise.', featured: true },
|
||||
{ name: 'Kubespray & Kubeadm', category: 'k8s', tags: ['Ansible', 'Bare Metal', 'Cluster Lifecycle'], description: 'Automated cluster bootstrapping, OS hardening, and zero-downtime upgrades.', featured: true },
|
||||
{ name: 'Istio Service Mesh', category: 'k8s', tags: ['mTLS', 'Traffic Management', 'Security'], description: 'Zero-trust networking, traffic splitting, and service-to-service encryption.' },
|
||||
{ name: 'Traefik & Ingress-Nginx', category: 'k8s', tags: ['Ingress Controllers', 'Let\'s Encrypt', 'Cert-Manager'], description: 'Edge routing, automatic SSL/TLS certificate lifecycle, and header mutation.' },
|
||||
{ name: 'Helm & Kustomize', category: 'k8s', tags: ['Package Management', 'Overlays', 'GitOps'], description: 'Declarative workload templating across staging and production clusters.' },
|
||||
{ name: 'ArgoCD & Flux', category: 'k8s', tags: ['GitOps', 'Continuous Deployment', 'Reconciliation'], description: 'Pull-based deployment engines enforcing cluster state synchronization.' },
|
||||
|
||||
// IaC & Automation
|
||||
{ name: 'Terraform', category: 'iac', tags: ['HCL', 'State Management', 'Provider Authoring'], description: 'Declarative cloud provisioning with reusable, modular design patterns.', featured: true },
|
||||
{ name: 'Terragrunt', category: 'iac', tags: ['DRY Modules', 'Multi-Account', 'State Locking'], description: 'Orchestrating complex multi-account and multi-region infrastructure hierarchies.', featured: true },
|
||||
{ name: 'Atlantis', category: 'iac', tags: ['GitOps for IaC', 'PR Workflows', 'Automated Plans'], description: 'PR-based Terraform execution ensuring full transparency and compliance.' },
|
||||
{ name: 'Ansible', category: 'iac', tags: ['Playbooks', 'Roles', 'Configuration Management'], description: 'Server provisioning, fleet configuration, and Kubespray automation.' },
|
||||
{ name: 'GitHub Actions & Jenkins', category: 'iac', tags: ['CI/CD Pipelines', 'Automated Testing', 'Docker Builds'], description: 'Building resilient automated pipelines for container builds and lint checks.' },
|
||||
{ name: 'Python (Scripting & Boto3)', category: 'iac', tags: ['Automation', 'CLI Tooling', 'AWS SDK'], description: 'Custom cloud automation scripts, CLI utilities, and API wrappers.' },
|
||||
|
||||
// Systems & Networking
|
||||
{ name: 'BGP & IP Routing', category: 'systems', tags: ['Border Gateway Protocol', 'Troubleshooting', 'Autonomous Systems'], description: 'Dynamic routing protocols, peering troubleshooting, and route reflection.', featured: true },
|
||||
{ name: 'VoIP, SIP & ISDN', category: 'systems', tags: ['Telecom', 'Least Cost Routing', 'Cirpack', 'Oracle SBC'], description: 'Architected SIP redirect servers and enterprise telephony carrier routing.', featured: true },
|
||||
{ name: 'Cisco ASR & Switching', category: 'systems', tags: ['Hardware Routers', 'VLANs', 'Core Infrastructure'], description: 'Carrier-grade hardware router operation, interface diagnostics, and QoS.' },
|
||||
{ name: 'Linux Performance & Internals', category: 'systems', tags: ['atop', 'eBPF', 'Kernel Diagnostics', 'Troubleshooting'], description: 'Deep performance analysis, memory leak isolation, and system optimization.' },
|
||||
{ name: 'Network Security & Firewalls', category: 'systems', tags: ['Fortinet', 'DMZ Design', 'Security Zones'], description: 'Designing perimeter security zones, DMZ isolation, and packet filtering.' },
|
||||
|
||||
// Data & Observability
|
||||
{ name: 'Enterprise Data Lake', category: 'data', tags: ['Product Owner', 'Service Owner', 'Architecture'], description: 'End-to-end service management of enterprise analytics and data pipelines.', featured: true },
|
||||
{ name: 'Amazon Redshift & S3', category: 'data', tags: ['Columnar Storage', 'Data Warehousing', 'Query Optimization'], description: 'Analytical storage layers and performance tuning for big data analytics.' },
|
||||
{ name: 'Prometheus & Grafana', category: 'data', tags: ['PromQL', 'Metrics', 'Dashboards', 'Alertmanager'], description: 'Comprehensive observability stacks and custom operational dashboards.' },
|
||||
{ name: 'Loki & ELK Stack', category: 'data', tags: ['Log Aggregation', 'LogQL', 'Elasticsearch'], description: 'Centralized log aggregation, fast querying, and audit retention.' },
|
||||
{ name: 'Matillion & ETL', category: 'data', tags: ['Pipelines', 'Transformation', 'Data Loading'], description: 'Automating high-volume data transforms and warehouse ingestion.' },
|
||||
|
||||
// Leadership & Agile
|
||||
{ name: 'Global Team Leadership', category: 'leadership', tags: ['People Management', 'Platform Strategy', 'Hiring'], description: 'Leading cross-functional distributed platform engineering teams.', featured: true },
|
||||
{ name: 'Enterprise Spin-Off Management', category: 'leadership', tags: ['Carve-Out', 'Separation', 'Migration Strategy'], description: 'Successfully governed IT infrastructure carve-out from Continental to AUMOVIO.', featured: true },
|
||||
{ name: 'Professional Scrum Master (PSM II)', category: 'leadership', tags: ['Agile Coaching', 'Scrum.org', 'Continuous Improvement'], description: 'Advanced Scrum Master fostering psychological safety and iterative delivery.', featured: true },
|
||||
{ name: 'Service & Product Ownership', category: 'leadership', tags: ['ITIL', 'SLAs / SLOs', 'Stakeholder Alignment'], description: 'Full lifecycle responsibility for enterprise platforms and user satisfaction.' },
|
||||
{ name: 'Community & Office Hours', category: 'leadership', tags: ['DevOps Advocacy', 'Knowledge Sharing', 'Mentorship'], description: 'Founded internal technical office hours to bridge platform teams and users.' },
|
||||
|
||||
// Hardware & IoT
|
||||
{ name: 'ESPHome & Microcontrollers', category: 'iot', tags: ['ESP32', 'Firmware', 'Sensor Networks'], description: 'Building custom firmware for environmental sensors and smart controllers.', featured: true },
|
||||
{ name: 'BLE & Sensor Telemetry', category: 'iot', tags: ['Bluetooth LE', 'Soil/Plant Monitors', 'Telemetry'], description: 'Low-power wireless telemetry pipelines bridging edge devices to MQTT.' },
|
||||
{ name: 'Self-Hosted Home Lab', category: 'iot', tags: ['Bare-Metal K8s', 'Storage', 'Home Automation'], description: 'Private infrastructure testing ground running production-grade workloads.' }
|
||||
]
|
||||
|
||||
export const engineeringPrinciples: Principle[] = [
|
||||
{
|
||||
title: 'Declarative Everything',
|
||||
statement: 'If it is not written in code and version-controlled, it does not exist.',
|
||||
explanation: 'From cloud landing zones and Kubernetes manifests to firewall policies and firmware: every single component is defined declaratively, reviewed via pull requests, and deployed through automated GitOps pipelines.',
|
||||
icon: 'FileCode'
|
||||
},
|
||||
{
|
||||
title: 'Self-Service over Gatekeeping',
|
||||
statement: 'A platform team is a product team whose customers are developers.',
|
||||
explanation: 'Great platform engineering does not create bureaucratic approval tickets. It provides secure, automated guardrails, golden paths, and self-service APIs so product developers can move fast with complete confidence.',
|
||||
icon: 'Zap'
|
||||
},
|
||||
{
|
||||
title: 'Ground-Up Systems Thinking',
|
||||
statement: 'Higher-level cloud abstractions always rest on physical networks and kernels.',
|
||||
explanation: 'Troubleshooting distributed microservices becomes effortless when you truly understand BGP routing, TCP handshake latency, Linux memory paging, and hardware constraints. Real-world reliability requires full-stack depth.',
|
||||
icon: 'Layers'
|
||||
},
|
||||
{
|
||||
title: 'Pragmatic & Sustainable Agility',
|
||||
statement: 'Scrum exists to empower engineers, not to worship dogma.',
|
||||
explanation: 'As a PSM II Scrum Master, I treat agility as a tool to protect team focus, foster psychological safety, align on clear goals, and eliminate friction so high-performing engineers can do their best work sustainably.',
|
||||
icon: 'HeartHandshake'
|
||||
}
|
||||
]
|
||||
|
||||
export const socialLinks: SocialLink[] = [
|
||||
{
|
||||
platform: 'LinkedIn',
|
||||
label: 'Connect on LinkedIn',
|
||||
url: 'https://www.linkedin.com/in/moritz-graf-92abaa91/',
|
||||
icon: 'Linkedin',
|
||||
handle: 'moritz-graf-92abaa91'
|
||||
},
|
||||
{
|
||||
platform: 'GitHub',
|
||||
label: 'Follow on GitHub',
|
||||
url: 'https://github.com/iptizer',
|
||||
icon: 'Github',
|
||||
handle: 'iptizer'
|
||||
},
|
||||
{
|
||||
platform: 'Email',
|
||||
label: 'Email Moritz',
|
||||
url: 'mailto:contact-moritz@moritzgraf.de',
|
||||
icon: 'Mail',
|
||||
handle: 'contact-moritz@moritzgraf.de'
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,5 @@
|
||||
import { createApp } from 'vue'
|
||||
import './style.css'
|
||||
import App from './App.vue'
|
||||
|
||||
createApp(App).mount('#app')
|
||||
@@ -0,0 +1,38 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
@layer base {
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
font-family: 'Plus Jakarta Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
}
|
||||
|
||||
code, pre, .font-mono {
|
||||
font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
}
|
||||
}
|
||||
|
||||
/* Smooth transitions for theme switching */
|
||||
body, nav, header, section, footer, div, button, input {
|
||||
transition-property: background-color, border-color, color, fill, stroke;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
|
||||
/* Custom scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(148, 163, 184, 0.3);
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(100, 116, 139, 0.5);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
export interface Credential {
|
||||
id: string
|
||||
title: string
|
||||
issuer: string
|
||||
badgeText: string
|
||||
icon: string
|
||||
highlight?: boolean
|
||||
}
|
||||
|
||||
export interface StatItem {
|
||||
value: string
|
||||
label: string
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface CapabilityDossier {
|
||||
id: string
|
||||
title: string
|
||||
category: string
|
||||
tagline: string
|
||||
overview: string
|
||||
icon: string
|
||||
keyAreas: string[]
|
||||
technologies: string[]
|
||||
impactNote: string
|
||||
}
|
||||
|
||||
export type SkillCategory =
|
||||
| 'all'
|
||||
| 'cloud'
|
||||
| 'k8s'
|
||||
| 'iac'
|
||||
| 'systems'
|
||||
| 'data'
|
||||
| 'leadership'
|
||||
| 'iot'
|
||||
|
||||
export interface SkillItem {
|
||||
name: string
|
||||
category: Exclude<SkillCategory, 'all'>
|
||||
tags: string[]
|
||||
description: string
|
||||
featured?: boolean
|
||||
}
|
||||
|
||||
export interface Principle {
|
||||
title: string
|
||||
statement: string
|
||||
explanation: string
|
||||
icon: string
|
||||
}
|
||||
|
||||
export interface SocialLink {
|
||||
platform: string
|
||||
label: string
|
||||
url: string
|
||||
icon: string
|
||||
handle: string
|
||||
}
|
||||
|
Before Width: | Height: | Size: 888 KiB |
|
Before Width: | Height: | Size: 874 KiB |
@@ -1,4 +0,0 @@
|
||||
exampleSite/public
|
||||
.DS_Store
|
||||
*/**/.DS_Store
|
||||
*.sw*
|
||||
@@ -1,9 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 [Rajesh Shenoy](https://github.com/shenoybr)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,210 +0,0 @@
|
||||
|
||||
# Goa
|
||||
|
||||
Goa is a clean, simple and minimalist theme for blogs and personal websites.
|
||||
|
||||
<img src="http://i.imgur.com/vqMd1Mx.png" width="40%" height="40%" />
|
||||
|
||||
[](https://raw.githubusercontent.com/shenoybr/hugo-goa/master/LICENSE)
|
||||
[](https://github.com/shenoybr/hugo-goa/stargazers)
|
||||
[](https://github.com/shenoybr/hugo-goa/network)
|
||||
[](http://www.emergencykitten.com/)
|
||||
[](https://twitter.com/intent/tweet?text=Wow:&url=%5Bobject%20Object%5D)
|
||||
## Demo
|
||||
|
||||
You can find the demo site in action [here](https://shenoybr.github.io/hugo-goa-demo) and the source [here](https://github.com/shenoybr/hugo-goa-demo).
|
||||
|
||||
## Installation
|
||||
|
||||
From the root of your blog:
|
||||
|
||||
```
|
||||
mkdir -p themes
|
||||
cd themes
|
||||
git clone https://github.com/shenoybr/hugo-goa
|
||||
```
|
||||
|
||||
## Content creation
|
||||
|
||||
### Creating a post
|
||||
|
||||
To create a new page or post:
|
||||
|
||||
````
|
||||
hugo new about.md
|
||||
````
|
||||
or
|
||||
|
||||
````
|
||||
hugo new posts/first.md
|
||||
````
|
||||
|
||||
You can now go ahead an edit the newly created file under the `content` directory. Once you are finished editing, to have hugo generate the page, set `draft = false` in the articles front matter.
|
||||
|
||||
### Organizing pages
|
||||
|
||||
The above example demonstrates how to create a pages and posts. Hugo automatically applies the list templates for a directory of pages/posts, which works well for blogs and posts. However, you may want at times want to override this behavior and create a standalone page (like an about page or projects page) or have more control of what content is listed from within the directory. In such cases, you can override the default behavior by placing an index.md file in the corresponding content
|
||||
directory.
|
||||
|
||||
````
|
||||
hugo new projects/index.md
|
||||
````
|
||||
|
||||
### Page settings
|
||||
|
||||
These settings are at the page level.
|
||||
|
||||
- `showpagemeta`: default=`true`. This allows you to disable page meta information from being displayed. For example, this setting is disabled [here](https://shenoybr.github.io/hugo-goa-demo/about/) and enabled [here](https://shenoybr.github.io/hugo-goa-demo/coderag/).
|
||||
- `showcomments`: default=`true`. Enables or disable comments. For example, this setting is disabled [here](https://shenoybr.github.io/hugo-goa-demo/blog/third/) and enabled [here](https://shenoybr.github.io/hugo-goa-demo/blog/first/).
|
||||
|
||||
## Configuration
|
||||
|
||||
The provided [config.toml](https://github.com/shenoybr/hugo-goa/blob/master/exampleSite/config.toml) describes all options and features that are supported. Configure it your way!
|
||||
|
||||
### Basic Configuration
|
||||
|
||||
These are site wide configuration parameters that are used by this template.
|
||||
|
||||
- `baseurl`: This is the root of your site.
|
||||
- `builddrafts`: default=`false`. Enables or Disable building drafts when hugo is run.
|
||||
- `canonifyurls`: default=`false`. Prefix all relative URLs with your base URL. [More Information](https://gohugo.io/extras/urls#canonicalization).
|
||||
- `languageCode`: Used to set site localization preferences. eg. `en-US`.
|
||||
- `contentdir`: Where hugo can find your content. eg. `content`.
|
||||
- `layoutdir`: Where hugo can find your templates. eg. `layouts`.
|
||||
- `publishdir`: Where hugo generates the static site. eg. `public`.
|
||||
- `author`: Site author name. eg. `Erlich Bachman`.
|
||||
- `title`: Site title name. eg. `Erlich Bachman`.
|
||||
- `theme`: Your theme name should be set to `hugo-goa` if using this theme.
|
||||
|
||||
## Hugo Built-in Features
|
||||
|
||||
These are features that hugo provides and are used by this template.
|
||||
|
||||
- `disqusShortname`: Your discusShortname if you want to enable comments on your posts.
|
||||
- `googleAnalytics`: Your google analytics id for tracking.
|
||||
- `enableRobotsTXT`: Enable or disable search engines from crawling your site.
|
||||
|
||||
## Site Settings `[params]`
|
||||
|
||||
These are settings that are specific to this theme.
|
||||
|
||||
- `author`: Main author name. eg. `Erlich Bachman`.
|
||||
- `intro`: Author introduction. This field supports markdown. eg. `Startup Guru Extraordinaire`.
|
||||
- `description`: Author description. This field supports markdown. eg. `Now @Pied Piper. Previously @Hacker Hostel, @Bachmanity and @Aviato. <br/> \"What is F times 5? It's Fleventy-five.\"`.
|
||||
- `authorimage`: Location of author image under static/img directory. eg. `headshot.jpg`
|
||||
- `dateformat`: Golang date format to be used on this site. eg. `Jan 2, 2006`
|
||||
|
||||
### Site Meta Settings `[params.meta]`
|
||||
|
||||
These settings are included in the site's meta section.
|
||||
|
||||
- `description`: User this field to describe your site to search engines. eg. `Simple minimalist theme`.
|
||||
- `keywords`: Keywords that desribe your site. eg. `minimalist,blog,goa,hugo,developer`.
|
||||
|
||||
### Social Accounts `[params.social]`
|
||||
|
||||
These settings to display your social accounts.
|
||||
|
||||
- `github`: Your github username.
|
||||
- `instagram`: Your instagram username.
|
||||
- `xing`: Your xing username.
|
||||
- `linkedin`: Your linkedIn username.
|
||||
- `twitter`: Your twitter username.
|
||||
- `facebook`: Your facebook username.
|
||||
- `google`: Your google username.
|
||||
- `email`: Your email.
|
||||
|
||||
### Extras `[params.extra]`
|
||||
|
||||
These settings for extra features that this site uses.
|
||||
|
||||
- `copyright`: Add a copyright statement to the bottom of the theme. eg. `© 2016. Erlich Bachman. [Some Rights Reserved](http://creativecommons.org/licenses/by/3.0/)."`
|
||||
- `poweredby`: Help promote this theme and give the authors credit. eg. `true` or `false`.
|
||||
- `highlightjs`: Use highlightJS to highlight code on your site. eg. `true` or `false`.
|
||||
|
||||
### Main Menu `[[menu.main]]`
|
||||
|
||||
These settings for the main menu that is displayed on the home page.
|
||||
|
||||
- `name`: Name of menu item. eg. `blog`
|
||||
- `weight`: Weight of this menu item. Higher items go to the bottom. eg. `100`
|
||||
- `url`: Root URL for this section/page. eg. `/blog/`.
|
||||
|
||||
Example:
|
||||
```
|
||||
[[menu.main]]
|
||||
name = "blog"
|
||||
weight = 100
|
||||
url = "/blog/"
|
||||
[[menu.main]]
|
||||
name = "about"
|
||||
weight = 200
|
||||
url = "/about/"
|
||||
[[menu.main]]
|
||||
name = "coderag"
|
||||
weight = 300
|
||||
url = "/coderag/"
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
* Responsive
|
||||
* Minimalist
|
||||
* Bootstrap 3
|
||||
* Fontawesome
|
||||
* HighlighJS
|
||||
* Disqus support for commenting
|
||||
* Built-in support for 404 pages, Disqus comments and Google Analytics.
|
||||
|
||||
## Screenshots
|
||||
|
||||
<img src="http://i.imgur.com/vqMd1Mx.png" width="30%" height="30%" />
|
||||
<img src="http://i.imgur.com/dfj8MHz.png" width="30%" height="30%" />
|
||||
<img src="http://i.imgur.com/mMFfkZY.png" width="30%" height="30%" />
|
||||
<img src="http://i.imgur.com/7e67ypn.png" width="30%" height="30%" />
|
||||
<img src="http://i.imgur.com/lz3RGH9.png" width="30%" height="30%" />
|
||||
<img src="http://i.imgur.com/IPggNGk.png" width="30%" height="30%" />
|
||||
<img src="http://i.imgur.com/FW1Bdln.png" width="10%" height="10%" />
|
||||
<img src="http://i.imgur.com/vTY5GeX.png" width="10%" height="10%" />
|
||||
<img src="http://i.imgur.com/aJZQYZ6.png" width="10%" height="10%" />
|
||||
<img src="http://i.imgur.com/rGQJAF3.png" width="10%" height="10%" />
|
||||
|
||||
## Contributing
|
||||
|
||||
### Bug Reports
|
||||
|
||||
1. Search Github Issues to see if the bug has been previously filed.
|
||||
2. If it has been filed, +1 the post. This helps us in assessing impact and priortitizing the bug.
|
||||
3. If not previously filed, open a new Github Issue and describe in detail. Attach error traces and provide relavant details to help us solve it.
|
||||
4. For Hugo issues, search the Hugo [Forum](https://discuss.gohugo.io/)
|
||||
|
||||
### Feature Requests
|
||||
|
||||
1. Search Github Issues to see if the feature has been previously requested.
|
||||
2. If it has been filed, +1 the post. This helps us in assessing popularity and priortitizing the feature.
|
||||
3. If not previously filed, open a new Github Issue and describe it in detail.
|
||||
|
||||
### Pull Requests
|
||||
|
||||
1. Clone the repository, create the feature/bug branch.
|
||||
2. Code.
|
||||
3. Make sure your code follows the style of the project.
|
||||
4. Test it thoroughly.
|
||||
5. Open a PR requesting for it to be merged.
|
||||
6. Describe the feature or issue your are solving in detail.
|
||||
7. Wait for its approval.
|
||||
8. Merge and Rejoice.
|
||||
|
||||
## Attribution
|
||||
|
||||
The theme's design was inspired by many blogs and themes:
|
||||
|
||||
1. Bruno de Carvalho's [blog](http://biasedbit.com).
|
||||
2. [Hugo Cocoa](http://themes.gohugo.io/cocoa/).
|
||||
3. [Hugo Vec](http://themes.gohugo.io/hugo-theme-vec/).
|
||||
4. [Hugo Agency](http://themes.gohugo.io/hugo-agency/).
|
||||
|
||||
## License
|
||||
|
||||
Licensed under the [MIT](https://opensource.org/licenses/MIT) License. See the [LICENSE](https://raw.githubusercontent.com/shenoybr/hugo-goa/master/LICENSE) file for more details.
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
+++
|
||||
draft = false
|
||||
comments = false
|
||||
slug = ""
|
||||
tags = []
|
||||
categories = []
|
||||
|
||||
showpagemeta = true
|
||||
showcomments = true
|
||||
+++
|
||||
@@ -1,3 +0,0 @@
|
||||
.DS_Store
|
||||
*/**/.DS_Store
|
||||
public
|
||||
@@ -1,9 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 [Rajesh Shenoy](https://github.com/shenoybr)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,16 +0,0 @@
|
||||
# Hugo Goa Demo
|
||||
|
||||
## About
|
||||
|
||||
This site demonstrates the use of the [Hugo Goa Theme](https://github.com/shenoybr/hugo-goa) by [@shenoybr](https://github.com/shenoybr).
|
||||
|
||||
## Usage
|
||||
|
||||
* Install [Hugo](gohugo.io)
|
||||
* git clone https://github.com/shenoybr/hugo-goa-demo
|
||||
* Run `hugo server -w -v` from the root directory
|
||||
* Site will be visible at `localhost:1313`
|
||||
|
||||
## License
|
||||
|
||||
Licensed under the [MIT](https://opensource.org/licenses/MIT) License. See the [LICENSE](https://raw.githubusercontent.com/shenoybr/hugo-goa-demo/master/LICENSE) file for more details.
|
||||
@@ -1,68 +0,0 @@
|
||||
## Basic Configuration
|
||||
|
||||
baseurl = "https://shenoybr.github.io/hugo-goa-demo/"
|
||||
builddrafts = false
|
||||
canonifyurls = false
|
||||
languageCode = "en-US"
|
||||
|
||||
contentdir = "content"
|
||||
layoutdir = "layouts"
|
||||
publishdir = "public"
|
||||
|
||||
author = "Erlich Bachman"
|
||||
title = "Erlich Bachman"
|
||||
|
||||
theme = "hugo-goa"
|
||||
|
||||
## Hugo Built-in Features
|
||||
disqusShortname = "https-shenoybr-github-io-hugo-goa-demo" # Comment out to disable Disqus.
|
||||
googleAnalytics = "XYZ"
|
||||
enableRobotsTXT = true
|
||||
|
||||
## Site Settings
|
||||
[params]
|
||||
author = "Erlich Bachman"
|
||||
intro = "Startup Guru Extraordinaire"
|
||||
description = "Now @Pied Piper. Previously @Hacker Hostel, @Bachmanity and @Aviato. <br/> \"What is F times 5? It's Fleventy-five.\""
|
||||
authorimage = "headshot.jpg"
|
||||
dateformat = "Jan 2, 2006"
|
||||
|
||||
## Site Meta Settings
|
||||
[params.meta]
|
||||
description = "Simple minimalist theme"
|
||||
keywords = "minimalist,blog,goa,hugo,developer"
|
||||
|
||||
## Social Accounts
|
||||
[params.social]
|
||||
github = "<username>"
|
||||
instagram = "<username>"
|
||||
xing = "<username>"
|
||||
linkedin = "<username>"
|
||||
twitter = "<username>"
|
||||
facebook = "<username>"
|
||||
google = "<username>"
|
||||
stackoverflow = "<username>"
|
||||
email = "you@example.com"
|
||||
|
||||
## Extras
|
||||
[params.extra]
|
||||
copyright = "© 2016. Erlich Bachman. [Some Rights Reserved](http://creativecommons.org/licenses/by/3.0/)."
|
||||
poweredby = true
|
||||
highlightjs = true
|
||||
|
||||
## Main Menu
|
||||
[[menu.main]]
|
||||
name = "blog"
|
||||
weight = 100
|
||||
identifier = "blog"
|
||||
url = "/blog/"
|
||||
[[menu.main]]
|
||||
name = "about"
|
||||
identifier = "about"
|
||||
weight = 200
|
||||
url = "/about/"
|
||||
[[menu.main]]
|
||||
name = "coderag"
|
||||
identifier = "coderag"
|
||||
weight = 300
|
||||
url = "/coderag/"
|
||||
@@ -1,18 +0,0 @@
|
||||
+++
|
||||
categories = ["about"]
|
||||
comments = false
|
||||
date = "2016-10-02T22:55:05-04:00"
|
||||
draft = false
|
||||
slug = ""
|
||||
tags = ["about"]
|
||||
title = "About"
|
||||
|
||||
showpagemeta = false
|
||||
+++
|
||||
|
||||
Erlich administers the Hacker Hostel, a tech incubator where Richard, Big Head, Dinesh, and Gilfoyle live and work in exchange for 10 percent of their potential businesses. Erlich clings to his glory days, when he sold aviation start-up Aviato, a move that, at least in his mind, qualifies him to be a svengali lording over other tech nerds. He still drives a car emblazoned with multiple Aviato logos and smokes copious amounts of weed.[1]
|
||||
|
||||
In Fiduciary Duties, a drunk Richard makes Erlich a Pied Piper board member, a decision he later regrets.
|
||||
|
||||
In Two Days of the Condor, it is revealed that Erlich no longer codes due to carpal tunnel syndrome.
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
+++
|
||||
categories = ["ad"]
|
||||
comments = false
|
||||
date = "2016-10-02T15:59:13-04:00"
|
||||
draft = false
|
||||
showpagemeta = true
|
||||
showcomments = true
|
||||
slug = ""
|
||||
tags = ["gavin", "belson", "hooli", "hardware"]
|
||||
title = "Gavin Belson"
|
||||
description = "A post about the amazingness of Gavin Belson"
|
||||
|
||||
+++
|
||||
|
||||
Note: This article originally appeared in [CodeRag](http://www.coderag.com/10-facts-you-didnt-know-about-gavin-belson/).
|
||||
|
||||
Gavin Belson’s name has long been synonymous with innovation—but beyond his work at Hooli, do we even know the man behind the rampant success? In a Code/Rag exclusive, Belson revealed to us 10 things about himself that aren’t common knowledge.
|
||||
|
||||
* He’s a nature fanatic! “It’s important to me that Hooli has an effective recycling program, and a personal passion of mine is donating to elephant sanctuaries. In fact, I’ve recently spearheaded the creation of Hooli’s newest charity: the Hooli Elephant Wildlife Trust.”
|
||||
* He likes to go analog! His company may have been founded on email, but Belson has a soft spot for the written word. “There’s something nostalgic about receiving a letter—it’s a message from the past.”
|
||||
* He’s an honorary shaman! “I studied at the Institute of Spiritual Therapy and Vision Quests, where I was honored with the title of shaman. The Institute has fundamentally furthered my understanding of the world and its capacity for greatness.”
|
||||
* His favorite pizza topping is pepperoni! “Uncured, preferably, with some aged parmesan.”
|
||||
* He drives a Tesla! “Black. I try not to bee too conspicuous. And of course, the benefit to the environment is incomparable.”
|
||||
* His health is important to him! “I eat at least one piece of unprocessed fruit a day.”
|
||||
* He has bad habits too! “I never reboot my laptop. I know it’s bad, but I’m always in a hurry.”
|
||||
* He doesn’t have a Nobel Peace Prize…yet! “It’s a common misconception, especially with the amount of charity work I do with Hooli, and sometimes people associate my status as a shaman with working towards world peace, which of course is something I’m striving for.”
|
||||
* He’s loyal to his barber! “I’ve been going to the same barber for years—he was based in Palo Alto, but I’ve convinced him to move to the South Bay.”
|
||||
* He really, really loves animals! “I would say I’m a cat person. And a dog person. I’m an animal person, and I don’t just mean that human beings are animals. Did you know the African bush elephant is still an endangered species? Programs like the Hooli Elephant Wildlife Trust won’t save them immediately, but it’s a start.”
|
||||
@@ -1,21 +0,0 @@
|
||||
+++
|
||||
categories = ["propaganda"]
|
||||
comments = false
|
||||
date = "2016-10-01T16:59:13-04:00"
|
||||
draft = false
|
||||
showpagemeta = true
|
||||
slug = ""
|
||||
tags = ["gavin", "belson", "hooli", "ceo"]
|
||||
title = "Belson Back on Top"
|
||||
description = "Hooli’s CEO Focusing on the Bottom Line"
|
||||
|
||||
+++
|
||||
Note: This post originally appeared in [CodeRag](http://www.coderag.com/belson-back-on-top-hoolis-ceo-focusing-on-the-bottom-line/)
|
||||
|
||||
Gavin Belson’s brilliant leadership is unstoppable. While allegedly taking a personal hiatus from day-to-day operations at Hooli, Belson couldn’t resist brokering a blockbuster deal between Hooli and Maleant Data Systems, deploying Hooli’s innovative new Hooli/Endframe Box. The multi-year deal brings handsome margins for Hooli and can easily be a template to apply across the entire Fortune 1000. An inside source tells me that, no surprise, the board was no longer satisfied to let Belson fade into the background. Hat in hand, they begged Belson to come back to the helm.
|
||||
|
||||
It’s been barely two months since Belson announced he would be taking a leave from his leadership role in order to focus on spiritual wellness and family. What drove Belson to this decision remains unclear. Was it additional fallout from the Hooli-Search scandal we broke at this very blog? Or perhaps his cutthroat methods in managing Hooli-Search results indicate a demand across the board for higher standards, and as such, Belson could not tolerate the sloppy security upgrades we also reported on. In any case, it seems the company’s electrical issues are resolved, and with it, any spirituality Belson felt the need to examine. The company’s Chief Innovation Officer has returned.
|
||||
|
||||
Belson, who is single, has had a slew of successes at Hooli this past year. When he announced the dissolution of the Nucleus project, his bravery was rewarded as Hooli stock jumped 7 percent. The cost savings was compounded when Belson pulled the trigger on company-wide layoffs, and the hole left by Nucleus was quickly filled when he acquired middle-out company Endframe. Endframe’s tech has long dominated the middle-out space, and it’s no surprise that the Hooli/Endframe relationship has combined Hooli’s resources and innovation with Endframe’s impressive tech to create something obscenely profitable: the Hooli/Endframe Box.
|
||||
|
||||
With Belson back on top at Hooli, shareholders can breathe a sigh of relief. While Belson’s career is certainly not without drama, his shrewd business sense and innovative technological accomplishments are what’s shaped Hooli into the company it is today. Without Gavin Belson, there is no Hooli. And without Hooli, where would any of us be?
|
||||
@@ -1,23 +0,0 @@
|
||||
+++
|
||||
categories = ["propaganda"]
|
||||
comments = false
|
||||
date = "2016-10-03T16:59:13-04:00"
|
||||
draft = false
|
||||
showpagemeta = true
|
||||
showcomments = false
|
||||
slug = ""
|
||||
tags = ["gavin", "belson", "hooli"]
|
||||
title = "Hooli’s Hot New Hardware"
|
||||
description = "Middle out compression technology for the win!"
|
||||
|
||||
+++
|
||||
|
||||
Note: This article originally appeared in [CodeRag](http://www.coderag.com/hoolis-hot-new-hardware/).
|
||||
|
||||
When Hooli acquired Endframe, no one could wait to see what the tech giant would do with the revolutionary, middle-out compression technology. The wait is over: In a savvy pivot, Hooli took the software and created their newest hardware: the Hooli/Endframe Box. While initially conceived as a consumer-facing platform, the Endframe technology has been repurposed to “make the world a better place through secure, high-capacity enterprise data storage servers utilizing middle-out compression technology.”
|
||||
|
||||
Insanely simple and beautifully crafted, the hardware backs up large amounts of data at a fraction of what it used to cost, so it’s no surprise that enterprise top dog Maleant Data System Solutions jumped at the chance to make a multi-year deal for the product. At a press conference announcing the blockbuster deal, CEO Calvin Greene stated, “The Hooli/Endframe Box is a welcome integration with our banking and medical system clients, who need to process large amounts of secure data at an accelerated pace.” Hooli’s Gavin Belson quipped that the world “always needs more money and doctors.”
|
||||
|
||||
The hardware features a Dual Intel Haswell 2.6 Ghz processor, NVIDIA Tesla K80, Lightnight Eco Gen II SanDisk (800 TB, no less) and two 4TB Seagate hard drives. Matched with Endframe’s middle out algorithm, there’s a reason Maleant promises a 200 percent ROI to any client who elects to incorporate the Hooli/Endframe Box into their service deal with Maleant.
|
||||
|
||||
The two companies flirted with multiple deal options before sealing the deal on a white label agreement, and becoming the power couple of enterprise data storage. Hooli could sell upgraded versions of these boxes to Maleant indefinitely, and Maleant’s penetration in the enterprise market ensures high demand for more Hooli/Endframe Boxes as long as Hooli can keep making them. Hundreds of millions of dollars could follow for Hooli’s enterprise sales division. With these two joining to sell storage solutions, no one will be able to touch their profits—or their data.
|
||||
@@ -1,19 +0,0 @@
|
||||
+++
|
||||
categories = ["articles"]
|
||||
comments = false
|
||||
date = "2016-10-02T22:55:05-04:00"
|
||||
draft = false
|
||||
slug = ""
|
||||
tags = ["bachman", "pied piper", "aviato"]
|
||||
title = "CodeRag"
|
||||
|
||||
showpagemeta = true
|
||||
+++
|
||||
Notes: This article originally appeared in [CodeRag](http://www.coderag.com/-note-from-the-editor/).
|
||||
|
||||
Code/Rag has always been more than just another online tech blog. We strive to be part of the tech community—serving an essential, fundamental look at what’s going on in the valley. It’s why our tagline is “Know Your Code.” We think a tech news publication should know the basics. Be the source. Get down to brass tacks. We can do that because we’re part of the very ecosystem we’re reporting on. And like any other company in the Silicon Valley community, Code/Rag is an ever-evolving entity.
|
||||
|
||||
Part of Code/Rag’s evolution includes our new investor: Bachmanity Capital. Bachmanity, as of this writing, owns a 50 percent stake in Code/Rag. I want to assure our readers this will in no way affect the quality or integrity of our journalism (it takes more than a couple dollars to shut me up). It simply means we have more funds to chase down the story, more resources to analyze trends and more assurances that when you read Code/Rag, you’ll know your code.
|
||||
|
||||
C.J. Cantwell
|
||||
Editor-in-Chief
|
||||
|
Before Width: | Height: | Size: 3.3 KiB |
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<TileColor>#ffc40d</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
|
Before Width: | Height: | Size: 269 B |
|
Before Width: | Height: | Size: 436 B |
|
Before Width: | Height: | Size: 31 KiB |
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"name": "hugo-goa",
|
||||
"icons": [
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="152.000000pt" height="152.000000pt" viewBox="0 0 152.000000 152.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.11, written by Peter Selinger 2001-2013
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,152.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M719 1506 c-177 -61 -281 -271 -231 -465 14 -57 55 -145 71 -155 5
|
||||
-3 12 -27 16 -53 3 -27 9 -62 11 -79 6 -36 -58 -232 -90 -279 -25 -37 -62 -72
|
||||
-98 -91 -25 -13 -28 -12 -47 12 -12 15 -21 32 -22 38 0 6 -2 20 -5 31 -2 11
|
||||
-5 32 -6 47 -2 29 -13 27 -42 -9 -10 -13 -23 -22 -30 -20 -14 5 -35 -22 -36
|
||||
-48 -1 -19 -2 -19 -9 -1 -7 18 -9 17 -35 -10 -97 -103 -111 -239 -31 -314 26
|
||||
-24 149 -70 175 -65 94 20 146 37 173 57 18 13 44 30 59 37 15 8 35 30 44 50
|
||||
9 20 23 39 30 44 7 4 25 30 40 57 14 27 32 51 40 54 13 5 14 11 -30 -143 -22
|
||||
-74 -21 -134 2 -154 20 -18 84 -47 105 -47 7 0 21 6 31 13 16 12 19 31 22 123
|
||||
5 170 6 175 42 189 30 13 31 13 46 -26 9 -22 16 -48 16 -58 0 -21 67 -128 98
|
||||
-155 38 -33 130 -73 159 -69 16 2 49 4 74 3 37 0 51 5 79 30 18 16 36 30 40
|
||||
30 3 0 13 12 22 28 22 37 17 116 -10 167 -18 34 -23 34 -22 0 1 -10 -33 -69
|
||||
-46 -82 -18 -19 -52 -16 -80 6 l-25 20 36 17 c19 9 42 28 51 43 9 15 32 36 52
|
||||
46 74 40 89 123 39 222 l-24 46 -7 -39 c-8 -43 -31 -84 -46 -84 -6 0 -10 7
|
||||
-10 15 0 24 -16 17 -17 -7 -3 -30 -27 -46 -64 -42 -74 9 -129 56 -182 155 -22
|
||||
42 -24 55 -20 150 4 112 5 129 15 144 62 103 82 176 75 281 -10 145 -82 266
|
||||
-191 319 -64 31 -152 40 -207 21z m167 -1062 c2 -16 6 -39 9 -51 7 -29 -13
|
||||
-40 -20 -11 -9 36 -13 82 -8 91 7 12 13 3 19 -29z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 284 KiB |
|
Before Width: | Height: | Size: 125 KiB |
@@ -1,5 +0,0 @@
|
||||
{{ partial "header.html" . }}
|
||||
|
||||
{{ partial "error.html" . }}
|
||||
|
||||
{{ partial "footer.html" . }}
|
||||
@@ -1,7 +0,0 @@
|
||||
{{ partial "header.html" . }}
|
||||
|
||||
{{ partial "li.html" . }}
|
||||
|
||||
{{ partial "menu.html" . }}
|
||||
|
||||
{{ partial "footer.html" . }}
|
||||
@@ -1,14 +0,0 @@
|
||||
{{ partial "header.html" . }}
|
||||
|
||||
{{ partial "content.html" . }}
|
||||
|
||||
{{ if ne .Params.showcomments false }}
|
||||
|
||||
{{ template "_internal/disqus.html" . }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ partial "menu.html" . }}
|
||||
|
||||
{{ partial "footer.html" . }}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
{{ partial "header.html" . }}
|
||||
|
||||
{{ partial "avatar.html" . }}
|
||||
|
||||
{{ partial "info.html" . }}
|
||||
|
||||
{{ partial "social.html" . }}
|
||||
|
||||
{{ partial "main_menu.html" . }}
|
||||
|
||||
{{ partial "footer.html" . }}
|
||||
@@ -1,4 +0,0 @@
|
||||
<header class="row text-center header">
|
||||
{{ with .Site.Params.authorimage }} <img src="img/{{ . }}" alt="Author Image" class="img-circle text-center headshot"> {{ end }}
|
||||
<h1 class="author">{{ .Site.Params.author }}</h1>
|
||||
</header>
|
||||
@@ -1,45 +0,0 @@
|
||||
<header class="row text-left title">
|
||||
<h1 class="title">{{ .Title }}</h1>
|
||||
</header>
|
||||
<section id="category-pane" class="row meta">
|
||||
{{ if ne .Params.showpagemeta false }}
|
||||
<div class="col-md-12">
|
||||
<h6 class="text-left meta">
|
||||
PUBLISHED ON {{ .Date.Format .Site.Params.dateformat | upper }}
|
||||
{{ if isset .Params "categories" }}
|
||||
{{ $total := len .Params.categories }}
|
||||
{{ if gt $total 0 }}
|
||||
—
|
||||
{{ $total := sub $total 1 }}
|
||||
{{ range $i, $cat := sort $.Params.categories }}
|
||||
<a class="meta" href="{{ "/categories/" | relURL }}{{ $cat | urlize }}">{{ $cat | upper }}</a>{{ if lt $i $total }}, {{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</h6>
|
||||
</div>
|
||||
{{ end }}
|
||||
</section>
|
||||
<section id="content-pane" class="row">
|
||||
<div class="col-md-12 text-justify content">
|
||||
{{ .Content }}
|
||||
</div>
|
||||
</section>
|
||||
<section id="tag-pane" class="row meta">
|
||||
{{ if ne .Params.showpagemeta false }}
|
||||
<div class="col-md-12">
|
||||
<h6 class="text-right meta">
|
||||
{{ if isset .Params "tags" }}
|
||||
{{ $total := len .Params.tags }}
|
||||
{{ if gt $total 0 }}
|
||||
TAGS:
|
||||
{{ $subtotal := sub $total 1 }}
|
||||
{{ range $i, $tag := sort $.Params.tags }}
|
||||
<a class="meta" href="{{ "/tags/" | relURL }}{{ $tag | urlize }}">{{ $tag | upper }}</a>{{ if lt $i $subtotal }}, {{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</h6>
|
||||
</div>
|
||||
{{ end }}
|
||||
</section>
|
||||
@@ -1,4 +0,0 @@
|
||||
<section id="error-pane" class="row text-center error">
|
||||
<h1 class="text-center">404</h1>
|
||||
<p>Sorry! That page does not exist. It may have been removed or moved to a new section on the site. Click <a href="/">here</a> go back to the home page.</p>
|
||||
</section>
|
||||
@@ -1,23 +0,0 @@
|
||||
|
||||
{{ partial "sub_footer.html" . }}
|
||||
</div>
|
||||
|
||||
{{ with .Site.Params.extra }}
|
||||
{{ if .highlightjs }}
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
|
||||
{{ range .highlightjslanguages }}
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/languages/{{.}}.min.js"></script>
|
||||
{{ end }}
|
||||
|
||||
<script type="text/javascript">
|
||||
hljs.initHighlightingOnLoad();
|
||||
</script>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ template "_internal/google_analytics.html" . }}
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,48 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ .Site.LanguageCode }}">
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
|
||||
{{ with .Site.Params.meta.description }}<meta name="description" content="{{ . }}">{{ end }}
|
||||
{{ with .Site.Params.meta.keywords }}<meta name="keywords" content="{{.}}">{{ end }}
|
||||
|
||||
<base href="{{ .Site.BaseURL }}">
|
||||
|
||||
<title>{{ .Site.Title }}</title>
|
||||
|
||||
{{ hugo.Generator }}
|
||||
|
||||
{{ with .OutputFormats.Get "rss" }}
|
||||
<link href="{{ .Permalink }}" rel="alternate" type="application/rss+xml" title="{{ $.Site.Title }}" />
|
||||
<link href="{{ .Permalink }}" rel="feed" type="application/rss+xml" title="{{ $.Site.Title }}" />
|
||||
{{ end }}
|
||||
|
||||
{{ if .Site.Params.extra.highlightjs }}
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/{{ .Site.Params.extra.highlightjsstyle | default "default" }}.min.css">
|
||||
{{ end }}
|
||||
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato:300,400|Roboto+Slab:400,700|Roboto:300,300i,400,400i,500,500i,700,700i">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="{{ "css/main.css" | absURL }}">
|
||||
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
</head>
|
||||
<body lang="{{ .Site.LanguageCode }}">
|
||||
<div class="container">
|
||||
@@ -1,8 +0,0 @@
|
||||
<section id="info-pane" class="row text-center info">
|
||||
{{ with .Site.Params.intro }}
|
||||
<h3 class="intro">{{ . | markdownify }}</h3>
|
||||
{{ end }}
|
||||
{{ with .Site.Params.description }}
|
||||
<h4 class="description">{{ . | markdownify }}</h4>
|
||||
{{ end }}
|
||||
</section>
|
||||
@@ -1,27 +0,0 @@
|
||||
<section id="list-pane" class="row list">
|
||||
<ul >
|
||||
{{ range .Data.Pages.ByPublishDate }}
|
||||
<li class="list-entry">
|
||||
<a class="list-entry-link" href="{{ .Permalink }}">{{ .Title }}</a>
|
||||
<p class="meta">
|
||||
{{ .Date.Format .Site.Params.dateformat | upper }}
|
||||
<span class="category">
|
||||
{{ if isset .Params "categories" }}
|
||||
{{ $total := len .Params.categories }}
|
||||
{{ if gt $total 0 }}
|
||||
—
|
||||
{{ $total := sub $total 1 }}
|
||||
{{ range $i, $cat := sort .Params.categories }}
|
||||
<a class="category" href="{{ "/categories/" | relURL }}{{ $cat | humanize | urlize }}">{{ $cat | upper }}</a>{{ if lt $i $total }}, {{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</span>
|
||||
</p>
|
||||
<p class="excerpt">
|
||||
{{ .Description }}
|
||||
</p>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</section>
|
||||
@@ -1,7 +0,0 @@
|
||||
<section id="main-menu-pane" class="row text-center main-menu">
|
||||
{{ with .Site.Menus.main }}
|
||||
{{ range sort . }}
|
||||
<h4><a class="menu-item" href="{{ .URL }}">{{ .Name | lower }}</a></h4>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</section>
|
||||
@@ -1,14 +0,0 @@
|
||||
<section id="menu-pane" class="row menu text-center">
|
||||
{{ if not .IsNode }}
|
||||
{{ with .PrevInSection }}
|
||||
<span><a class="menu-item" href="{{ .Permalink }}">< prev | </a></span>
|
||||
{{ end }}
|
||||
{{ if (or (.PrevInSection) (.NextInSection)) }}
|
||||
<span><a class="menu-item" href="{{ .Section | relURL }}">{{ .Section | lower }}</a></span>
|
||||
{{ end }}
|
||||
{{ with .NextInSection }}
|
||||
<span><a class="menu-item" href="{{ .Permalink }}"> | next ></a></span>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
<h4 class="text-center"><a class="menu-item" href="{{ .Site.BaseURL }}">home</a></h4>
|
||||
</section>
|
||||
@@ -1,38 +0,0 @@
|
||||
<section id="social-pane" class="row text-center social">
|
||||
{{ with .Site.Params.social.twitter }}
|
||||
<a href="https://twitter.com/{{.}}"><i class="fa fa-twitter" aria-hidden="true"></i></a>
|
||||
{{ end }}
|
||||
{{ with .Site.Params.social.google }}
|
||||
<a href="https://plus.google.com/{{.}}/about"><i class="fa fa-google-plus-official" aria-hidden="true"></i></a>
|
||||
{{ end }}
|
||||
{{ with .Site.Params.social.facebook }}
|
||||
<a href="https://facebook.com/{{.}}"><i class="fa fa-facebook-official" aria-hidden="true"></i></a>
|
||||
{{ end }}
|
||||
{{ with .Site.Params.social.github }}
|
||||
<a href="https://github.com/{{.}}"><i class="fa fa-github" aria-hidden="true"></i></a>
|
||||
{{ end }}
|
||||
{{ with .Site.Params.social.instagram }}
|
||||
<a href="https://instagram.com/{{.}}"><i class="fa fa-instagram" aria-hidden="true"></i></a>
|
||||
{{ end }}
|
||||
{{ with .Site.Params.social.linkedin }}
|
||||
<a href="https://linkedin.com/in/{{.}}"><i class="fa fa-linkedin" aria-hidden="true"></i></a>
|
||||
{{ end }}
|
||||
{{ with .Site.Params.social.stackoverflow }}
|
||||
<a href="https://stackoverflow.com/users/{{.}}"><i class="fa fa-stack-overflow" aria-hidden="true"></i></a>
|
||||
{{ end }}
|
||||
{{ with .Site.Params.social.medium}}
|
||||
<a href="https://medium.com/{{.}}"><i class="fa fa-medium" aria-hidden="true"></i></a>
|
||||
{{ end }}
|
||||
{{ with .Site.Params.social.devto}}
|
||||
<a href="https://dev.to/{{.}}"><i class="fa fa-terminal" aria-hidden="true"></i></a>
|
||||
{{ end }}
|
||||
{{ with .Site.Params.social.angellist}}
|
||||
<a href="https://angel.co/{{.}}"><i class="fa fa-angellist" aria-hidden="true"></i></a>
|
||||
{{ end }}
|
||||
{{ with .Site.Params.social.email }}
|
||||
<a href="mailto:{{.}}"><i class="fa fa-envelope-o" aria-hidden="true"></i></a>
|
||||
{{ end }}
|
||||
{{ with .Site.Params.social.xing }}
|
||||
<a href="https://www.xing.com/profile/{{.}}"><i class="fa fa-xing" aria-hidden="true"></i></a>
|
||||
{{ end }}
|
||||
</section>
|
||||
@@ -1,9 +0,0 @@
|
||||
<footer class="row text-center footer">
|
||||
<hr />
|
||||
{{ with .Site.Params.extra }}
|
||||
<h6 class="text-center copyright">{{ .copyright | markdownify }}</h6>
|
||||
{{ if ne .poweredby false }}
|
||||
<h6 class="text-center powered">Powered by <a href="https://gohugo.io/">Hugo</a> & <a href="https://github.com/shenoybr/hugo-goa">Goa</a>.</h6>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</footer>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB |
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<TileColor>#ffc40d</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
@@ -1,257 +0,0 @@
|
||||
@-ms-viewport
|
||||
{
|
||||
width: device-width;
|
||||
}
|
||||
@-o-viewport
|
||||
{
|
||||
width: device-width;
|
||||
}
|
||||
@viewport
|
||||
{
|
||||
width: device-width;
|
||||
}
|
||||
a,
|
||||
a:hover,
|
||||
a:visited,
|
||||
a:link
|
||||
{
|
||||
text-decoration: none;
|
||||
|
||||
color: #526d7a;
|
||||
}
|
||||
body
|
||||
{
|
||||
font-family: 'Roboto Slab', 'Roboto', 'Lato', sans-serif;
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
|
||||
width: auto;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
margin: 0;
|
||||
|
||||
background-color: #fcfcfc;
|
||||
}
|
||||
html
|
||||
{
|
||||
position: relative;
|
||||
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
.author
|
||||
{
|
||||
color: #444;
|
||||
}
|
||||
.author h1,
|
||||
.author h2,
|
||||
.author h3,
|
||||
.author h4,
|
||||
.author h5,
|
||||
.author h6,
|
||||
.author p
|
||||
{
|
||||
font-family: 'Roboto Slab', serif;
|
||||
}
|
||||
.category
|
||||
{
|
||||
color: #966 !important;
|
||||
}
|
||||
.container
|
||||
{
|
||||
overflow: auto;
|
||||
|
||||
max-width: 680px;
|
||||
padding: 0 15px;
|
||||
}
|
||||
.content
|
||||
{
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
div.content
|
||||
{
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 18px;
|
||||
font-weight: 300;
|
||||
|
||||
color: #777;
|
||||
}
|
||||
.copyright
|
||||
{
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 200;
|
||||
}
|
||||
.description
|
||||
{
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-weight: 200;
|
||||
color: #bbb;
|
||||
}
|
||||
.error
|
||||
{
|
||||
font-family: 'Roboto', sans-serif;
|
||||
|
||||
margin-top: 100px;
|
||||
}
|
||||
.error h1
|
||||
{
|
||||
font-size: 80px;
|
||||
|
||||
color: #bbb;
|
||||
}
|
||||
.error p
|
||||
{
|
||||
margin-top: 150px;
|
||||
|
||||
color: #777;
|
||||
}
|
||||
.excerpt
|
||||
{
|
||||
font-family: 'Lato', sans-serif;
|
||||
font-size: 18px;
|
||||
font-weight: 200;
|
||||
|
||||
color: #555;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.footer
|
||||
{
|
||||
margin-top: 10px;
|
||||
|
||||
color: #bbb;
|
||||
}
|
||||
.header
|
||||
{
|
||||
margin-top: 60px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.headshot
|
||||
{
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
.info
|
||||
{
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.intro
|
||||
{
|
||||
font-family: 'Roboto Slab', serif;
|
||||
font-weight: 200;
|
||||
color: #777;
|
||||
}
|
||||
h3.intro
|
||||
{
|
||||
margin-top: 0px
|
||||
}
|
||||
.list
|
||||
{
|
||||
font-family: 'Roboto', sans-serif;
|
||||
|
||||
margin-top: 50px;
|
||||
}
|
||||
.list li
|
||||
{
|
||||
list-style: none;
|
||||
}
|
||||
.list-entry
|
||||
{
|
||||
font-family: 'Roboto Slab', serif;
|
||||
font-size: 25px;
|
||||
font-weight: 400;
|
||||
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.list-entry-link
|
||||
{
|
||||
padding: 0;
|
||||
}
|
||||
.main-menu
|
||||
{
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.menu
|
||||
{
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.menu-item
|
||||
{
|
||||
font-family: 'Lato', sans-serif;
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
|
||||
text-decoration: none;
|
||||
|
||||
color: #966 !important;
|
||||
}
|
||||
.meta
|
||||
{
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 200;
|
||||
|
||||
color: #bbb;
|
||||
}
|
||||
a.meta
|
||||
{
|
||||
color: #966;
|
||||
}
|
||||
h6.meta
|
||||
{
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 200;
|
||||
|
||||
color: #bbb;
|
||||
}
|
||||
section.meta {
|
||||
margin-top:
|
||||
}
|
||||
.powered
|
||||
{
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
}
|
||||
.social {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.social,
|
||||
.social a
|
||||
{
|
||||
color: #526d7a;
|
||||
}
|
||||
.title
|
||||
{
|
||||
font-family: 'Roboto Slab', serif;
|
||||
font-weight: 400;
|
||||
|
||||
color: #555;
|
||||
}
|
||||
header.title
|
||||
{
|
||||
margin-top: 50px;
|
||||
padding: 0 15px;
|
||||
}
|
||||
@media (max-width: 991px)
|
||||
{
|
||||
.header
|
||||
{
|
||||
margin-top: 50px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.list
|
||||
{
|
||||
font-family: 'Roboto', sans-serif;
|
||||
|
||||
margin-top: 30px;
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 269 B |
|
Before Width: | Height: | Size: 436 B |
@@ -1,11 +0,0 @@
|
||||
// Copyright 2014-2015 Twitter, Inc.
|
||||
// Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
if (navigator.userAgent.match(/IEMobile\/10\.0/)) {
|
||||
var msViewportStyle = document.createElement('style')
|
||||
msViewportStyle.appendChild(
|
||||
document.createTextNode(
|
||||
'@-ms-viewport{width:auto!important}'
|
||||
)
|
||||
)
|
||||
document.querySelector('head').appendChild(msViewportStyle)
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"name": "hugo-goa",
|
||||
"icons": [
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="152.000000pt" height="152.000000pt" viewBox="0 0 152.000000 152.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.11, written by Peter Selinger 2001-2013
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,152.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M719 1506 c-177 -61 -281 -271 -231 -465 14 -57 55 -145 71 -155 5
|
||||
-3 12 -27 16 -53 3 -27 9 -62 11 -79 6 -36 -58 -232 -90 -279 -25 -37 -62 -72
|
||||
-98 -91 -25 -13 -28 -12 -47 12 -12 15 -21 32 -22 38 0 6 -2 20 -5 31 -2 11
|
||||
-5 32 -6 47 -2 29 -13 27 -42 -9 -10 -13 -23 -22 -30 -20 -14 5 -35 -22 -36
|
||||
-48 -1 -19 -2 -19 -9 -1 -7 18 -9 17 -35 -10 -97 -103 -111 -239 -31 -314 26
|
||||
-24 149 -70 175 -65 94 20 146 37 173 57 18 13 44 30 59 37 15 8 35 30 44 50
|
||||
9 20 23 39 30 44 7 4 25 30 40 57 14 27 32 51 40 54 13 5 14 11 -30 -143 -22
|
||||
-74 -21 -134 2 -154 20 -18 84 -47 105 -47 7 0 21 6 31 13 16 12 19 31 22 123
|
||||
5 170 6 175 42 189 30 13 31 13 46 -26 9 -22 16 -48 16 -58 0 -21 67 -128 98
|
||||
-155 38 -33 130 -73 159 -69 16 2 49 4 74 3 37 0 51 5 79 30 18 16 36 30 40
|
||||
30 3 0 13 12 22 28 22 37 17 116 -10 167 -18 34 -23 34 -22 0 1 -10 -33 -69
|
||||
-46 -82 -18 -19 -52 -16 -80 6 l-25 20 36 17 c19 9 42 28 51 43 9 15 32 36 52
|
||||
46 74 40 89 123 39 222 l-24 46 -7 -39 c-8 -43 -31 -84 -46 -84 -6 0 -10 7
|
||||
-10 15 0 24 -16 17 -17 -7 -3 -30 -27 -46 -64 -42 -74 9 -129 56 -182 155 -22
|
||||
42 -24 55 -20 150 4 112 5 129 15 144 62 103 82 176 75 281 -10 145 -82 266
|
||||
-191 319 -64 31 -152 40 -207 21z m167 -1062 c2 -16 6 -39 9 -51 7 -29 -13
|
||||
-40 -20 -11 -9 36 -13 82 -8 91 7 12 13 3 19 -29z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |