1267 lines
32 KiB
CSS
1267 lines
32 KiB
CSS
/* ============================================================
|
||
WEB THEME — light frosted-glass website shell.
|
||
------------------------------------------------------------
|
||
Default theme (`<html data-theme="web">`). Light by default,
|
||
dark via `<html data-scheme="dark">`. The desktop theme
|
||
(`skeuo.css`) overrides the shared tokens while it is active.
|
||
============================================================ */
|
||
:root {
|
||
--web-accent: #6d5efc;
|
||
--web-accent-2: #2f8bff;
|
||
--web-page: #e9edfb;
|
||
--web-ink: #10182b;
|
||
--web-muted: #5b6785;
|
||
|
||
--glass-hi: rgba(255, 255, 255, 0.58);
|
||
--glass-lo: rgba(255, 255, 255, 0.22);
|
||
--glass-border: rgba(255, 255, 255, 0.72);
|
||
--glass-inset: rgba(255, 255, 255, 0.9);
|
||
/* Near-opaque frosted fill for the footer version pill. */
|
||
--pill-bg: rgba(255, 255, 255, 0.86);
|
||
--glass-shadow: 0 26px 55px -30px rgba(23, 32, 60, 0.55);
|
||
--hover: rgba(109, 94, 252, 0.12);
|
||
--accent-soft: rgba(109, 94, 252, 0.35);
|
||
--accent-line: rgba(109, 94, 252, 0.5);
|
||
|
||
--blob-a: rgba(109, 94, 252, 0.3);
|
||
--blob-b: rgba(47, 139, 255, 0.24);
|
||
--blob-c: rgba(236, 72, 153, 0.18);
|
||
--blob-d: rgba(45, 212, 191, 0.16);
|
||
|
||
/* Tokens shared with ServiceButton / ContributionHeatmap */
|
||
--fg: var(--web-ink);
|
||
--white: var(--web-ink);
|
||
--gray: var(--web-muted);
|
||
--bg: var(--web-page);
|
||
--bg-hard: rgba(16, 24, 43, 0.07);
|
||
--accent: var(--web-accent);
|
||
--accent2: var(--web-accent-2);
|
||
--accent3: var(--web-accent);
|
||
--green: #15a34a;
|
||
--aqua: #0d9488;
|
||
--blue: #2563eb;
|
||
--purple: #9333ea;
|
||
--red: #e11d48;
|
||
--darkpink: #db2777;
|
||
|
||
--web-font: "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
||
"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||
--font-custom-mono: ui-monospace, "JetBrains Mono", "SFMono-Regular", Menlo, Consolas,
|
||
monospace;
|
||
}
|
||
|
||
html[data-theme="web"][data-scheme="dark"] {
|
||
--web-accent: #8b7cff;
|
||
--web-accent-2: #4aa8ff;
|
||
--web-page: #070a15;
|
||
--web-ink: #e9edf9;
|
||
--web-muted: #97a3bd;
|
||
|
||
--glass-hi: rgba(255, 255, 255, 0.07);
|
||
--glass-lo: rgba(255, 255, 255, 0.045);
|
||
--glass-border: rgba(255, 255, 255, 0.12);
|
||
--glass-inset: rgba(255, 255, 255, 0.1);
|
||
--pill-bg: rgba(23, 28, 45, 0.92);
|
||
--glass-shadow: 0 26px 60px -28px rgba(0, 0, 0, 0.85);
|
||
--hover: rgba(139, 124, 255, 0.14);
|
||
--accent-soft: rgba(139, 124, 255, 0.4);
|
||
--accent-line: rgba(139, 124, 255, 0.55);
|
||
|
||
--blob-a: rgba(109, 94, 252, 0.09);
|
||
--blob-b: rgba(47, 139, 255, 0.07);
|
||
--blob-c: rgba(236, 72, 153, 0.05);
|
||
--blob-d: rgba(45, 212, 191, 0.04);
|
||
|
||
--btn-bg: rgba(255, 255, 255, 0.06);
|
||
--btn-bg-hi: rgba(255, 255, 255, 0.12);
|
||
--btn-border: rgba(255, 255, 255, 0.16);
|
||
--btn-shadow: 0 10px 24px -14px rgba(0, 0, 0, 0.9);
|
||
|
||
--bg-hard: rgba(255, 255, 255, 0.08);
|
||
--green: #34d399;
|
||
--aqua: #2dd4bf;
|
||
--blue: #60a5fa;
|
||
--purple: #c084fc;
|
||
--red: #fb7185;
|
||
--darkpink: #f472b6;
|
||
}
|
||
|
||
html[data-theme="web"] {
|
||
scroll-behavior: smooth;
|
||
|
||
/* Interactive surfaces (buttons, cards, chips). Kept a step more opaque and
|
||
more clearly outlined than the glass panels, so they don't melt into the
|
||
light background. The base rules read these with the old glass tokens as
|
||
fallbacks, which keeps the desktop theme untouched. */
|
||
--btn-bg: rgba(255, 255, 255, 0.62);
|
||
--btn-bg-hi: rgba(255, 255, 255, 0.92);
|
||
--btn-border: rgba(20, 28, 52, 0.14);
|
||
--btn-shadow: 0 8px 20px -12px rgba(23, 32, 60, 0.45);
|
||
}
|
||
|
||
html[data-theme="web"] body {
|
||
min-height: 100vh;
|
||
background: var(--web-page);
|
||
color: var(--fg);
|
||
font-family: var(--web-font);
|
||
font-size: 15px;
|
||
line-height: 1.6;
|
||
-webkit-font-smoothing: antialiased;
|
||
-moz-osx-font-smoothing: grayscale;
|
||
overflow-x: hidden;
|
||
}
|
||
|
||
/* Soft colour blobs behind the frosted panels. */
|
||
.web-bg {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 0;
|
||
pointer-events: none;
|
||
background:
|
||
radial-gradient(58rem 44rem at 6% -6%, var(--blob-a), transparent 68%),
|
||
radial-gradient(56rem 42rem at 100% 4%, var(--blob-b), transparent 66%),
|
||
radial-gradient(50rem 46rem at 82% 88%, var(--blob-c), transparent 68%),
|
||
radial-gradient(48rem 44rem at 14% 94%, var(--blob-d), transparent 70%),
|
||
var(--web-page);
|
||
}
|
||
|
||
/* ---------- shell layout ---------- */
|
||
.web-root {
|
||
position: relative;
|
||
z-index: 1;
|
||
display: grid;
|
||
grid-template-columns: 288px minmax(0, 1fr);
|
||
gap: 26px;
|
||
width: 100%;
|
||
max-width: 1320px;
|
||
margin: 0 auto;
|
||
padding: 22px clamp(16px, 3vw, 34px) 44px;
|
||
align-items: start;
|
||
}
|
||
|
||
.web-main {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 26px;
|
||
min-width: 0;
|
||
}
|
||
|
||
/* ---------- glass utility ---------- */
|
||
.glass {
|
||
position: relative;
|
||
background-color: var(--glass-lo);
|
||
background-image: linear-gradient(155deg, var(--glass-hi), var(--glass-lo));
|
||
border: 1px solid var(--glass-border);
|
||
border-radius: 22px;
|
||
box-shadow: var(--glass-shadow), inset 0 1px 0 var(--glass-inset);
|
||
-webkit-backdrop-filter: blur(28px) saturate(180%);
|
||
backdrop-filter: blur(28px) saturate(180%);
|
||
}
|
||
|
||
/* ============================================================
|
||
SIDEBAR
|
||
============================================================ */
|
||
.web-sidebar {
|
||
position: sticky;
|
||
top: 22px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 18px;
|
||
height: calc(100vh - 44px);
|
||
height: calc(100dvh - 44px);
|
||
overflow-y: auto;
|
||
overscroll-behavior: contain;
|
||
padding: 20px;
|
||
}
|
||
|
||
.web-brand {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
.web-brand .logo {
|
||
display: block;
|
||
width: 46px;
|
||
height: 46px;
|
||
border-radius: 14px;
|
||
object-fit: cover;
|
||
border: 1px solid var(--glass-border);
|
||
flex-shrink: 0;
|
||
}
|
||
.web-brand-text {
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-width: 0;
|
||
}
|
||
.web-brand-name {
|
||
font-size: 0.92rem;
|
||
font-weight: 750;
|
||
letter-spacing: -0.01em;
|
||
white-space: nowrap;
|
||
}
|
||
.web-brand-tag {
|
||
font-size: 0.76rem;
|
||
color: var(--web-muted);
|
||
}
|
||
|
||
.web-nav {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
}
|
||
.web-nav a {
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: 9px 12px;
|
||
border-radius: 12px;
|
||
color: var(--web-muted);
|
||
font-size: 0.86rem;
|
||
font-weight: 550;
|
||
text-decoration: none;
|
||
transition: background-color 0.18s ease, color 0.18s ease;
|
||
}
|
||
.web-nav a::before {
|
||
content: "";
|
||
width: 6px;
|
||
height: 6px;
|
||
border-radius: 50%;
|
||
background: currentColor;
|
||
opacity: 0.45;
|
||
transition: opacity 0.18s ease, background-color 0.18s ease;
|
||
}
|
||
.web-nav a:hover {
|
||
background: var(--hover);
|
||
color: var(--fg);
|
||
}
|
||
.web-nav a:hover::before {
|
||
opacity: 1;
|
||
background: var(--accent);
|
||
}
|
||
|
||
.web-controls {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
margin-top: auto;
|
||
}
|
||
.web-ctl {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: 9px 12px;
|
||
border-radius: 12px;
|
||
border: 1px solid var(--btn-border, var(--glass-border));
|
||
background: var(--btn-bg, var(--glass-lo));
|
||
box-shadow: var(--btn-shadow, none);
|
||
color: var(--fg);
|
||
font: inherit;
|
||
font-size: 0.82rem;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: border-color 0.18s ease, box-shadow 0.18s ease, background-color 0.18s ease;
|
||
}
|
||
.web-ctl:hover {
|
||
border-color: var(--accent-line);
|
||
background: var(--btn-bg-hi, var(--glass-hi));
|
||
box-shadow: 0 0 0 1px var(--accent-soft);
|
||
}
|
||
.web-ctl-icon {
|
||
font-size: 0.95rem;
|
||
line-height: 1;
|
||
}
|
||
.web-ctl-label {
|
||
min-width: 0;
|
||
}
|
||
|
||
.web-switch {
|
||
position: relative;
|
||
margin-left: auto;
|
||
width: 36px;
|
||
height: 20px;
|
||
border-radius: 999px;
|
||
background: var(--bg-hard);
|
||
border: 1px solid var(--btn-border, var(--glass-border));
|
||
transition: background-color 0.2s ease, border-color 0.2s ease;
|
||
flex-shrink: 0;
|
||
}
|
||
.web-switch-knob {
|
||
position: absolute;
|
||
top: 2px;
|
||
left: 2px;
|
||
width: 14px;
|
||
height: 14px;
|
||
border-radius: 50%;
|
||
background: var(--fg);
|
||
transition: transform 0.2s ease, background-color 0.2s ease;
|
||
}
|
||
.web-switch.on {
|
||
background: var(--accent);
|
||
border-color: var(--accent);
|
||
}
|
||
.web-switch.on .web-switch-knob {
|
||
transform: translateX(16px);
|
||
background: #ffffff;
|
||
}
|
||
|
||
.web-side-foot {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
padding-top: 14px;
|
||
border-top: 1px solid var(--glass-border);
|
||
font-size: 0.74rem;
|
||
color: var(--web-muted);
|
||
}
|
||
.web-side-foot a {
|
||
color: var(--accent);
|
||
text-decoration: none;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.web-side-foot a:hover {
|
||
text-decoration: underline;
|
||
}
|
||
.web-clock {
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
/* ============================================================
|
||
HERO
|
||
============================================================ */
|
||
.web-hero {
|
||
/* Same horizontal inset as the sections/tiles so every panel's content lines
|
||
up on the same left edge; keep a roomier vertical padding. */
|
||
padding: clamp(24px, 4vw, 42px) clamp(20px, 3vw, 30px);
|
||
}
|
||
.web-eyebrow {
|
||
display: inline-block;
|
||
margin-bottom: 10px;
|
||
font-size: 0.68rem;
|
||
font-weight: 700;
|
||
letter-spacing: 0.16em;
|
||
text-transform: uppercase;
|
||
color: var(--accent);
|
||
}
|
||
.web-title {
|
||
font-size: clamp(1.9rem, 4vw, 2.8rem);
|
||
font-weight: 800;
|
||
line-height: 1.08;
|
||
letter-spacing: -0.03em;
|
||
}
|
||
.web-lead {
|
||
margin-top: 14px;
|
||
max-width: 62ch;
|
||
font-size: 1rem;
|
||
color: var(--web-muted);
|
||
}
|
||
/* ============================================================
|
||
QUICK TILES
|
||
============================================================ */
|
||
.web-quick {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 20px;
|
||
/* Equal-height tiles (symmetric with the taller Gym tile); the shorter
|
||
"Right now" text centres itself vertically instead of pooling at the top. */
|
||
align-items: stretch;
|
||
}
|
||
.web-tile {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 14px;
|
||
/* Match .web-section (and .web-hero) so the tile text starts on the same
|
||
left edge as every other panel. */
|
||
padding: clamp(20px, 3vw, 30px);
|
||
}
|
||
.web-tile-title {
|
||
font-size: 0.7rem;
|
||
font-weight: 700;
|
||
letter-spacing: 0.14em;
|
||
text-transform: uppercase;
|
||
color: var(--accent);
|
||
}
|
||
.web-tile-text {
|
||
font-size: 1.12rem;
|
||
line-height: 1.7;
|
||
color: var(--web-muted);
|
||
}
|
||
.web-tile-note {
|
||
margin-top: auto;
|
||
font-size: 0.74rem;
|
||
color: var(--web-muted);
|
||
opacity: 0.85;
|
||
}
|
||
|
||
/* Compact socials row inside the hero. */
|
||
.web-social-strip {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
margin-top: 26px;
|
||
padding-top: 20px;
|
||
border-top: 1px solid var(--glass-border);
|
||
}
|
||
.web-social-strip .social-buttons {
|
||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||
}
|
||
|
||
/* ============================================================
|
||
SECTIONS
|
||
============================================================ */
|
||
.web-section {
|
||
padding: clamp(20px, 3vw, 30px);
|
||
}
|
||
.web-section-head {
|
||
margin-bottom: 20px;
|
||
}
|
||
.web-section-title {
|
||
font-size: clamp(1.3rem, 2.4vw, 1.6rem);
|
||
font-weight: 750;
|
||
letter-spacing: -0.02em;
|
||
}
|
||
.web-split {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
|
||
gap: 26px;
|
||
align-items: stretch;
|
||
}
|
||
.web-feature {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.web-shot {
|
||
/* One fixed frame for every feature shot, regardless of the source image's
|
||
aspect ratio, so the two feature panels line up. */
|
||
aspect-ratio: 16 / 10;
|
||
border-radius: 16px;
|
||
overflow: hidden;
|
||
border: 1px solid var(--glass-border);
|
||
margin-bottom: 14px;
|
||
box-shadow: 0 18px 36px -22px rgba(23, 32, 60, 0.5);
|
||
}
|
||
.web-shot img {
|
||
display: block;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
object-position: center top;
|
||
}
|
||
.web-feature-text {
|
||
flex: 1;
|
||
font-size: 0.9rem;
|
||
color: var(--web-muted);
|
||
}
|
||
|
||
/* Inline text links (buttons/cards/pills have their own colour). */
|
||
.web-lead a,
|
||
.web-tile-text a,
|
||
.web-feature-text a,
|
||
.mail-contact a,
|
||
.contribution-entry a {
|
||
color: var(--accent);
|
||
text-decoration: none;
|
||
}
|
||
.web-lead a:hover,
|
||
.web-tile-text a:hover,
|
||
.web-feature-text a:hover,
|
||
.mail-contact a:hover,
|
||
.contribution-entry a:hover {
|
||
color: var(--accent2);
|
||
text-decoration: underline;
|
||
}
|
||
|
||
/* ============================================================
|
||
SERVICES + FRIENDS
|
||
============================================================ */
|
||
.services-buttons {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||
gap: 12px;
|
||
}
|
||
.services-buttons.services-main {
|
||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||
}
|
||
/* Friends: always exactly 2 columns at every width, so the grid stays a clean
|
||
rectangle/square instead of ending in an orphan "L" row. */
|
||
.services-buttons.friends-grid {
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
}
|
||
|
||
.service-button {
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 14px 16px;
|
||
border-radius: 16px;
|
||
border: 1px solid var(--btn-border, var(--glass-border));
|
||
background: var(--btn-bg, var(--glass-lo));
|
||
box-shadow: var(--btn-shadow, none);
|
||
color: var(--fg);
|
||
text-decoration: none;
|
||
min-width: 0;
|
||
transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease,
|
||
background-color 0.2s ease;
|
||
}
|
||
.service-button:hover {
|
||
transform: translateY(-3px);
|
||
border-color: var(--accent-line);
|
||
background: var(--btn-bg-hi, var(--glass-hi));
|
||
box-shadow: 0 18px 34px -18px rgba(23, 32, 60, 0.5), 0 0 0 1px var(--accent-soft);
|
||
}
|
||
.service-button.disabled {
|
||
opacity: 0.85;
|
||
}
|
||
.service-button.disabled:hover {
|
||
transform: none;
|
||
border-color: var(--btn-border, var(--glass-border));
|
||
background: var(--btn-bg, var(--glass-lo));
|
||
box-shadow: var(--btn-shadow, none);
|
||
}
|
||
.service-button.disabled:hover .service-icon {
|
||
transform: none;
|
||
}
|
||
|
||
.service-icon-wrap {
|
||
position: relative;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
}
|
||
.service-status-dot {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: 50%;
|
||
background: radial-gradient(circle, var(--status-glow) 0%, rgba(0, 0, 0, 0) 70%);
|
||
transform: translate(-50%, -50%);
|
||
filter: blur(1px);
|
||
pointer-events: none;
|
||
}
|
||
.service-icon {
|
||
position: relative;
|
||
z-index: 1;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 30px;
|
||
height: 30px;
|
||
font-size: 1.3rem;
|
||
filter: drop-shadow(0 0 6px var(--status-glow));
|
||
transition: transform 0.15s ease;
|
||
}
|
||
.service-button:hover .service-icon {
|
||
transform: scale(1.08);
|
||
}
|
||
.service-text {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 1px;
|
||
min-width: 0;
|
||
}
|
||
.service-name {
|
||
font-size: 0.9rem;
|
||
font-weight: 700;
|
||
white-space: nowrap;
|
||
}
|
||
.service-desc {
|
||
font-size: 0.75rem;
|
||
color: var(--web-muted);
|
||
}
|
||
.service-button:hover .service-desc {
|
||
color: var(--fg);
|
||
}
|
||
|
||
/* Friend cards: a big emoji beside the name + website, sized to roughly the
|
||
two text lines combined so the (wide) card doesn't look empty. */
|
||
.service-button.friend {
|
||
flex-direction: row;
|
||
align-items: center;
|
||
gap: 16px;
|
||
padding: 16px 20px;
|
||
}
|
||
/* Let the corner status dot anchor to the card, not to the emoji. */
|
||
.service-button.friend .service-icon-wrap {
|
||
position: static;
|
||
}
|
||
.service-button.friend .service-text {
|
||
align-items: flex-start;
|
||
width: auto;
|
||
min-width: 0;
|
||
}
|
||
.service-button.friend .service-name {
|
||
white-space: normal;
|
||
}
|
||
.service-button.friend .service-icon {
|
||
width: auto;
|
||
height: auto;
|
||
font-size: 2.5rem;
|
||
line-height: 1;
|
||
filter: none;
|
||
}
|
||
/* Long domains must wrap on narrow cards instead of overflowing. */
|
||
.service-button.friend .service-desc {
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.service-button.friend .service-status-dot {
|
||
top: 12px;
|
||
right: 12px;
|
||
left: auto;
|
||
width: 11px;
|
||
height: 11px;
|
||
border-radius: 50%;
|
||
background: var(--status-color);
|
||
box-shadow: 0 0 8px var(--status-color);
|
||
transform: none;
|
||
filter: none;
|
||
}
|
||
|
||
/* ============================================================
|
||
PROJECTS / HARDWARE
|
||
============================================================ */
|
||
.project-grid {
|
||
/* Masonry-ish gallery: multi-column packs the cards tightly, so a short card
|
||
never leaves a half-empty slot under a tall neighbour. */
|
||
columns: 3 250px;
|
||
column-gap: 14px;
|
||
}
|
||
.project-card {
|
||
display: block;
|
||
break-inside: avoid;
|
||
-webkit-column-break-inside: avoid;
|
||
margin-bottom: 14px;
|
||
padding: 12px;
|
||
border-radius: 16px;
|
||
border: 1px solid var(--btn-border, var(--glass-border));
|
||
background: var(--btn-bg, var(--glass-lo));
|
||
box-shadow: var(--btn-shadow, none);
|
||
color: inherit;
|
||
text-decoration: none;
|
||
transition: transform 0.22s ease, border-color 0.22s ease, box-shadow 0.22s ease,
|
||
background-color 0.22s ease;
|
||
}
|
||
.project-card:hover {
|
||
transform: translateY(-4px);
|
||
border-color: var(--accent-line);
|
||
background: var(--btn-bg-hi, var(--glass-hi));
|
||
box-shadow: 0 20px 38px -20px rgba(23, 32, 60, 0.55);
|
||
}
|
||
.project-image {
|
||
height: 150px;
|
||
width: 100%;
|
||
margin-bottom: 12px;
|
||
border-radius: 12px;
|
||
border: 1px solid var(--glass-border);
|
||
background-size: cover;
|
||
background-position: center;
|
||
background-repeat: no-repeat;
|
||
}
|
||
.project-title {
|
||
font-size: 0.95rem;
|
||
font-weight: 700;
|
||
}
|
||
.project-card:hover .project-title {
|
||
color: var(--accent);
|
||
}
|
||
.project-desc {
|
||
margin-top: 4px;
|
||
font-size: 0.8rem;
|
||
color: var(--web-muted);
|
||
}
|
||
|
||
/* ============================================================
|
||
SOCIALS
|
||
============================================================ */
|
||
.social-buttons {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
||
gap: 8px;
|
||
}
|
||
.social-button {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 9px;
|
||
padding: 9px 12px;
|
||
border-radius: 12px;
|
||
border: 1px solid var(--btn-border, transparent);
|
||
background: var(--btn-bg, var(--glass-lo));
|
||
box-shadow: var(--btn-shadow, none);
|
||
color: var(--fg);
|
||
font-size: 0.82rem;
|
||
font-weight: 550;
|
||
text-decoration: none;
|
||
transition: background-color 0.18s ease, border-color 0.18s ease, transform 0.18s ease;
|
||
}
|
||
.social-button:hover {
|
||
transform: translateY(-2px);
|
||
background: var(--btn-bg-hi, var(--glass-hi));
|
||
border-color: var(--btn-border, var(--glass-border));
|
||
color: var(--accent);
|
||
}
|
||
.social-icon {
|
||
width: 16px;
|
||
height: 16px;
|
||
opacity: 0.75;
|
||
}
|
||
.social-button:hover .social-icon {
|
||
opacity: 1;
|
||
}
|
||
html[data-theme="web"][data-scheme="dark"] .social-icon {
|
||
filter: invert(1) brightness(1.5);
|
||
opacity: 0.85;
|
||
}
|
||
|
||
.mail-contact {
|
||
margin-top: 14px;
|
||
font-size: 0.8rem;
|
||
color: var(--web-muted);
|
||
text-align: center;
|
||
}
|
||
|
||
/* ============================================================
|
||
GYM
|
||
============================================================ */
|
||
.gym-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||
gap: 10px;
|
||
}
|
||
.gym-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 12px 8px;
|
||
border-radius: 14px;
|
||
border: 1px solid var(--btn-border, var(--glass-border));
|
||
background: var(--btn-bg, var(--glass-lo));
|
||
box-shadow: var(--btn-shadow, none);
|
||
text-align: center;
|
||
transition: border-color 0.18s ease, transform 0.18s ease;
|
||
}
|
||
.gym-item:hover {
|
||
border-color: var(--accent-line);
|
||
transform: translateY(-2px);
|
||
}
|
||
/* Custom CSS pictograms (no SVGs), drawn as layered solid gradients in
|
||
currentColor so they follow the theme: squat = a squat rack, bench = a
|
||
bench-press station, deadlift = a loaded barbell. Each background layer is
|
||
one rectangle (size = w×h, position = top-left). */
|
||
.gym-icon {
|
||
display: block;
|
||
width: 52px;
|
||
height: 34px;
|
||
background-repeat: no-repeat;
|
||
}
|
||
/* Squat rack: two tall uprights on feet with a loaded barbell racked at
|
||
mid-height (its plates make it read as a barbell, not a table). */
|
||
.gym-icon--squat {
|
||
background-image:
|
||
linear-gradient(currentColor, currentColor),
|
||
linear-gradient(currentColor, currentColor),
|
||
linear-gradient(currentColor, currentColor),
|
||
linear-gradient(currentColor, currentColor),
|
||
linear-gradient(currentColor, currentColor),
|
||
linear-gradient(currentColor, currentColor),
|
||
linear-gradient(currentColor, currentColor);
|
||
background-size: 4px 28px, 4px 28px, 12px 3px, 12px 3px, 44px 3px, 6px 18px, 6px 18px;
|
||
background-position: 14px 3px, 34px 3px, 9px 29px, 31px 29px, 4px 10px, 0 3px,
|
||
46px 3px;
|
||
}
|
||
/* Bench press: a barbell above a padded bench on two legs. */
|
||
.gym-icon--bench {
|
||
background-image:
|
||
linear-gradient(currentColor, currentColor),
|
||
linear-gradient(currentColor, currentColor),
|
||
linear-gradient(currentColor, currentColor),
|
||
linear-gradient(currentColor, currentColor),
|
||
linear-gradient(currentColor, currentColor),
|
||
linear-gradient(currentColor, currentColor);
|
||
background-size: 40px 3px, 5px 12px, 5px 12px, 34px 5px, 4px 11px, 4px 11px;
|
||
background-position: 6px 4px, 2px 0, 45px 0, 9px 16px, 13px 21px, 35px 21px;
|
||
}
|
||
/* Deadlift: a long bar with a stack of plates at each end. */
|
||
.gym-icon--deadlift {
|
||
background-image:
|
||
linear-gradient(currentColor, currentColor),
|
||
linear-gradient(currentColor, currentColor),
|
||
linear-gradient(currentColor, currentColor),
|
||
linear-gradient(currentColor, currentColor),
|
||
linear-gradient(currentColor, currentColor);
|
||
background-size: 42px 3px, 4px 14px, 5px 22px, 5px 22px, 4px 14px;
|
||
background-position: 5px 15px, 1px 10px, 6px 6px, 41px 6px, 47px 10px;
|
||
}
|
||
.gym-label {
|
||
font-size: 0.66rem;
|
||
font-weight: 700;
|
||
letter-spacing: 0.08em;
|
||
text-transform: uppercase;
|
||
color: var(--web-muted);
|
||
}
|
||
.gym-weight {
|
||
font-size: 1.25rem;
|
||
font-weight: 800;
|
||
}
|
||
.gym-date {
|
||
font-size: 0.62rem;
|
||
color: var(--web-muted);
|
||
}
|
||
|
||
/* ============================================================
|
||
CONTRIBUTION HEATMAP
|
||
============================================================ */
|
||
.contribution-heatmap {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
min-width: 0;
|
||
}
|
||
.contribution-command {
|
||
min-width: 0;
|
||
font-family: var(--font-custom-mono);
|
||
font-size: 0.78rem;
|
||
color: var(--web-muted);
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.contribution-total-title {
|
||
margin: 4px 0 8px;
|
||
text-align: center;
|
||
font-size: 1.5rem;
|
||
font-weight: 750;
|
||
}
|
||
.heatmap-container {
|
||
display: flex;
|
||
gap: 3px;
|
||
padding: 6px;
|
||
border: 1px solid var(--btn-border, var(--glass-border));
|
||
border-radius: 14px;
|
||
background: var(--btn-bg, var(--glass-lo));
|
||
overflow-x: auto;
|
||
scrollbar-width: none;
|
||
}
|
||
.heatmap-week {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 3px;
|
||
flex: 1;
|
||
}
|
||
.heatmap-cell {
|
||
width: 100%;
|
||
aspect-ratio: 1;
|
||
border-radius: 4px;
|
||
background-color: var(--bg-hard);
|
||
transition: transform 0.12s ease;
|
||
}
|
||
.heatmap-cell:hover {
|
||
transform: scale(1.25);
|
||
box-shadow: 0 0 8px var(--accent-soft);
|
||
}
|
||
.heatmap-cell[data-level="1"] {
|
||
background-color: rgba(109, 94, 252, 0.22);
|
||
}
|
||
.heatmap-cell[data-level="2"] {
|
||
background-color: rgba(109, 94, 252, 0.42);
|
||
}
|
||
.heatmap-cell[data-level="3"] {
|
||
background-color: rgba(109, 94, 252, 0.68);
|
||
}
|
||
.heatmap-cell[data-level="4"] {
|
||
background-color: #6d5efc;
|
||
}
|
||
html[data-theme="web"][data-scheme="dark"] .heatmap-cell[data-level="1"] {
|
||
background-color: rgba(139, 124, 255, 0.3);
|
||
}
|
||
html[data-theme="web"][data-scheme="dark"] .heatmap-cell[data-level="2"] {
|
||
background-color: rgba(139, 124, 255, 0.5);
|
||
}
|
||
html[data-theme="web"][data-scheme="dark"] .heatmap-cell[data-level="3"] {
|
||
background-color: rgba(139, 124, 255, 0.75);
|
||
}
|
||
html[data-theme="web"][data-scheme="dark"] .heatmap-cell[data-level="4"] {
|
||
background-color: #8b7cff;
|
||
}
|
||
.heatmap-status-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 10px;
|
||
min-width: 0;
|
||
margin-top: 2px;
|
||
font-size: 0.75rem;
|
||
color: var(--web-muted);
|
||
}
|
||
.heatmap-status-text {
|
||
min-width: 0;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.heatmap-legend {
|
||
display: flex;
|
||
flex-shrink: 0;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
.heatmap-legend-cell {
|
||
width: 11px;
|
||
height: 11px;
|
||
border-radius: 3px;
|
||
background-color: var(--bg-hard);
|
||
flex-shrink: 0;
|
||
}
|
||
.heatmap-legend-cell[data-level="1"] {
|
||
background-color: rgba(109, 94, 252, 0.22);
|
||
}
|
||
.heatmap-legend-cell[data-level="2"] {
|
||
background-color: rgba(109, 94, 252, 0.42);
|
||
}
|
||
.heatmap-legend-cell[data-level="3"] {
|
||
background-color: rgba(109, 94, 252, 0.68);
|
||
}
|
||
.heatmap-legend-cell[data-level="4"] {
|
||
background-color: #6d5efc;
|
||
}
|
||
html[data-theme="web"][data-scheme="dark"] .heatmap-legend-cell[data-level="1"] {
|
||
background-color: rgba(139, 124, 255, 0.3);
|
||
}
|
||
html[data-theme="web"][data-scheme="dark"] .heatmap-legend-cell[data-level="2"] {
|
||
background-color: rgba(139, 124, 255, 0.5);
|
||
}
|
||
html[data-theme="web"][data-scheme="dark"] .heatmap-legend-cell[data-level="3"] {
|
||
background-color: rgba(139, 124, 255, 0.75);
|
||
}
|
||
html[data-theme="web"][data-scheme="dark"] .heatmap-legend-cell[data-level="4"] {
|
||
background-color: #8b7cff;
|
||
}
|
||
|
||
.contribution-log {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
min-width: 0;
|
||
margin-top: 4px;
|
||
}
|
||
.contribution-log-title {
|
||
font-size: 0.68rem;
|
||
font-weight: 700;
|
||
letter-spacing: 0.14em;
|
||
text-transform: uppercase;
|
||
color: var(--web-muted);
|
||
}
|
||
.contribution-entry {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 3px;
|
||
min-width: 0;
|
||
padding: 10px 12px;
|
||
border-radius: 12px;
|
||
border: 1px solid var(--btn-border, var(--glass-border));
|
||
background: var(--btn-bg, var(--glass-lo));
|
||
box-shadow: var(--btn-shadow, none);
|
||
font-size: 0.78rem;
|
||
}
|
||
.contribution-entry-header {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
gap: 10px;
|
||
min-width: 0;
|
||
color: var(--web-muted);
|
||
}
|
||
.contribution-repo {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.contribution-date {
|
||
flex-shrink: 0;
|
||
max-width: 48%;
|
||
color: var(--green);
|
||
font-size: 0.67rem;
|
||
line-height: 1.25;
|
||
text-align: right;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.contribution-message {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* ============================================================
|
||
FOOTER
|
||
============================================================ */
|
||
.web-footer {
|
||
position: relative;
|
||
margin-top: 6px;
|
||
padding: 16px 20px;
|
||
border-radius: 18px;
|
||
text-align: center;
|
||
font-size: 0.8rem;
|
||
color: var(--web-muted);
|
||
}
|
||
.web-footer-label {
|
||
position: absolute;
|
||
top: -10px;
|
||
right: 18px;
|
||
padding: 3px 9px;
|
||
border-radius: 999px;
|
||
background-color: var(--pill-bg);
|
||
/* Glass sheen + inset highlight so the pill reads as a small glass surface
|
||
(matches the panels in both light and dark) instead of a flat block. */
|
||
background-image: linear-gradient(155deg, var(--glass-hi), var(--glass-lo));
|
||
border: 1px solid var(--glass-border);
|
||
-webkit-backdrop-filter: blur(16px) saturate(180%);
|
||
backdrop-filter: blur(16px) saturate(180%);
|
||
box-shadow: 0 8px 18px -14px rgba(0, 0, 0, 0.6), inset 0 1px 0 var(--glass-inset);
|
||
font-size: 0.66rem;
|
||
font-weight: 700;
|
||
letter-spacing: 0.08em;
|
||
color: var(--accent);
|
||
}
|
||
|
||
/* ============================================================
|
||
SHARED BITS
|
||
============================================================ */
|
||
.joker-flash-overlay {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 99999;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
pointer-events: none;
|
||
background: rgba(0, 0, 0, 0.1);
|
||
}
|
||
.joker-img {
|
||
max-width: 90vw;
|
||
max-height: 90vh;
|
||
animation: jokerFlash 1.2s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
|
||
filter: drop-shadow(0 0 50px rgba(255, 0, 67, 0.8));
|
||
}
|
||
@keyframes jokerFlash {
|
||
0% {
|
||
opacity: 0;
|
||
transform: scale(0.1);
|
||
}
|
||
15% {
|
||
opacity: 1;
|
||
transform: scale(2.5) rotate(-5deg);
|
||
}
|
||
60% {
|
||
opacity: 1;
|
||
transform: scale(1.5) rotate(5deg);
|
||
}
|
||
100% {
|
||
opacity: 0;
|
||
transform: scale(0) rotate(-20deg);
|
||
}
|
||
}
|
||
|
||
/* Scrollbar + selection */
|
||
::-webkit-scrollbar {
|
||
width: 10px;
|
||
height: 10px;
|
||
}
|
||
::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
::-webkit-scrollbar-thumb {
|
||
background: var(--accent-soft);
|
||
border: 3px solid transparent;
|
||
background-clip: content-box;
|
||
border-radius: 999px;
|
||
}
|
||
::-webkit-scrollbar-thumb:hover {
|
||
background: var(--accent);
|
||
background-clip: content-box;
|
||
}
|
||
::selection {
|
||
background: var(--accent-soft);
|
||
color: var(--fg);
|
||
}
|
||
|
||
/* ============================================================
|
||
RESPONSIVE — sidebar collapses to a header on phones/tablets
|
||
============================================================ */
|
||
@media (max-width: 960px) {
|
||
.web-root {
|
||
grid-template-columns: minmax(0, 1fr);
|
||
gap: 18px;
|
||
padding: 18px 14px 56px;
|
||
}
|
||
.web-sidebar {
|
||
position: static;
|
||
gap: 14px;
|
||
height: auto;
|
||
overflow: visible;
|
||
padding: 16px;
|
||
border-radius: 20px;
|
||
}
|
||
.web-nav {
|
||
flex-direction: row;
|
||
gap: 6px;
|
||
overflow-x: auto;
|
||
scrollbar-width: none;
|
||
padding-bottom: 2px;
|
||
}
|
||
.web-nav a {
|
||
white-space: nowrap;
|
||
padding: 8px 12px;
|
||
}
|
||
.web-controls {
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
margin-top: 0;
|
||
}
|
||
.web-ctl {
|
||
flex: 1 1 140px;
|
||
}
|
||
/* No desktop toggle on phones: the desktop shell is a desktop-only shell. */
|
||
.web-ctl-desktop {
|
||
display: none;
|
||
}
|
||
.services-buttons,
|
||
.services-buttons.services-main {
|
||
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||
}
|
||
}
|
||
|
||
@media (hover: none) and (pointer: coarse) {
|
||
.web-ctl-desktop {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 820px) {
|
||
.web-quick {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 640px) {
|
||
.web-title {
|
||
font-size: 1.7rem;
|
||
}
|
||
/* Phone hero: center the heading/lead (the block, not just the text) and the
|
||
social-button labels. The desktop hero stays left-aligned. */
|
||
.web-hero {
|
||
text-align: center;
|
||
}
|
||
.web-lead {
|
||
margin-inline: auto;
|
||
}
|
||
.social-button {
|
||
justify-content: center;
|
||
}
|
||
/* Center the note under the gym PR tiles. */
|
||
.web-tile-note {
|
||
text-align: center;
|
||
}
|
||
/* Finish the job: center every panel title (with its eyebrow) and the
|
||
quick-tile titles too. */
|
||
.web-section-head,
|
||
.web-tile-title {
|
||
text-align: center;
|
||
}
|
||
/* Section nav: fit every link on screen instead of a sideways-scrolling row,
|
||
so the collapsed top panel never scrolls horizontally — a 3-up button
|
||
grid (2-up on very narrow phones, see the 374px block below) styled like
|
||
the Light/Dark toggle (.web-ctl): same surface, radius, shadow and type. */
|
||
.web-nav {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||
gap: 8px;
|
||
overflow: visible;
|
||
padding-bottom: 0;
|
||
}
|
||
.web-nav a {
|
||
justify-content: center;
|
||
border: 1px solid var(--btn-border, var(--glass-border));
|
||
background: var(--btn-bg, var(--glass-lo));
|
||
box-shadow: var(--btn-shadow, none);
|
||
color: var(--fg);
|
||
font-size: 0.82rem;
|
||
font-weight: 600;
|
||
}
|
||
/* Projects/hardware gallery: 2-up on phones instead of one full-width card
|
||
per row, with a shorter image + tighter card so a row is compact. */
|
||
.project-grid {
|
||
columns: 2;
|
||
column-gap: 10px;
|
||
}
|
||
.project-card {
|
||
margin-bottom: 10px;
|
||
padding: 10px;
|
||
}
|
||
.project-image {
|
||
height: 90px;
|
||
margin-bottom: 8px;
|
||
}
|
||
.project-title {
|
||
font-size: 0.88rem;
|
||
}
|
||
.project-desc {
|
||
font-size: 0.75rem;
|
||
}
|
||
.services-buttons,
|
||
.services-buttons.services-main {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
/* Friend cards stay 2-up here, so tighten the padding to give the text room
|
||
next to the big emoji. */
|
||
.service-button.friend {
|
||
gap: 12px;
|
||
padding: 12px 14px;
|
||
}
|
||
.social-buttons {
|
||
grid-template-columns: 1fr 1fr;
|
||
}
|
||
.gym-grid {
|
||
gap: 6px;
|
||
}
|
||
.gym-weight {
|
||
font-size: 1.05rem;
|
||
}
|
||
/* Phones drop the year-grid heatmap (it's unreadable that small); the total
|
||
and the commit history stay. */
|
||
.heatmap-container,
|
||
.heatmap-status-row {
|
||
display: none;
|
||
}
|
||
/* Phones drop the sidebar foot (clock + mail + its divider) too: in the
|
||
collapsed top panel it's just noise. The desktop sidebar keeps it. */
|
||
.web-side-foot {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 400px) {
|
||
.social-buttons {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
/* Very narrow phones: 3-up is too tight for the longest labels ("Overview",
|
||
"Hardware"), so fall back to a 2-up grid — still no horizontal scrolling. */
|
||
@media (max-width: 374px) {
|
||
.web-nav {
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
}
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
html[data-theme="web"] *,
|
||
html[data-theme="web"] *::before,
|
||
html[data-theme="web"] *::after {
|
||
animation-duration: 0.01ms !important;
|
||
animation-iteration-count: 1 !important;
|
||
transition-duration: 0.01ms !important;
|
||
scroll-behavior: auto !important;
|
||
}
|
||
}
|