diff options
| author | sillylaird <sillyfanboy@gmail.com> | 2026-09-03 00:33:59 +0000 |
|---|---|---|
| committer | sillylaird <sillyfanboy@gmail.com> | 2026-09-03 00:33:59 +0000 |
| commit | 898b52edcb47bcb3e9d6106e74ca73e74ea01e70 (patch) | |
| tree | 85c6ee5ad58b860144551184d4cf86b560c62b91 /startpage/startpage.css | |
| download | www-main.tar.gz www-main.zip | |
Diffstat (limited to '')
| -rw-r--r-- | startpage/startpage.css | 1892 |
1 files changed, 1892 insertions, 0 deletions
diff --git a/startpage/startpage.css b/startpage/startpage.css new file mode 100644 index 0000000..307e9cc --- /dev/null +++ b/startpage/startpage.css @@ -0,0 +1,1892 @@ +:root { + --bg: #000000; + --text: #8b0000; + --accent: #ff0000; + --gold: #8b0000; + --border: #2a1515; + --neon-green: #39ff14; + --neon-cyan: #ff1a1a; + --accent-cyan: #8b0000; + --accent-pink: #a00000; + --accent-magenta: #8b0000; + --font-mono: "JetBrains Mono", "IBM Plex Mono", "Fira Code", "Source Code Pro", ui-monospace, SFMono-Regular, "Cascadia Code", Menlo, Consolas, monospace; + --lh-body: 1.55; + --lh-comfort: 1.85; + --link-glow: 255, 0, 0; + --btn-glow: 139, 0, 0; + --radius: 4px; + /* Sharper edges for a retro feel */ + --logo-size: clamp(80px, 10vw, 100px); + --fs-base: clamp(14px, 1.1vw, 17px); +} + +* { + box-sizing: border-box; +} + +/* site.css sets html,body{height:100%}, which clamps the page to one viewport + and makes the overflowing body scroll inside that box — a second scrollbar + next to the browser's. Let the page grow naturally; min-height keeps the + footer pinned to the bottom. */ +html, +body { + height: auto; + min-height: 100vh; + min-height: 100dvh; /* dynamic vh: excludes mobile address bar so content isn't cut off */ +} + +body { + margin: 0; + background: var(--bg); + color: var(--text); + font: 400 var(--fs-base) var(--font-mono); + line-height: var(--lh-body); + display: flex; + flex-direction: column; + min-height: 100vh; + min-height: 100dvh; + position: relative; + /* Cyber grid + retro wallpaper */ + background-image: + linear-gradient(rgba(0, 0, 0, 0.72), rgba(0, 0, 0, 0.72)), + linear-gradient(rgba(255, 0, 0, 0.03) 1px, transparent 1px), + linear-gradient(90deg, rgba(255, 0, 0, 0.03) 1px, transparent 1px), + url('wallpaper.jpg'); + background-size: cover, 28px 28px, 28px 28px, cover; + background-position: center, center, center, center; + background-attachment: fixed, fixed, fixed, fixed; + background-blend-mode: normal; +} + +.crt-overlay { + pointer-events: none; + position: fixed; + inset: 0; + z-index: 9998; + background: + repeating-linear-gradient( + 0deg, + rgba(0, 0, 0, 0.12) 0px, + rgba(0, 0, 0, 0.12) 1px, + transparent 1px, + transparent 3px + ); + opacity: 0.35; + mix-blend-mode: multiply; +} + +body::after { + content: ''; + pointer-events: none; + position: fixed; + inset: 0; + z-index: 9997; + background: radial-gradient(ellipse at center, transparent 55%, rgba(0, 0, 0, 0.45) 100%); +} + +@media (prefers-reduced-motion: reduce) { + .crt-overlay { display: none; } + body.startpage-booting .boot-overlay { display: none !important; } + body.startpage-ready { animation: none; } + .boot-log .boot-cursor { animation: none; } +} + +/* No scrollbars at any resolution: the overlay never scrolls, and the page + behind it is locked while booting. .boot-log is scaled down by JS + (fitBootLog) whenever it would exceed the viewport, so nothing is clipped. */ +html:has(> body.startpage-booting), +body.startpage-booting { + overflow: hidden; +} + +.boot-overlay { + position: fixed; + inset: 0; + z-index: 10000; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + overscroll-behavior: none; + background: #000; + padding: 2rem; + transition: opacity 0.35s ease; + /* Failsafe: if JS errors, don't trap users on a black screen */ + animation: boot-dismiss-failsafe 0s 3s forwards; +} + +/* Short/narrow viewports: give the log the padding back as usable room so the + auto-fit scale stays readable instead of shrinking to fit 2rem gutters. */ +@media (max-height: 600px), (max-width: 600px) { + .boot-overlay { padding: 0.75rem; } +} + +.boot-overlay.boot-fade { + opacity: 0; + pointer-events: none; +} + +.boot-overlay[hidden] { + display: none !important; + animation: none; +} + +@keyframes boot-dismiss-failsafe { + to { + opacity: 0; + visibility: hidden; + pointer-events: none; + } +} + +.boot-log { + margin: auto; + max-width: 42rem; + width: 100%; + color: #f2ca30; + font: inherit; + line-height: 1.6; + /* Scale set by JS. Origin is the top edge: an over-tall flex item with + margin:auto is start-aligned, so scaling from the centre would leave the + shrunk box hanging past the bottom. Below scale 1 the log is centered by + flex anyway, so the origin only matters while shrinking. */ + transform-origin: center top; +} + +.boot-title { + color: #f2ca30; + margin-bottom: 0.9rem; +} + +.boot-line { + white-space: pre-wrap; +} + +.boot-login { + margin-top: 0.9rem; +} + +.boot-log .boot-cursor { + color: #f2ca30; + animation: cursor-blink 0.9s step-end infinite; +} + +body.startpage-ready { + animation: boot-reveal 0.45s ease; +} + +@keyframes boot-reveal { + from { opacity: 0.55; } +} + +body.startpage-ready .search-section h2 { + animation: cursor-blink 1.1s step-end infinite; +} + +@keyframes cursor-blink { + 50% { opacity: 0.55; } +} + +a { + color: var(--accent); + text-decoration: none; + transition: 0.1s; +} + +a:hover { + background: var(--accent); + color: #000 !important; +} + +/* Layout Overrides for Unified Header */ +.header-inner { + max-width: none !important; + margin: 0 !important; + padding-left: 20px !important; + padding-right: 20px !important; +} + +main, +footer { + max-width: none !important; + margin: 0 !important; + padding-left: 20px; + padding-right: 20px; +} + +/* Search Section: Left Aligned */ +.search-section { + padding: 10px 0 40px; + text-align: center; + position: relative; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.top-left-links { + position: absolute; + top: 10px; + left: 20px; + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 5px; + text-align: left; + z-index: 1; + width: max-content; +} + +/* Windows-style cascading right-click menu for the Sports links */ +.ctx-menu, +.ctx-menu ul { + list-style: none; + margin: 0; + padding: 0; +} + +.ctx-parent { + position: relative; +} + +.ctx-trigger { + cursor: pointer; + user-select: none; +} + +/* Flyout panels: hidden until parent opened */ +.ctx-flyout { + min-width: 170px; + display: none; + flex-direction: column; + padding: 4px; + background: #0a0a0a; + border: 1px solid var(--accent); + border-radius: 4px; + box-shadow: 0 0 12px rgba(255, 0, 0, 0.4); + z-index: 60; + max-height: 80vh; + overflow-y: auto; + /* scroll works, scrollbar hidden */ + scrollbar-width: none; + -ms-overflow-style: none; +} + +.ctx-flyout::-webkit-scrollbar { + display: none; +} + +/* Root dropdown (the category list under "Sports") drops straight down */ +.ctx-root > .ctx-parent > .ctx-flyout { + position: absolute; + top: calc(100% + 4px); + left: 0; +} + +/* Top-left dropdown (Sports) flies out to the RIGHT of the trigger */ +.top-left-links .ctx-root > .ctx-parent > .ctx-flyout { + top: 0; + left: calc(100% + 6px); +} + +/* Top-right dropdown (Links) flies out to the LEFT of the trigger. */ +.top-right-links .ctx-root > .ctx-parent > .ctx-flyout { + top: 0; + left: auto; + right: calc(100% + 6px); +} + +/* Category link lists float free (fixed) so the scrolling root menu can't clip them; + exact left/top set by JS so they cascade to the right and flip near screen edges */ +.ctx-root .ctx-flyout .ctx-flyout { + position: fixed; + top: 0; + left: 0; +} + +.ctx-parent.open > .ctx-flyout { + display: flex; +} + +/* Individual menu rows */ +.ctx-item { + display: flex; + align-items: center; + justify-content: space-between; + gap: 14px; + padding: 4px 10px; + font-size: 0.95rem; + font-weight: bold; + white-space: nowrap; + color: var(--accent); + text-decoration: none; + border-radius: 2px; + cursor: pointer; +} + +.ctx-item:hover, +.ctx-parent.open > .ctx-item { + background: var(--accent); + color: #000 !important; +} + +/* Submenu arrow indicator */ +.ctx-has-sub::after { + content: '▶'; + font-size: 0.8em; + opacity: 0.7; + display: inline-block; + transition: transform 0.2s ease; +} + +/* Open submenu: right-pointing arrow rotates 90deg to point down */ +.ctx-parent.open > .ctx-has-sub::after { + transform: rotate(90deg); +} + +/* Both menus use a crisp CSS border triangle instead of the ◀ / ▶ glyphs + (the fallback font renders those diagonally). Rotates 90deg down on open. */ +.top-left-links .ctx-arrow, +.top-left-links .ctx-has-sub::after, +.top-right-links .ctx-arrow, +.top-right-links .ctx-has-sub::after { + content: ''; + display: inline-block; + width: 0; + height: 0; + border-top: 8px solid transparent; + border-bottom: 8px solid transparent; + opacity: 0.85; + transform: none; + transition: transform 0.2s ease; + vertical-align: middle; +} + +/* Top-right triangles point left */ +.top-right-links .ctx-arrow, +.top-right-links .ctx-has-sub::after { + border-right: 11px solid currentColor; + border-left: 0; +} + +/* Top-left triangles point right */ +.top-left-links .ctx-arrow, +.top-left-links .ctx-has-sub::after { + border-left: 11px solid currentColor; + border-right: 0; +} + +/* Trigger arrows (Sports / Google Services / Radio's) — bigger, matching the old glyph */ +.top-left-links .ctx-arrow, +.top-right-links .ctx-arrow { + border-top-width: 9px; + border-bottom-width: 9px; +} +.top-right-links .ctx-arrow { margin-right: 0.4em; border-right-width: 12px; } +.top-left-links .ctx-arrow { margin-left: 0.4em; border-left-width: 12px; } +.top-left-links .ctx-has-sub::after { margin-left: 0.4em; } + +/* Top-right submenus cascade LEFT, so their arrow sits to the LEFT of the + label (before it), with the whole row packed to the left edge. */ +.top-right-links .ctx-has-sub { + flex-direction: row-reverse; + justify-content: flex-end; +} +.top-right-links .ctx-has-sub::after { + margin-left: 0; + margin-right: 0.4em; +} + +/* On open the left-pointing (top-right) triangle rotates down to the bottom */ +.top-right-links .ctx-parent.open > .ctx-has-sub::after { + transform: rotate(-90deg); +} + +/* Trigger arrows rotate to point down on open; direction depends on which way they point */ +.ctx-arrow { + display: inline-block; + transition: transform 0.2s ease; +} + +/* Top-left trigger arrow points right (▶), rotates +90 -> down on open */ +.top-left-links .ctx-parent.open > .ctx-trigger .ctx-arrow { + transform: rotate(90deg); +} + +/* Top-right trigger arrow rotates 90deg down (left -> bottom) on open */ +.top-right-links .ctx-parent.open > .ctx-trigger .ctx-arrow { + transform: rotate(-90deg); +} + +.search-link { + font-size: 1.1rem; + font-weight: bold; + text-decoration: none; + transition: 0.1s; + color: var(--accent); +} + +.top-right-links { + position: absolute; + top: 10px; + right: 20px; + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 5px; + z-index: 1; + width: max-content; +} + +.search-link:hover { + background: var(--accent); + color: #000 !important; +} + +body a:hover, +body a:focus-visible { + color: #ffd0d0; +} + +.search-section h2 { + color: var(--accent); + margin-bottom: 15px; + font-size: 1.2rem; + font-weight: bold; +} + +.search-section h2 a { + color: inherit; + text-decoration: none; + transition: color 0.1s; +} + +.search-section h2 a:hover, +.search-section h2 a:focus-visible { + color: var(--accent) !important; + background: transparent !important; + text-shadow: + 0 0 4px rgba(255, 0, 0, .95), + 0 0 12px rgba(139, 0, 0, .75), + 0 0 24px rgba(139, 0, 0, .45); + box-shadow: + 0 0 0 1px rgba(139, 0, 0, .55), + 0 0 10px rgba(255, 0, 0, .45), + 0 0 24px -4px rgba(139, 0, 0, .4); +} + +/* Plain destination links stacked above the engine title; same look as it. */ +.search-section h2.quick-search-title { + margin-bottom: 6px; +} + +/* First quick link sits on the section's top line; the rest stack under it. */ +.search-section h2.quick-search-title:first-of-type { + margin-top: 0; +} + +.search-box { + width: 100%; + max-width: 584px; + background: #000; + border: 1px solid var(--accent); + padding: 12px; + color: var(--accent); + font-family: inherit; + outline: none; + border-radius: var(--radius); + transition: box-shadow 0.2s; + margin: 0 auto; + display: block; +} + +.search-box:focus { + box-shadow: 0 0 10px var(--accent); +} + +/* Skeleton sets input[type=...]{font-size:var(--fs-base)} (specificity 0,1,1) and + loads after this file, so it wins over plain .search-box. On mobile --fs-base + clamps to 14px, which makes iOS Safari auto-zoom (and reflow) on focus. Force + >=16px on the search inputs to stop that. !important needed to beat Skeleton. */ +input.search-box { font-size: 16px !important; } + +/* Unified search with engine toggle */ +.unified-search { + display: flex; + align-items: stretch; + width: 100%; + max-width: 584px; + margin: 0 auto; + border: 1px solid var(--accent); + border-radius: var(--radius); + background: #000; + position: relative; +} + +.unified-search:focus-within { + box-shadow: 0 0 10px var(--accent); +} + +.search-engine-picker { + position: relative; + display: flex; +} + +.search-engine-toggle { + background: #000; + color: var(--accent); + border: none; + border-right: 1px solid var(--accent); + font-family: inherit; + font-weight: bold; + font-size: 1rem; + padding: 0 14px; + cursor: pointer; + min-width: 64px; + transition: 0.15s; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 6px; +} + +.search-engine-toggle:hover { + background: var(--accent); + color: #000; +} + +.search-engine-caret { + font-size: 0.7rem; + opacity: 0.8; +} + +.search-engine-menu { + position: absolute; + top: calc(100% + 2px); + left: 0; + min-width: 180px; + margin: 0; + padding: 0; + list-style: none; + background: #000; + border: 1px solid var(--accent); + border-radius: var(--radius); + z-index: 50; + text-align: left; + box-shadow: 0 0 10px rgba(255, 0, 0, 0.4); +} + +.search-engine-menu[hidden] { + display: none; +} + +.search-engine-menu li { + padding: 8px 12px; + color: var(--accent); + cursor: pointer; + font-family: inherit; + font-size: 0.9rem; + display: flex; + align-items: center; + gap: 10px; + border-bottom: 1px solid var(--border); +} + +.search-engine-menu li:last-child { + border-bottom: none; +} + +.search-engine-menu li:hover, +.search-engine-menu li[aria-selected="true"] { + background: var(--accent); + color: #000; +} + +.search-engine-menu .engine-icon { + font-weight: bold; + min-width: 28px; + text-align: center; +} + +.unified-search .unified-search-input { + border: none; + border-radius: 0; + max-width: none; + flex: 1; + width: auto; + margin: 0; + text-align: center; +} + +.unified-search .unified-search-input:focus { + box-shadow: none; +} + +/* kill ugly native search clear (webkit) + edge clear, use custom .search-clear */ +.unified-search .unified-search-input::-webkit-search-cancel-button, +.unified-search .unified-search-input::-webkit-search-decoration { + -webkit-appearance: none; + appearance: none; + display: none; +} +.unified-search .unified-search-input::-ms-clear, +.unified-search .unified-search-input::-ms-reveal { + display: none; + width: 0; + height: 0; +} + +.search-engine-spacer { + min-width: 64px; + display: flex; + align-items: center; + justify-content: center; +} + +.search-clear { + background: #8b0000; + border: 1px solid #8b0000; + cursor: pointer; + padding: 0; + width: 24px; + height: 24px; + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + opacity: 1; + transition: background 0.15s, border-color 0.15s, transform 0.15s; +} + +.search-clear svg { + display: block; + stroke: #ff5555 !important; +} + +.search-clear svg line { + stroke: #ff5555 !important; +} + +.search-clear:hover { + background: #ff0000; + border-color: #ff0000; + transform: scale(1.05); +} + +.search-clear:hover svg, +.search-clear:hover svg line { + stroke: #8b0000 !important; +} + +.search-clear:active { + transform: scale(0.92); +} + +.search-clear:focus-visible { + outline: 1px solid var(--accent); + outline-offset: 1px; + opacity: 1; +} + +.search-clear[hidden] { + display: none; +} + +.clock-container { + margin-top: 15px; + color: var(--text); + font-size: 0.9rem; + opacity: 0.8; + text-align: center; +} + +.clock-time { + color: var(--accent); + letter-spacing: 1px; +} + +/* Grid Layout: Left Aligned */ +.grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); + gap: 24px; + padding: 0; +} + +.card { + border: 1px solid var(--border); + background: #050505; + padding: 0; + display: flex; + flex-direction: column; + align-items: stretch; + transition: transform .2s ease, border-color .2s ease, box-shadow .25s ease; + min-width: 0; + overflow: hidden; + border-radius: var(--radius); + box-shadow: + 0 0 0 1px rgba(255, 0, 0, 0.08), + 0 4px 18px rgba(0, 0, 0, 0.45); +} + +.card:hover { + border-color: var(--accent); + transform: translateY(-2px) scale(1.01); + box-shadow: + 0 0 0 1px rgba(255, 0, 0, 0.45), + 0 0 14px rgba(255, 0, 0, 0.25), + 0 8px 24px rgba(0, 0, 0, 0.55); +} + +.card-titlebar { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 12px; + background: linear-gradient(180deg, #1a0000, #0a0000); + border-bottom: 1px solid var(--border); + font-weight: 700; + font-size: 0.85rem; + letter-spacing: 0.3px; + text-transform: uppercase; +} + +.card-dots { + display: inline-flex; + gap: 5px; +} + +.card-dots span { + width: 8px; + height: 8px; + border-radius: 50%; + background: #333; + border: 1px solid #555; +} + +.card-dots span:nth-child(1) { background: #ff5f57; border-color: #cc4a44; } +.card-dots span:nth-child(2) { background: #febc2e; border-color: #c99524; } +.card-dots span:nth-child(3) { background: #28c840; border-color: #1f9a32; } + +.card-title { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: var(--accent); + text-shadow: 0 0 6px rgba(255, 0, 0, 0.35); +} + +.card-body { + padding: 15px 15px 20px; + display: flex; + flex-direction: column; + align-items: flex-start; +} + +.card.hidden { + display: none !important; +} + +.card-filter-wrap { + width: 100%; + max-width: 584px; + margin: 10px auto 0; + position: relative; +} + +.card-filter { + margin: 0; + text-align: center; + font-size: 0.95rem; + padding: 10px 12px; + padding-right: 55px; + border-radius: var(--radius); + /* always-on active glow, matches focused search look even when not focused */ + box-shadow: 0 0 10px var(--accent); +} + +/* kill native search clear for the card filter too */ +.card-filter::-webkit-search-cancel-button, +.card-filter::-webkit-search-decoration { + -webkit-appearance: none; + appearance: none; + display: none; +} +.card-filter::-ms-clear { + display: none; +} + +.filter-count { + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + font-size: 0.75rem; + color: #666; + white-space: nowrap; + pointer-events: none; + opacity: 0.7; +} + +body.light-mode .filter-count { + color: #8b0000; +} + +.card .thumb { + width: 100%; + height: 70px; + display: flex; + align-items: center; + justify-content: flex-start; + margin-bottom: 15px; + opacity: 0.85; + transition: opacity .2s ease, transform .2s ease; +} + +.card:hover .thumb { + opacity: 1; + transform: scale(1.03); +} + +.card img { + max-width: 80px; + max-height: 100%; + object-fit: contain; +} + +.card-links { + font-size: 0.95rem; + line-height: 1.6; + border-top: 1px solid var(--border); + width: 100%; + padding-top: 16px; + text-align: left; + display: grid; + grid-template-columns: 1fr; + align-items: flex-start; + gap: 12px; +} + +.card-links a { + display: inline-block; + width: auto; + max-width: 100%; + /* Dotted labels ("wiki.realmofespionage") are one unbreakable token: their + min-content width sets the grid track and pushes the whole link column + past the card's right edge on narrow columns. Let them wrap. */ + min-width: 0; + overflow-wrap: anywhere; + padding: 2px 6px 4px; + line-height: 1.45; + border-radius: 2px; + background: #0a0a0a; + text-shadow: + 0 0 4px rgba(255, 0, 0, .9), + 0 0 10px rgba(255, 0, 0, .5); + box-shadow: + 0 0 0 1px rgba(255, 0, 0, .9), + 0 0 6px rgba(255, 0, 0, .35); + transition: transform .2s ease, box-shadow .25s ease, background .2s ease; +} + +.card-links a:hover, +.card-links a:focus-visible { + transform: translateY(-1px) scale(1.02); + background: color-mix(in srgb, var(--accent) 12%, #0a0a0a); + color: #ffd0d0; + box-shadow: + 0 0 0 1px rgba(var(--link-glow), .65), + 0 0 12px rgba(var(--link-glow), .4); +} + +footer { + border-top: 1px solid var(--border); + margin-top: auto; + color: #555; +} + +/* Last.fm now-playing bar */ +.lastfm-widget { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 20px; + margin-top: 12px; + border-bottom: 1px solid var(--border); + background: #050505; + font-size: 0.8rem; + color: #888; + flex-wrap: wrap; +} + + +.lastfm-label { + color: #d51007; +} + +#lastfm-status { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.lastfm-profile { + color: #d51007; + white-space: nowrap; + flex-shrink: 0; +} + +.lastfm-profile:hover { + background: #d51007; + color: #000 !important; +} + +/* Phones / touch devices: bigger text, easier tap target, stacked layout */ +@media (max-width: 600px), (hover: none) and (pointer: coarse) and (max-width: 820px) { + + .lastfm-widget { + flex-direction: column; + align-items: stretch; + gap: 8px; + padding: 12px 16px; + font-size: 0.95rem; + text-align: center; + } + + .lastfm-label { + font-size: 1.2rem; + } + + #lastfm-status { + white-space: normal; + overflow: visible; + text-overflow: clip; + flex: none; + line-height: 1.4; + display: flex; + flex-direction: column; + align-items: center; + gap: 2px; + word-break: break-word; + overflow-wrap: anywhere; + } + + #lastfm-status .lastfm-prefix, + #lastfm-status .lastfm-track-name, + #lastfm-status .lastfm-track-artist { + display: block; + max-width: 100%; + } + + #lastfm-status .lastfm-track-name { + font-weight: bold; + } + + #lastfm-status .lastfm-sep { + display: none; + } + + #lastfm-status .lastfm-track-artist::before { + content: '— '; + opacity: 0.6; + } + + .lastfm-profile { + align-self: center; + padding: 10px 18px; + min-height: 44px; + display: inline-flex; + align-items: center; + justify-content: center; + border: 1px solid #d51007; + border-radius: 4px; + font-weight: bold; + } +} + + + +/* Persistent red glow halo on the link groups (visible on desktop and touch) */ +.top-left-links .search-link, +.top-right-links .search-link { + display: inline-block; + padding: 1px 4px; + border-radius: 2px; + text-shadow: + 0 0 4px rgba(255, 0, 0, .9), + 0 0 12px rgba(139, 0, 0, .6), + 0 0 24px rgba(139, 0, 0, .35); + box-shadow: + 0 0 0 1px rgba(255, 0, 0, .5), + 0 0 10px rgba(139, 0, 0, .55), + 0 0 24px -4px rgba(139, 0, 0, .4); +} + +/* Phone-only: stack the link groups vertically so they don't overlap the search */ +@media (max-width: 600px), (hover: none) and (pointer: coarse) and (max-width: 820px) { + .search-section { + padding-top: 20px; + } + + .top-left-links, + .top-right-links { + position: static; + flex-direction: column; + align-items: center; + gap: 6px; + padding: 0 10px; + margin-bottom: 20px; + text-align: center; + z-index: auto; + width: 100%; + } + + .top-left-links .search-link, + .top-right-links .search-link { + font-size: 0.95rem; + } +} + +@media (max-width: 850px) { + + header, + main, + footer { + padding: 20px; + } + + header { + flex-direction: column; + } + + .nav-columns { + width: 100%; + } + + .age-modal-content { + margin: 20px; + padding: 20px; + } + + .search-section { + padding: 20px 0 20px 0; + } + + .search-box { + padding: 8px 10px; + font-size: 16px; /* >=16px: prevent iOS Safari auto-zoom on focus */ + } + + .unified-search { + max-width: 100%; + } + + .card-filter-wrap { + max-width: 100%; + } + + .search-engine-toggle { + min-width: 48px; + padding: 0 8px; + font-size: 0.9rem; + } + + .unified-search .unified-search-input { + min-width: 0; + } +} + + + +/* Light mode: snow/ice startpage theme */ +body.light-mode { + --bg: #fff8f1; + --surface: #fffdf9; + --text: #1a2a33; + --ink: #1a2a33; + --muted: #5c6b73; + --accent: #8b0000; + --gold: #8b0000; + --border: #d9c9b8; + --shadow: rgba(92, 72, 52, .12); + --link-glow: 139, 0, 0; + background: #fff8f1 !important; + background-image: none !important; + color: var(--text); +} + +body.light-mode main, +body.light-mode footer, +body.light-mode .search-section, +body.light-mode .grid { + background: #fff8f1; +} + +body.light-mode .card, +body.light-mode .lastfm-widget, +body.light-mode .ctx-flyout, +body.light-mode .unified-search, +body.light-mode .search-box, +body.light-mode .search-engine-toggle, +body.light-mode .search-engine-menu, +body.light-mode .unified-search .unified-search-input, +body.light-mode .age-modal-content, +body.light-mode .age-modal-btn, +body.light-mode .modal-search-input, +body.light-mode .modal-search-button { + background: #ffffff !important; + color: var(--text) !important; + border-color: var(--border) !important; +} + +body.light-mode .card-links, +body.light-mode .search-engine-toggle, +body.light-mode .ctx-flyout, +body.light-mode .search-engine-menu li, +body.light-mode footer, +body.light-mode .lastfm-widget, +body.light-mode .modal-search { + border-color: var(--border) !important; +} + +body.light-mode .top-left-links .search-link, +body.light-mode .top-right-links .search-link { + color: var(--accent); + text-shadow: none; + box-shadow: none; +} + +body.light-mode .card { + box-shadow: + 0 0 0 1px rgba(255, 255, 255, .95), + 0 0 16px rgba(139, 0, 0, .18), + 0 0 34px rgba(255, 255, 255, .88); +} + +body.light-mode .card-links a { + background: #fff4f4; + color: var(--accent); + text-shadow: + 0 0 3px rgba(255, 255, 255, 1), + 0 0 10px rgba(255, 255, 255, .9), + 0 0 18px rgba(139, 0, 0, .28); + box-shadow: + 0 0 0 1px rgba(255, 255, 255, .95), + 0 0 10px rgba(255, 255, 255, .9), + 0 0 20px -4px rgba(139, 0, 0, .36); +} + +body.light-mode .ctx-flyout, +body.light-mode .search-engine-menu { + box-shadow: 0 8px 22px var(--shadow); +} + +body.light-mode .ctx-item, +body.light-mode .search-engine-menu li { + color: var(--text); + text-shadow: none; +} + +body.light-mode .ctx-parent.open > .ctx-item { + background: var(--accent); + color: #ffffff !important; +} + +body.light-mode .search-section h2, +body.light-mode .search-section h2 a, +body.light-mode .clock-time, +body.light-mode .lastfm-label, +body.light-mode .lastfm-profile, +body.light-mode .age-modal-content h2 { + color: var(--accent); +} + +body.light-mode .clock-container, +body.light-mode .lastfm-widget, +body.light-mode footer { + color: var(--muted); +} + +body.light-mode .card:hover, +body.light-mode .unified-search:focus-within, +body.light-mode .search-box:focus { + border-color: var(--accent); +} + +body.light-mode .card:hover { + box-shadow: + 0 0 0 1px rgba(255, 255, 255, 1), + 0 0 18px rgba(139, 0, 0, .32), + 0 0 42px rgba(255, 255, 255, .95); +} + +body.light-mode .search-engine-menu { + box-shadow: 0 8px 22px var(--shadow); +} + +body.light-mode button.search-clear { + background: #f8e8e8 !important; + border-color: var(--border) !important; +} + +body.light-mode .search-clear svg, +body.light-mode .search-clear svg line { + stroke: var(--accent) !important; +} + +body.light-mode button.search-clear:hover { + background: var(--accent) !important; + border-color: var(--accent) !important; +} + +body.light-mode .search-clear:hover svg, +body.light-mode .search-clear:hover svg line { + stroke: #ffffff !important; +} + +body.light-mode a:hover, +body.light-mode .search-link:hover, +body.light-mode .ctx-item:hover, +body.light-mode .search-engine-toggle:hover, +body.light-mode .search-engine-menu li:hover, +body.light-mode .search-engine-menu li[aria-selected="true"], +body.light-mode .lastfm-profile:hover, +body.light-mode .card-links a:hover, +body.light-mode .age-modal-btn:hover, +body.light-mode .age-modal-content form button:hover { + background: var(--accent) !important; + color: #ffffff !important; + border-color: var(--accent) !important; + text-shadow: none; +} + +body.light-mode .age-modal { + background: rgba(255, 248, 241, 0.92); +} + +body.light-mode .age-modal-btn.deny:hover { + background: #555 !important; + border-color: #555 !important; + color: #ffffff !important; +} + +body .site-header .brand-text strong, +body .site-header .brand-text span, +body .site-header .header-button, +body .site-header #menu-toggle, +body .site-header nav.site-nav a, +body .site-header .lang-menu a { + color: var(--accent); +} + +/* --------------------------------------------------------------------------- + Mobile / touch: the desktop context menus cascade as absolute/fixed flyouts + that run off the right edge and below the viewport. On small / touch screens + render every flyout in-flow instead: full-width, stacked under its trigger, + nested levels indented (accordion). Kills all overflow. !important is needed + to override the inline left/top the JS (positionFlyout) sets and the fixed + positioning from the base rules. + --------------------------------------------------------------------------- */ +@media (max-width: 600px), (hover: none) and (pointer: coarse) and (max-width: 820px) { + .ctx-menu .ctx-flyout { + position: static !important; + inset: auto !important; + left: auto !important; + top: auto !important; + right: auto !important; + width: 100% !important; + min-width: 0 !important; + max-width: 100% !important; + max-height: none !important; + box-sizing: border-box; + margin: 4px 0; + box-shadow: none; + overflow: visible !important; + } + + /* indent + accent rule on nested submenus so the hierarchy reads clearly */ + .ctx-menu .ctx-flyout .ctx-flyout { + margin: 4px 0 4px 14px; + border-left: 2px solid var(--accent); + } + + /* left-align rows and give finger-friendly tap targets */ + .ctx-menu .ctx-item { + justify-content: flex-start; + padding: 10px 12px; + } + + /* Mobile uses a distinct chevron (❯) that rotates down on open, instead of + the desktop filled triangles. Applies to both submenu arrows and the + top-level trigger arrows (Sports / Google Services / Radio's). */ + .ctx-menu .ctx-has-sub::after { + content: '❯'; + transform: rotate(0deg); + transition: transform 0.2s ease; + } + .ctx-menu .ctx-parent.open > .ctx-has-sub::after { + transform: rotate(90deg); + } + + /* Trigger arrows: hide the desktop glyph (◀ / ▶) and show the same chevron, + rotating down on open. */ + .ctx-menu .ctx-arrow { font-size: 0; } + .ctx-menu .ctx-arrow::after { + content: '❯'; + font-size: 1rem; + display: inline-block; + transition: transform 0.2s ease; + } + .ctx-menu .ctx-parent.open > .ctx-trigger .ctx-arrow { + transform: none; + } + .ctx-menu .ctx-parent.open > .ctx-trigger .ctx-arrow::after { + transform: rotate(90deg); + } + + /* Drop the desktop triangles on mobile; the chevron (❯) is used instead */ + .top-left-links .ctx-arrow, + .top-left-links .ctx-has-sub::after, + .top-right-links .ctx-arrow, + .top-right-links .ctx-has-sub::after { + border: 0; + width: auto; + height: auto; + margin: 0; + } + .top-left-links .ctx-has-sub::after, + .top-right-links .ctx-has-sub::after { content: '❯'; } + /* Mobile accordion: keep chevron on the right (undo desktop left-flip) */ + .top-right-links .ctx-has-sub { + flex-direction: row; + justify-content: space-between; + } +} + +/* Comfortable line spacing toggle for long link lists */ +.comfort-toggle { + margin-top: 12px; + padding: 6px 12px; + border: 1px solid var(--border); + border-radius: var(--radius); + background: #111; + color: var(--accent); + font: inherit; + font-size: 0.82rem; + cursor: pointer; + transition: background .15s, color .15s, border-color .15s, box-shadow .2s; +} + +.comfort-toggle:hover, +.comfort-toggle:focus-visible { + background: var(--accent); + color: #000; + outline: none; +} + +.comfort-toggle[aria-pressed="true"] { + border-color: var(--neon-green); + color: var(--neon-green); + box-shadow: 0 0 10px rgba(57, 255, 20, .25); +} + +body.startpage-comfort, +body.comfort-spacing { + line-height: var(--lh-comfort); +} + +body.startpage-comfort .card-body, +body.startpage-comfort .clock-container, +body.startpage-comfort .lastfm-widget { + line-height: var(--lh-comfort); +} + +body.startpage-comfort .card-links { + line-height: var(--lh-comfort); + gap: 22px; +} + +body.startpage-comfort .card-links a { + line-height: 1.7; + padding: 4px 8px 6px; +} + +.search-section h2 { + font-weight: 800; + position: relative; +} + +.search-section h2::after { + content: ''; + display: block; + width: 2.5rem; + height: 2px; + margin: 6px auto 0; + border-radius: 2px; + background: linear-gradient(90deg, #8b0000, var(--accent)); + box-shadow: 0 0 8px color-mix(in srgb, var(--accent) 45%, transparent); + animation: sp-neon-pulse 3.5s ease-in-out infinite; +} + +@keyframes sp-neon-pulse { + 50% { + box-shadow: + 0 0 10px color-mix(in srgb, var(--accent) 50%, transparent), + 0 0 18px color-mix(in srgb, #8b0000 25%, transparent); + } +} + +body.light-mode .comfort-toggle { + background: #fff8f1; + color: #8b0000; + border-color: #e8c8c8; +} + +body.light-mode .comfort-toggle:hover { + background: #8b0000; + color: #fff; +} + +body.light-mode .comfort-toggle[aria-pressed="true"] { + color: #5a0000; + border-color: #8b0000; + box-shadow: 0 0 10px rgba(139, 0, 0, .2); +} + +/* Konaste / BEMANI PC maintenance status card (under the Last.fm widget) */ +.konaste-status { + margin: 12px 20px 0; + padding: 12px 16px; + border: 1px solid var(--border); + border-radius: 4px; + background: #050505; + font-size: 0.8rem; + color: #888; +} + +.konaste-head { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 10px; + flex-wrap: wrap; + margin-bottom: 8px; + border-bottom: 1px solid var(--border); + padding-bottom: 6px; +} + +.konaste-title { + margin: 0; + font-size: 0.85rem; + color: var(--accent); + letter-spacing: 0.03em; +} + +.konaste-updated { color: #666; } + +.konaste-clock { color: #666; } + +.konaste-clock-time { + color: var(--neon-cyan); + font-variant-numeric: tabular-nums; + letter-spacing: 0.02em; +} + +.konaste-clock-colon { opacity: 0.6; margin: 0 1px; } + +.konaste-clock-ap { color: var(--neon-cyan); font-size: 0.9em; } + +.konaste-clock-tz { color: #666; margin-left: 4px; } + +.konaste-names { + display: inline-flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; +} + +.konaste-name-sep { color: #555; flex-shrink: 0; } + +.konaste-game { + display: grid; + grid-template-columns: 12px auto minmax(0, 1fr); + align-items: start; + column-gap: 10px; + row-gap: 5px; + padding: 5px 0; +} + +.konaste-dot { grid-column: 1; grid-row: 1; margin-top: 3px; } + +.konaste-names { grid-column: 2; grid-row: 1; } + +.konaste-dot { + width: 10px; + height: 10px; + border-radius: 50%; + flex-shrink: 0; + background: #555; + box-shadow: 0 0 6px rgba(255, 255, 255, 0.15); +} + +.konaste-dot.is-online { + background: var(--neon-green); + box-shadow: 0 0 8px var(--neon-green); +} + +.konaste-dot.is-maintenance { + background: #febc2e; + box-shadow: 0 0 8px #febc2e; +} + +.konaste-name { + color: #d51007; + white-space: nowrap; + flex-shrink: 0; + font-weight: 600; +} + +.konaste-name:hover { + background: #d51007; + color: #000 !important; +} + +.konaste-text { + grid-column: 3; + grid-row: 1; + color: #aaa; + line-height: 1.55; + text-align: right; + white-space: pre-line; +} + +.konaste-countdown { + grid-column: 3; + text-align: right; + color: var(--neon-cyan); + font-variant-numeric: tabular-nums; +} + +.konaste-note { + display: block; + margin-top: 8px; + padding-top: 6px; + border-top: 1px solid var(--border); + color: #666; + line-height: 1.4; +} + +.konaste-note a { color: #8b0000; } + +@media (max-width: 600px), (hover: none) and (pointer: coarse) and (max-width: 820px) { + .konaste-status { font-size: 0.9rem; } + .konaste-game { padding: 7px 0; } + .konaste-dot { width: 12px; height: 12px; } +} + +/* --------------------------------------------------------------------------- + Mid-size viewports (portrait monitors, half-screen windows, small laptops): + the absolutely-positioned link columns overlap the centered search box + between the phone breakpoint (600px) and ~1100px. Pull them into the flow + as centered wrapping rows above the search instead. Root dropdowns drop + straight down under their trigger; nested flyouts keep the JS fixed + positioning, which already clamps to the viewport. + On touch screens <=820px the accordion rules above still win (!important). + --------------------------------------------------------------------------- */ +@media (min-width: 601px) and (max-width: 1100px) { + .search-section { + padding-top: 16px; + } + + .top-left-links, + .top-right-links { + position: static; + width: 100%; + flex-direction: row; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: 8px 16px; + margin-bottom: 12px; + text-align: center; + z-index: auto; + } + + /* Root dropdowns open downward under the trigger instead of sideways */ + .top-left-links .ctx-root > .ctx-parent > .ctx-flyout, + .top-right-links .ctx-root > .ctx-parent > .ctx-flyout { + top: calc(100% + 4px); + left: 0; + right: auto; + max-width: 92vw; + } +} + +/* Narrow viewports: the 3-column konaste row (dot | names | right-aligned text) + squeezes the status text into a sliver. Stack text/countdown under the names. */ +@media (max-width: 850px) { + .konaste-game { + grid-template-columns: 12px minmax(0, 1fr); + } + + .konaste-text { + grid-column: 2; + grid-row: 2; + text-align: left; + } + + .konaste-countdown { + grid-column: 2; + text-align: left; + } + + .grid { + gap: 16px; + } +} + +/* Light mode */ +body.light-mode .konaste-status { + background: #f4f4f4; + border-color: #d9c2c2; + color: #555; +} +body.light-mode .konaste-title { color: #b00020; } +body.light-mode .konaste-text { color: #444; } +body.light-mode .konaste-updated, +body.light-mode .konaste-note { color: #888; } +body.light-mode .konaste-countdown { color: #8b0000; } +body.light-mode .konaste-name, +body.light-mode .konaste-note a { color: #b00020; } + +/* ===== OpenBSD-style boot screen additions ===== */ +.boot-log { + text-align: center; +} +/* Whole boot log is centered (ASCII blocks keep their own left-aligned + internal alignment via .boot-ascii-wrap's column flex). */ +.boot-log .boot-line, +.boot-log .boot-login, +.boot-log .boot-title { + text-align: center; +} +.boot-avatar { + width: clamp(72px, 10vw, 96px); + height: clamp(72px, 10vw, 96px); + border-radius: 50%; + object-fit: cover; + border: 3px solid #f2ca30; + box-shadow: 0 0 14px rgba(242, 202, 48, 0.55); + display: block; + margin: 0 auto 10px; +} +/* Lain hero card pinned above Puffy's bubble column. The wrap reserves the + avatar's height as padding so the absolute placement never clips the ASCII + or the lines above it. */ +.boot-puffy-wrap { + position: relative; + display: inline-block; + padding-top: calc(clamp(72px, 10vw, 96px) + 10px); + /* Right padding just under half an avatar: lands the avatar centre on the + top bubble without escaping the wrap, which .boot-ascii-wrap clips + horizontally. Scales with the avatar so the alignment holds at every + clamped ASCII size. */ + padding-right: calc(clamp(72px, 10vw, 96px) * 0.385); +} +.boot-puffy-wrap .boot-avatar { + position: absolute; + top: 0; + right: 0; + margin: 0; +} +/* Puffy stacked over the OpenBSD wordmark. */ +.boot-ascii-wrap { + /* overflow-x:hidden alone forces overflow-y to auto, which put a real + scrollbar inside the boot screen. `clip` has no such side effect: the + wide ASCII is still clipped horizontally, y stays visible, nothing + scrolls. `overflow: hidden` is the fallback for pre-Chrome-90 UAs. */ + overflow: hidden; + overflow-x: clip; + overflow-y: visible; + margin-bottom: 12px; + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; +} +.boot-openbsd-wordmark { + display: inline-block; + margin: 0; + color: #f2ca30; + text-shadow: + 0 0 8px rgba(242, 202, 48, 0.65), + 0 0 18px rgba(242, 202, 48, 0.3); + font-family: var(--font-mono); + font-size: clamp(4px, min(0.85vw, 0.95vh), 10px); + line-height: 1.15; + text-align: left; + white-space: pre; +} +/* Puffy is 23 lines tall (Gentoo's g was 9) — smaller type keeps the whole + boot log on screen at 1080p and under. */ +.boot-puffy-ascii { + display: inline-block; + margin: 0; + color: #f2ca30; + text-shadow: 0 0 6px rgba(242, 202, 48, 0.4); + font-family: var(--font-mono); + font-size: clamp(4px, min(0.9vw, 1.0vh), 11px); + line-height: 1.15; + text-align: left; + white-space: pre; +} +.boot-log .boot-dim { color: #f2ca30; } +.boot-log .boot-user { + color: #f2ca30; + font-weight: 600; + text-shadow: 0 0 6px rgba(242, 202, 48, 0.55); +} +.boot-log .boot-path { color: #f2ca30; font-weight: 600; } +.boot-log .boot-line, +.boot-log .boot-svc, +.boot-log .boot-login { + color: #f2ca30; +} + +/* --------------------------------------------------------------------------- + Responsive pass: portrait / vertical monitors + denser desktop grid. + The hero used to eat ~440px of height on 1080p, pushing every card below + the fold; portrait viewports over 1100px kept the absolute side link + columns and squeezed the search box into the middle third. + --------------------------------------------------------------------------- */ + +/* Tighter quick-link stack (same type, less air between the rules). */ +.search-section h2.quick-search-title { + line-height: 1.25; + margin-bottom: 2px; +} + +.search-section h2.quick-search-title::after { + margin-top: 4px; +} + +/* Desktop hero compaction: cards start above the fold on 1080p. */ +@media (min-width: 851px) { + .search-section { + padding: 10px 0 22px; + } + + .clock-container { + margin-top: 10px; + } + + /* More cards per row: 200px tracks + tighter gutters. */ + .grid { + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 18px; + } +} + +/* Wide screens have spare horizontal room: let the search inputs use it. */ +@media (min-width: 1400px) { + .unified-search, + .card-filter-wrap, + .search-box { + max-width: 680px; + } +} + +@media (min-width: 1600px) { + .grid { + grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); + gap: 20px; + } +} + +/* Portrait / vertical monitors and tablets: drop the absolute side columns so + the search block gets the full width instead of the middle third. */ +@media (orientation: portrait) and (min-width: 601px) and (max-width: 1500px) { + .search-section { + padding-top: 12px; + } + + .top-left-links, + .top-right-links { + position: static; + width: 100%; + flex-direction: row; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: 8px 16px; + margin-bottom: 10px; + text-align: center; + z-index: auto; + } + + /* Root dropdowns open downward under the trigger instead of sideways */ + .top-left-links .ctx-root > .ctx-parent > .ctx-flyout, + .top-right-links .ctx-root > .ctx-parent > .ctx-flyout { + top: calc(100% + 4px); + left: 0; + right: auto; + max-width: 92vw; + } +} + +/* Tall-and-narrow portrait (vertical 1080p panel): keep card text legible but + pack more columns than the default 220px track would allow. */ +@media (orientation: portrait) and (min-width: 900px) { + .grid { + grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); + gap: 16px; + } +} + +/* The search form is a flex item, so it shrink-wrapped to ~384px and the + 584/680px max-width on .unified-search never applied. Let it fill the row. */ +#unified-search-form { + width: 100%; + display: flex; + justify-content: center; +} + +/* Short desktop viewports (720p, laptop panels): shave the hero further so the + first card row clears the fold. */ +@media (min-width: 851px) and (max-height: 820px) { + .search-section { + padding: 6px 0 14px; + } + + .search-section h2.quick-search-title { + margin-bottom: 0; + } + + .clock-container { + margin-top: 6px; + } +} + +/* Red/dark-red terminal cursor on startpage controls (overrides cursor:pointer). */ +@media (hover: hover) and (pointer: fine) { + a, + button, + summary, + .search-link, + .ctx-item, + .ctx-trigger, + .comfort-toggle, + .search-engine-menu li, + .card, + label[for], + input[type="checkbox"], + input[type="radio"], + select { + cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='20' viewBox='0 0 16 20'%3E%3Crect x='6' y='0' width='4' height='14' fill='%23ff1a1a'/%3E%3Crect x='4' y='14' width='8' height='4' fill='%238b0000'/%3E%3C/svg%3E") 8 10, pointer; + } +} |
