Portfolio — About / Bio Block
A warm, personable about section for a single-person portfolio. Pairs a CSS-gradient avatar shape and a rotating currently line with a short narrative bio, a column of quick facts (location, focus, experience, availability badge), values and now lists, inline social links, and a download-CV button. Two columns on desktop, gracefully stacked on mobile, on a clean neutral base with working vanilla JS interactions.
MCP
Code
/* =================================================================
Portfolio — About / Bio Block
Neutral base palette, "Inter". Warm two-column about section.
================================================================= */
:root {
--ink: #16151a;
--ink-soft: #46434f;
--ink-mute: #76737f;
--line: #e7e5ea;
--line-strong: #d6d3dc;
--paper: #ffffff;
--paper-2: #faf9fb;
--paper-3: #f3f1f6;
--accent: #5b54ff;
--accent-ink: #4640e6;
--accent-soft: #eeedff;
--warm: #ff8a5b;
--warm-soft: #fff1ea;
--ok: #19a974;
--ok-soft: #e7f8ef;
--ok-ink: #0f7a55;
--radius-sm: 10px;
--radius: 16px;
--radius-lg: 26px;
--shadow-sm: 0 1px 2px rgba(22, 21, 26, 0.06);
--shadow-md: 0 14px 40px -18px rgba(22, 21, 26, 0.28);
--shadow-lg: 0 36px 90px -40px rgba(22, 21, 26, 0.45);
--ease: cubic-bezier(0.22, 1, 0.36, 1);
--maxw: 1120px;
--font: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
--font-display: "Inter Tight", "Inter", system-ui, sans-serif;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
font-family: var(--font);
font-size: 16px;
line-height: 1.55;
color: var(--ink);
background:
radial-gradient(120% 70% at 50% -10%, var(--paper-3), transparent 60%),
var(--paper-2);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
a {
color: inherit;
text-decoration: none;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
:focus-visible {
outline: 3px solid var(--accent);
outline-offset: 3px;
border-radius: 4px;
}
.skip-link {
position: absolute;
left: 16px;
top: -64px;
z-index: 50;
padding: 10px 16px;
background: var(--ink);
color: #fff;
border-radius: var(--radius-sm);
font-weight: 600;
transition: top 0.18s var(--ease);
}
.skip-link:focus {
top: 16px;
}
/* ---------------- Page chrome ---------------- */
.page-head {
border-bottom: 1px solid var(--line);
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(8px);
}
.page-head__inner {
max-width: var(--maxw);
margin: 0 auto;
padding: 22px 24px;
display: flex;
align-items: center;
gap: 16px;
}
.page-head__mark {
font-size: 1.6rem;
color: var(--accent);
line-height: 1;
}
.page-head__title {
margin: 0;
font-family: var(--font-display);
font-size: clamp(1.1rem, 2.6vw, 1.4rem);
font-weight: 800;
letter-spacing: -0.02em;
}
.page-head__sub {
margin: 2px 0 0;
color: var(--ink-mute);
font-size: 0.92rem;
}
.stack {
max-width: var(--maxw);
margin: 0 auto;
padding: 36px 24px 24px;
}
.page-foot {
max-width: var(--maxw);
margin: 0 auto;
padding: 8px 24px 56px;
color: var(--ink-mute);
font-size: 0.85rem;
}
/* ---------------- About shell ---------------- */
.about {
position: relative;
border: 1px solid var(--line);
border-radius: var(--radius-lg);
background:
radial-gradient(
420px 360px at 100% 0%,
var(--warm-soft),
transparent 60%
),
radial-gradient(380px 300px at 0% 100%, var(--accent-soft), transparent 62%),
var(--paper);
box-shadow: var(--shadow-md);
overflow: hidden;
padding: clamp(26px, 5vw, 52px);
padding-top: clamp(54px, 7vw, 64px);
}
.about__tag {
position: absolute;
top: 18px;
left: 18px;
display: inline-block;
padding: 6px 12px;
border-radius: 999px;
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--ink-soft);
background: var(--paper-3);
border: 1px solid var(--line);
}
.about__grid {
display: grid;
grid-template-columns: 290px 1fr;
gap: clamp(28px, 5vw, 56px);
align-items: start;
}
/* ---------------- Left column ---------------- */
.about__side {
display: flex;
flex-direction: column;
gap: 22px;
}
/* Avatar shape */
.avatar {
position: relative;
width: clamp(180px, 60vw, 230px);
aspect-ratio: 1;
margin: 0 auto;
}
.avatar__blob {
position: absolute;
inset: 0;
border-radius: 46% 54% 58% 42% / 52% 44% 56% 48%;
background:
conic-gradient(from 140deg, var(--accent), #8a84ff, #ffb38a, var(--accent));
box-shadow: var(--shadow-lg);
animation: blob-morph 16s ease-in-out infinite;
}
@keyframes blob-morph {
0%,
100% {
border-radius: 46% 54% 58% 42% / 52% 44% 56% 48%;
transform: rotate(0deg);
}
50% {
border-radius: 60% 40% 42% 58% / 44% 58% 42% 56%;
transform: rotate(5deg);
}
}
.avatar__face {
position: absolute;
inset: 10% 10% 0;
width: 80%;
height: auto;
filter: drop-shadow(0 18px 24px rgba(22, 21, 26, 0.25));
}
.avatar__status {
position: absolute;
right: 8%;
bottom: 10%;
display: grid;
place-items: center;
width: 30px;
height: 30px;
border-radius: 50%;
background: var(--paper);
border: 1px solid var(--line);
box-shadow: var(--shadow-md);
}
.avatar__dot {
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--ok);
box-shadow: 0 0 0 0 rgba(25, 169, 116, 0.5);
animation: ping 2.4s var(--ease) infinite;
}
@keyframes ping {
0% {
box-shadow: 0 0 0 0 rgba(25, 169, 116, 0.5);
}
70%,
100% {
box-shadow: 0 0 0 10px rgba(25, 169, 116, 0);
}
}
/* Currently line */
.about__currently {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 7px;
margin: 0;
padding: 12px 14px;
border-radius: var(--radius);
background: var(--paper);
border: 1px solid var(--line);
box-shadow: var(--shadow-sm);
font-size: 0.88rem;
line-height: 1.4;
cursor: pointer;
transition:
border-color 0.18s var(--ease),
box-shadow 0.18s var(--ease),
transform 0.18s var(--ease);
}
.about__currently:hover {
border-color: var(--accent);
box-shadow: var(--shadow-md);
transform: translateY(-2px);
}
.about__pulse {
width: 9px;
height: 9px;
border-radius: 50%;
background: var(--warm);
flex: none;
box-shadow: 0 0 0 0 rgba(255, 138, 91, 0.55);
animation: ping-warm 2.6s var(--ease) infinite;
}
@keyframes ping-warm {
0% {
box-shadow: 0 0 0 0 rgba(255, 138, 91, 0.55);
}
70%,
100% {
box-shadow: 0 0 0 9px rgba(255, 138, 91, 0);
}
}
.about__currently-label {
font-size: 0.66rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--ink-mute);
}
.about__currently-text {
flex-basis: 100%;
font-weight: 600;
color: var(--ink);
}
/* Quick facts */
.facts {
display: flex;
flex-direction: column;
gap: 2px;
}
.fact {
display: grid;
grid-template-columns: 22px 1fr auto;
align-items: center;
gap: 10px;
padding: 9px 4px;
border-bottom: 1px solid var(--line);
font-size: 0.9rem;
}
.fact:last-child {
border-bottom: 0;
}
.fact__icon {
text-align: center;
color: var(--accent);
}
.fact__key {
color: var(--ink-mute);
}
.fact__val {
font-weight: 600;
text-align: right;
color: var(--ink);
}
.badge {
display: inline-block;
padding: 4px 11px;
border-radius: 999px;
font-size: 0.76rem;
font-weight: 700;
letter-spacing: 0.01em;
}
.badge--open {
color: var(--ok-ink);
background: var(--ok-soft);
border: 1px solid #bfead7;
}
.badge--busy {
color: #9a5500;
background: #fdf0e0;
border: 1px solid #f2d9b8;
}
/* ---------------- Buttons ---------------- */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 13px 20px;
border-radius: 999px;
font-family: var(--font);
font-size: 0.95rem;
font-weight: 600;
cursor: pointer;
border: 1px solid transparent;
transition:
transform 0.18s var(--ease),
background 0.18s var(--ease),
color 0.18s var(--ease),
border-color 0.18s var(--ease),
box-shadow 0.18s var(--ease);
}
.btn:active {
transform: translateY(1px) scale(0.99);
}
.btn__icon {
font-size: 1rem;
}
.btn--solid {
background: var(--ink);
color: #fff;
box-shadow: var(--shadow-sm);
}
.btn--solid:hover {
background: var(--accent);
box-shadow: 0 12px 26px -12px var(--accent);
}
.btn--cv {
width: 100%;
}
.btn--cv.is-busy {
pointer-events: none;
opacity: 0.85;
}
/* ---------------- Right column ---------------- */
.about__main {
min-width: 0;
}
.about__eyebrow {
display: inline-block;
margin: 0 0 6px;
padding: 6px 13px;
border-radius: 999px;
font-size: 0.78rem;
font-weight: 600;
color: var(--accent-ink);
background: var(--accent-soft);
}
.about__name {
margin: 0;
font-family: var(--font-display);
font-weight: 800;
letter-spacing: -0.035em;
line-height: 1;
font-size: clamp(2.1rem, 5.5vw, 3.1rem);
}
.about__role {
margin: 12px 0 0;
font-size: clamp(1.05rem, 2.4vw, 1.3rem);
font-weight: 600;
color: var(--ink-soft);
}
.about__bio {
margin-top: 18px;
max-width: 56ch;
color: var(--ink-soft);
font-size: 1.02rem;
}
.about__bio p {
margin: 0 0 14px;
}
.about__bio p:last-child {
margin-bottom: 0;
}
/* Values / Now columns */
.about__cols {
margin-top: 28px;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.mini {
padding: 18px;
border-radius: var(--radius);
background: var(--paper);
border: 1px solid var(--line);
box-shadow: var(--shadow-sm);
}
.mini__title {
margin: 0 0 12px;
font-family: var(--font-display);
font-size: 0.78rem;
font-weight: 800;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--ink-mute);
}
.mini__list {
display: flex;
flex-direction: column;
gap: 9px;
font-size: 0.94rem;
color: var(--ink);
}
.mini__list li {
position: relative;
padding-left: 22px;
line-height: 1.4;
}
.mini__list li::before {
content: "✦";
position: absolute;
left: 0;
top: 0;
color: var(--accent);
font-size: 0.8rem;
}
.mini__list--now li::before {
content: "→";
color: var(--warm);
font-weight: 700;
}
.mini__list em {
color: var(--ink-soft);
font-style: italic;
}
/* Social links */
.socials {
margin-top: 26px;
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.social {
display: inline-flex;
align-items: center;
gap: 9px;
padding: 9px 15px 9px 11px;
border-radius: 999px;
background: var(--paper);
border: 1px solid var(--line-strong);
font-size: 0.9rem;
font-weight: 600;
color: var(--ink);
transition:
transform 0.18s var(--ease),
border-color 0.18s var(--ease),
color 0.18s var(--ease),
background 0.18s var(--ease),
box-shadow 0.18s var(--ease);
}
.social:hover {
transform: translateY(-2px);
border-color: var(--accent);
color: var(--accent-ink);
background: var(--accent-soft);
box-shadow: var(--shadow-md);
}
.social:active {
transform: translateY(0);
}
.social__icon {
display: grid;
place-items: center;
width: 24px;
height: 24px;
border-radius: 50%;
background: var(--ink);
color: #fff;
font-size: 0.74rem;
font-weight: 700;
flex: none;
transition: background 0.18s var(--ease);
}
.social:hover .social__icon {
background: var(--accent);
}
/* ---------------- Toast ---------------- */
.toast {
position: fixed;
left: 50%;
bottom: 28px;
transform: translate(-50%, 24px);
z-index: 60;
max-width: min(90vw, 380px);
padding: 13px 20px;
border-radius: 999px;
background: var(--ink);
color: #fff;
font-size: 0.9rem;
font-weight: 600;
box-shadow: var(--shadow-lg);
opacity: 0;
pointer-events: none;
transition:
opacity 0.25s var(--ease),
transform 0.25s var(--ease);
}
.toast.is-visible {
opacity: 1;
transform: translate(-50%, 0);
}
/* ---------------- Responsive ---------------- */
@media (max-width: 880px) {
.about__grid {
grid-template-columns: 1fr;
}
.about__side {
flex-direction: row;
flex-wrap: wrap;
align-items: center;
gap: 18px 24px;
}
.avatar {
margin: 0;
width: clamp(140px, 28vw, 180px);
}
.about__currently {
flex: 1 1 220px;
}
.facts {
flex: 1 1 240px;
}
.btn--cv {
flex: 1 1 200px;
width: auto;
}
}
@media (max-width: 560px) {
.stack {
padding: 24px 16px 16px;
}
.page-head__inner {
padding: 18px 16px;
}
.about {
padding: 44px 18px 22px;
}
.about__tag {
top: 16px;
left: 16px;
}
.about__cols {
grid-template-columns: 1fr;
}
.about__side {
flex-direction: column;
align-items: stretch;
text-align: left;
}
.avatar {
margin: 0 auto;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
}
}/* =================================================================
Portfolio — About / Bio Block
Vanilla JS: CV download (simulated), social toasts,
rotating "currently" line, availability toggle.
================================================================= */
(function () {
"use strict";
/* ---------- Toast helper ---------- */
var toastEl = document.getElementById("toast");
var toastTimer = null;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("is-visible");
window.clearTimeout(toastTimer);
toastTimer = window.setTimeout(function () {
toastEl.classList.remove("is-visible");
}, 2400);
}
var reduceMotion =
window.matchMedia &&
window.matchMedia("(prefers-reduced-motion: reduce)").matches;
/* ---------- Download CV (illustrative) ---------- */
var cvBtn = document.getElementById("cv-btn");
if (cvBtn) {
var cvLabel = cvBtn.querySelector("span:last-child");
var cvDefault = cvLabel ? cvLabel.textContent : "";
cvBtn.addEventListener("click", function () {
if (cvBtn.classList.contains("is-busy")) return;
cvBtn.classList.add("is-busy");
if (cvLabel) cvLabel.textContent = "Preparing PDF…";
toast("Generating Maya’s CV…");
window.setTimeout(
function () {
cvBtn.classList.remove("is-busy");
if (cvLabel) cvLabel.textContent = cvDefault;
toast("CV ready — download is illustrative in this demo");
},
reduceMotion ? 200 : 1100
);
});
}
/* ---------- Social links (illustrative) ---------- */
var socials = document.querySelectorAll(".social");
Array.prototype.forEach.call(socials, function (link) {
link.addEventListener("click", function (e) {
e.preventDefault();
toast(link.getAttribute("data-toast") || "Link is illustrative");
});
});
/* ---------- Rotating "currently" line ---------- */
var currentlyText = document.getElementById("currently-text");
if (currentlyText) {
var lines = [
"Refining a fintech design system in Lisbon",
"Pairing with engineers on tokens & theming",
"Mentoring two junior product designers",
"Sketching a side project after hours ☕"
];
var lineIdx = 0;
function showNextLine() {
lineIdx = (lineIdx + 1) % lines.length;
if (reduceMotion) {
currentlyText.textContent = lines[lineIdx];
return;
}
currentlyText.style.transition = "opacity 0.28s ease";
currentlyText.style.opacity = "0";
window.setTimeout(function () {
currentlyText.textContent = lines[lineIdx];
currentlyText.style.opacity = "1";
}, 300);
}
// Click the card to cycle manually.
var currentlyCard = document.getElementById("currently");
if (currentlyCard) {
currentlyCard.addEventListener("click", showNextLine);
}
// Auto-rotate (paused for reduced motion).
if (!reduceMotion) {
window.setInterval(showNextLine, 4200);
}
}
/* ---------- Availability toggle (click the badge) ---------- */
var availability = document.getElementById("availability");
if (availability) {
availability.style.cursor = "pointer";
availability.setAttribute("role", "button");
availability.setAttribute("tabindex", "0");
availability.setAttribute("aria-pressed", "false");
var states = [
{ text: "Available · June ’26", cls: "badge--open", msg: "Open to new work in June 2026" },
{ text: "Booked through Q3", cls: "badge--busy", msg: "Currently booked — back in Q4" }
];
var stateIdx = 0;
function toggleAvailability() {
stateIdx = (stateIdx + 1) % states.length;
var s = states[stateIdx];
availability.classList.remove("badge--open", "badge--busy");
availability.classList.add(s.cls);
availability.textContent = s.text;
availability.setAttribute("aria-pressed", stateIdx === 1 ? "true" : "false");
toast(s.msg);
}
availability.addEventListener("click", toggleAvailability);
availability.addEventListener("keydown", function (e) {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
toggleAvailability();
}
});
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Portfolio — About / Bio Block</title>
<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@400;500;600;700;800&family=Inter+Tight:wght@600;700;800;900&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#about">Skip to content</a>
<header class="page-head">
<div class="page-head__inner">
<span class="page-head__mark" aria-hidden="true">◖</span>
<div>
<h1 class="page-head__title">About / Bio Block</h1>
<p class="page-head__sub">
A warm, drop-in about section — narrative, quick facts, values &
social links.
</p>
</div>
</div>
</header>
<main class="stack">
<section id="about" class="about" aria-labelledby="about-name">
<span class="about__tag">About</span>
<div class="about__grid">
<!-- ============ Left column: portrait + at-a-glance ============ -->
<aside class="about__side" aria-label="At a glance">
<div class="avatar" aria-hidden="true">
<div class="avatar__blob"></div>
<svg class="avatar__face" viewBox="0 0 200 200" role="img">
<defs>
<linearGradient id="skin" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#f6d2b8" />
<stop offset="1" stop-color="#d99b76" />
</linearGradient>
</defs>
<circle cx="100" cy="86" r="46" fill="url(#skin)" />
<path
d="M54 78c0-30 24-50 46-50s46 18 46 48c-6-14-20-22-34-22-6 8-18 12-30 10-10-2-20 2-28 14z"
fill="#2a2320"
/>
<circle cx="84" cy="86" r="4" fill="#2a2320" />
<circle cx="116" cy="86" r="4" fill="#2a2320" />
<path
d="M86 104q14 12 28 0"
fill="none"
stroke="#2a2320"
stroke-width="4"
stroke-linecap="round"
/>
<path d="M60 158c8-22 26-34 40-34s32 12 40 34z" fill="#5b54ff" />
</svg>
<span class="avatar__status" title="Open to new work">
<span class="avatar__dot"></span>
</span>
</div>
<p class="about__currently" id="currently">
<span class="about__pulse" aria-hidden="true"></span>
<span class="about__currently-label">Currently</span>
<span class="about__currently-text" id="currently-text"
>Refining a fintech design system in Lisbon</span
>
</p>
<ul class="facts" role="list">
<li class="fact">
<span class="fact__icon" aria-hidden="true">📍</span>
<span class="fact__key">Based in</span>
<span class="fact__val">Lisbon, Portugal</span>
</li>
<li class="fact">
<span class="fact__icon" aria-hidden="true">↳</span>
<span class="fact__key">Focus</span>
<span class="fact__val">Design systems & UX</span>
</li>
<li class="fact">
<span class="fact__icon" aria-hidden="true">★</span>
<span class="fact__key">Experience</span>
<span class="fact__val">9 yrs shipping product</span>
</li>
<li class="fact">
<span class="fact__icon" aria-hidden="true">●</span>
<span class="fact__key">Status</span>
<span class="badge badge--open" id="availability"
>Available · June ’26</span
>
</li>
</ul>
<button class="btn btn--solid btn--cv" type="button" id="cv-btn">
<span class="btn__icon" aria-hidden="true">⬇</span>
<span>Download CV (PDF)</span>
</button>
</aside>
<!-- ============ Right column: narrative + values + links ====== -->
<div class="about__main">
<p class="about__eyebrow">Hi, I’m</p>
<h2 class="about__name" id="about-name">Maya Okafor</h2>
<p class="about__role">
Senior Product Designer & design-systems lead.
</p>
<div class="about__bio">
<p>
I design calm, accountable interfaces for complex products — the
kind people reach for without thinking. For nine years I’ve moved
between the fuzzy front of a problem and the last pixel of a
shipped component, and I’m happiest where strategy meets craft.
</p>
<p>
Right now I lead the design system at a Lisbon fintech, where I
pair with engineers to turn scattered patterns into a library the
whole company trusts. Before that I rebuilt onboarding for two
million users at a mapping startup, cutting first-week drop-off by
a third. I write a little code, prototype a lot, and believe the
best motion is the kind you never notice.
</p>
</div>
<div class="about__cols">
<section class="mini" aria-labelledby="values-h">
<h3 class="mini__title" id="values-h">What I value</h3>
<ul class="mini__list" role="list">
<li>Clarity over cleverness</li>
<li>Accessibility as a default, not a fix</li>
<li>Shipping & learning in the open</li>
<li>Motion that means something</li>
</ul>
</section>
<section class="mini" aria-labelledby="now-h">
<h3 class="mini__title" id="now-h">Now</h3>
<ul class="mini__list mini__list--now" role="list">
<li>Building a tokens-first component kit</li>
<li>Mentoring two junior designers</li>
<li>Learning Portuguese (devagar!)</li>
<li>Reading <em>Designing Interfaces</em></li>
</ul>
</section>
</div>
<nav class="socials" aria-label="Social links">
<a class="social" href="#" data-toast="Opening email client…">
<span class="social__icon" aria-hidden="true">✉</span>
<span>maya@okafor.design</span>
</a>
<a class="social" href="#" data-toast="Opening LinkedIn…">
<span class="social__icon" aria-hidden="true">in</span>
<span>LinkedIn</span>
</a>
<a class="social" href="#" data-toast="Opening GitHub…">
<span class="social__icon" aria-hidden="true">⌥</span>
<span>GitHub</span>
</a>
<a class="social" href="#" data-toast="Opening Dribbble…">
<span class="social__icon" aria-hidden="true">◓</span>
<span>Dribbble</span>
</a>
</nav>
</div>
</div>
</section>
</main>
<footer class="page-foot">
<p>
Illustrative portfolio — fictional person and projects. Built with HTML,
CSS & vanilla JS.
</p>
</footer>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>About / Bio Block
A self-contained about/bio block for a single-person portfolio. The left column holds a morphing CSS-gradient avatar with an inline SVG face, a live “currently” line, a list of quick facts, and a full-width download-CV button. The right column carries the narrative bio, side-by-side “What I value” and “Now” lists, and a row of inline social links. The layout is two columns on desktop and collapses to a single stack on small screens.
Every interaction works with vanilla JS. The CV button simulates preparing a PDF
and then fires a toast (the download itself is illustrative). The “currently”
card auto-rotates through status lines and also advances on click. The
availability badge is a keyboard-operable toggle that flips between “available”
and “booked” states, and each social link confirms its action with a toast. All
animations respect prefers-reduced-motion.
Built on the neutral portfolio base (white/ink with one accent, “Inter”), so it drops straight into the rest of the portfolio system or stands on its own as an about section.
Illustrative portfolio — fictional person and projects.