🐐
This commit is contained in:
@@ -58,16 +58,19 @@ a:focus-visible, button:focus-visible, [data-tip]:focus-visible {
|
||||
|
||||
/* ── Nav ────────────────────────────────────────────────────── */
|
||||
#header {
|
||||
background: transparent;
|
||||
border-bottom: 3px solid transparent;
|
||||
background: var(--paper);
|
||||
border-bottom: var(--border);
|
||||
position: fixed; top: 0; left: 0; right: 0;
|
||||
height: var(--nav-h);
|
||||
z-index: 1000;
|
||||
transition: background 0.3s, border-color 0.3s;
|
||||
/* Hidden until scrolled; visibility keeps hidden links unfocusable */
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.3s, visibility 0.3s;
|
||||
}
|
||||
#header.scrolled {
|
||||
background: var(--paper);
|
||||
border-bottom-color: var(--ink);
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
nav {
|
||||
max-width: var(--max-w);
|
||||
@@ -77,13 +80,27 @@ nav {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
}
|
||||
.nav-logo {
|
||||
font-size: 1.2rem;
|
||||
width: 40px; height: 40px;
|
||||
border-radius: 50%;
|
||||
background: var(--yellow);
|
||||
border: var(--border);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.02em;
|
||||
color: var(--ink);
|
||||
}
|
||||
.nav-logo .bracket { color: var(--accent); }
|
||||
|
||||
.nav-links { display: flex; gap: 0.1rem; }
|
||||
.nav-links {
|
||||
display: flex; gap: 0.1rem;
|
||||
/* Sibling of #header, so it hides/shows on its own */
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.3s, visibility 0.3s;
|
||||
}
|
||||
#header.scrolled + .nav-links {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
@media (min-width: 769px) {
|
||||
.nav-links {
|
||||
@@ -123,6 +140,33 @@ nav {
|
||||
.burger.open span:nth-child(2) { opacity: 0; }
|
||||
.burger.open span:nth-child(3) { transform: rotate(-45deg) translate(5.5px, -5.5px); }
|
||||
|
||||
/* ── Scroll hint ────────────────────────────────────────────── */
|
||||
.scroll-hint {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
bottom: 1.5rem;
|
||||
transform: translateX(-50%);
|
||||
width: 44px; height: 44px;
|
||||
border-radius: 50%;
|
||||
background: var(--white);
|
||||
border: var(--border);
|
||||
box-shadow: var(--shadow-sm);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: var(--ink);
|
||||
font-size: 0.9rem;
|
||||
z-index: 900;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.6s, visibility 0.6s, background 150ms ease;
|
||||
}
|
||||
.scroll-hint.visible { opacity: 1; visibility: visible; }
|
||||
.scroll-hint:hover { background: var(--yellow); }
|
||||
.scroll-hint.visible i { animation: hintBob 1.6s ease-in-out infinite; }
|
||||
@keyframes hintBob {
|
||||
0%, 100% { transform: translateY(-2px); }
|
||||
50% { transform: translateY(3px); }
|
||||
}
|
||||
|
||||
/* ── Layout ─────────────────────────────────────────────────── */
|
||||
main { position: relative; z-index: 1; }
|
||||
|
||||
@@ -144,11 +188,13 @@ main { position: relative; z-index: 1; }
|
||||
|
||||
/* ── Hero ───────────────────────────────────────────────────── */
|
||||
.hero {
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
min-height: 100vh;
|
||||
display: flex; align-items: center;
|
||||
padding: calc(var(--nav-h) + 1rem) 2rem 10rem;
|
||||
padding: calc(var(--nav-h) + 1rem) 2rem 6rem;
|
||||
max-width: var(--max-w);
|
||||
margin: 0 auto 250px auto;
|
||||
margin: 0 auto;
|
||||
gap: 4rem;
|
||||
position: relative; z-index: 1;
|
||||
}
|
||||
@@ -162,22 +208,48 @@ main { position: relative; z-index: 1; }
|
||||
letter-spacing: -0.06em;
|
||||
margin-top: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.hero-name-accent {
|
||||
color: var(--accent);
|
||||
-webkit-text-stroke: 2px var(--ink);
|
||||
text-shadow: 4px 4px 0 var(--yellow);
|
||||
/* drop-shadow, not text-shadow: with per-glyph spans, Chromium mispaints
|
||||
stroke + text-shadow together; the filter shadows the rendered whole */
|
||||
filter: drop-shadow(4px 4px 0 var(--yellow));
|
||||
}
|
||||
/* Per-glyph spans for the anagram. letter-spacing is zeroed inside each
|
||||
box because the h1's tracking already applies once at every inline-block
|
||||
boundary — inheriting it too would double-tighten the name */
|
||||
.hero-name .ltr {
|
||||
display: inline-block;
|
||||
letter-spacing: normal;
|
||||
}
|
||||
.hero-chips {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
margin-left: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
.hero-role {
|
||||
display: flex;
|
||||
gap: 1ch;
|
||||
font-family: var(--mono);
|
||||
font-weight: 500;
|
||||
font-size: 0.9rem;
|
||||
color: var(--ink);
|
||||
margin-left: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
background: var(--yellow);
|
||||
border: var(--border-thin);
|
||||
border-radius: var(--radius-pill);
|
||||
padding: 0.35rem 0.9rem;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
/* Slot-machine reels: each slot is a one-line window onto a word column */
|
||||
.hero-role .slot { display: block; height: 1.6em; overflow: hidden; transition: width 0.2s ease; }
|
||||
.hero-role .reel { display: flex; flex-direction: column; }
|
||||
/* nowrap: a hyphenated word (FULL-STACK) would otherwise line-break while
|
||||
the slot is narrow mid-expansion, doubling a row's height and landing
|
||||
the whole strip one row off */
|
||||
.hero-role .reel span { display: block; text-align: center; white-space: nowrap; }
|
||||
/* Hero social links */
|
||||
.hero-links {
|
||||
display: flex;
|
||||
@@ -189,7 +261,7 @@ main { position: relative; z-index: 1; }
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.6rem 1.1rem;
|
||||
border-radius: var(--radius-pill);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 0.85rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.03em;
|
||||
@@ -239,8 +311,13 @@ main { position: relative; z-index: 1; }
|
||||
.avatar-placeholder img { width: 100%; height: 100%; object-fit: cover; }
|
||||
|
||||
/* Spin the image inside the ring, not the ring itself: the ring's hard
|
||||
offset shadow would visibly orbit if the shadowed element rotated */
|
||||
.avatar-ring.spinning .avatar-placeholder { animation: avatarSpin 0.8s var(--ease); }
|
||||
offset shadow would visibly orbit if the shadowed element rotated.
|
||||
The small icon circles have no offset shadow, so they spin whole. */
|
||||
.avatar-ring.spinning .avatar-placeholder,
|
||||
.skill-card-icon.spinning,
|
||||
.project-icon.spinning,
|
||||
.edu-icon.spinning,
|
||||
.nav-logo.spinning { animation: avatarSpin 0.8s var(--ease); }
|
||||
/* rotate is only keyed at 0%/100% so it sweeps through the scale midpoint
|
||||
without easing to a stop at 180° */
|
||||
@keyframes avatarSpin {
|
||||
@@ -249,7 +326,11 @@ main { position: relative; z-index: 1; }
|
||||
100% { rotate: 360deg; scale: 1; }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.avatar-ring.spinning .avatar-placeholder { animation: none; }
|
||||
.avatar-ring.spinning .avatar-placeholder,
|
||||
.skill-card-icon.spinning,
|
||||
.project-icon.spinning,
|
||||
.edu-icon.spinning,
|
||||
.nav-logo.spinning { animation: none; }
|
||||
}
|
||||
|
||||
|
||||
@@ -257,9 +338,56 @@ main { position: relative; z-index: 1; }
|
||||
font-family: var(--mono);
|
||||
font-size: 0.78rem;
|
||||
color: var(--ink);
|
||||
margin-left: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
background: var(--white);
|
||||
border: var(--border-thin);
|
||||
border-radius: var(--radius-pill);
|
||||
padding: 0.3rem 0.8rem;
|
||||
letter-spacing: 0.06em;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
/* own stacking context, so the ::before flag layer sits between this
|
||||
pill's background and its text instead of hiding behind the pill */
|
||||
isolation: isolate;
|
||||
}
|
||||
/* Non-binary flag sweep (click easter egg): four diagonal bands slide in,
|
||||
fill the pill, rest a beat, then slide out — z-index -1 paints above the
|
||||
pill's background but below its content */
|
||||
.hero-pronouns::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0; bottom: 0;
|
||||
left: -20%;
|
||||
width: 140%;
|
||||
z-index: -1;
|
||||
/* Wider than the pill, fading to transparent past diagonal stops on
|
||||
both ends, so the yellow and black bands keep slanted edges instead
|
||||
of ending at the panel's straight sides */
|
||||
background: linear-gradient(
|
||||
115deg,
|
||||
transparent 0 8%,
|
||||
#fcf434 8% 29%,
|
||||
#ffffff 29% 50%,
|
||||
#9c59d1 50% 71%,
|
||||
#2c2c2c 71% 92%,
|
||||
transparent 92% 100%
|
||||
);
|
||||
transform: translateX(-101%);
|
||||
}
|
||||
.hero-pronouns.waving::before { animation: nbSweep 1.35s linear; }
|
||||
/* Text yields while the flag holds the pill (ink would drown in the
|
||||
black band), then returns as the bands leave */
|
||||
.hero-pronouns.waving { animation: nbTextFade 1.35s ease; }
|
||||
/* No hold: heavy deceleration into the filled position, then a creeping
|
||||
re-acceleration out — a near-stop rather than a stop */
|
||||
@keyframes nbSweep {
|
||||
0% { transform: translateX(-101%); animation-timing-function: cubic-bezier(0.1, 0.7, 0.45, 1); }
|
||||
50% { transform: translateX(0); animation-timing-function: cubic-bezier(0.55, 0, 0.9, 0.3); }
|
||||
100% { transform: translateX(101%); }
|
||||
}
|
||||
@keyframes nbTextFade {
|
||||
0% { color: var(--ink); }
|
||||
20%, 80% { color: transparent; }
|
||||
100% { color: var(--ink); }
|
||||
}
|
||||
|
||||
/* ── About ──────────────────────────────────────────────────── */
|
||||
@@ -368,7 +496,7 @@ main { position: relative; z-index: 1; }
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
padding: 0.35rem 0.8rem;
|
||||
border-radius: var(--radius-pill);
|
||||
border-radius: var(--radius-md);
|
||||
border: var(--border-thin);
|
||||
background: var(--white);
|
||||
color: var(--ink);
|
||||
@@ -501,13 +629,77 @@ main { position: relative; z-index: 1; }
|
||||
/* ── Footer ─────────────────────────────────────────────────── */
|
||||
footer {
|
||||
position: relative; z-index: 1;
|
||||
background: var(--ink); color: var(--white);
|
||||
background: var(--ink);
|
||||
/* Deliberately dim: reads as boilerplate unless you actually look */
|
||||
color: rgba(255, 255, 255, 0.35);
|
||||
font-family: var(--mono);
|
||||
font-size: 0.78rem;
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
text-align: center; padding: 2.5rem 2rem;
|
||||
text-align: center; padding: 1.25rem 2rem;
|
||||
}
|
||||
.delete-site-wrap {
|
||||
position: relative; z-index: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 0 2rem 4rem;
|
||||
}
|
||||
.delete-site {
|
||||
font-weight: 700;
|
||||
font-size: 0.85rem;
|
||||
letter-spacing: 0.03em;
|
||||
color: #fff;
|
||||
background: #c92a2a;
|
||||
border: var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 0.6rem 1.1rem;
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: background 150ms ease;
|
||||
}
|
||||
.delete-site:hover { transform: translate(-2px, -2px); box-shadow: 5px 5px 0 var(--ink); }
|
||||
.delete-site:active { transform: translate(2px, 2px); box-shadow: 1px 1px 0 var(--ink); }
|
||||
|
||||
/* ── Delete-website dialog ──────────────────────────────────── */
|
||||
.delete-dialog {
|
||||
margin: auto;
|
||||
max-width: 26rem;
|
||||
background: var(--white);
|
||||
color: var(--ink);
|
||||
border: var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow);
|
||||
padding: 1.75rem;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
.delete-dialog::backdrop { background: rgba(23, 23, 23, 0.55); }
|
||||
.delete-dialog-title {
|
||||
font-size: 1.35rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.03em;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.delete-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0.75rem;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
.dialog-button {
|
||||
font-family: var(--font);
|
||||
font-weight: 700;
|
||||
font-size: 0.85rem;
|
||||
letter-spacing: 0.03em;
|
||||
padding: 0.6rem 1.1rem;
|
||||
border: var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--white);
|
||||
color: var(--ink);
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: background 150ms ease;
|
||||
}
|
||||
.dialog-button--danger { background: #c92a2a; color: #fff; }
|
||||
.dialog-button:hover { transform: translate(-2px, -2px); box-shadow: 5px 5px 0 var(--ink); }
|
||||
.dialog-button:active { transform: translate(2px, 2px); box-shadow: 1px 1px 0 var(--ink); }
|
||||
|
||||
/* ── Responsive ─────────────────────────────────────────────── */
|
||||
@media (max-width: 960px) {
|
||||
@@ -515,6 +707,10 @@ footer {
|
||||
.hero-visual { display: none; }
|
||||
.hero { flex-direction: column; text-align: center; padding-top: calc(var(--nav-h) + 2rem); }
|
||||
.hero-links { justify-content: center; }
|
||||
.hero-chips { justify-content: center; margin-left: 0; }
|
||||
/* Stacked hero: the base clamp bottoms out too small; the ceiling still
|
||||
keeps the longest anagram ("a joking deal") on one line at 360px */
|
||||
.hero-name { font-size: clamp(3.2rem, 11vw, 4.8rem); }
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
@@ -524,7 +720,7 @@ footer {
|
||||
--shadow-sm: 3px 3px 0 var(--ink);
|
||||
--radius-lg: 14px;
|
||||
}
|
||||
.hero-name-accent { -webkit-text-stroke-width: 1.5px; text-shadow: 3px 3px 0 var(--yellow); }
|
||||
.hero-name { -webkit-text-stroke-width: 1.5px; filter: drop-shadow(3px 3px 0 var(--yellow)); }
|
||||
.nav-links {
|
||||
position: fixed;
|
||||
top: var(--nav-h);
|
||||
@@ -535,7 +731,7 @@ footer {
|
||||
padding: 1.5rem;
|
||||
gap: 0.4rem;
|
||||
transform: translateY(-140%);
|
||||
transition: transform 0.4s var(--ease);
|
||||
transition: transform 0.4s var(--ease), opacity 0.3s, visibility 0.3s;
|
||||
border-bottom: var(--border);
|
||||
z-index: 999;
|
||||
}
|
||||
@@ -658,10 +854,62 @@ footer {
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
/* ── D20 roll on "Dungeons & Dragons" ───────────────────────── */
|
||||
.dnd-roll {
|
||||
position: relative;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
.die-scene {
|
||||
position: absolute;
|
||||
bottom: calc(100% + 10px);
|
||||
left: 50%;
|
||||
width: 110px;
|
||||
height: 110px;
|
||||
margin-left: -55px;
|
||||
perspective: 420px;
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
.die {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
.die-face {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
backface-visibility: hidden;
|
||||
color: var(--ink);
|
||||
font-weight: 700;
|
||||
}
|
||||
/* 6 and 9 get the disambiguating underline, like a real die */
|
||||
.die-face--mark {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: 0.09em;
|
||||
text-underline-offset: 0.12em;
|
||||
}
|
||||
/* Nat-20 burst bits: DOM sprinkles that fly out above the page content */
|
||||
.burst-bit {
|
||||
position: fixed;
|
||||
width: 5px;
|
||||
height: 14px;
|
||||
border-radius: var(--radius-pill);
|
||||
border: 2px solid var(--ink);
|
||||
z-index: 1200;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ── Reduced motion ─────────────────────────────────────────── */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
html { scroll-behavior: auto; }
|
||||
.hero-link, .project-link, .lang-tag,
|
||||
.project-tags span, .timeline-tags span,
|
||||
.nav-link::after, #header { transition: none; }
|
||||
.nav-link::after, #header, .hero-role .slot { transition: none; }
|
||||
.scroll-hint.visible i { animation: none; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user