663 lines
41 KiB
HTML
663 lines
41 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||
<meta name="theme-color" content="#0a0a10" />
|
||
<meta name="color-scheme" content="dark" />
|
||
<meta name="description" content="xdfnx — Senior Full Stack Engineer. Systems built for pressure. Rust, C++, scalable web applications, and the core infrastructure that holds them together." />
|
||
<meta name="author" content="xdfnx" />
|
||
<link rel="canonical" href="https://xdfnx.cfd/" />
|
||
<meta property="og:type" content="website" />
|
||
<meta property="og:url" content="https://xdfnx.cfd/" />
|
||
<meta property="og:title" content="xdfnx — Senior Full Stack Engineer" />
|
||
<meta property="og:description" content="Systems built for pressure. Rust & C++ where speed is not optional." />
|
||
<meta property="og:site_name" content="xdfnx" />
|
||
<meta property="og:locale" content="en_US" />
|
||
<meta name="twitter:card" content="summary_large_image" />
|
||
<meta name="twitter:title" content="xdfnx — Senior Full Stack Engineer" />
|
||
<meta name="twitter:description" content="Systems built for pressure. Rust & C++ where speed is not optional." />
|
||
<title>xdfnx — Senior Full Stack Engineer</title>
|
||
|
||
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='7' fill='%230a0a10'/%3E%3Cpath d='M9 9h14M9 16h9M9 23h14' stroke='%23c2a878' stroke-width='2.4' stroke-linecap='round'/%3E%3C/svg%3E" />
|
||
<link rel="apple-touch-icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='7' fill='%230a0a10'/%3E%3Cpath d='M9 9h14M9 16h9M9 23h14' stroke='%23c2a878' stroke-width='2.4' stroke-linecap='round'/%3E%3C/svg%3E" />
|
||
|
||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||
|
||
<script type="application/ld+json">
|
||
{
|
||
"@context":"https://schema.org","@type":"Person","name":"xdfnx",
|
||
"jobTitle":"Senior Full Stack Engineer","url":"https://xdfnx.cfd/",
|
||
"email":"contact@xdfnx.cfd",
|
||
"description":"Systems built for pressure. Rust & C++ where speed is not optional.",
|
||
"knowsAbout":["Rust","C++","TypeScript","System Performance","Scalable Web Applications","Infrastructure"]
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
:root{
|
||
--bg:#0a0a10;
|
||
--bg-2:#0e0e16;
|
||
--surface:#12121c;
|
||
--surface-2:#171723;
|
||
--ink:#f4f3f1;
|
||
--ink-dim:#aeb0bd;
|
||
--ink-faint:#6f7080;
|
||
--line:rgba(255,255,255,.07);
|
||
--line-2:rgba(255,255,255,.12);
|
||
--line-3:rgba(255,255,255,.18);
|
||
--accent:#c2a878;
|
||
--accent-bright:#e7cf9b;
|
||
--accent-dim:#7c6a47;
|
||
--accent-glow:rgba(194,168,120,.5);
|
||
--flame:#e07a4c;
|
||
--ok:#7fd1a3;
|
||
--info:#7aa2e6;
|
||
--focus:#7aa2e6;
|
||
--grad:linear-gradient(135deg,var(--accent) 0%,var(--flame) 100%);
|
||
--grad-soft:linear-gradient(135deg,rgba(194,168,120,.16),rgba(224,122,76,.10));
|
||
--ease:cubic-bezier(.2,.7,.2,1);
|
||
--ease-out:cubic-bezier(.16,1,.3,1);
|
||
}
|
||
*{margin:0;padding:0;box-sizing:border-box}
|
||
html{scroll-behavior:smooth}
|
||
body{
|
||
background:var(--bg);color:var(--ink);
|
||
font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
|
||
font-weight:300;line-height:1.6;letter-spacing:.01em;
|
||
-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
|
||
overflow-x:hidden;
|
||
background-image:
|
||
radial-gradient(1200px 600px at 50% -200px,rgba(194,168,120,.10),transparent 60%),
|
||
radial-gradient(900px 700px at 100% 30%,rgba(224,122,76,.06),transparent 60%);
|
||
}
|
||
::selection{background:var(--accent);color:#15200a}
|
||
:focus-visible{outline:2px solid var(--focus);outline-offset:3px;border-radius:2px}
|
||
a:focus-visible,button:focus-visible{outline-offset:4px}
|
||
|
||
.skip{position:absolute;left:50%;top:-60px;transform:translateX(-50%);background:var(--accent);color:#15120a;padding:10px 18px;border-radius:0 0 6px 6px;font-family:'JetBrains Mono',monospace;font-size:13px;font-weight:600;text-decoration:none;z-index:200;transition:top .25s var(--ease)}
|
||
.skip:focus{top:0}
|
||
|
||
/* atmosphere */
|
||
.grain{position:fixed;inset:0;z-index:1;pointer-events:none;opacity:.04;background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>")}
|
||
.grid-bg{position:fixed;inset:0;z-index:0;pointer-events:none;background-image:linear-gradient(rgba(255,255,255,.03) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,.03) 1px,transparent 1px);background-size:64px 64px;mask-image:radial-gradient(ellipse 90% 70% at 50% 0%,#000 35%,transparent 85%);-webkit-mask-image:radial-gradient(ellipse 90% 70% at 50% 0%,#000 35%,transparent 85%)}
|
||
.glow{position:fixed;z-index:0;pointer-events:none;border-radius:50%;filter:blur(140px);opacity:.5;will-change:transform}
|
||
.glow.a{top:-220px;left:25%;width:760px;height:460px;background:radial-gradient(circle,rgba(194,168,120,.16),transparent 70%)}
|
||
.glow.b{top:55vh;right:-200px;width:560px;height:560px;background:radial-gradient(circle,rgba(224,122,76,.10),transparent 70%)}
|
||
|
||
.wrap{max-width:1200px;margin:0 auto;padding:0 28px;position:relative;z-index:2}
|
||
section{position:relative;z-index:2}
|
||
|
||
/* top bar */
|
||
header.bar{position:fixed;top:0;left:0;right:0;z-index:50;backdrop-filter:blur(16px) saturate(1.2);-webkit-backdrop-filter:blur(16px) saturate(1.2);background:rgba(10,10,16,.72);border-bottom:1px solid var(--line);transition:transform .4s var(--ease)}
|
||
header.bar.hidden{transform:translateY(-100%)}
|
||
.bar-inner{display:flex;align-items:center;justify-content:space-between;height:64px;max-width:1200px;margin:0 auto;padding:0 28px}
|
||
.brand{font-family:'JetBrains Mono',monospace;font-size:14px;font-weight:700;letter-spacing:.02em;color:var(--ink);text-decoration:none;display:flex;align-items:center;gap:9px}
|
||
.brand .dot{width:9px;height:9px;border-radius:50%;background:var(--grad);box-shadow:0 0 14px var(--accent-glow)}
|
||
.nav-links{display:flex;gap:28px;align-items:center}
|
||
.nav-links a{color:var(--ink-dim);text-decoration:none;font-size:13px;font-family:'JetBrains Mono',monospace;transition:color .25s var(--ease);position:relative;padding:6px 0}
|
||
.nav-links a::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;background:var(--accent);transform:scaleX(0);transform-origin:left;transition:transform .3s var(--ease)}
|
||
.nav-links a:hover,.nav-links a.active{color:var(--ink)}
|
||
.nav-links a:hover::after,.nav-links a.active::after{transform:scaleX(1)}
|
||
.nav-cta{color:var(--accent)!important}.nav-cta::after{background:var(--accent)}
|
||
.menu-btn{display:none;background:none;border:1px solid var(--line-2);color:var(--ink);padding:8px 12px;font-family:'JetBrains Mono',monospace;font-size:12px;cursor:pointer;border-radius:4px;transition:border-color .25s,color .25s}
|
||
.menu-btn:hover{border-color:var(--accent);color:var(--accent)}
|
||
|
||
/* ---- hero ---- */
|
||
.hero{min-height:100svh;display:flex;align-items:center;padding:140px 0 80px}
|
||
.hero-grid{display:grid;grid-template-columns:1.05fr .95fr;gap:64px;align-items:center}
|
||
.hero-left{display:flex;flex-direction:column;gap:30px;max-width:560px}
|
||
.eyebrow{display:inline-flex;align-items:center;gap:12px;font-family:'JetBrains Mono',monospace;font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--ink-dim);width:fit-content}
|
||
.pulse{width:8px;height:8px;border-radius:50%;background:var(--grad);position:relative;box-shadow:0 0 0 0 var(--accent-glow)}
|
||
.pulse::after{content:"";position:absolute;inset:-6px;border-radius:50%;border:1px solid var(--accent);opacity:.6;animation:ping 2.4s var(--ease) infinite}
|
||
.status-pill{border:1px solid var(--line-2);padding:5px 12px;border-radius:999px;color:var(--ink);background:rgba(255,255,255,.02)}
|
||
.status-pill b{color:var(--accent);font-weight:600}
|
||
@media (prefers-reduced-motion:no-preference){@keyframes ping{0%{transform:scale(.7);opacity:.7}70%{transform:scale(1.7);opacity:0}100%{opacity:0}}}
|
||
|
||
h1.hero-title{font-size:clamp(40px,5.8vw,78px);line-height:.98;font-weight:800;letter-spacing:-.035em;color:var(--ink)}
|
||
h1.hero-title .soft{color:var(--ink-faint);font-weight:300;font-style:italic}
|
||
h1.hero-title .accent{background:var(--grad);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:var(--accent)}
|
||
|
||
.hero-sub{font-size:clamp(16px,1.8vw,19px);color:var(--ink-dim);font-weight:300}
|
||
.hero-sub b{color:var(--ink);font-weight:500}
|
||
.hero-sub .hl{color:var(--accent)}
|
||
|
||
.cta-row{display:flex;gap:14px;flex-wrap:wrap}
|
||
.btn{display:inline-flex;align-items:center;gap:10px;min-height:48px;padding:14px 24px;border-radius:5px;text-decoration:none;font-size:14px;font-weight:600;font-family:'JetBrains Mono',monospace;letter-spacing:.04em;transition:transform .35s var(--ease-out),box-shadow .35s var(--ease-out),background .3s var(--ease),color .3s var(--ease),border-color .3s var(--ease);cursor:pointer;border:1px solid transparent;position:relative}
|
||
.btn.primary{background:var(--grad);color:#1a1408;background-size:200% 100%;background-position:0 0}
|
||
.btn.primary:hover{transform:translateY(-3px);box-shadow:0 16px 40px -12px var(--accent-glow);background-position:100% 0}
|
||
.btn.ghost{color:var(--ink);border-color:var(--line-2);background:rgba(255,255,255,.02)}
|
||
.btn.ghost:hover{border-color:var(--accent);color:var(--accent);transform:translateY(-3px)}
|
||
.btn .arr{transition:transform .3s var(--ease)}
|
||
.btn:hover .arr{transform:translateX(5px)}
|
||
|
||
.hero-meta{display:flex;gap:36px;flex-wrap:wrap}
|
||
.hero-meta div{font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--ink-faint);letter-spacing:.06em}
|
||
.hero-meta b{color:var(--ink-dim);font-weight:500}
|
||
|
||
/* ---- hero viz: live system monitor ---- */
|
||
.monitor{position:relative;border:1px solid var(--line-2);border-radius:10px;overflow:hidden;background:linear-gradient(180deg,rgba(18,18,28,.92),rgba(10,10,16,.92));box-shadow:0 30px 70px -40px rgba(0,0,0,.9),inset 0 1px 0 rgba(255,255,255,.04)}
|
||
.mon-bar{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:12px 14px;border-bottom:1px solid var(--line);background:rgba(255,255,255,.025)}
|
||
.mon-dots{display:flex;gap:7px}
|
||
.mon-dot{width:10px;height:10px;border-radius:50%}
|
||
.mon-dot.r{background:#ff5f57}.mon-dot.y{background:#febc2e}.mon-dot.g{background:#28c840}
|
||
.mon-status{font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--ink-faint);letter-spacing:.08em;text-transform:uppercase;display:flex;align-items:center;gap:8px}
|
||
.mon-status .live{width:7px;height:7px;border-radius:50%;background:var(--ok);box-shadow:0 0 10px var(--ok);animation:livepulse 1.6s ease-in-out infinite}
|
||
@media (prefers-reduced-motion:no-preference){@keyframes livepulse{0%,100%{opacity:1}50%{opacity:.35}}}
|
||
.mon-body{padding:18px 16px 16px;display:grid;gap:14px}
|
||
|
||
.mon-row{display:grid;gap:6px}
|
||
.mon-row .lbl{display:flex;justify-content:space-between;font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--ink-dim);letter-spacing:.06em}
|
||
.mon-row .lbl b{color:var(--accent);font-weight:600}
|
||
.mon-track{height:6px;border-radius:3px;background:rgba(255,255,255,.06);overflow:hidden;position:relative}
|
||
.mon-fill{height:100%;border-radius:3px;background:var(--grad);transform-origin:left;transform:scaleX(0);width:100%;transition:transform .9s var(--ease-out)}
|
||
.mon-row.in .mon-fill{transform:scaleX(.0)}
|
||
|
||
.mon-chart{height:120px;position:relative;border:1px solid var(--line);border-radius:6px;background:linear-gradient(180deg,rgba(255,255,255,.02),transparent);overflow:hidden}
|
||
.mon-chart svg{display:block;width:100%;height:100%}
|
||
.mon-chart .lab{position:absolute;top:8px;left:10px;font-family:'JetBrains Mono',monospace;font-size:10px;color:var(--ink-faint);letter-spacing:.08em;text-transform:uppercase}
|
||
.mon-chart .lab b{color:var(--ok);font-weight:600}
|
||
.mon-chart .pct{position:absolute;bottom:8px;right:10px;font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--ink-dim)}
|
||
.mon-chart .pct b{color:var(--accent);font-weight:600}
|
||
|
||
/* ---- section heading ---- */
|
||
.sec{padding:120px 0;border-top:1px solid var(--line)}
|
||
.sec-head{display:flex;align-items:baseline;gap:18px;margin-bottom:58px}
|
||
.sec-id{font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--accent);letter-spacing:.1em;font-weight:500}
|
||
.sec-title{font-size:clamp(30px,4.2vw,46px);font-weight:700;letter-spacing:-.025em}
|
||
.sec-rule{flex:1;height:1px;background:linear-gradient(90deg,var(--line-2),transparent)}
|
||
.sec-sub{color:var(--ink-dim);font-size:16px;max-width:620px;margin-top:14px;font-weight:300}
|
||
|
||
/* ---- about: lead + what I do trio (replaces variant scratchpad) ---- */
|
||
.about-grid{display:grid;grid-template-columns:.95fr 1.05fr;gap:60px;align-items:start}
|
||
.about-lead{font-size:clamp(20px,2.4vw,27px);line-height:1.4;font-weight:300;color:var(--ink);letter-spacing:-.01em}
|
||
.about-lead .hl{background:var(--grad);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:var(--accent)}
|
||
.about-body{color:var(--ink-dim);font-size:15px;line-height:1.85;margin-top:22px;max-width:520px}
|
||
|
||
.do-trio{display:flex;flex-direction:column;gap:14px}
|
||
.do-card{display:grid;grid-template-columns:48px 1fr;gap:18px;padding:22px;border:1px solid var(--line);border-radius:8px;background:linear-gradient(180deg,var(--surface),var(--bg-2));transition:border-color .35s var(--ease),transform .35s var(--ease-out)}
|
||
.do-card:hover{border-color:var(--line-3);transform:translateX(6px)}
|
||
.do-ico{width:42px;height:42px;border-radius:8px;background:var(--grad-soft);border:1px solid var(--line-2);display:grid;place-items:center;font-family:'JetBrains Mono',monospace;font-size:13px;font-weight:700;color:var(--accent)}
|
||
.do-card h3{font-size:16px;font-weight:700;letter-spacing:-.01em}
|
||
.do-card p{color:var(--ink-dim);font-size:13.5px;line-height:1.65;margin-top:6px}
|
||
|
||
/* ---- stats (grounded) ---- */
|
||
.stats{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--line);border:1px solid var(--line);border-radius:10px;overflow:hidden}
|
||
.stat{background:var(--bg);padding:40px 28px;position:relative;overflow:hidden;transition:background .4s var(--ease)}
|
||
.stat::after{content:"";position:absolute;inset:0;background:radial-gradient(circle at 100% 0%,rgba(194,168,120,.07),transparent 55%);opacity:0;transition:opacity .4s var(--ease)}
|
||
.stat:hover{background:var(--bg-2)}
|
||
.stat:hover::after{opacity:1}
|
||
.stat-num{font-size:clamp(42px,5.6vw,62px);font-weight:700;letter-spacing:-.035em;color:var(--ink);line-height:1}
|
||
.stat-num .plus{background:var(--grad);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:var(--accent)}
|
||
.stat-label{font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--ink-faint);letter-spacing:.14em;text-transform:uppercase;margin-top:12px;font-weight:500}
|
||
.stat-desc{color:var(--ink-dim);font-size:13px;margin-top:6px;font-weight:300}
|
||
|
||
/* ---- experience ---- */
|
||
.timeline{display:flex;flex-direction:column}
|
||
.job{display:grid;grid-template-columns:160px 1fr;gap:40px;padding:46px 0;border-top:1px solid var(--line);transition:padding-left .4s var(--ease-out)}
|
||
.job:first-child{border-top:none}
|
||
.job:hover{padding-left:10px}
|
||
.job-period{font-family:'JetBrains Mono',monospace;font-size:13px;color:var(--accent);letter-spacing:.04em;padding-top:5px;font-weight:500}
|
||
.job-body h3{font-size:21px;font-weight:700;letter-spacing:-.01em}
|
||
.job-co{color:var(--ink-dim);font-weight:300;font-size:15px;margin-top:3px}
|
||
.job-co span{color:var(--ink-faint)}
|
||
.job-desc{color:var(--ink-dim);font-size:15px;line-height:1.75;margin-top:16px;max-width:680px}
|
||
.job-desc b{color:var(--ink);font-weight:500}
|
||
.job-divider{height:1px;background:var(--line);margin:0 auto;width:0;transition:width .5s var(--ease-out)}
|
||
|
||
/* ---- arsenal: grouped, no marquee ---- */
|
||
.arsenal{display:grid;grid-template-columns:repeat(3,1fr);gap:22px}
|
||
.arm-card{border:1px solid var(--line);background:linear-gradient(180deg,var(--surface),var(--bg));padding:32px 28px;border-radius:10px;position:relative;transition:transform .4s var(--ease-out),border-color .35s var(--ease);overflow:hidden}
|
||
.arm-card::before{content:"";position:absolute;top:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent,var(--accent),transparent);opacity:0;transition:opacity .4s var(--ease)}
|
||
.arm-card:hover{border-color:var(--line-3);transform:translateY(-6px)}
|
||
.arm-card:hover::before{opacity:1}
|
||
.arm-ico{font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--ink-faint);letter-spacing:.14em;font-weight:500;display:flex;align-items:center;gap:8px}
|
||
.arm-ico .n{color:var(--accent)}
|
||
.arm-title{font-size:19px;font-weight:700;margin:12px 0 10px;letter-spacing:-.01em}
|
||
.arm-desc{color:var(--ink-dim);font-size:14px;line-height:1.7;font-weight:300}
|
||
.arm-stack{display:flex;flex-wrap:wrap;gap:7px;margin-top:20px;padding-top:16px;border-top:1px solid var(--line)}
|
||
.arm-stack span{font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--ink-dim);border:1px solid var(--line);padding:5px 10px;border-radius:5px;letter-spacing:.04em;transition:color .25s var(--ease),border-color .25s var(--ease)}
|
||
.arm-stack span:hover{color:var(--accent);border-color:var(--accent-dim)}
|
||
|
||
/* ---- work ---- */
|
||
.work-grid{display:flex;flex-direction:column}
|
||
.work{display:grid;grid-template-columns:80px 1fr 260px;gap:36px;padding:48px 0;border-top:1px solid var(--line);align-items:start;transition:padding-left .4s var(--ease-out),background .4s var(--ease)}
|
||
.work:first-child{border-top:none}
|
||
.work:hover{padding-left:12px;background:linear-gradient(90deg,rgba(255,255,255,.02),transparent 40%)}
|
||
.work-idx{font-family:'JetBrains Mono',monospace;font-size:13px;color:var(--accent);letter-spacing:.08em;padding-top:6px;font-weight:500}
|
||
.work-main h3{font-size:clamp(22px,2.6vw,30px);font-weight:700;letter-spacing:-.02em}
|
||
.work-metric{font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--accent);margin-top:10px;letter-spacing:.04em}
|
||
.work-main p{color:var(--ink-dim);font-size:15px;line-height:1.75;margin-top:14px;max-width:580px;font-weight:300}
|
||
.work-main p b{color:var(--ink);font-weight:500}
|
||
.work-side .lbl{font-family:'JetBrains Mono',monospace;font-size:10px;color:var(--ink-faint);letter-spacing:.14em;text-transform:uppercase;margin-bottom:12px;font-weight:500}
|
||
.work-stack{display:flex;flex-wrap:wrap;gap:8px}
|
||
.work-stack span{font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--ink-dim);border:1px solid var(--line-2);padding:5px 10px;border-radius:5px}
|
||
|
||
/* ---- final cta ---- */
|
||
.cta-sec{padding:140px 0;text-align:center;border-top:1px solid var(--line);position:relative}
|
||
.cta-sec::before{content:"";position:absolute;inset:0;background:radial-gradient(ellipse 60% 80% at 50% 100%,rgba(194,168,120,.10),transparent 70%);pointer-events:none}
|
||
.cta-eyebrow{font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--accent);letter-spacing:.16em;text-transform:uppercase;margin-bottom:24px;position:relative;font-weight:600}
|
||
h2.cta-title{font-size:clamp(34px,5.2vw,66px);font-weight:700;letter-spacing:-.035em;line-height:1.05;position:relative;max-width:820px;margin:0 auto}
|
||
.cta-title .soft{color:var(--ink-faint);font-weight:300}
|
||
.cta-title .accent{background:var(--grad);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:var(--accent)}
|
||
.cta-sub{color:var(--ink-dim);font-size:17px;margin-top:24px;font-weight:300;max-width:540px;margin-left:auto;margin-right:auto;position:relative}
|
||
.cta-sec .cta-row{justify-content:center;margin-top:38px;position:relative}
|
||
.email-copy{position:relative}
|
||
.copy-tip{position:absolute;left:50%;bottom:calc(100% + 10px);transform:translateX(-50%) translateY(6px);background:var(--surface-2);border:1px solid var(--line-2);color:var(--accent);font-family:'JetBrains Mono',monospace;font-size:11px;padding:7px 13px;border-radius:5px;opacity:0;pointer-events:none;transition:opacity .25s var(--ease),transform .25s var(--ease);white-space:nowrap}
|
||
.copy-tip.show{opacity:1;transform:translateX(-50%) translateY(0)}
|
||
|
||
/* ---- footer ---- */
|
||
footer{border-top:1px solid var(--line);padding:50px 0;padding-bottom:max(56px,env(safe-area-inset-bottom))}
|
||
.foot{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:24px}
|
||
.foot-left{font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--ink-faint);letter-spacing:.04em}
|
||
.foot-left b{color:var(--ink-dim);font-weight:500}
|
||
.foot-right{display:flex;gap:26px;align-items:center}
|
||
.foot-right a{font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--ink-dim);text-decoration:none;letter-spacing:.04em;transition:color .25s var(--ease);display:inline-flex;align-items:center;gap:7px}
|
||
.foot-right a:hover{color:var(--accent)}
|
||
.foot-right svg{width:14px;height:14px;fill:currentColor}
|
||
|
||
/* reveal */
|
||
.reveal{opacity:0;transform:translateY(26px);transition:opacity .9s var(--ease-out),transform .9s var(--ease-out)}
|
||
.reveal.in{opacity:1;transform:none}
|
||
.reveal.d1{transition-delay:.08s}.reveal.d2{transition-delay:.16s}.reveal.d3{transition-delay:.24s}.reveal.d4{transition-delay:.32s}.reveal.d5{transition-delay:.4s}
|
||
|
||
@media(max-width:920px){
|
||
.hero-grid{grid-template-columns:1fr;gap:44px}
|
||
.hero-left{max-width:none}
|
||
.about-grid{grid-template-columns:1fr;gap:44px}
|
||
}
|
||
@media(max-width:880px){
|
||
.nav-links{position:fixed;inset:64px 0 auto 0;background:rgba(10,10,16,.97);backdrop-filter:blur(16px);flex-direction:column;gap:0;padding:8px 0;border-bottom:1px solid var(--line);transform:translateY(-130%);transition:transform .35s var(--ease);border-radius:0 0 12px 12px}
|
||
.nav-links.open{transform:translateY(0)}
|
||
.nav-links a{padding:18px 28px;width:100%;border-bottom:1px solid var(--line)}
|
||
.menu-btn{display:block}
|
||
.stats,.arsenal{grid-template-columns:1fr}
|
||
.job{grid-template-columns:1fr;gap:12px;padding-left:0!important}
|
||
.job-period{padding-top:0}
|
||
.work{grid-template-columns:1fr;gap:18px;padding-left:0!important}
|
||
.work-side{padding-top:4px}
|
||
.hero{padding-top:120px}
|
||
.sec{padding:84px 0}
|
||
}
|
||
@media(prefers-reduced-motion:reduce){
|
||
*{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important;scroll-behavior:auto!important}
|
||
.reveal{opacity:1;transform:none}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<a class="skip" href="#main">Skip to content</a>
|
||
<div class="grid-bg" aria-hidden="true"></div>
|
||
<div class="glow a" aria-hidden="true"></div>
|
||
<div class="glow b" aria-hidden="true"></div>
|
||
<div class="grain" aria-hidden="true"></div>
|
||
|
||
<header class="bar" id="topbar">
|
||
<div class="bar-inner">
|
||
<a class="brand" href="#about"><span class="dot" aria-hidden="true"></span>xdfnx</a>
|
||
<nav class="nav-links" id="nav" aria-label="Primary">
|
||
<a href="#about">About</a>
|
||
<a href="#work">Work</a>
|
||
<a href="#arsenal">Arsenal</a>
|
||
<a href="#experience">Record</a>
|
||
<a href="#contact" class="nav-cta">Contact →</a>
|
||
</nav>
|
||
<button class="menu-btn" id="menuBtn" aria-expanded="false" aria-controls="nav">Menu</button>
|
||
</div>
|
||
</header>
|
||
|
||
<main class="wrap" id="main">
|
||
|
||
<!-- HERO -->
|
||
<section class="hero" aria-labelledby="hero-title">
|
||
<div class="hero-grid">
|
||
<div class="hero-left">
|
||
<div class="eyebrow reveal">
|
||
<span class="pulse" aria-hidden="true"></span>
|
||
<span class="status-pill"><b>Open</b> for Complex Projects</span>
|
||
</div>
|
||
<h1 class="hero-title reveal d1" id="hero-title">
|
||
Systems built<br/>
|
||
<span class="soft">for</span> <span class="accent">pressure</span>.
|
||
</h1>
|
||
<p class="hero-sub reveal d2">
|
||
Senior Full Stack Engineer. I build <b>scalable web applications</b> and engineer the <b>core infrastructure</b> that holds them together — <span class="hl">Rust & C++</span> on the paths where speed is not optional.
|
||
</p>
|
||
<div class="cta-row reveal d3">
|
||
<a href="#contact" class="btn primary">Contact Me <span class="arr" aria-hidden="true">→</span></a>
|
||
<a href="#work" class="btn ghost">View Work</a>
|
||
</div>
|
||
<div class="hero-meta reveal d5">
|
||
<div>FOCUS / <b>System Performance</b></div>
|
||
<div>STACK / <b>Rust · C++ · TS</b></div>
|
||
<div>UPTIME / <b>Built to hold</b></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- live system monitor -->
|
||
<div class="monitor reveal d4" role="img" aria-label="Live system monitor showing throughput, latency and p99 latency chart holding steady under load.">
|
||
<div class="mon-bar">
|
||
<div class="mon-dots"><span class="mon-dot r"></span><span class="mon-dot y"></span><span class="mon-dot g"></span></div>
|
||
<div class="mon-status"><span class="live" aria-hidden="true"></span>system · holding</div>
|
||
</div>
|
||
<div class="mon-body">
|
||
<div class="mon-row" data-fill="0.82">
|
||
<div class="lbl"><span>Throughput</span><b>82k req/s</b></div>
|
||
<div class="mon-track"><div class="mon-fill"></div></div>
|
||
</div>
|
||
<div class="mon-row" data-fill="0.12">
|
||
<div class="lbl"><span>p50 latency</span><b>4.1 ms</b></div>
|
||
<div class="mon-track"><div class="mon-fill"></div></div>
|
||
</div>
|
||
<div class="mon-row" data-fill="0.4">
|
||
<div class="lbl"><span>p99 latency</span><b>18.3 ms</b></div>
|
||
<div class="mon-track"><div class="mon-fill"></div></div>
|
||
</div>
|
||
<div class="mon-chart">
|
||
<div class="lab">load · <b>holding</b></div>
|
||
<svg viewBox="0 0 300 120" preserveAspectRatio="none" aria-hidden="true">
|
||
<defs>
|
||
<linearGradient id="cf" x1="0" y1="0" x2="0" y2="1">
|
||
<stop offset="0%" stop-color="#c2a878" stop-opacity="0.4"/>
|
||
<stop offset="100%" stop-color="#c2a878" stop-opacity="0"/>
|
||
</linearGradient>
|
||
</defs>
|
||
<path id="cArea" d="" fill="url(#cf)"/>
|
||
<path id="cLine" d="" fill="none" stroke="#e7cf9b" stroke-width="2" stroke-linejoin="round" stroke-linecap="round"/>
|
||
</svg>
|
||
<div class="pct">↓ <b id="cVal">40%</b> latency</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ABOUT -->
|
||
<section class="sec" id="about" aria-labelledby="about-title">
|
||
<div class="sec-head reveal">
|
||
<span class="sec-id">01 / ABOUT</span>
|
||
<h2 class="sec-title" id="about-title">The Engineer</h2>
|
||
<span class="sec-rule" aria-hidden="true"></span>
|
||
</div>
|
||
<div class="about-grid">
|
||
<div class="reveal">
|
||
<p class="about-lead">
|
||
I design <span class="hl">infrastructure that withstands pressure</span> — write code that respects the machine, and ship systems built to outlast their first traffic spike.
|
||
</p>
|
||
<p class="about-body">
|
||
Six years in the engine room. Scalable web applications, optimized core infrastructure, and the low-level work most teams avoid. When the load hits, the system holds — because it was designed to.
|
||
</p>
|
||
</div>
|
||
<div class="do-trio reveal d2">
|
||
<div class="do-card reveal">
|
||
<div class="do-ico" aria-hidden="true">FX</div>
|
||
<div><h3>Frontend that earns its pixels</h3><p>Type-safe, accessible, responsive interfaces in React, Next.js and TypeScript — UX built in, not bolted on.</p></div>
|
||
</div>
|
||
<div class="do-card reveal d1">
|
||
<div class="do-ico" aria-hidden="true">IN</div>
|
||
<div><h3>Backend & infrastructure</h3><p>Scalable architectures in Node and Python — Dockerized, cloud-native, built to grow without breaking under their own weight.</p></div>
|
||
</div>
|
||
<div class="do-card reveal d2">
|
||
<div class="do-ico" aria-hidden="true">C</div>
|
||
<div><h3>Systems on the critical path</h3><p>When the runtime isn't fast enough: memory-safe, high-performance Rust & C++ modules — the work most teams avoid.</p></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- STATS -->
|
||
<section class="sec" aria-labelledby="stats-title">
|
||
<div class="sec-head reveal">
|
||
<span class="sec-id">02 / SIGNAL</span>
|
||
<h2 class="sec-title" id="stats-title">By the Numbers</h2>
|
||
<span class="sec-rule" aria-hidden="true"></span>
|
||
<p class="sec-sub">Representative outcomes from recent production work — figures anonymized, results real.</p>
|
||
</div>
|
||
<div class="stats reveal">
|
||
<div class="stat"><div class="stat-num">6<span class="plus">+</span></div><div class="stat-label">Years in the Field</div><div class="stat-desc">Engine room to architecture.</div></div>
|
||
<div class="stat"><div class="stat-num">40<span class="plus">%</span></div><div class="stat-label">Avg Latency Cut</div><div class="stat-desc">On the event pipeline.</div></div>
|
||
<div class="stat"><div class="stat-num">3<span class="plus">×</span></div><div class="stat-label">Throughput Win</div><div class="stat-desc">Native modules vs. JS.</div></div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ARSENAL -->
|
||
<section class="sec" id="arsenal" aria-labelledby="arsenal-title">
|
||
<div class="sec-head reveal">
|
||
<span class="sec-id">03 / ARSENAL</span>
|
||
<h2 class="sec-title" id="arsenal-title">Technical Arsenal</h2>
|
||
<span class="sec-rule" aria-hidden="true"></span>
|
||
</div>
|
||
<div class="arsenal">
|
||
<div class="arm-card reveal">
|
||
<div class="arm-ico"><span class="n">A</span>/ FRONT</div>
|
||
<div class="arm-title">Modern Frontend</div>
|
||
<div class="arm-desc">Type-safe, responsive interfaces. UX and accessibility built in — not bolted on. Every pixel earns its place.</div>
|
||
<div class="arm-stack"><span>React</span><span>Next.js</span><span>TypeScript</span></div>
|
||
</div>
|
||
<div class="arm-card reveal d1">
|
||
<div class="arm-ico"><span class="n">B</span>/ INFRA</div>
|
||
<div class="arm-title">Backend & Infra</div>
|
||
<div class="arm-desc">Scalable architectures in Node and Python. Dockerized, cloud-native — built to grow without breaking under its own weight.</div>
|
||
<div class="arm-stack"><span>Node.js</span><span>Python</span><span>Docker</span><span>Redis</span><span>WebSockets</span></div>
|
||
</div>
|
||
<div class="arm-card reveal d2">
|
||
<div class="arm-ico"><span class="n">C</span>/ CORE</div>
|
||
<div class="arm-title">Systems & Low Level</div>
|
||
<div class="arm-desc">When the runtime isn't fast enough. Memory-safe, high-performance modules for the critical path. The work most teams avoid.</div>
|
||
<div class="arm-stack"><span>Rust</span><span>C++</span><span>System Design</span></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- WORK -->
|
||
<section class="sec" id="work" aria-labelledby="work-title">
|
||
<div class="sec-head reveal">
|
||
<span class="sec-id">04 / SELECTED WORK</span>
|
||
<h2 class="sec-title" id="work-title">Case Studies</h2>
|
||
<span class="sec-rule" aria-hidden="true"></span>
|
||
</div>
|
||
<div class="work-grid">
|
||
<article class="work reveal">
|
||
<div class="work-idx">01</div>
|
||
<div class="work-main">
|
||
<h3>Fintech Analytics Engine</h3>
|
||
<div class="work-metric">— real-time · zero-cost abstractions</div>
|
||
<p>Real-time financial dashboard. The backend aggregates streams from multiple sources through a <b>custom Rust service</b> — zero-cost abstractions, real performance. Built to render data the moment it arrives, not a cycle later.</p>
|
||
</div>
|
||
<div class="work-side"><div class="lbl">Stack</div><div class="work-stack"><span>Next.js</span><span>Rust</span><span>WebSockets</span></div></div>
|
||
</article>
|
||
<article class="work reveal">
|
||
<div class="work-idx">02</div>
|
||
<div class="work-main">
|
||
<h3>AI Data Pipeline</h3>
|
||
<div class="work-metric">— terabytes · Python + C++ bindings</div>
|
||
<p>Automated ETL across <b>terabytes of data</b>. Python for flexibility, <b>C++ bindings</b> for the heavy lifting. The pipeline doesn't choke when the data grows — it was designed for the next order of magnitude.</p>
|
||
</div>
|
||
<div class="work-side"><div class="lbl">Stack</div><div class="work-stack"><span>Python</span><span>C++</span><span>Docker</span></div></div>
|
||
</article>
|
||
<article class="work reveal">
|
||
<div class="work-idx">03</div>
|
||
<div class="work-main">
|
||
<h3>Distributed Task Queue</h3>
|
||
<div class="work-metric">— fault-tolerant · low-memory</div>
|
||
<p>Fault-tolerant job scheduler. Celery-inspired, rebuilt for <b>low-memory environments</b>. Jobs survive crashes. The queue doesn't bleed — engineered to hold the line when the system is under pressure.</p>
|
||
</div>
|
||
<div class="work-side"><div class="lbl">Stack</div><div class="work-stack"><span>Node.js</span><span>Redis</span><span>System Design</span></div></div>
|
||
</article>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- EXPERIENCE -->
|
||
<section class="sec" id="experience" aria-labelledby="exp-title">
|
||
<div class="sec-head reveal">
|
||
<span class="sec-id">05 / RECORD</span>
|
||
<h2 class="sec-title" id="exp-title">Professional Experience</h2>
|
||
<span class="sec-rule" aria-hidden="true"></span>
|
||
</div>
|
||
<div class="timeline">
|
||
<div class="job reveal">
|
||
<div class="job-period">2024 — Present</div>
|
||
<div class="job-body">
|
||
<h3>Senior Full Stack Engineer</h3>
|
||
<div class="job-co">TechFlow Systems <span>// full-stack · infra · perf</span></div>
|
||
<p class="job-desc">Leading the backend <b>migration to microservices</b>. Built a <b>high-throughput event processing pipeline</b> in Python and Rust — <b>latency cut by 40%</b>. Own the React architecture behind the main dashboard, from data layer to render.</p>
|
||
</div>
|
||
</div>
|
||
<div class="job reveal">
|
||
<div class="job-period">2021 — 2024</div>
|
||
<div class="job-body">
|
||
<h3>Software Engineer</h3>
|
||
<div class="job-co">Creative Solutions Ltd. <span>// full-stack · native modules</span></div>
|
||
<p class="job-desc">Full-stack web applications in <b>Node.js and TypeScript</b>. When the runtime slowed, I went lower — <b>native C++ modules</b> for image processing pushed workflows <b>3× faster</b>. Where JavaScript couldn't keep up, the machine did.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- FINAL CTA -->
|
||
<section class="cta-sec" id="contact" aria-labelledby="cta-title">
|
||
<div class="cta-eyebrow reveal">06 / TRANSMISSION OPEN</div>
|
||
<h2 class="cta-title reveal d1" id="cta-title">Let's build something <span class="accent">that won't break</span>.</h2>
|
||
<p class="cta-sub reveal d2">Systems that scale without noise — performance where it matters, infrastructure that holds the line.</p>
|
||
<div class="cta-row reveal d3">
|
||
<span class="email-copy">
|
||
<span class="copy-tip" id="copyTip" role="status" aria-live="polite">Copied to clipboard</span>
|
||
<a href="mailto:contact@xdfnx.cfd" class="btn primary" id="emailBtn" data-email="contact@xdfnx.cfd">contact@xdfnx.cfd <span class="arr" aria-hidden="true">→</span></a>
|
||
</span>
|
||
<a href="#work" class="btn ghost">View Work</a>
|
||
</div>
|
||
</section>
|
||
|
||
</main>
|
||
|
||
<footer>
|
||
<div class="wrap foot">
|
||
<div class="foot-left">© 2026 <b>xdfnx</b> // Built to hold.</div>
|
||
<div class="foot-right">
|
||
<a href="#contact" aria-label="Email">contact@xdfnx.cfd</a>
|
||
<a href="#about" aria-label="Back to top">Top ↑</a>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
|
||
<script>
|
||
(function(){
|
||
var reduce = window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||
|
||
// reveal
|
||
if ('IntersectionObserver' in window) {
|
||
var io = new IntersectionObserver(function(es){
|
||
es.forEach(function(e){ if(e.isIntersecting){ e.target.classList.add('in'); io.unobserve(e.target); } });
|
||
},{threshold:.12, rootMargin:'0px 0px -8% 0px'});
|
||
document.querySelectorAll('.reveal').forEach(function(el){ io.observe(el); });
|
||
} else {
|
||
document.querySelectorAll('.reveal').forEach(function(el){ el.classList.add('in'); });
|
||
}
|
||
|
||
// monitor: animate bars when visible
|
||
var monRows = document.querySelectorAll('.mon-row');
|
||
if ('IntersectionObserver' in window) {
|
||
var mo = new IntersectionObserver(function(es){
|
||
es.forEach(function(e){
|
||
if(e.isIntersecting){
|
||
monRows.forEach(function(r,i){
|
||
var f = parseFloat(r.getAttribute('data-fill'));
|
||
var fill = r.querySelector('.mon-fill');
|
||
var baseScale = reduce ? f : 0;
|
||
fill.style.transform = 'scaleX(' + baseScale + ')';
|
||
setTimeout(function(){
|
||
var target = reduce ? f : f;
|
||
fill.style.transition = 'transform 1s cubic-bezier(.16,1,.3,1)';
|
||
fill.style.transform = 'scaleX(' + target + ')';
|
||
}, 120 + i*140);
|
||
});
|
||
mo.disconnect();
|
||
}
|
||
});
|
||
},{threshold:.3});
|
||
var mon = document.querySelector('.monitor'); if(mon) mo.observe(mon);
|
||
} else {
|
||
monRows.forEach(function(r){ var f=parseFloat(r.getAttribute('data-fill')); r.querySelector('.mon-fill').style.transform='scaleX('+f+')'; });
|
||
}
|
||
|
||
// monitor: animated chart path (sine-ish wave that "holds the line")
|
||
var cLine = document.getElementById('cLine'), cArea = document.getElementById('cArea');
|
||
var W=300, H=120, N=42, phase=0, raf=null;
|
||
function buildPath(p){
|
||
var pts=[]; var mid=H/2;
|
||
for(var i=0;i<=N;i++){
|
||
var x=(i/N)*W;
|
||
var y= mid + Math.sin((i/N)*Math.PI*3 + p)*14*((i/N)) + Math.sin((i/N)*Math.PI*7 + p*1.3)*5;
|
||
pts.push(x.toFixed(1)+','+y.toFixed(1));
|
||
}
|
||
var d='M'+pts[0]+' L'+pts.slice(1).join(' L');
|
||
var area=d+' L'+W+','+H+' L0,'+H+' Z';
|
||
return {l:d,a:area};
|
||
}
|
||
function draw(){
|
||
var o = buildPath(phase);
|
||
cLine.setAttribute('d', o.l); cArea.setAttribute('d', o.a);
|
||
}
|
||
if (!reduce) {
|
||
if ('IntersectionObserver' in window) {
|
||
var co=new IntersectionObserver(function(es){ es.forEach(function(e){
|
||
if(e.isIntersecting){ if(!raf) loop(); }
|
||
else { if(raf){ cancelAnimationFrame(raf); raf=null; } }
|
||
}); },{threshold:.2});
|
||
var ch=document.querySelector('.mon-chart'); if(ch) co.observe(ch);
|
||
}
|
||
function loop(){ phase+=0.06; draw(); raf=requestAnimationFrame(loop); }
|
||
} else { draw(); }
|
||
|
||
// glow parallax (rAF-throttled)
|
||
if (!reduce) {
|
||
var glows=document.querySelectorAll('.glow'), ticking=false;
|
||
window.addEventListener('scroll',function(){
|
||
if(ticking) return; ticking=true;
|
||
requestAnimationFrame(function(){
|
||
var y=window.scrollY;
|
||
glows.forEach(function(g,i){ g.style.transform='translateY('+(y*(i?-.06:.04))+'px)'+(i?' translateX(0)':'translateX(-25%)'); });
|
||
ticking=false;
|
||
});
|
||
},{passive:true});
|
||
}
|
||
|
||
// hide top bar on scroll-down
|
||
var bar=document.getElementById('topbar'), lastY=0;
|
||
window.addEventListener('scroll',function(){
|
||
var y=window.scrollY;
|
||
if(y>220 && y>lastY) bar.classList.add('hidden'); else bar.classList.remove('hidden');
|
||
lastY=y;
|
||
},{passive:true});
|
||
|
||
// mobile menu
|
||
var btn=document.getElementById('menuBtn'), nav=document.getElementById('nav');
|
||
function closeMenu(){ nav.classList.remove('open'); btn.setAttribute('aria-expanded','false'); btn.textContent='Menu'; }
|
||
btn.addEventListener('click',function(){ var open=nav.classList.toggle('open'); btn.setAttribute('aria-expanded',open?'true':'false'); btn.textContent=open?'Close':'Menu'; });
|
||
nav.querySelectorAll('a').forEach(function(a){ a.addEventListener('click', closeMenu); });
|
||
|
||
// scroll-spy
|
||
var links=Array.prototype.slice.call(nav.querySelectorAll('a')), map={};
|
||
links.forEach(function(a){ var id=a.getAttribute('href').slice(1); var sec=document.getElementById(id); if(sec) map[id]=a; });
|
||
if ('IntersectionObserver' in window) {
|
||
var spy=new IntersectionObserver(function(es){ es.forEach(function(e){ if(e.isIntersecting){
|
||
links.forEach(function(l){ l.classList.remove('active'); });
|
||
var l=map[e.target.id]; if(l) l.classList.add('active');
|
||
}}); },{rootMargin:'-45% 0px -50% 0px'});
|
||
Object.keys(map).forEach(function(id){ var s=document.getElementById(id); if(s) spy.observe(s); });
|
||
}
|
||
|
||
// copy email
|
||
var emailBtn=document.getElementById('emailBtn'), tip=document.getElementById('copyTip'), email=emailBtn.getAttribute('data-email');
|
||
function flash(){ tip.classList.add('show'); setTimeout(function(){ tip.classList.remove('show'); },1600); }
|
||
emailBtn.addEventListener('click',function(e){
|
||
if(e.shiftKey||e.ctrlKey||e.metaKey){ e.preventDefault(); if(navigator.clipboard) navigator.clipboard.writeText(email).then(flash).catch(function(){}); else flash(); return; }
|
||
if(matchMedia('(pointer:coarse)').matches){ e.preventDefault(); if(navigator.clipboard) navigator.clipboard.writeText(email).then(flash).catch(function(){}); else window.location.href='mailto:'+email; }
|
||
});
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|