The address is the one thing to act on in the contact section, but it was a plain underlined line next to a glowing message card. A slow sheen now runs through the letters and a light travels along the underline; on hover the whole line lights up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1465 lines
29 KiB
CSS
1465 lines
29 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;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* The contact address on a dark surface: a slow sheen runs through the letters
|
||
* and a light travels along the underline. On hover the whole line lights up.
|
||
*/
|
||
.bit-mail {
|
||
position: relative;
|
||
display: inline-block;
|
||
padding-bottom: 0.42em;
|
||
background: linear-gradient(
|
||
100deg,
|
||
#ffffff 0 38%,
|
||
#a5b4fc 50%,
|
||
#ffffff 62% 100%
|
||
);
|
||
background-size: 260% 100%;
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
color: transparent;
|
||
-webkit-text-fill-color: transparent;
|
||
animation: bit-mail-sheen 7s ease-in-out infinite;
|
||
transition: filter 300ms ease;
|
||
}
|
||
.bit-mail::before,
|
||
.bit-mail::after {
|
||
content: "";
|
||
position: absolute;
|
||
left: 0;
|
||
right: 0;
|
||
border-radius: 4px;
|
||
pointer-events: none;
|
||
}
|
||
/* The resting underline. */
|
||
.bit-mail::before {
|
||
bottom: 1px;
|
||
height: 2px;
|
||
background: rgba(255, 255, 255, 0.26);
|
||
}
|
||
/* The light travelling along it. */
|
||
.bit-mail::after {
|
||
bottom: 0;
|
||
height: 4px;
|
||
background: linear-gradient(
|
||
90deg,
|
||
transparent,
|
||
#818cf8 30%,
|
||
#ffffff 50%,
|
||
#818cf8 70%,
|
||
transparent
|
||
)
|
||
no-repeat;
|
||
background-size: 42% 100%;
|
||
filter: drop-shadow(0 0 8px rgba(129, 140, 248, 0.95));
|
||
animation: bit-mail-run 3.8s ease-in-out infinite alternate;
|
||
transition: background-size 400ms ease;
|
||
}
|
||
@media (hover: hover) {
|
||
.bit-mail:hover {
|
||
filter: drop-shadow(0 0 18px rgba(129, 140, 248, 0.7));
|
||
}
|
||
.bit-mail:hover::after {
|
||
background-size: 100% 100%;
|
||
}
|
||
}
|
||
@keyframes bit-mail-sheen {
|
||
0%,
|
||
100% {
|
||
background-position: 100% 0;
|
||
}
|
||
50% {
|
||
background-position: 0% 0;
|
||
}
|
||
}
|
||
@keyframes bit-mail-run {
|
||
from {
|
||
background-position: -30% 0;
|
||
}
|
||
to {
|
||
background-position: 130% 0;
|
||
}
|
||
}
|
||
|
||
/* 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);
|
||
}
|
||
}
|