diff --git a/.gitignore b/.gitignore
index 3139398..cf5a2ef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,3 +30,4 @@ CLAUDE.md
node_modules/
dist/
npm-debug.log*
+.superpowers/
diff --git a/docs/superpowers/specs/2026-06-12-terminal-hero-design.md b/docs/superpowers/specs/2026-06-12-terminal-hero-design.md
new file mode 100644
index 0000000..70dfa76
--- /dev/null
+++ b/docs/superpowers/specs/2026-06-12-terminal-hero-design.md
@@ -0,0 +1,48 @@
+# Terminal Hero — Design
+
+## Goal
+
+Replace the cropped square photo hero on the home page with a macOS-style
+terminal window that presents the portrait photo at its natural 2:3 ratio and
+absorbs the `about/` section, so the home page is a single coherent element.
+
+## Concept
+
+The home page becomes one terminal session with two commands:
+
+1. `~ $ cd davidbingmann/.` is typed (reusing the existing typing-animation
+ idea), then the "output" fades in: photo (left, natural 2:3, ~220px wide)
+ and a neofetch-style fact sheet (right): name, role, `study`/`uni`/`work`
+ key-value lines (keys in accent yellow), social icon links.
+2. `~/davidbingmann $ cat about.txt` is typed, then the existing about prose
+ fades in as command output, including the mail link.
+3. A final empty prompt `~/davidbingmann $` with a blinking block cursor.
+
+The separate `about/` section below the hero is removed — its facts live in
+the fact sheet, its prose in the `cat about.txt` output.
+
+## Visuals
+
+- Whole page stays in the existing dark theme (`#0b0b0b`, already hardcoded
+ via `data-theme="dark"`).
+- Terminal window: slightly lighter surface (`#141414`), 1px border
+ (`#2a2c27`), 12px radius, title bar with macOS traffic lights and
+ `david@bingmann.de — zsh` title. On dark backgrounds the window separates
+ via surface + border (shadows don't read).
+- Colors via `--term-*` CSS variables with a light-theme fallback so the
+ light palette keeps working if the theme is ever switched back.
+- Prompt cwd in green, `$` muted, fact keys in yellow (`#febc2e`).
+
+## Behavior
+
+- Animation sequence: type cmd 1 → profile output fades in → type cmd 2 →
+ about output fades in → final blinking prompt. ~90ms per character,
+ short pauses between stages.
+- On small screens (≤720px) and `prefers-reduced-motion`: everything renders
+ immediately (same policy as the current hero).
+- Mobile layout: photo and fact sheet stack vertically inside the terminal.
+
+## Out of scope
+
+- Nav, footer, projects/resume/impressum pages stay unchanged.
+- No new dependencies.
diff --git a/src/data/projects.js b/src/data/projects.js
index 0e26a77..b47ea61 100644
--- a/src/data/projects.js
+++ b/src/data/projects.js
@@ -1,30 +1,13 @@
export const projects = [
- {
- slug: 'paper-humanoid-robots-industry-5',
- title: 'Paper',
- listTitle: 'Paper',
- headline: 'What potential do humanoid robots offer in Industry 5.0?',
- tags: ['industry-5.0', 'humanoid-robots', 'ai', 'paper'],
- repo: {
- prefix: 'PDF',
- label: 'davidbingmann/paper',
- href: '/papers/paper.pdf',
- },
- summary:
- 'My paper looks at whether humanoid robots can support the shift from Industry 4.0 to Industry 5.0: more resilient, more human-centered, and better suited to real factories.',
- body: [
- 'As part of a seminar at the University of Trier, I wrote this paper on humanoid robots in the context of Industry 5.0: not as a replacement for humans, but as a possible way to make industrial work more resilient, flexible, and human-centered.',
- 'The paper discusses why current automation still struggles in human-designed factories, what recent robot learning research makes possible, and where the technical, economic, and regulatory limits still are. You can read the public PDF version above.',
- ],
- },
{
slug: 'echotype',
title: 'EchoType',
+ type: 'software',
headline: 'Speak your prompts instead of typing.',
tags: ['agents', 'ai', 'productivity', 'voice'],
repo: {
- label: 'davidbingmann/EchoType',
href: 'https://github.com/davidbingmann/EchoType',
+ linkText: 'github.com/davidbingmann/EchoType',
},
summary:
'I was looking for a faster and more intuitive way to write prompts for AI agents like Codex or Claude Code, especially longer prompts that are tedious to type.',
@@ -36,4 +19,21 @@ export const projects = [
'If you want to install EchoType, you can find the installation instructions in the GitHub repository.',
],
},
+ {
+ slug: 'paper-humanoid-robots-industry-5',
+ title: 'Humanoid Robots in Industry 5.0',
+ type: 'paper',
+ headline: 'What potential do humanoid robots offer in Industry 5.0?',
+ tags: ['industry-5.0', 'humanoid-robots', 'ai', 'paper'],
+ repo: {
+ href: '/papers/paper.pdf',
+ linkText: 'paper.pdf',
+ },
+ summary:
+ 'My paper looks at whether humanoid robots can support the shift from Industry 4.0 to Industry 5.0: more resilient, more human-centered, and better suited to real factories.',
+ body: [
+ 'As part of a seminar at the University of Trier, I wrote this paper on humanoid robots in the context of Industry 5.0: not as a replacement for humans, but as a possible way to make industrial work more resilient, flexible, and human-centered.',
+ 'The paper discusses why current automation still struggles in human-designed factories, what recent robot learning research makes possible, and where the technical, economic, and regulatory limits still are. You can read the public PDF version above.',
+ ],
+ },
];
diff --git a/src/data/timeline.js b/src/data/timeline.js
index 85ae512..8ff5d23 100644
--- a/src/data/timeline.js
+++ b/src/data/timeline.js
@@ -1,32 +1,34 @@
export const timelineItems = [
{
- year: 'October 2025 → Present',
- title: 'Switch to University of Trier',
- description:
- 'Switch to the University of Trier to study Business Informatics and Artificial Intelligence',
+ period: 'Oct 2025 → present',
+ title: 'University of Trier',
+ description: 'B.Sc. Business Informatics & Artificial Intelligence',
+ category: 'education',
+ current: true,
},
{
- year: 'October 2024 → Present',
- title: 'Research Assistant at DFKI',
+ period: 'Oct 2024 → present',
+ title: 'Research Assistant — DFKI',
description:
- 'Start of my part-time job at the German Research Center for Artificial Intelligence (DFKI) alongside my studies',
+ 'German Research Center for Artificial Intelligence, part-time alongside my studies',
+ category: 'work',
+ current: true,
},
{
- year: 'October 2024',
- title: "Start of my Bachelor's degree",
- description:
- "Start of my Bachelor's degree: Artificial Intelligence and Data Science at the University of Applied Sciences in Trier",
+ period: 'Oct 2024 → Sep 2025',
+ title: 'University of Applied Sciences Trier',
+ description: 'B.Sc. Artificial Intelligence & Data Science',
+ category: 'education',
},
{
- year: 'May 2024',
- title: 'Internship at Tesla Automation',
- description:
- 'Internship from 27/05/2024 until 07/06/2024 in Controls Engineering',
+ period: 'May → Jun 2024',
+ title: 'Intern — Tesla Automation',
+ description: 'Controls Engineering',
+ category: 'internship',
},
{
- year: 'March 2024',
- title: 'Abitur',
- description:
- 'Finished my Abitur at the Staatliches Eifelgymnasium in Neuerburg',
+ period: 'Mar 2024',
+ title: 'Abitur — Staatliches Eifelgymnasium Neuerburg',
+ category: 'education',
},
];
diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx
index 7fc5b96..b03f03a 100644
--- a/src/pages/Home.jsx
+++ b/src/pages/Home.jsx
@@ -1,155 +1,210 @@
-import { useEffect, useRef, useState } from 'react';
+import { useEffect, useState } from 'react';
+import { FaEnvelope, FaGithub, FaLinkedinIn } from 'react-icons/fa';
+import { FaXTwitter } from 'react-icons/fa6';
import profilePicture800 from '../assets/profile_picture_800.jpeg';
import profilePicture1200 from '../assets/profile_picture_1200.jpeg';
import profilePictureFull from '../assets/profile_picture.jpeg';
-export default function Home() {
- const fullPrompt = '~ cd';
- const fullPath = 'davidbingmann/.';
- const totalLength = fullPrompt.length + fullPath.length;
-
- const [reduceMotion, setReduceMotion] = useState(() =>
- typeof window !== 'undefined' &&
- window.matchMedia('(prefers-reduced-motion: reduce)').matches
- );
- const [isSmallScreen, setIsSmallScreen] = useState(() =>
- typeof window !== 'undefined' && window.matchMedia('(max-width: 720px)').matches
- );
- const disableHeroAnimation = reduceMotion || isSmallScreen;
-
- const [typedCount, setTypedCount] = useState(() =>
- disableHeroAnimation ? totalLength : 0
+const COMMAND_CD = 'cd davidbingmann/.';
+const COMMAND_CAT = 'cat about.txt';
+const TYPE_INTERVAL = 90;
+
+const STAGE = {
+ TYPING_CD: 0,
+ PROFILE: 1,
+ TYPING_CAT: 2,
+ ABOUT: 3,
+ DONE: 4,
+};
+
+// Module-level so the intro only plays on the first visit per page load,
+// not every time the user navigates back to home via the tabs.
+let introPlayed = false;
+
+const socialLinks = [
+ {
+ label: 'LinkedIn',
+ href: 'https://www.linkedin.com/in/david-bingmann-13b897293/',
+ Icon: FaLinkedinIn,
+ },
+ { label: 'Mail', href: 'mailto:contact@davidbingmann.de', Icon: FaEnvelope },
+ { label: 'GitHub', href: 'https://github.com/davidbingmann', Icon: FaGithub },
+ { label: 'X', href: 'https://x.com/dxv1d04', Icon: FaXTwitter },
+];
+
+function useMediaQuery(query) {
+ const [matches, setMatches] = useState(
+ () => typeof window !== 'undefined' && window.matchMedia(query).matches
);
- const [hidePrompt, setHidePrompt] = useState(() => disableHeroAnimation);
- const promptRef = useRef(null);
- const [promptWidth, setPromptWidth] = useState(0);
-
- const promptText = fullPrompt.slice(0, Math.min(typedCount, fullPrompt.length));
- const pathText =
- typedCount > fullPrompt.length
- ? fullPath.slice(0, typedCount - fullPrompt.length)
- : '';
useEffect(() => {
- document.title = 'David Bingmann';
- }, []);
+ const mediaQuery = window.matchMedia(query);
+ const handleChange = (event) => setMatches(event.matches);
- useEffect(() => {
- const motionQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
- const handleMotionChange = (event) => setReduceMotion(event.matches);
+ setMatches(mediaQuery.matches);
+ mediaQuery.addEventListener('change', handleChange);
+ return () => mediaQuery.removeEventListener('change', handleChange);
+ }, [query]);
- setReduceMotion(motionQuery.matches);
- if (motionQuery.addEventListener) {
- motionQuery.addEventListener('change', handleMotionChange);
- return () => motionQuery.removeEventListener('change', handleMotionChange);
- }
+ return matches;
+}
- motionQuery.addListener(handleMotionChange);
- return () => motionQuery.removeListener(handleMotionChange);
- }, []);
+function useTerminalTimeline(instant) {
+ const [stage, setStage] = useState(instant ? STAGE.DONE : STAGE.TYPING_CD);
+ const [typedCd, setTypedCd] = useState(instant ? COMMAND_CD.length : 0);
+ const [typedCat, setTypedCat] = useState(instant ? COMMAND_CAT.length : 0);
useEffect(() => {
- const screenQuery = window.matchMedia('(max-width: 720px)');
- const handleScreenChange = (event) => setIsSmallScreen(event.matches);
-
- setIsSmallScreen(screenQuery.matches);
- if (screenQuery.addEventListener) {
- screenQuery.addEventListener('change', handleScreenChange);
- return () => screenQuery.removeEventListener('change', handleScreenChange);
+ if (instant) {
+ setStage(STAGE.DONE);
+ setTypedCd(COMMAND_CD.length);
+ setTypedCat(COMMAND_CAT.length);
+ return undefined;
}
- screenQuery.addListener(handleScreenChange);
- return () => screenQuery.removeListener(handleScreenChange);
- }, []);
-
- useEffect(() => {
- if (disableHeroAnimation) {
- setTypedCount(totalLength);
- setHidePrompt(true);
+ let delay;
+ let action;
+
+ if (stage === STAGE.TYPING_CD && typedCd < COMMAND_CD.length) {
+ delay = TYPE_INTERVAL;
+ action = () => setTypedCd((count) => count + 1);
+ } else if (stage === STAGE.TYPING_CD) {
+ delay = 400;
+ action = () => setStage(STAGE.PROFILE);
+ } else if (stage === STAGE.PROFILE) {
+ delay = 900;
+ action = () => setStage(STAGE.TYPING_CAT);
+ } else if (stage === STAGE.TYPING_CAT && typedCat < COMMAND_CAT.length) {
+ delay = TYPE_INTERVAL;
+ action = () => setTypedCat((count) => count + 1);
+ } else if (stage === STAGE.TYPING_CAT) {
+ delay = 400;
+ action = () => setStage(STAGE.ABOUT);
+ } else if (stage === STAGE.ABOUT) {
+ delay = 500;
+ action = () => setStage(STAGE.DONE);
+ } else {
return undefined;
}
- const timer = setInterval(() => {
- setTypedCount((current) => {
- if (current >= totalLength) {
- return current;
- }
- return current + 1;
- });
- }, 150);
+ const timer = setTimeout(action, delay);
+ return () => clearTimeout(timer);
+ }, [instant, stage, typedCd, typedCat]);
- return () => {
- clearInterval(timer);
- };
- }, [disableHeroAnimation, totalLength]);
+ return { stage, typedCd, typedCat };
+}
- useEffect(() => {
- if (disableHeroAnimation) {
- return undefined;
- }
+function Reveal({ open, children }) {
+ return (
+
+ );
+}
- if (typedCount === totalLength) {
- const timer = setTimeout(() => setHidePrompt(true), 500);
- return () => clearTimeout(timer);
- }
- return undefined;
- }, [disableHeroAnimation, typedCount, totalLength]);
+export default function Home() {
+ const reduceMotion = useMediaQuery('(prefers-reduced-motion: reduce)');
+ const [skipIntro] = useState(() => introPlayed);
+ const { stage, typedCd, typedCat } = useTerminalTimeline(
+ reduceMotion || skipIntro
+ );
useEffect(() => {
- if (disableHeroAnimation) {
- return undefined;
- }
-
- if (promptText.length === fullPrompt.length && promptRef.current) {
- setPromptWidth(promptRef.current.getBoundingClientRect().width);
- }
- return undefined;
- }, [disableHeroAnimation, promptText, fullPrompt.length]);
+ introPlayed = true;
+ document.title = 'David Bingmann';
+ }, []);
return (
-
-
-
-
-
-
- {promptText}
-
- {pathText}
-
-
Bachelor’s student
+
+
+
+
+
+ david@bingmann.de — zsh
-
-
-
- about/
-
-
- My name is David Bingmann and I am currently studying Business
- Informatics & Artificial Intelligence at the University of Trier.
-
-
- In addition to my studies, I work as a research assistant at the
- German Research Center for Artificial Intelligence (DFKI).
-
-
- Feel free to check out my social media channels or write me a{' '}
- mail . I look forward to
- connecting!
+
+
+ ~ {' '}
+ $ {' '}
+ {COMMAND_CD.slice(0, typedCd)}
+ {stage === STAGE.TYPING_CD && }
+
+
= STAGE.PROFILE}>
+
+
+
+
David Bingmann
+
Bachelor’s student
+
+
+ Study
+ Business Informatics & AI
+
+
+ Uni
+ University of Trier
+
+
+ Work
+ Research assistant @ DFKI
+
+
+ {socialLinks.map(({ label, href, Icon }) => (
+
+
+
+ ))}
+
+
+
+
+
+
= STAGE.TYPING_CAT}>
+
+ ~/davidbingmann {' '}
+ $ {' '}
+ {COMMAND_CAT.slice(0, typedCat)}
+ {stage === STAGE.TYPING_CAT && (
+
+ )}
+
+
+
+
= STAGE.ABOUT}>
+
+ My name is David Bingmann and I am currently studying Business
+ Informatics & Artificial Intelligence at the University of
+ Trier. In addition to my studies, I work as a research assistant
+ at the German Research Center for Artificial Intelligence (DFKI).
+ Feel free to check out my social media channels or write me a{' '}
+ mail — I look
+ forward to connecting!
+
+
+
+
= STAGE.DONE}>
+
+ ~/davidbingmann {' '}
+ $ {' '}
+
+
+
diff --git a/src/pages/Impressum.jsx b/src/pages/Impressum.jsx
index 735ca1b..7675d69 100644
--- a/src/pages/Impressum.jsx
+++ b/src/pages/Impressum.jsx
@@ -7,7 +7,10 @@ export default function Impressum() {
return (
- imprint/
+
+
imprint/
+ legal
+
{project.title}
- {project.title}
+
+
+ {project.title}
+
+ {project.type}
+
-
+
diff --git a/src/pages/Projects.jsx b/src/pages/Projects.jsx
index 2c4af12..c0d905b 100644
--- a/src/pages/Projects.jsx
+++ b/src/pages/Projects.jsx
@@ -15,21 +15,31 @@ export default function Projects() {
+ {project.type}
- {project.listTitle ?? `${project.title} Project`}
+ {project.title} ↗
{project.headline}
{project.summary}
-
-
- {project.repo.label}
+
))}
diff --git a/src/pages/Resume.jsx b/src/pages/Resume.jsx
index 8787c03..a7412ff 100644
--- a/src/pages/Resume.jsx
+++ b/src/pages/Resume.jsx
@@ -9,16 +9,31 @@ export default function Resume() {
return (
resume/
-
+
{timelineItems.map((item, index) => (
- {item.year}
- {item.title}
- {item.description}
+
+
+
+ {item.category}
+
+
+
+ {item.title}
+ {item.current && current }
+
+ {item.period}
+
+ {item.description && (
+
{item.description}
+ )}
+
))}
diff --git a/src/styles.css b/src/styles.css
index baa07e7..1ef9558 100644
--- a/src/styles.css
+++ b/src/styles.css
@@ -10,6 +10,15 @@
--accent: #2f2f2f;
--shadow: rgba(21, 20, 18, 0.05);
--hover: rgba(47, 47, 47, 0.08);
+ --term-bg: #ffffff;
+ --term-bar: #ecebe5;
+ --term-border: #e1dfd8;
+ --term-text: #1c1c1c;
+ --term-muted: #5f5f5f;
+ --term-key: #b07d10;
+ --term-green: #1f9e4b;
+ --term-blue: #2563a8;
+ --term-purple: #7a4fb5;
}
:root[data-theme='dark'] {
@@ -23,6 +32,24 @@
--accent: #f5f3ee;
--shadow: rgba(0, 0, 0, 0.35);
--hover: rgba(245, 243, 238, 0.08);
+ --term-bg: #141414;
+ --term-bar: #1d1f1b;
+ --term-border: #2a2c27;
+ --term-text: #f5f3ee;
+ --term-muted: #b7b2a8;
+ --term-key: #febc2e;
+ --term-green: #28c840;
+ --term-blue: #7eb8f2;
+ --term-purple: #c89bf5;
+}
+
+/* Space Mono draws § and parentheses as small raised glyphs that read as
+ broken superscripts; serve just these characters from a local mono font. */
+@font-face {
+ font-family: 'Space Mono Glyph Fix';
+ src: local('Menlo'), local('Consolas'), local('DejaVu Sans Mono'),
+ local('Courier New');
+ unicode-range: U+00A7, U+0028-0029;
}
* {
@@ -37,7 +64,7 @@ body {
margin: 0;
min-height: 100vh;
min-height: 100svh;
- font-family: 'Space Mono', monospace;
+ font-family: 'Space Mono Glyph Fix', 'Space Mono', monospace;
color: var(--text);
background: var(--bg);
overflow-x: hidden;
@@ -115,88 +142,179 @@ p {
padding-right: calc(var(--page-gutter) + env(safe-area-inset-right, 0px));
}
-.hero {
- width: auto;
- max-width: min(100%, 760px);
- aspect-ratio: 1 / 1;
- container-type: inline-size;
+.terminal {
margin: 0 auto;
- height: 70vh;
- height: 70svh;
- min-height: 420px;
- background: #0b0b0b;
- position: relative;
+ border: 1px solid var(--term-border);
+ border-radius: 12px;
+ background: var(--term-bg);
+ overflow: hidden;
+}
+
+.terminal-bar {
display: flex;
- align-items: flex-end;
- justify-content: center;
- color: #ffffff;
- border-radius: 18px;
+ align-items: center;
+ gap: 7px;
+ padding: 10px 14px;
+ background: var(--term-bar);
+ border-bottom: 1px solid var(--term-border);
+}
+
+.terminal-dot {
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+}
+
+.terminal-dot--close {
+ background: #ff5f57;
+}
+
+.terminal-dot--minimize {
+ background: #febc2e;
+}
+
+.terminal-dot--zoom {
+ background: #28c840;
+}
+
+.terminal-title {
+ margin: 0 auto;
+ font-size: 0.8rem;
+ color: var(--term-muted);
+}
+
+.terminal-body {
+ padding: 1.8rem 1.9rem 1.9rem;
+ font-size: 1.15rem;
+ line-height: 1.75;
+ color: var(--term-text);
+}
+
+.terminal-line {
+ margin: 0;
+ color: var(--term-text);
+}
+
+.terminal-cwd {
+ color: var(--term-green);
+}
+
+.terminal-dollar {
+ color: var(--term-muted);
+}
+
+.terminal-cursor {
+ display: inline-block;
+ width: 0.6em;
+ height: 1.05em;
+ margin-left: 2px;
+ background: var(--term-text);
+ vertical-align: text-bottom;
+}
+
+.terminal-cursor--blink {
+ animation: blink 1.1s steps(1) infinite;
+}
+
+.terminal-reveal {
+ display: grid;
+ grid-template-rows: 0fr;
+ transition: grid-template-rows 0.5s ease;
+}
+
+.terminal-reveal--open {
+ grid-template-rows: 1fr;
+}
+
+.terminal-reveal-inner {
+ min-height: 0;
overflow: hidden;
+ opacity: 0;
+ transition: opacity 0.45s ease 0.15s;
}
-.hero-image {
- position: absolute;
- inset: 0;
- width: 100%;
- height: 100%;
- object-fit: cover;
- object-position: 50% 20%;
- pointer-events: none;
+.terminal-reveal--open .terminal-reveal-inner {
+ opacity: 1;
}
-.hero-overlay {
- position: absolute;
- inset: 0;
- background: linear-gradient(180deg, rgba(0, 0, 0, 0.15) 0%, rgba(0, 0, 0, 0.55) 70%);
- z-index: 1;
+.terminal-profile {
+ display: flex;
+ gap: 1.4rem;
+ margin: 1.1rem 0 1.3rem;
}
-.hero-content {
- position: relative;
- z-index: 2;
- text-align: center;
- padding: 0 1.5rem 2.5rem;
- animation: fadeUp 0.8s ease forwards;
+.terminal-photo {
+ width: min(290px, 40%);
+ aspect-ratio: 2 / 3;
+ object-fit: cover;
+ border-radius: 8px;
+ flex-shrink: 0;
}
-.hero-command {
- display: inline-flex;
- align-items: center;
- font-size: clamp(2.2rem, 5vw, 3rem);
- /* sized so the longest animation state "~ cd davidbingmann/." (20ch) never exceeds the hero */
- font-size: clamp(1.4rem, calc((100cqw - 3.5rem) / 12), 3rem);
+.terminal-facts {
+ display: flex;
+ flex-direction: column;
+ min-width: 0;
+}
+
+.terminal-name {
+ margin: 0;
+ font-size: 1.8rem;
font-weight: 700;
- line-height: 1.1;
- text-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);
+ color: var(--term-text);
+ line-height: 1.3;
}
-.hero-tagline {
- margin-top: 0.85rem;
- font-size: clamp(1.05rem, 2.2vw, 1.4rem);
- font-weight: 600;
- letter-spacing: 0.06em;
- opacity: 0.9;
- text-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);
+.terminal-role {
+ margin: 0.1rem 0 0;
+ color: var(--term-muted);
+}
+
+.terminal-rule {
+ width: 100%;
+ margin: 0.8rem 0;
+ border: 0;
+ border-top: 1px solid var(--term-border);
+}
+
+.terminal-fact {
+ margin: 0 0 0.35rem;
+ color: var(--term-text);
}
-.hero-command .prompt {
+.terminal-key {
display: inline-block;
- overflow: hidden;
- white-space: nowrap;
- max-width: var(--prompt-width, 6ch);
- margin-right: 1ch;
- opacity: 0.9;
- transition: max-width 0.35s ease, margin-right 0.35s ease, opacity 0.35s ease;
+ min-width: 7ch;
+ color: var(--term-key);
+ font-weight: 700;
}
-.hero-command .prompt--hidden {
- max-width: 0;
- margin-right: 0;
- opacity: 0;
+.terminal-socials {
+ display: flex;
+ gap: 1.1rem;
+ margin-top: auto;
+ padding-top: 1rem;
}
-.hero-command .path {
- white-space: nowrap;
+.terminal-socials a {
+ display: inline-flex;
+ color: var(--term-muted);
+ transition: color 0.2s ease;
+}
+
+.terminal-social-icon {
+ width: 21px;
+ height: 21px;
+}
+
+.terminal-about {
+ margin: 0.4rem 0 1.2rem;
+ color: var(--term-muted);
+}
+
+.terminal-about a {
+ color: var(--term-text);
+ border-bottom: 1px solid var(--term-text);
}
.section {
@@ -216,11 +334,14 @@ p {
}
.section-body {
- background: var(--surface);
- font-size: 1.2rem;
- padding: 2rem;
border: 1px solid var(--line);
- box-shadow: 0 20px 40px var(--shadow);
+ border-radius: 10px;
+ background: var(--term-bg);
+ font-size: 1.05rem;
+ padding: 1.5rem 1.6rem;
+ opacity: 0;
+ transform: translateY(14px);
+ animation: fadeIn 0.5s ease forwards;
}
.section-body h2 {
@@ -279,53 +400,44 @@ p {
transform: translateY(0);
}
-.social-links {
- display: flex;
- flex-wrap: wrap;
- gap: 1.5rem;
- font-size: 0.95rem;
- text-transform: lowercase;
-}
-
-.social-links a {
- color: var(--muted);
- transition: color 0.2s ease;
-}
-
.empty-note {
padding: 2rem;
border: 1px dashed var(--line);
+ border-radius: 10px;
color: var(--muted);
- background: var(--surface);
+ background: var(--term-bg);
}
.project-list {
display: grid;
- gap: 1.25rem;
+ gap: 1rem;
}
.project-card {
position: relative;
- font-size: 1.2rem;
- padding: 1.7rem;
border: 1px solid var(--line);
- background: var(--surface);
- box-shadow: 0 20px 40px var(--shadow);
- display: grid;
- gap: 0.9rem;
+ border-radius: 10px;
+ background: var(--term-bg);
+ padding: 1.15rem 1.3rem;
+ cursor: pointer;
opacity: 0;
- transform: translateY(12px);
+ transform: translateY(14px);
animation: fadeIn 0.5s ease forwards;
animation-delay: var(--delay);
- cursor: pointer;
transition: border-color 0.2s ease;
}
.project-card-title {
- font-size: 1.35rem;
+ margin: 0;
+ font-size: 1.25rem;
letter-spacing: 0.02em;
}
+.project-card-arrow {
+ color: var(--muted);
+ font-weight: 400;
+}
+
.project-title-link::after {
content: '';
position: absolute;
@@ -333,27 +445,81 @@ p {
}
.project-card-headline {
- margin: 0;
+ margin: 0.5rem 0 0;
+ font-size: 1rem;
color: var(--text);
font-weight: 700;
}
.project-card-summary {
- margin: 0;
+ margin: 0.45rem 0 0;
+ font-size: 0.95rem;
+ line-height: 1.65;
}
-.project-card-meta {
- margin: 0;
- font-size: 0.9rem;
- color: var(--muted);
+.project-card-foot {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-between;
+ align-items: baseline;
+ gap: 0.35rem 1rem;
+ margin-top: 0.9rem;
}
-.project-card-meta a {
+.project-card-link {
position: relative;
z-index: 1;
- color: var(--muted);
+ font-size: 0.85rem;
+ color: var(--term-green);
border-bottom: 1px solid transparent;
- transition: color 0.2s ease, border-color 0.2s ease;
+ transition: border-color 0.2s ease;
+}
+
+.project-card-link-arrow {
+ color: var(--muted);
+}
+
+.project-card-tags {
+ font-size: 0.8rem;
+ color: var(--muted);
+ opacity: 0.75;
+}
+
+.project-head {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 0.9rem;
+}
+
+.project-head .tag {
+ margin-bottom: 0;
+}
+
+.project-detail {
+ border: 1px solid var(--line);
+ border-radius: 10px;
+ background: var(--term-bg);
+ padding: 1.5rem 1.6rem;
+ font-size: 1.05rem;
+ opacity: 0;
+ transform: translateY(14px);
+ animation: fadeIn 0.5s ease forwards;
+}
+
+.project-detail-link {
+ margin: 0 0 1.2rem;
+}
+
+.project-detail-paragraph {
+ opacity: 0;
+ transform: translateY(12px);
+ animation: fadeIn 0.5s ease forwards;
+ animation-delay: var(--delay);
+}
+
+.project-detail-paragraph:last-child {
+ margin-bottom: 0;
}
.project-breadcrumb {
@@ -372,50 +538,129 @@ p {
}
.project-headline {
- margin-top: 0;
+ margin: 0 0 0.9rem;
color: var(--text);
font-weight: 700;
}
-.timeline {
+.resume-timeline {
+ position: relative;
margin-top: 0.5rem;
- padding-left: 1.5rem;
- border-left: 1px solid var(--line);
+ padding-left: 1.6rem;
display: grid;
- gap: 1.8rem;
+ gap: 1rem;
}
-.timeline-item {
+.resume-timeline::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ width: 1.5px;
+ background: var(--line);
+ transform: scaleY(0);
+ transform-origin: top;
+ animation: drawLine 1s ease forwards;
+}
+
+.resume-item {
position: relative;
- padding-left: 1rem;
opacity: 0;
- transform: translateY(12px);
+ transform: translateY(14px);
animation: fadeIn 0.5s ease forwards;
animation-delay: var(--delay);
}
-.timeline-item::before {
- content: '';
+.resume-dot {
position: absolute;
- left: -1.1rem;
- top: 0.35rem;
+ left: calc(-1.6rem - 4px);
+ top: 1.35rem;
width: 10px;
height: 10px;
border-radius: 50%;
- background: var(--accent);
+ background: var(--muted);
}
-.timeline-year {
- margin: 0 0 0.4rem;
+.resume-dot--current {
+ background: var(--term-green);
+ animation: pulse 2s ease-out infinite;
+}
+
+.resume-card {
+ border: 1px solid var(--line);
+ border-radius: 10px;
+ background: var(--term-bg);
+ padding: 1.15rem 1.3rem;
+}
+
+.tag {
+ display: inline-block;
+ margin-bottom: 0.55rem;
+ padding: 0.1rem 0.6rem;
+ border: 1px solid var(--line);
+ border-radius: 999px;
+ font-size: 0.68rem;
+ letter-spacing: 0.1em;
text-transform: uppercase;
- letter-spacing: 0.15em;
- font-size: 0.75rem;
color: var(--muted);
}
-.timeline-item h3 {
- margin-bottom: 0.4rem;
- font-size: 1.3rem;
+.tag--education,
+.tag--paper {
+ color: var(--term-key);
+ border-color: color-mix(in srgb, var(--term-key) 40%, transparent);
+}
+
+.tag--work,
+.tag--software {
+ color: var(--term-blue);
+ border-color: color-mix(in srgb, var(--term-blue) 40%, transparent);
+}
+
+.tag--internship {
+ color: var(--term-purple);
+ border-color: color-mix(in srgb, var(--term-purple) 40%, transparent);
+}
+
+.resume-card-row {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-between;
+ align-items: baseline;
+ gap: 0.25rem 1rem;
+}
+
+.resume-card-title {
+ margin: 0;
+ font-size: 1.15rem;
+ line-height: 1.5;
+}
+
+.resume-badge {
+ display: inline-block;
+ margin-left: 0.6rem;
+ padding: 0.05rem 0.5rem;
+ border: 1px solid color-mix(in srgb, var(--term-green) 40%, transparent);
+ border-radius: 999px;
+ font-size: 0.68rem;
+ font-weight: 400;
+ color: var(--term-green);
+ vertical-align: 2px;
+ white-space: nowrap;
+}
+
+.resume-period {
+ flex-shrink: 0;
+ color: var(--muted);
+ font-size: 0.85rem;
+ white-space: nowrap;
+}
+
+.resume-card-description {
+ margin: 0.5rem 0 0;
+ font-size: 0.95rem;
+ line-height: 1.6;
}
.footer {
@@ -482,14 +727,24 @@ p {
border: 0;
}
-@keyframes fadeUp {
- from {
+@keyframes blink {
+ 50% {
opacity: 0;
- transform: translateY(20px);
}
+}
+
+@keyframes drawLine {
to {
- opacity: 1;
- transform: translateY(0);
+ transform: scaleY(1);
+ }
+}
+
+@keyframes pulse {
+ from {
+ box-shadow: 0 0 0 0 color-mix(in srgb, var(--term-green) 50%, transparent);
+ }
+ to {
+ box-shadow: 0 0 0 9px transparent;
}
}
@@ -514,18 +769,17 @@ p {
border-color: var(--text);
}
- .project-card-meta a:hover {
- color: var(--text);
- border-color: var(--text);
+ .project-card-link:hover {
+ border-color: var(--term-green);
}
- .project-breadcrumb-link:hover {
- color: var(--text);
- border-color: var(--text);
+ .terminal-socials a:hover {
+ color: var(--term-text);
}
- .social-links a:hover {
+ .project-breadcrumb-link:hover {
color: var(--text);
+ border-color: var(--text);
}
.footer-impressum:hover {
@@ -552,59 +806,80 @@ p {
gap: 1.25rem 1.75rem;
}
- .hero {
- height: 58vh;
- height: 58svh;
- min-height: 320px;
- border-radius: 14px;
+ .terminal-body {
+ padding: 1.1rem 1rem 1.2rem;
+ font-size: 0.92rem;
}
- .hero-tagline {
- margin-top: 0.75rem;
- font-size: clamp(0.95rem, 4vw, 1.2rem);
+ .terminal-profile {
+ flex-direction: column;
+ align-items: center;
+ gap: 1rem;
}
- .hero-content {
- animation: none;
- padding: 0 1.25rem 2rem;
+ .terminal-photo {
+ width: min(240px, 64%);
}
- .hero-command .prompt {
- transition: none;
+ .terminal-facts {
+ width: 100%;
+ }
+
+ .terminal-name,
+ .terminal-role {
+ text-align: center;
+ }
+
+ .terminal-socials {
+ justify-content: center;
+ }
+
+ .terminal-name {
+ font-size: 1.3rem;
}
.section-body {
font-size: 1rem;
- padding: 1.5rem;
+ padding: 1.2rem 1.25rem;
}
.section-body h2 {
font-size: 1.05rem;
}
- .timeline {
- padding-left: 1rem;
+ .resume-timeline {
+ padding-left: 1.2rem;
}
- .timeline-item h3 {
- font-size: 1.1rem;
+ .resume-dot {
+ left: calc(-1.2rem - 4px);
}
- .timeline-item {
- opacity: 1;
- transform: none;
- animation: none;
+ .resume-card {
+ padding: 1rem 1.1rem;
+ }
+
+ .resume-card-title {
+ font-size: 1.05rem;
+ }
+
+ .resume-card-row {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0.15rem;
}
.project-card {
- font-size: 1rem;
- opacity: 1;
- transform: none;
- animation: none;
+ padding: 1rem 1.1rem;
}
.project-card-title {
- font-size: 1.2rem;
+ font-size: 1.15rem;
+ }
+
+ .project-detail {
+ padding: 1.2rem 1.25rem;
+ font-size: 1rem;
}
.footer-impressum {
@@ -618,17 +893,8 @@ p {
--page-gutter: 0.95rem;
}
- .hero-tagline {
- margin-top: 0.7rem;
- }
-
- .hero-content {
- padding-left: 1rem;
- padding-right: 1rem;
- }
-
- .hero-command .path {
- white-space: nowrap;
+ .terminal-body {
+ font-size: 0.85rem;
}
.section {