/* ============================================================ main.css — single stylesheet for clove.is-a.dev Merged from style.css + discord-styles.css Sections: 1. Base & reset (all pages) 2. Shared layout & header (all pages) 3. Link hub (index page) 4. Now-playing widget (shared, top-left) 5. Page nav (shared, bottom-left) 6. System badges (shared, bottom-right) 7. Theme switcher (shared, top-right) 8. Tech-stack page 9. Changelog page 10. Secret cat modes (toast + picker modal) 11. Discord bots page 12. Responsive overrides ============================================================ */ @import url(/css/fonts.css); /* ============================================================ 1. BASE & RESET ============================================================ */ * { box-sizing: border-box; } /* Smooth cross-fade between pages */ @view-transition { navigation: auto; } ::view-transition-old(root), ::view-transition-new(root) { animation-duration: 0.35s; animation-timing-function: ease; } @media (prefers-reduced-motion: reduce) { ::view-transition-old(root), ::view-transition-new(root) { animation: none; } } html, body { height: 100%; overflow: hidden; } html { background: linear-gradient(135deg, var(--base) 0%, var(--mantle) 60%, var(--crust) 100%); } body { font-family: 'Comic Code', sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; height: 100dvh; margin: 0; padding: 1.5rem 1rem; background: linear-gradient(135deg, var(--base) 0%, var(--mantle) 60%, var(--crust) 100%); color: var(--text); } /* Estrogen watermark blended into the background */ body::before { content: ""; position: fixed; inset: 0; background: url(/assets/theme/estrogen.svg) center / cover no-repeat; filter: invert(86%) sepia(8%) saturate(900%) hue-rotate(190deg) brightness(105%); opacity: 0.05; pointer-events: none; z-index: 0; } /* ============================================================ 2. SHARED LAYOUT & HEADER (all pages) ============================================================ */ .hub { position: relative; z-index: 1; width: 100%; max-width: 460px; } .pfp { width: 96px; height: 96px; border-radius: 50%; object-fit: cover; border: 3px solid var(--yellow); box-shadow: 0 4px 18px rgba(245, 194, 231, 0.25); margin-bottom: 0.75rem; } .hub-header { text-align: center; margin-bottom: 2.25rem; } .hub-header h1 { margin: 0; font-size: 2rem; font-weight: 700; color: rgb(var(--accent-rgb)); transition: color 0.6s ease; } .tagline { margin: 0.35rem 0 0; color: var(--subtext-0); font-size: 0.95rem; letter-spacing: 0.04em; text-transform: uppercase; } .pronouns { margin: 0.35rem 0 0; color: var(--mauve); font-size: 0.95rem; letter-spacing: 0.04em; text-transform: lowercase; } /* ============================================================ 3. LINK HUB (index page) ============================================================ */ .links { display: grid; grid-template-columns: repeat(auto-fit, 72px); gap: 0.9rem; justify-content: center; } .link-card { position: relative; display: flex; align-items: center; justify-content: center; width: 72px; height: 72px; border-radius: 16px; background: var(--surface-0); border: 1px solid var(--surface-1); color: var(--text); text-decoration: none; transition: transform 0.15s ease, border-color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease; } .link-card:hover { transform: translateY(-3px); background: var(--surface-1); border-color: rgb(var(--accent-rgb)); box-shadow: 0 6px 20px rgba(var(--accent-rgb), 0.22); } .icon { width: 30px; height: 30px; flex-shrink: 0; filter: brightness(0) invert(1); transition: filter 0.15s ease; } .link-card:hover .icon { filter: none; } /* Tooltip revealed on hover */ .link-text { position: absolute; bottom: calc(100% + 10px); left: 50%; transform: translateX(-50%) translateY(4px); display: flex; flex-direction: column; align-items: center; gap: 0.1rem; padding: 0.5rem 0.75rem; border-radius: 10px; background: var(--crust); border: 1px solid rgb(var(--accent-rgb)); box-shadow: 0 6px 18px rgba(17, 17, 27, 0.55); white-space: nowrap; line-height: 1.3; opacity: 0; pointer-events: none; transition: opacity 0.15s ease, transform 0.15s ease; z-index: 10; } .link-text::after { content: ""; position: absolute; top: 100%; left: 50%; transform: translateX(-50%); border: 6px solid transparent; border-top-color: rgb(var(--accent-rgb)); } .link-card:hover .link-text, .link-card:focus-visible .link-text { opacity: 1; transform: translateX(-50%) translateY(0); } .link-title { font-weight: 500; font-size: 0.95rem; color: var(--text); } .link-sub { font-size: 0.78rem; color: var(--subtext-0); } /* ============================================================ 4. NOW-PLAYING WIDGET (shared, pinned top-left) Discord presence + Spotify via Lanyard. The album-art accent colour is exposed as --np-accent (r, g, b) while .has-accent set. ============================================================ */ .now-playing { --np-accent: 245, 194, 231; /* fallback ~ Catppuccin pink */ position: fixed; top: 1rem; left: 1rem; z-index: 6; display: flex; align-items: center; gap: 0.55rem; max-width: 260px; padding: 0.4rem 0.7rem 0.4rem 0.4rem; border-radius: 999px; background: var(--surface-0); border: 1px solid var(--surface-1); color: var(--text); text-decoration: none; transition: transform 0.15s ease, border-color 0.2s ease, border-radius 0.25s ease, box-shadow 0.2s ease; } .now-playing[hidden] { display: none; } .now-playing:hover { transform: translateY(2px); border-color: var(--pink); } /* While a track is live the pill grows and squares off for the progress bar, picking up a faint album-art glow. */ .now-playing.is-live { align-items: stretch; border-radius: 16px; } .now-playing.is-live.has-accent { border-color: rgba(var(--np-accent), 0.55); box-shadow: 0 6px 22px -10px rgba(var(--np-accent), 0.6); } .now-playing.is-live:hover.has-accent { border-color: rgba(var(--np-accent), 0.85); } /* Head row: status dot + status word, always visible */ .np-head { display: flex; align-items: center; gap: 0.35rem; } .np-status { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; background: var(--overlay-0); } .now-playing[data-status="online"] .np-status { background: var(--green); } .now-playing[data-status="idle"] .np-status { background: var(--yellow); } .now-playing[data-status="dnd"] .np-status { background: var(--red); } .now-playing[data-status="offline"] .np-status { background: var(--overlay-0); } .np-status-label { font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--subtext-0); white-space: nowrap; } .np-art { width: 34px; height: 34px; border-radius: 50%; object-fit: cover; flex-shrink: 0; align-self: center; display: none; } .now-playing.is-live .np-art { display: block; border-radius: 8px; } /* Equalizer bars (only animate while live) */ .np-bars { display: none; align-items: flex-end; gap: 2px; height: 16px; flex-shrink: 0; } .now-playing.is-live .np-bars { display: flex; } .np-bars i { width: 3px; height: 100%; border-radius: 2px; background: var(--pink); transform-origin: bottom; animation: np-eq 0.9s ease-in-out infinite; } .now-playing.has-accent .np-bars i { background: rgb(var(--np-accent)); } .np-bars i:nth-child(2) { animation-delay: 0.15s; } .np-bars i:nth-child(3) { animation-delay: 0.3s; } .np-bars i:nth-child(4) { animation-delay: 0.45s; } @keyframes np-eq { 0%, 100% { transform: scaleY(0.3); } 50% { transform: scaleY(1); } } @media (prefers-reduced-motion: reduce) { .np-bars i { animation: none; transform: scaleY(0.6); } } .np-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; } /* "Now playing" tag — only while a track is live, pushed right */ .np-label { display: none; font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--pink); } .now-playing.is-live .np-label { display: inline; margin-left: auto; padding-left: 0.5rem; } .now-playing.is-live.has-accent .np-label { color: rgb(var(--np-accent)); } .np-track { font-size: 0.8rem; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .np-track:empty { display: none; } .np-artist { font-size: 0.72rem; color: var(--subtext-0); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .np-artist:empty { display: none; } /* Spotify progress bar — only shown while a track has timestamps */ .np-progress { display: none; flex-direction: column; gap: 3px; margin-top: 4px; } .now-playing.is-live.has-progress .np-progress { display: flex; } .np-bar { height: 3px; width: 100%; border-radius: 999px; background: var(--surface-2); overflow: hidden; } .np-fill { display: block; height: 100%; width: 0%; border-radius: 999px; background: var(--pink); transition: width 0.3s linear; } .now-playing.has-accent .np-fill { background: rgb(var(--np-accent)); box-shadow: 0 0 8px 1px rgba(var(--np-accent), 0.55); } .np-times { display: flex; justify-content: space-between; font-size: 0.58rem; font-variant-numeric: tabular-nums; color: var(--subtext-0); } /* ============================================================ 5. PAGE NAV (shared, pinned bottom-left) ============================================================ */ .nav { position: fixed; left: 1rem; bottom: 1rem; z-index: 6; } .nav-links { display: flex; flex-direction: column; align-items: flex-start; gap: 0.4rem; } .nav-link { position: relative; display: inline-flex; align-items: center; padding: 0.3rem 0.7rem; border-radius: 999px; background: var(--surface-0); border: 1px solid var(--surface-1); color: var(--subtext-1); font-size: 0.8rem; text-decoration: none; transition: transform 0.15s ease, border-color 0.15s ease, background 0.15s ease, color 0.15s ease; } .nav-link:hover { border-color: rgb(var(--accent-rgb)); color: var(--text); transform: translateX(2px); } .nav-link.selected { background: rgb(var(--accent-rgb)); border-color: rgb(var(--accent-rgb)); color: var(--crust); font-weight: 700; margin-left: 14px; } .nav-link.is-a-dev { background: var(--surface-1); border-color: var(--surface-2); color: var(--text); } .nav-link.is-a-dev:hover { border-color: var(--saphire); transform: translateX(2px); } /* Triangle pointing at the selected item */ .nav-link.selected::before { content: ""; position: absolute; left: -14px; top: 50%; transform: translateY(-50%); border: 6px solid transparent; border-left-color: rgb(var(--accent-rgb)); } /* ============================================================ 6. SYSTEM BADGES (shared, pinned bottom-right) ============================================================ */ .badges { position: fixed; right: 1rem; bottom: 1rem; display: flex; flex-direction: column; align-items: flex-end; gap: 0.4rem; pointer-events: none; z-index: 5; } .badge { display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.3rem 0.6rem; border-radius: 999px; background: var(--surface-0); border: 1px solid rgba(var(--accent-rgb), 0.45); color: var(--subtext-1); font-size: 0.75rem; white-space: nowrap; transition: border-color 0.6s ease, box-shadow 0.6s ease; } .badge:hover { border-color: rgb(var(--accent-rgb)); box-shadow: 0 4px 14px -6px rgba(var(--accent-rgb), 0.5); } .badge-icon { width: 15px; height: 15px; filter: invert(78%) sepia(36%) saturate(640%) hue-rotate(280deg) brightness(105%); } /* ============================================================ 7. THEME SWITCHER (shared, icon button top-right) ============================================================ */ .beta-bar { position: fixed; top: 1rem; right: 1rem; z-index: 7; display: flex; align-items: center; gap: 0.5rem; } /* cat-collection button icon: the classic oneko idle frame, scaled down from the 256x128 sprite sheet (idle frame lives at -96px,-96px) */ .beta-cat-icon { width: 22px; height: 22px; display: block; background-image: url('/assets/oneko/classics/classic.png'); background-repeat: no-repeat; background-size: 176px 88px; background-position: -66px -66px; image-rendering: pixelated; } .beta-btn { display: inline-flex; align-items: center; justify-content: center; padding: 0.35rem; border-radius: 999px; background: var(--surface-0); border: 1px solid var(--surface-1); cursor: pointer; transition: border-color 0.15s ease, transform 0.15s ease; } .beta-btn:hover { border-color: var(--pink); transform: translateY(2px); } .beta-icon { width: 22px; height: 22px; display: block; } /* ============================================================ 8. TECH-STACK PAGE ============================================================ */ /* Let only the tech-stack page scroll; link hub stays locked */ html:has(.tech-stack), body:has(.tech-stack) { height: auto; min-height: 100dvh; overflow-y: auto; } body:has(.tech-stack) { align-items: flex-start; } body:has(.tech-stack) .hub { max-width: 860px; } .tech-stack { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.5rem; margin: 0 auto; padding-bottom: 4.5rem; } /* Simple Icons rendered via CSS mask so colour is theme-driven. Markup: */ .tech-icon { position: relative; width: 30px; height: 30px; display: inline-block; transition: transform 0.15s ease, filter 0.15s ease; } /* The icon shape — masked so the label (::after) stays visible */ .tech-icon::before { content: ""; position: absolute; inset: 0; background-color: currentColor; -webkit-mask: var(--si) center / contain no-repeat; mask: var(--si) center / contain no-repeat; } .tech-icon:hover { transform: translateY(-2px) scale(1.12); filter: drop-shadow(0 4px 8px currentColor); } /* Hover label — pulls text from aria-label, tinted to match the icon */ .tech-icon::after { content: attr(aria-label); position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%) translateY(4px); padding: 0.25rem 0.5rem; border-radius: 6px; background: var(--crust); border: 1px solid currentColor; color: var(--text); font-size: 0.72rem; line-height: 1; white-space: nowrap; pointer-events: none; opacity: 0; transition: opacity 0.15s ease, transform 0.15s ease; z-index: 10; } .tech-icon:hover::after { opacity: 1; transform: translateX(-50%) translateY(0); } /* Catppuccin accent classes — pull from the active flavour's vars */ .tech-icon.rosewater { color: var(--rosewater); } .tech-icon.pink { color: var(--pink); } .tech-icon.mauve { color: var(--mauve); } .tech-icon.red { color: var(--red); } .tech-icon.maroon { color: var(--maroon); } .tech-icon.peach { color: var(--peach); } .tech-icon.yellow { color: var(--yellow); } .tech-icon.green { color: var(--green); } .tech-icon.teal { color: var(--teal); } .tech-icon.sky { color: var(--sky); } .tech-icon.sapphire { color: var(--saphire); } .tech-icon.blue { color: var(--blue); } .tech-icon.lavender { color: var(--lavender); } /* ============================================================ 9. CHANGELOG PAGE ============================================================ */ html:has(.changelog), body:has(.changelog) { height: auto; min-height: 100dvh; overflow-y: auto; } body:has(.changelog) { align-items: flex-start; } body:has(.changelog) .hub { max-width: 540px; } .changelog { display: flex; flex-direction: column; gap: 1rem; padding-bottom: 4.5rem; } .release { background: var(--surface-0); border: 1px solid var(--surface-1); border-radius: 14px; padding: 1rem 1.15rem; } .release-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; margin-bottom: 0.6rem; } .release-version { font-weight: 700; font-size: 1.05rem; color: var(--pink); } .release-date { font-size: 0.78rem; color: var(--subtext-0); } .release-notes { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.45rem; } .release-notes li { font-size: 0.88rem; line-height: 1.4; color: var(--text); } .tag { display: inline-block; margin-right: 0.4rem; padding: 0.05rem 0.45rem; border-radius: 6px; font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; vertical-align: 1px; } .tag-add { background: rgba(166, 227, 161, 0.18); color: var(--green); } .tag-change { background: rgba(137, 180, 250, 0.18); color: var(--blue); } /* ============================================================ 10. SECRET CAT MODES (toast + picker modal) ============================================================ */ .cat-toast { position: fixed; left: 50%; bottom: 4.5rem; transform: translateX(-50%) translateY(8px); z-index: 2147483647; padding: 0.4rem 0.9rem; border-radius: 999px; background: var(--crust); border: 1px solid var(--pink); color: var(--text); font-size: 0.8rem; white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity 0.2s ease, transform 0.2s ease; } .cat-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); } .cat-picker { position: fixed; inset: 0; z-index: 2147483646; display: flex; align-items: center; justify-content: center; padding: 1rem; background: rgba(17, 17, 27, 0.65); backdrop-filter: blur(2px); } .cat-picker[hidden] { display: none; } .cat-picker-panel { width: min(94vw, 430px); max-height: 82vh; overflow-y: auto; background: var(--base); border: 1px solid var(--surface-1); border-radius: 16px; padding: 1rem; box-shadow: 0 16px 48px rgba(0, 0, 0, 0.55); } .cat-picker-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.85rem; font-weight: 700; color: var(--pink); } .cat-picker-close { background: none; border: none; color: var(--subtext-0); font-size: 1.35rem; line-height: 1; cursor: pointer; padding: 0 0.25rem; } .cat-picker-close:hover { color: var(--text); } .cat-grid { display: flex; flex-direction: column; gap: 1rem; } .cat-section-title { margin: 0 0 0.55rem; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--mauve); border-bottom: 1px solid var(--surface-1); padding-bottom: 0.3rem; } .cat-section-items { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.6rem; } .cat-option { display: flex; flex-direction: column; align-items: center; gap: 0.3rem; padding: 0.75rem 0.4rem 0.6rem; border-radius: 12px; background: var(--surface-0); border: 1px solid var(--surface-1); color: var(--text); font-family: inherit; font-size: 0.74rem; text-align: center; cursor: pointer; transition: transform 0.15s ease, border-color 0.15s ease; } .cat-option:hover:not(.locked) { transform: translateY(-2px); border-color: var(--pink); } .cat-option.current { border-color: var(--pink); box-shadow: inset 0 0 0 1px var(--pink); } .cat-option.locked { cursor: default; opacity: 0.75; } .cat-preview { /* 30px window + the -97px,-97px position = the idle frame inset 1px on every side, so no preview can catch pixels from neighbouring frames */ width: 30px; height: 30px; margin: 7px 1px 11px; background-repeat: no-repeat; image-rendering: pixelated; transform: scale(1.7); transform-origin: center; } .cat-name { font-weight: 500; } .cat-hint { margin: 0.85rem 0 0; font-size: 0.68rem; color: var(--subtext-0); text-align: center; } /* ============================================================ 11. DISCORD BOTS PAGE ============================================================ */ /* On the bots page the header sits closer to the sections */ body:has(.bot-grid) .hub-header { position: relative; z-index: 1; margin-bottom: 0.25rem; } .section { position: relative; z-index: 1; display: flex; flex-direction: column; align-items: center; gap: 1.5rem; width: 100%; } .section-title { margin: 0; font-size: 1.1rem; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; color: var(--mauve); } .bot-grid { display: grid; grid-template-columns: repeat(auto-fit, 96px); justify-content: center; gap: 2rem 1.5rem; max-width: 560px; } .bot { position: relative; display: flex; align-items: center; justify-content: center; width: 96px; height: 96px; color: var(--text); text-decoration: none; transition: transform 0.15s ease, border-color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease; } .bot-pfp { width: 96px; height: 96px; border-radius: 50%; object-fit: cover; border: 3px solid var(--yellow); box-shadow: 0 4px 18px rgba(var(--accent-rgb), 0.25); } .bot:hover { transform: translateY(-3px); } .bot:hover .bot-pfp { filter: none; } /* Bot tooltip revealed on hover */ .bot-link-text { position: absolute; bottom: calc(100% + 10px); left: 50%; transform: translateX(-50%) translateY(4px); display: flex; flex-direction: column; align-items: center; gap: 0.1rem; padding: 0.5rem 0.75rem; border-radius: 10px; background: var(--crust); border: 1px solid rgb(var(--accent-rgb)); box-shadow: 0 6px 18px rgba(17, 17, 27, 0.55); white-space: nowrap; line-height: 1.3; opacity: 0; pointer-events: none; transition: opacity 0.15s ease, transform 0.15s ease; z-index: 10; } .bot-link-text::after { content: ""; position: absolute; top: 100%; left: 50%; transform: translateX(-50%); border: 6px solid transparent; border-top-color: rgb(var(--accent-rgb)); } .bot:hover .bot-link-text, .bot:focus-visible .bot-link-text { opacity: 1; transform: translateX(-50%) translateY(0); } .bot-link-title { font-weight: 500; font-size: 0.95rem; color: var(--text); } .bot-link-sub { font-size: 0.78rem; color: var(--subtext-0); } .bot-link-sub.closed { color: var(--red); } /* ============================================================ 12. RESPONSIVE OVERRIDES ============================================================ */ /* Shrink to fit narrow / short screens so the hub never scrolls */ @media (max-width: 420px), (max-height: 640px) { .hub-header { margin-bottom: 1.25rem; } .hub-header h1 { font-size: 1.6rem; } .pfp { width: 72px; height: 72px; margin-bottom: 0.5rem; } .links { grid-template-columns: repeat(auto-fit, 60px); gap: 0.6rem; } .link-card { width: 60px; height: 60px; border-radius: 13px; } .icon { width: 26px; height: 26px; } } /* Cat picker: 2 columns on very narrow screens */ @media (max-width: 420px) { .cat-grid { grid-template-columns: repeat(2, 1fr); } } /* Mobile layout — single vertical scroll, stacked widgets */ @media (max-width: 640px) { html { height: auto; /* Single vertical scroll root on mobile; clip horizontal overflow so the absolutely-positioned link tooltips can't pan the page. */ overflow-x: hidden; overflow-y: auto; } body, body:has(.tech-stack), body:has(.changelog) { flex-direction: column; justify-content: flex-start; align-items: center; gap: 1rem; height: auto; min-height: 100dvh; overflow-x: hidden; overflow-y: visible; padding: 1.25rem 1rem 2rem; } /* 1 — Top bar: the two single-item widgets, side by side */ .topbar { order: 1; display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: center; gap: 0.6rem; width: 100%; } .topbar .now-playing, .topbar .beta-bar { position: static; inset: auto; } .now-playing { max-width: 100%; } /* 2 — Main content */ .hub, body:has(.tech-stack) .hub, body:has(.changelog) .hub { order: 2; width: 100%; max-width: 100%; } /* 3 — Page nav as a centered group */ .nav { order: 3; position: static; inset: auto; width: 100%; } .nav-links { flex-direction: row; flex-wrap: wrap; justify-content: center; gap: 0.5rem; } /* The selected-item pointer triangle / offset only makes sense in the vertical desktop nav — drop it on mobile */ .nav-link.selected { margin-left: 0; } .nav-link.selected::before { display: none; } /* 4 — System badges, centered and wrapping */ .badges { order: 4; position: static; inset: auto; width: 100%; flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: center; } .hub-header { margin-bottom: 1.5rem; } /* Keep the tech-stack / changelog content from butting up against the nav below it */ .tech-stack, .changelog { padding-bottom: 0; } } /* ============================================================ 12. HIDDEN POKÉBALL (unlocks the Pokémon cats once clicked) ============================================================ */ /* Small, low-contrast and tucked among the badges so it has to be hunted for. Still clickable — that's the whole point of finding it. */ #pokeball-secret { width: 14px; height: 14px; opacity: 0.18; align-self: center; cursor: default; pointer-events: auto; /* parent .badges sets none — re-enable here */ image-rendering: auto; transition: opacity 0.2s ease, transform 0.2s ease; -webkit-user-drag: none; user-select: none; } #pokeball-secret:hover { opacity: 0.85; transform: scale(1.15); } #pokeball-secret.found { opacity: 1; cursor: default; filter: drop-shadow(0 0 4px var(--red)); }