The site now exists in German (at the root, with /impressum and /datenschutz) and English (/en). A request without a language follows the visitor's choice from the language switch, then the browser language, then German; the old English legal addresses move to /en for good. All visible texts are data in src/content/de.ts and en.ts, so components hold none of their own. The header carries a DE/EN switch whose bit curtain covers the page while it is swapped and reopens at the same reading position. Title, description, canonical, hreflang, Open Graph, the share image, the sitemap and the structured data are per language. The privacy notice now describes the cookie that remembers the chosen language. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1384 lines
28 KiB
CSS
1384 lines
28 KiB
CSS
@tailwind base;
|
||
@tailwind components;
|
||
@tailwind utilities;
|
||
|
||
/* Base styles - Tailwind only */
|
||
@layer base {
|
||
html {
|
||
scroll-behavior: smooth;
|
||
}
|
||
|
||
body {
|
||
@apply bg-white text-slate-800 font-serif antialiased selection:bg-slate-900 selection:text-white;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
/* Typography */
|
||
h1,
|
||
h2,
|
||
h3,
|
||
h4,
|
||
h5,
|
||
h6 {
|
||
@apply font-sans font-bold text-slate-900 tracking-tighter;
|
||
}
|
||
|
||
h1 {
|
||
@apply text-4xl md:text-6xl lg:text-8xl leading-[1.1] md:leading-[1] lg:leading-[0.95] mb-6 md:mb-8 lg:mb-12;
|
||
}
|
||
|
||
h2 {
|
||
@apply text-2xl md:text-4xl lg:text-6xl leading-tight mb-4 md:mb-6 lg:mb-8 mt-12 lg:mt-16;
|
||
}
|
||
|
||
h3 {
|
||
@apply text-xl md:text-3xl lg:text-5xl leading-tight mb-3 md:mb-4 lg:mb-6 mt-8 lg:mt-12;
|
||
}
|
||
|
||
h4 {
|
||
@apply text-lg md:text-2xl lg:text-3xl leading-tight mb-3 lg:mb-4 mt-6 lg:mt-8;
|
||
}
|
||
|
||
p {
|
||
@apply mb-6 text-base leading-relaxed text-slate-700;
|
||
}
|
||
|
||
.lead {
|
||
@apply text-base md:text-xl lg:text-2xl text-slate-600 mb-6 leading-relaxed;
|
||
font-weight: 400;
|
||
}
|
||
|
||
a {
|
||
@apply text-slate-900 hover:text-slate-700 transition-colors no-underline;
|
||
}
|
||
|
||
ul,
|
||
ol {
|
||
@apply ml-5 mb-4;
|
||
}
|
||
|
||
li {
|
||
@apply mb-1;
|
||
}
|
||
|
||
code:not([class*="language-"]) {
|
||
@apply bg-slate-50 px-1.5 py-0.5 rounded-md font-mono text-[0.9em] text-slate-800 border border-slate-100;
|
||
}
|
||
|
||
blockquote {
|
||
@apply border-l-4 border-slate-900 pl-6 italic text-slate-700 my-8 text-lg md:text-2xl font-serif;
|
||
}
|
||
|
||
/* Focus states */
|
||
a:focus,
|
||
button:focus,
|
||
input:focus,
|
||
textarea:focus {
|
||
outline: none !important;
|
||
box-shadow: none !important;
|
||
}
|
||
|
||
/* Interactive elements pointer */
|
||
a,
|
||
button,
|
||
[role="button"],
|
||
input[type="button"],
|
||
input[type="submit"],
|
||
input[type="reset"],
|
||
select,
|
||
summary,
|
||
label[for] {
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* Ensure disabled elements don't show pointer */
|
||
button:disabled,
|
||
[role="button"]:disabled,
|
||
input:disabled,
|
||
select:disabled,
|
||
button[disabled] {
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
/* Remove default tap highlight on mobile */
|
||
* {
|
||
-webkit-tap-highlight-color: transparent !important;
|
||
}
|
||
}
|
||
|
||
/* Components - Tailwind utility classes */
|
||
@layer components {
|
||
/* Legacy hooks required by tests */
|
||
.file-example {
|
||
@apply w-full;
|
||
}
|
||
|
||
.container {
|
||
@apply max-w-6xl mx-auto px-5 md:px-6 lg:px-8 py-8 md:py-10 lg:py-12;
|
||
}
|
||
|
||
.wide-container {
|
||
@apply max-w-7xl mx-auto px-5 md:px-6 lg:px-8 py-10 md:py-12 lg:py-16;
|
||
}
|
||
|
||
.narrow-container {
|
||
@apply max-w-4xl mx-auto px-5 md:px-6 lg:px-8 py-6 md:py-8 lg:py-10;
|
||
}
|
||
|
||
.highlighter-tag {
|
||
@apply inline-block text-[10px] uppercase tracking-wider font-bold px-3 py-1 rounded-full cursor-pointer transition-all duration-300;
|
||
position: relative;
|
||
}
|
||
|
||
.search-box {
|
||
@apply w-full px-6 py-4 border border-slate-200 rounded-2xl focus:outline-none focus:border-slate-400 transition-all duration-300;
|
||
background: rgba(255, 255, 255, 0.9);
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.search-box::placeholder {
|
||
@apply text-slate-400;
|
||
}
|
||
|
||
/* Blog post card */
|
||
.post-card {
|
||
@apply mb-8 last:mb-0;
|
||
}
|
||
|
||
.post-meta {
|
||
@apply text-xs text-slate-500 font-sans mb-2;
|
||
}
|
||
|
||
.post-excerpt {
|
||
@apply text-slate-700 mb-2 leading-relaxed;
|
||
}
|
||
|
||
.post-tags {
|
||
@apply flex flex-wrap gap-1;
|
||
}
|
||
|
||
/* Article page */
|
||
.article-header {
|
||
@apply mb-12;
|
||
}
|
||
|
||
.article-title {
|
||
@apply text-4xl md:text-5xl font-bold mb-3;
|
||
}
|
||
|
||
.article-meta {
|
||
@apply text-sm text-slate-500 font-sans mb-5;
|
||
}
|
||
|
||
.article-content {
|
||
@apply font-serif antialiased text-slate-700;
|
||
}
|
||
|
||
.article-content p {
|
||
@apply text-lg md:text-xl leading-relaxed mb-6;
|
||
}
|
||
|
||
.article-content h1 {
|
||
@apply text-3xl md:text-5xl font-bold text-slate-900 mb-8 mt-12 tracking-tight leading-[1.1] font-sans;
|
||
}
|
||
|
||
.article-content h2 {
|
||
@apply text-2xl md:text-4xl font-bold text-slate-900 mb-6 mt-10 tracking-tight leading-tight font-sans;
|
||
}
|
||
|
||
.article-content h3 {
|
||
@apply text-xl md:text-2xl font-bold text-slate-900 mb-4 mt-8 tracking-tight leading-snug font-sans;
|
||
}
|
||
|
||
.article-content ul,
|
||
.article-content ol {
|
||
@apply ml-6 mb-6 text-lg md:text-xl;
|
||
}
|
||
|
||
.article-content li {
|
||
@apply mb-2;
|
||
}
|
||
|
||
.article-content blockquote {
|
||
@apply border-l-4 border-slate-900 pl-6 italic text-slate-700 my-10 text-xl md:text-2xl;
|
||
}
|
||
|
||
/* Buttons */
|
||
.btn {
|
||
@apply inline-flex items-center justify-center px-6 py-3 border border-slate-200 bg-white text-slate-600 font-sans font-bold text-sm uppercase tracking-widest rounded-full transition-all duration-500 hover:border-slate-400 hover:text-slate-900 hover:bg-slate-50 hover:-translate-y-0.5 hover:shadow-xl hover:shadow-slate-100 active:translate-y-0 active:shadow-sm;
|
||
transition-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
|
||
}
|
||
|
||
.btn-primary {
|
||
@apply border-slate-900 text-slate-900 hover:bg-slate-900 hover:text-white;
|
||
}
|
||
|
||
.btn-secondary {
|
||
@apply border-slate-200 text-slate-500 hover:border-slate-400 hover:text-slate-900;
|
||
}
|
||
|
||
/* Hide scrollbars but keep functionality */
|
||
.no-scrollbar::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
.no-scrollbar {
|
||
-ms-overflow-style: none;
|
||
/* IE and Edge */
|
||
scrollbar-width: none;
|
||
/* Firefox */
|
||
}
|
||
|
||
/* Empty state */
|
||
.empty-state {
|
||
@apply text-center py-8 text-slate-500;
|
||
}
|
||
|
||
.empty-state svg {
|
||
@apply mx-auto mb-2 text-slate-300;
|
||
}
|
||
|
||
/* Line clamp utility */
|
||
.line-clamp-3 {
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 3;
|
||
-webkit-box-orient: vertical;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* Reading progress indicator */
|
||
.reading-progress-bar {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 1px;
|
||
background: #0f172a;
|
||
transform-origin: left;
|
||
transform: scaleX(0);
|
||
z-index: 100;
|
||
transition: transform 0.1s ease-out;
|
||
}
|
||
|
||
/* Floating back to top button */
|
||
.floating-back-to-top {
|
||
position: fixed;
|
||
bottom: 2rem;
|
||
right: 2rem;
|
||
width: 3rem;
|
||
height: 3rem;
|
||
background: white;
|
||
border: 1px solid #e2e8f0;
|
||
border-radius: 9999px;
|
||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #64748b;
|
||
transition: all 0.5s cubic-bezier(0.23, 1, 0.32, 1);
|
||
opacity: 0;
|
||
transform: translateY(8px);
|
||
z-index: 50;
|
||
}
|
||
|
||
.floating-back-to-top.visible {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
|
||
.floating-back-to-top:hover {
|
||
background: #f8fafc;
|
||
color: #1e293b;
|
||
border-color: #cbd5e1;
|
||
transform: translateY(-4px);
|
||
box-shadow:
|
||
0 20px 25px -5px rgb(0 0 0 / 0.1),
|
||
0 8px 10px -6px rgb(0 0 0 / 0.1);
|
||
}
|
||
|
||
/* Reduced motion support */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
html {
|
||
scroll-behavior: auto;
|
||
}
|
||
|
||
* {
|
||
animation-duration: 0.01ms !important;
|
||
animation-iteration-count: 1 !important;
|
||
transition-duration: 0.01ms !important;
|
||
}
|
||
}
|
||
|
||
/* Print styles */
|
||
@media print {
|
||
.floating-back-to-top,
|
||
.reading-progress-bar {
|
||
display: none !important;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Additional global styles from BaseLayout */
|
||
|
||
/* Selection color */
|
||
::selection {
|
||
background: #0f172a;
|
||
color: #ffffff;
|
||
}
|
||
|
||
/* Tag Styles */
|
||
.highlighter-tag {
|
||
animation: tagPopIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1) both;
|
||
animation-delay: calc(var(--tag-index, 0) * 0.05s);
|
||
}
|
||
|
||
.highlighter-tag:hover {
|
||
@apply -translate-y-0.5 scale-105 shadow-lg shadow-slate-200;
|
||
}
|
||
|
||
@keyframes tagPopIn {
|
||
from {
|
||
opacity: 0;
|
||
transform: scale(0.8) translateY(10px);
|
||
}
|
||
|
||
to {
|
||
opacity: 1;
|
||
transform: scale(1) translateY(0);
|
||
}
|
||
}
|
||
|
||
.highlighter-yellow {
|
||
background: linear-gradient(
|
||
135deg,
|
||
rgba(255, 235, 59, 0.95) 0%,
|
||
rgba(255, 213, 79, 0.95) 100%
|
||
);
|
||
color: #3f2f00;
|
||
}
|
||
|
||
.highlighter-pink {
|
||
background: linear-gradient(
|
||
135deg,
|
||
rgba(255, 167, 209, 0.95) 0%,
|
||
rgba(255, 122, 175, 0.95) 100%
|
||
);
|
||
color: #3f0018;
|
||
}
|
||
|
||
.highlighter-green {
|
||
background: linear-gradient(
|
||
135deg,
|
||
rgba(129, 199, 132, 0.95) 0%,
|
||
rgba(102, 187, 106, 0.95) 100%
|
||
);
|
||
color: #002f0a;
|
||
}
|
||
|
||
.highlighter-blue {
|
||
background: linear-gradient(
|
||
135deg,
|
||
rgba(226, 232, 240, 0.95) 0%,
|
||
rgba(203, 213, 225, 0.95) 100%
|
||
);
|
||
color: #0f172a;
|
||
}
|
||
|
||
.highlighter-tag:hover::before {
|
||
content: "";
|
||
position: absolute;
|
||
inset: -2px;
|
||
background: inherit;
|
||
filter: blur(8px);
|
||
opacity: 0.4;
|
||
z-index: -1;
|
||
border-radius: inherit;
|
||
}
|
||
|
||
.highlighter-tag:active {
|
||
transform: rotate(-1deg) translateY(0) scale(0.98);
|
||
transition: transform 0.1s ease;
|
||
}
|
||
|
||
.highlighter-tag:focus {
|
||
@apply -translate-y-0.5 scale-105;
|
||
outline: none !important;
|
||
}
|
||
|
||
/* Marker Title Styles */
|
||
.marker-title::before {
|
||
content: "";
|
||
position: absolute;
|
||
left: -0.15em;
|
||
right: -0.15em;
|
||
bottom: 0.05em;
|
||
height: 0.62em;
|
||
border-radius: 0.18em;
|
||
z-index: -1;
|
||
|
||
background: linear-gradient(
|
||
180deg,
|
||
rgba(255, 255, 255, 0) 0%,
|
||
rgba(255, 255, 255, 0) 20%,
|
||
rgba(253, 230, 138, 0.7) 20%,
|
||
rgba(253, 230, 138, 0.7) 100%
|
||
);
|
||
|
||
transform-origin: left center;
|
||
transform: rotate(calc((var(--marker-seed, 0) - 3) * 0.45deg))
|
||
skewX(calc((var(--marker-seed, 0) - 3) * -0.25deg));
|
||
|
||
filter: saturate(1.05);
|
||
}
|
||
|
||
.marker-title::after {
|
||
content: "";
|
||
position: absolute;
|
||
left: -0.18em;
|
||
right: -0.05em;
|
||
bottom: 0.05em;
|
||
height: 0.62em;
|
||
border-radius: 0.18em;
|
||
z-index: -1;
|
||
|
||
background: linear-gradient(
|
||
90deg,
|
||
rgba(253, 230, 138, 0) 0%,
|
||
rgba(253, 230, 138, 0.6) 8%,
|
||
rgba(253, 230, 138, 0.55) 60%,
|
||
rgba(253, 230, 138, 0.35) 100%
|
||
);
|
||
|
||
opacity: 0.75;
|
||
mix-blend-mode: multiply;
|
||
transform: rotate(calc((var(--marker-seed, 0) - 3) * 0.45deg))
|
||
translateY(0.02em);
|
||
|
||
mask-image: linear-gradient(
|
||
180deg,
|
||
rgba(0, 0, 0, 0) 0%,
|
||
rgba(0, 0, 0, 1) 20%,
|
||
rgba(0, 0, 0, 1) 100%
|
||
);
|
||
}
|
||
|
||
@media (prefers-reduced-motion: no-preference) {
|
||
.post-link:hover .marker-title::before,
|
||
.post-link:hover .marker-title::after {
|
||
filter: saturate(1.08) contrast(1.02);
|
||
}
|
||
}
|
||
|
||
/* Mermaid Styles */
|
||
.mermaid-container svg {
|
||
width: 100% !important;
|
||
max-width: 100%;
|
||
height: auto;
|
||
display: block;
|
||
background-color: transparent !important;
|
||
}
|
||
|
||
.mermaid-container rect,
|
||
.mermaid-container circle,
|
||
.mermaid-container ellipse,
|
||
.mermaid-container polygon,
|
||
.mermaid-container path,
|
||
.mermaid-container .actor,
|
||
.mermaid-container .node {
|
||
fill: white !important;
|
||
stroke: #cbd5e1 !important;
|
||
stroke-width: 1.5px !important;
|
||
}
|
||
|
||
.mermaid-container .edgePath .path,
|
||
.mermaid-container .messageLine0,
|
||
.mermaid-container .messageLine1,
|
||
.mermaid-container .flowchart-link {
|
||
stroke: #cbd5e1 !important;
|
||
stroke-width: 1.5px !important;
|
||
}
|
||
|
||
.mermaid-container text,
|
||
.mermaid-container .label,
|
||
.mermaid-container .labelText,
|
||
.mermaid-container .edgeLabel,
|
||
.mermaid-container .node text,
|
||
.mermaid-container tspan {
|
||
font-family: "Inter", sans-serif !important;
|
||
fill: #334155 !important;
|
||
color: #334155 !important;
|
||
stroke: none !important;
|
||
font-size: 16px !important;
|
||
}
|
||
|
||
.mermaid-container .marker,
|
||
.mermaid-container marker path {
|
||
fill: #cbd5e1 !important;
|
||
stroke: #cbd5e1 !important;
|
||
}
|
||
|
||
/* Generic Embed Styles */
|
||
.generic-embed {
|
||
--max-width: 100%;
|
||
--border-radius: 24px;
|
||
--bg-color: #ffffff;
|
||
--border-color: #e2e8f0;
|
||
--shadow: none;
|
||
|
||
margin: 1.5rem 0;
|
||
width: 100%;
|
||
max-width: var(--max-width);
|
||
}
|
||
|
||
.embed-wrapper {
|
||
border-radius: var(--border-radius);
|
||
border: 1px solid var(--border-color);
|
||
background: var(--bg-color);
|
||
box-shadow: var(--shadow);
|
||
overflow: hidden;
|
||
transition: all 0.2s ease;
|
||
position: relative;
|
||
}
|
||
|
||
.generic-embed[data-type="video"] .embed-wrapper {
|
||
aspect-ratio: 16/9;
|
||
height: 0;
|
||
padding-bottom: 56.25%;
|
||
/* 16:9 */
|
||
}
|
||
|
||
.generic-embed[data-type="video"] .embed-wrapper iframe {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.embed-wrapper:hover {
|
||
box-shadow:
|
||
0 4px 6px -1px rgb(0 0 0 / 0.1),
|
||
0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||
border-color: #cbd5e1;
|
||
}
|
||
|
||
.generic-embed[data-provider="youtube.com"] {
|
||
--bg-color: #000000;
|
||
}
|
||
|
||
.generic-embed[data-provider="vimeo.com"] {
|
||
--bg-color: #1a1a1a;
|
||
}
|
||
|
||
.generic-embed[data-provider="codepen.io"] {
|
||
--bg-color: #1e1e1e;
|
||
--border-color: #333;
|
||
}
|
||
|
||
.embed-fallback {
|
||
padding: 1.5rem;
|
||
background: #f8fafc;
|
||
border: 1px dashed #cbd5e1;
|
||
border-radius: var(--border-radius);
|
||
text-align: center;
|
||
}
|
||
|
||
.fallback-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
color: #64748b;
|
||
font-size: 0.875rem;
|
||
}
|
||
|
||
.fallback-link {
|
||
@apply text-slate-900 underline underline-offset-4;
|
||
text-decoration: none;
|
||
font-weight: 500;
|
||
margin-top: 0.25rem;
|
||
word-break: break-all;
|
||
}
|
||
|
||
.fallback-link:hover {
|
||
text-decoration: underline;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.generic-embed {
|
||
margin: 1rem 0;
|
||
}
|
||
|
||
.embed-fallback {
|
||
padding: 1rem;
|
||
}
|
||
|
||
.embed-wrapper:hover {
|
||
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||
}
|
||
}
|
||
|
||
/* File Example Styles */
|
||
[data-file-example] {
|
||
box-shadow: 0 1px 0 rgba(15, 23, 42, 0.04);
|
||
}
|
||
|
||
.copy-btn,
|
||
.download-btn {
|
||
color: #475569;
|
||
}
|
||
|
||
.copy-btn[data-copied="true"] {
|
||
color: #065f46;
|
||
background: rgba(16, 185, 129, 0.1);
|
||
border-color: rgba(16, 185, 129, 0.35);
|
||
}
|
||
|
||
/* Prism.js syntax highlighting - light, low-noise */
|
||
code[class*="language-"],
|
||
pre[class*="language-"],
|
||
pre:has(code[class*="language-"]) {
|
||
color: #0f172a;
|
||
background: transparent;
|
||
text-shadow: none;
|
||
font-family:
|
||
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
|
||
"Courier New", monospace;
|
||
font-size: 0.8125rem;
|
||
line-height: 1.65;
|
||
direction: ltr;
|
||
text-align: left;
|
||
white-space: pre;
|
||
word-spacing: normal;
|
||
word-break: normal;
|
||
tab-size: 2;
|
||
hyphens: none;
|
||
}
|
||
|
||
.token.comment,
|
||
.token.prolog,
|
||
.token.doctype,
|
||
.token.cdata {
|
||
color: #64748b;
|
||
font-style: italic;
|
||
}
|
||
|
||
.token.punctuation {
|
||
color: #94a3b8;
|
||
}
|
||
|
||
.token.operator {
|
||
color: #64748b;
|
||
}
|
||
|
||
.token.property,
|
||
.token.tag,
|
||
.token.constant,
|
||
.token.symbol,
|
||
.token.deleted {
|
||
color: #c2410c;
|
||
}
|
||
|
||
.token.boolean,
|
||
.token.number {
|
||
color: #a16207;
|
||
}
|
||
|
||
.token.selector,
|
||
.token.attr-name,
|
||
.token.string,
|
||
.token.char,
|
||
.token.builtin,
|
||
.token.inserted {
|
||
color: #059669;
|
||
}
|
||
|
||
.token.operator,
|
||
.token.entity,
|
||
.token.url,
|
||
.language-css .token.string,
|
||
.style .token.string {
|
||
color: #475569;
|
||
}
|
||
|
||
.token.atrule,
|
||
.token.attr-value,
|
||
.token.keyword {
|
||
color: #7c3aed;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.token.function,
|
||
.token.class-name {
|
||
color: #2563eb;
|
||
}
|
||
|
||
.token.regex,
|
||
.token.important,
|
||
.token.variable {
|
||
color: #db2777;
|
||
}
|
||
|
||
/* ═══════════════════════════════════════════════
|
||
TECH AESTHETIC – Animation Keyframes
|
||
═══════════════════════════════════════════════ */
|
||
|
||
/* Gradient Mesh Blob Animations */
|
||
@keyframes gradient-blob-1 {
|
||
0%,
|
||
100% {
|
||
transform: translate(0, 0) scale(1);
|
||
}
|
||
|
||
25% {
|
||
transform: translate(30px, -20px) scale(1.05);
|
||
}
|
||
|
||
50% {
|
||
transform: translate(-15px, 25px) scale(0.95);
|
||
}
|
||
|
||
75% {
|
||
transform: translate(20px, 15px) scale(1.02);
|
||
}
|
||
}
|
||
|
||
@keyframes gradient-blob-2 {
|
||
0%,
|
||
100% {
|
||
transform: translate(0, 0) scale(1);
|
||
}
|
||
|
||
33% {
|
||
transform: translate(-25px, 15px) scale(1.03);
|
||
}
|
||
|
||
66% {
|
||
transform: translate(20px, -20px) scale(0.97);
|
||
}
|
||
}
|
||
|
||
@keyframes gradient-blob-3 {
|
||
0%,
|
||
100% {
|
||
transform: translate(0, 0) scale(1);
|
||
}
|
||
|
||
20% {
|
||
transform: translate(15px, 10px) scale(1.06);
|
||
}
|
||
|
||
40% {
|
||
transform: translate(-10px, 20px) scale(0.98);
|
||
}
|
||
|
||
60% {
|
||
transform: translate(25px, -15px) scale(1.02);
|
||
}
|
||
|
||
80% {
|
||
transform: translate(-20px, -10px) scale(0.96);
|
||
}
|
||
}
|
||
|
||
/* Binary Stream Scroll */
|
||
@keyframes binary-scroll {
|
||
0% {
|
||
transform: translateY(-50%);
|
||
}
|
||
|
||
100% {
|
||
transform: translateY(0%);
|
||
}
|
||
}
|
||
|
||
/* Circuit Pulse (used for node glow effects) */
|
||
@keyframes circuit-pulse {
|
||
0%,
|
||
100% {
|
||
opacity: 0.2;
|
||
box-shadow: 0 0 0 0 rgba(148, 163, 184, 0);
|
||
}
|
||
|
||
50% {
|
||
opacity: 0.5;
|
||
box-shadow: 0 0 12px 2px rgba(148, 163, 184, 0.15);
|
||
}
|
||
}
|
||
|
||
/* Data Packet Flow */
|
||
@keyframes data-packet-flow {
|
||
0% {
|
||
transform: translateX(-100%);
|
||
}
|
||
|
||
100% {
|
||
transform: translateX(100vw);
|
||
}
|
||
}
|
||
|
||
/* Tech Border Glow */
|
||
@keyframes border-trace {
|
||
0% {
|
||
background-position: 0% 50%;
|
||
}
|
||
|
||
100% {
|
||
background-position: 200% 50%;
|
||
}
|
||
}
|
||
|
||
/* Section Fade-In Glow */
|
||
@keyframes section-glow {
|
||
0% {
|
||
opacity: 0;
|
||
}
|
||
|
||
50% {
|
||
opacity: 0.08;
|
||
}
|
||
|
||
100% {
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
/* Tailwind-compatible animation classes */
|
||
.animate-gradient-blob-1 {
|
||
animation: gradient-blob-1 20s ease-in-out infinite;
|
||
}
|
||
|
||
.animate-gradient-blob-2 {
|
||
animation: gradient-blob-2 25s ease-in-out infinite;
|
||
}
|
||
|
||
.animate-gradient-blob-3 {
|
||
animation: gradient-blob-3 30s ease-in-out infinite;
|
||
}
|
||
|
||
.animate-circuit-pulse {
|
||
animation: circuit-pulse 3s ease-in-out infinite;
|
||
}
|
||
|
||
.animate-border-trace {
|
||
animation: border-trace 4s linear infinite;
|
||
}
|
||
|
||
/* Glassmorphism Utilities */
|
||
.glass {
|
||
backdrop-filter: blur(20px);
|
||
-webkit-backdrop-filter: blur(20px);
|
||
background: rgba(255, 255, 255, 0.7);
|
||
border: 1px solid rgba(226, 232, 240, 0.5);
|
||
}
|
||
|
||
.glass-subtle {
|
||
backdrop-filter: blur(12px);
|
||
-webkit-backdrop-filter: blur(12px);
|
||
background: rgba(255, 255, 255, 0.5);
|
||
border: 1px solid rgba(241, 245, 249, 0.8);
|
||
}
|
||
|
||
.glass-dark {
|
||
backdrop-filter: blur(20px);
|
||
-webkit-backdrop-filter: blur(20px);
|
||
background: rgba(15, 23, 42, 0.8);
|
||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||
}
|
||
|
||
/* Tech Border – animated gradient trace */
|
||
.tech-border {
|
||
position: relative;
|
||
}
|
||
|
||
.tech-border::after {
|
||
content: "";
|
||
position: absolute;
|
||
inset: 0;
|
||
border-radius: inherit;
|
||
padding: 1px;
|
||
background: linear-gradient(
|
||
90deg,
|
||
transparent 0%,
|
||
rgba(148, 163, 184, 0.3) 25%,
|
||
rgba(191, 206, 228, 0.2) 50%,
|
||
rgba(148, 163, 184, 0.3) 75%,
|
||
transparent 100%
|
||
);
|
||
background-size: 200% 100%;
|
||
animation: border-trace 4s linear infinite;
|
||
-webkit-mask:
|
||
linear-gradient(#fff 0 0) content-box,
|
||
linear-gradient(#fff 0 0);
|
||
mask:
|
||
linear-gradient(#fff 0 0) content-box,
|
||
linear-gradient(#fff 0 0);
|
||
-webkit-mask-composite: xor;
|
||
mask-composite: exclude;
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* Noise texture overlay */
|
||
.noise-overlay::before {
|
||
content: "";
|
||
position: absolute;
|
||
inset: 0;
|
||
opacity: 0.02;
|
||
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
|
||
pointer-events: none;
|
||
z-index: 1;
|
||
}
|
||
|
||
/* ═══════════════════════════════════════════════
|
||
ABSTRACT CIRCUIT – Trace Pulse Animations
|
||
═══════════════════════════════════════════════ */
|
||
|
||
@keyframes tracePulse1 {
|
||
0% {
|
||
stroke-dashoffset: 1280;
|
||
}
|
||
|
||
100% {
|
||
stroke-dashoffset: 0;
|
||
}
|
||
}
|
||
|
||
@keyframes tracePulse2 {
|
||
0% {
|
||
stroke-dashoffset: 650;
|
||
}
|
||
|
||
100% {
|
||
stroke-dashoffset: 0;
|
||
}
|
||
}
|
||
|
||
@keyframes tracePulse3 {
|
||
0% {
|
||
stroke-dashoffset: 1280;
|
||
}
|
||
|
||
100% {
|
||
stroke-dashoffset: 0;
|
||
}
|
||
}
|
||
|
||
@keyframes tracePulse4 {
|
||
0% {
|
||
stroke-dashoffset: 440;
|
||
}
|
||
|
||
100% {
|
||
stroke-dashoffset: 0;
|
||
}
|
||
}
|
||
|
||
@keyframes junctionGlow {
|
||
0%,
|
||
100% {
|
||
opacity: 0.1;
|
||
}
|
||
|
||
50% {
|
||
opacity: 0.4;
|
||
}
|
||
}
|
||
|
||
/* ── Binary micro-interactions ─────────────────────────────────────────────── */
|
||
|
||
/* Digits that fly out from a pressed link or button (see BitBurst). */
|
||
.bit-burst {
|
||
position: fixed;
|
||
z-index: 200;
|
||
pointer-events: none;
|
||
/* On the layer, not the digits: a fixed layer blends as a whole with the page. */
|
||
mix-blend-mode: difference;
|
||
}
|
||
.bit-burst span {
|
||
position: absolute;
|
||
left: 0;
|
||
top: 0;
|
||
font-family: var(--font-jetbrains-mono), ui-monospace, monospace;
|
||
font-size: 13px;
|
||
font-weight: 700;
|
||
color: #ffffff;
|
||
opacity: 0;
|
||
animation: bit-burst-fly 620ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||
}
|
||
@keyframes bit-burst-fly {
|
||
0% {
|
||
opacity: 1;
|
||
transform: translate(-50%, -50%) scale(1);
|
||
}
|
||
100% {
|
||
opacity: 0;
|
||
transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy)))
|
||
scale(0.6);
|
||
}
|
||
}
|
||
|
||
/* Corner marks that snap onto a card on hover or focus. */
|
||
.bit-card {
|
||
position: relative;
|
||
}
|
||
.bit-card::before,
|
||
.bit-card::after {
|
||
content: "";
|
||
position: absolute;
|
||
width: 14px;
|
||
height: 14px;
|
||
border: 1.5px solid currentColor;
|
||
opacity: 0;
|
||
transition:
|
||
opacity 250ms ease,
|
||
transform 350ms cubic-bezier(0.16, 1, 0.3, 1);
|
||
pointer-events: none;
|
||
}
|
||
.bit-card::before {
|
||
top: 12px;
|
||
right: 12px;
|
||
border-left: 0;
|
||
border-bottom: 0;
|
||
transform: translate(-6px, 6px);
|
||
}
|
||
.bit-card::after {
|
||
bottom: 12px;
|
||
left: 12px;
|
||
border-right: 0;
|
||
border-top: 0;
|
||
transform: translate(6px, -6px);
|
||
}
|
||
.bit-card:hover::before,
|
||
.bit-card:hover::after,
|
||
.bit-card:focus-visible::before,
|
||
.bit-card:focus-visible::after,
|
||
.bit-card[aria-selected="true"]::before,
|
||
.bit-card[aria-selected="true"]::after {
|
||
opacity: 0.55;
|
||
transform: translate(0, 0);
|
||
}
|
||
|
||
/* Light that follows the pointer on a `data-glow` surface (see BitGlow). */
|
||
.bit-glow {
|
||
position: absolute;
|
||
inset: 0;
|
||
border-radius: inherit;
|
||
pointer-events: none;
|
||
opacity: 0;
|
||
transition: opacity 400ms ease;
|
||
}
|
||
@media (hover: hover) {
|
||
[data-glow]:hover > .bit-glow {
|
||
opacity: 1;
|
||
}
|
||
}
|
||
/* Dark surfaces: the dot grid lights up around the pointer. */
|
||
.bit-glow-dots {
|
||
background-image:
|
||
radial-gradient(
|
||
320px circle at var(--gx, 50%) var(--gy, 50%),
|
||
rgba(148, 163, 184, 0.22),
|
||
transparent 70%
|
||
),
|
||
radial-gradient(#e2e8f0 1.25px, transparent 1.25px);
|
||
background-size:
|
||
auto,
|
||
20px 20px;
|
||
-webkit-mask-image: radial-gradient(
|
||
280px circle at var(--gx, 50%) var(--gy, 50%),
|
||
#000,
|
||
transparent 75%
|
||
);
|
||
mask-image: radial-gradient(
|
||
280px circle at var(--gx, 50%) var(--gy, 50%),
|
||
#000,
|
||
transparent 75%
|
||
);
|
||
}
|
||
/* Cards: only the border lights up, in the card's text colour. */
|
||
.bit-glow-edge {
|
||
inset: -1px;
|
||
padding: 2px;
|
||
background: radial-gradient(
|
||
320px circle at var(--gx, 50%) var(--gy, 50%),
|
||
currentColor,
|
||
transparent 70%
|
||
);
|
||
-webkit-mask:
|
||
linear-gradient(#000 0 0) content-box,
|
||
linear-gradient(#000 0 0);
|
||
-webkit-mask-composite: xor;
|
||
mask:
|
||
linear-gradient(#000 0 0) content-box exclude,
|
||
linear-gradient(#000 0 0);
|
||
}
|
||
/* Dark buttons: a spot of light under the pointer. */
|
||
.bit-glow-spot {
|
||
background: radial-gradient(
|
||
90px circle at var(--gx, 50%) var(--gy, 50%),
|
||
rgba(129, 140, 248, 0.5),
|
||
transparent 70%
|
||
);
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.bit-burst,
|
||
.bit-glow {
|
||
display: none;
|
||
}
|
||
.bit-card::before,
|
||
.bit-card::after {
|
||
transition: none;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Calls to action: a light travels round the edge of the pill and glows softly
|
||
* behind it; on hover the glow comes up. `bit-cta-light` is for white pills on
|
||
* dark surfaces, `bit-cta-quiet` only lights up on hover.
|
||
*/
|
||
@property --cta-angle {
|
||
syntax: "<angle>";
|
||
inherits: false;
|
||
initial-value: 0deg;
|
||
}
|
||
.bit-cta {
|
||
--cta-a: rgba(129, 140, 248, 0.95);
|
||
--cta-b: #ffffff;
|
||
position: relative;
|
||
isolation: isolate;
|
||
}
|
||
.bit-cta-light {
|
||
--cta-a: rgba(99, 102, 241, 0.95);
|
||
--cta-b: #312e81;
|
||
}
|
||
.bit-cta-quiet {
|
||
--cta-a: rgba(99, 102, 241, 0.9);
|
||
--cta-b: #0f172a;
|
||
}
|
||
.bit-cta::before,
|
||
.bit-cta::after {
|
||
content: "";
|
||
position: absolute;
|
||
border-radius: inherit;
|
||
pointer-events: none;
|
||
background: conic-gradient(
|
||
from var(--cta-angle),
|
||
transparent 0 58%,
|
||
var(--cta-a) 76%,
|
||
var(--cta-b) 86%,
|
||
var(--cta-a) 93%,
|
||
transparent 100%
|
||
);
|
||
animation: bit-cta-turn 6s linear infinite;
|
||
}
|
||
/* The edge itself. */
|
||
.bit-cta::before {
|
||
inset: -1px;
|
||
padding: 1.5px;
|
||
-webkit-mask:
|
||
linear-gradient(#000 0 0) content-box,
|
||
linear-gradient(#000 0 0);
|
||
-webkit-mask-composite: xor;
|
||
mask:
|
||
linear-gradient(#000 0 0) content-box exclude,
|
||
linear-gradient(#000 0 0);
|
||
}
|
||
/* The glow behind the edge. */
|
||
.bit-cta::after {
|
||
inset: -2px;
|
||
z-index: -1;
|
||
padding: 6px;
|
||
-webkit-mask:
|
||
linear-gradient(#000 0 0) content-box,
|
||
linear-gradient(#000 0 0);
|
||
-webkit-mask-composite: xor;
|
||
mask:
|
||
linear-gradient(#000 0 0) content-box exclude,
|
||
linear-gradient(#000 0 0);
|
||
filter: blur(10px);
|
||
opacity: 0.55;
|
||
transition: opacity 300ms ease;
|
||
}
|
||
.bit-cta-quiet::before,
|
||
.bit-cta-quiet::after {
|
||
opacity: 0;
|
||
transition: opacity 300ms ease;
|
||
}
|
||
@media (hover: hover) {
|
||
.bit-cta:hover::after,
|
||
.group:hover > .bit-cta::after {
|
||
opacity: 1;
|
||
}
|
||
.bit-cta-quiet:hover::before {
|
||
opacity: 1;
|
||
}
|
||
.bit-cta-quiet:hover::after {
|
||
opacity: 0.6;
|
||
}
|
||
}
|
||
@keyframes bit-cta-turn {
|
||
to {
|
||
--cta-angle: 360deg;
|
||
}
|
||
}
|
||
|
||
/* Slow light drifting over a dark picture (see PortraitSection). */
|
||
.bit-aurora {
|
||
position: absolute;
|
||
inset: -10%;
|
||
pointer-events: none;
|
||
mix-blend-mode: screen;
|
||
background:
|
||
radial-gradient(
|
||
38% 46% at 38% 42%,
|
||
rgba(99, 102, 241, 0.55),
|
||
transparent 70%
|
||
),
|
||
radial-gradient(
|
||
30% 40% at 64% 72%,
|
||
rgba(129, 140, 248, 0.35),
|
||
transparent 70%
|
||
);
|
||
filter: blur(40px);
|
||
animation: bit-aurora-drift 16s ease-in-out infinite alternate;
|
||
}
|
||
@keyframes bit-aurora-drift {
|
||
from {
|
||
transform: translate3d(-4%, -3%, 0) scale(1);
|
||
}
|
||
to {
|
||
transform: translate3d(5%, 4%, 0) scale(1.12);
|
||
}
|
||
}
|
||
|
||
/* The current station on the career line: a node that breathes light. */
|
||
.bit-node-live {
|
||
animation: bit-node-breathe 3.2s ease-in-out infinite;
|
||
}
|
||
@keyframes bit-node-breathe {
|
||
0%,
|
||
100% {
|
||
box-shadow:
|
||
0 0 0 4px rgba(129, 140, 248, 0.22),
|
||
0 0 18px 2px rgba(129, 140, 248, 0.55);
|
||
}
|
||
50% {
|
||
box-shadow:
|
||
0 0 0 8px rgba(129, 140, 248, 0.12),
|
||
0 0 34px 8px rgba(129, 140, 248, 0.85);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Native feel: the page should behave like a well-made app on every input type.
|
||
*/
|
||
html {
|
||
color-scheme: light;
|
||
/* Anchor jumps stop below the fixed header. */
|
||
scroll-padding-top: 6rem;
|
||
-webkit-text-size-adjust: 100%;
|
||
text-size-adjust: 100%;
|
||
accent-color: #0f172a;
|
||
caret-color: #0f172a;
|
||
}
|
||
|
||
body {
|
||
text-rendering: optimizeLegibility;
|
||
font-kerning: normal;
|
||
}
|
||
|
||
/* Headlines break into even lines, running text avoids lonely last words. */
|
||
h1,
|
||
h2,
|
||
h3,
|
||
h4 {
|
||
text-wrap: balance;
|
||
}
|
||
p,
|
||
li,
|
||
dd,
|
||
summary {
|
||
text-wrap: pretty;
|
||
}
|
||
|
||
/* Controls react at once to a tap and are not selected like text. */
|
||
a,
|
||
button,
|
||
summary,
|
||
[role="tab"],
|
||
[role="button"] {
|
||
touch-action: manipulation;
|
||
}
|
||
button,
|
||
summary,
|
||
[role="tab"],
|
||
[role="button"],
|
||
a[href^="#"] {
|
||
-webkit-user-select: none;
|
||
user-select: none;
|
||
}
|
||
|
||
/* Decoration is neither selectable nor draggable. */
|
||
[aria-hidden="true"],
|
||
canvas,
|
||
img,
|
||
svg {
|
||
-webkit-user-select: none;
|
||
user-select: none;
|
||
}
|
||
img,
|
||
svg,
|
||
a {
|
||
-webkit-user-drag: none;
|
||
}
|
||
|
||
/* One visible keyboard focus, none for mouse and touch. */
|
||
:focus:not(:focus-visible) {
|
||
outline: none;
|
||
}
|
||
:focus-visible {
|
||
outline: 2px solid #0f172a;
|
||
outline-offset: 3px;
|
||
}
|
||
a:focus-visible,
|
||
button:focus-visible,
|
||
summary:focus-visible {
|
||
border-radius: 6px;
|
||
}
|
||
|
||
/* A short press-in on anything that can be pressed. */
|
||
@media (prefers-reduced-motion: no-preference) {
|
||
button:active,
|
||
[role="tab"]:active,
|
||
a[class*="rounded-full"]:active,
|
||
a:active > [class*="rounded-full"] {
|
||
scale: 0.97;
|
||
transition-duration: 80ms;
|
||
}
|
||
}
|
||
|
||
/* Dark surfaces: light selection, light focus ring, dark system controls. */
|
||
.bg-slate-900,
|
||
.bg-slate-950 {
|
||
color-scheme: dark;
|
||
}
|
||
.bg-slate-900 ::selection,
|
||
.bg-slate-950 ::selection {
|
||
background: #f8fafc;
|
||
color: #0f172a;
|
||
}
|
||
.bg-slate-900 :focus-visible,
|
||
.bg-slate-950 :focus-visible {
|
||
outline-color: #f8fafc;
|
||
}
|
||
|
||
/* Fixed controls stay clear of the home indicator and rounded corners. */
|
||
@supports (padding: env(safe-area-inset-bottom)) {
|
||
body {
|
||
padding-left: env(safe-area-inset-left);
|
||
padding-right: env(safe-area-inset-right);
|
||
}
|
||
}
|