/* ============================================================ WEB THEME — light frosted-glass website shell. ------------------------------------------------------------ Default theme (``). Light by default, dark via ``. 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; } }