web: replace Revival Inc./RustyWolf feature screenshots with asset-free CSS windows
This commit is contained in:
parent
ef9d44f7d3
commit
7f6ac177b4
7 changed files with 520 additions and 52 deletions
|
|
@ -7,7 +7,8 @@ from the saved theme, all from the same content:
|
|||
|
||||
- **`web`** (default) — a light, frosted-glass **website**: a sticky full-height sidebar
|
||||
(section nav, light/dark switch, desktop switch) beside a column of glass cards — hero with
|
||||
a socials row, quick tiles, the Revival Inc. / RustyWolf feature panels, live services,
|
||||
a socials row, quick tiles, the Revival Inc. / RustyWolf feature panels (drawn as little
|
||||
CSS windows, not screenshots), live services,
|
||||
activity heatmap, projects and hardware (a masonry gallery), and friends. Dark by default
|
||||
with a persisted light mode, and fully responsive, so phones and desktops get the same shell.
|
||||
- **`skeuo`** — an alternate KDE Plasma / Windows-style **desktop simulation** (desktop
|
||||
|
|
@ -31,6 +32,9 @@ more.
|
|||
- **Glassmorphic web layout** — a sticky sidebar with section navigation beside a column of
|
||||
frosted-glass panels (subtle gradients + `backdrop-filter` blur), reorganised into
|
||||
hero / quick-tile / section cards.
|
||||
- **Asset-free feature windows** — the two feature panels (Revival Inc. / RustyWolf) are
|
||||
hand-built CSS mock windows instead of screenshots: a little necromancy game scene with a
|
||||
blinking "PRESS START", and a little browser window with a tab strip, toolbar and start page.
|
||||
- **Draggable desktop icons** — free placement that snaps to a measured grid, one icon per
|
||||
cell, with a follow-the-cursor drag ghost. Icons shrink on cramped viewports so they never
|
||||
collide.
|
||||
|
|
@ -72,7 +76,7 @@ more.
|
|||
- **Fonts:** the web theme uses the system sans stack; Monocraft (self-hosted in `public/`) is
|
||||
used by the desktop theme's SMP menu and the in-desktop terminal.
|
||||
- **Images:** `next/image`
|
||||
- **Version:** `2.2.8` (see `package.json`)
|
||||
- **Version:** `2.2.9` (see `package.json`)
|
||||
- **Dependencies:** `npm audit` reports **0 vulnerabilities**. `next` is kept at a patched
|
||||
release (`>=16.3.6`) for the `next/og` RCE, and `brace-expansion` is force-upgraded via
|
||||
`overrides`. `braces` (a transitive ESLint/`fast-glob` dependency) has **no upstream fix**,
|
||||
|
|
@ -194,6 +198,7 @@ src/app/
|
|||
useTheme.ts → theme state via useSyncExternalStore
|
||||
useColorScheme.ts → light/dark state via useSyncExternalStore
|
||||
WebLayout.tsx → default web shell: sidebar + glass content
|
||||
FeatureWindow.tsx → decorative CSS windows for the two feature panels
|
||||
WindowsDesktop.tsx → desktop shell: icons, windows, taskbar, Start menu
|
||||
WinWindow.tsx → one draggable/resizable window
|
||||
apps.tsx → APPS registry, window contents, in-app terminal
|
||||
|
|
|
|||
19
agents.md
19
agents.md
|
|
@ -34,7 +34,7 @@ When in doubt, prefer updating. A stale README **or** agents.md is treated as a
|
|||
## Bump the version on EVERY prompt — also do this every time
|
||||
|
||||
Another standing requirement: increment the **patch** version (`x.x.+1`, so
|
||||
`2.2.7` → `2.2.8` → …) as part of every task, even the small ones. Keep all of
|
||||
`2.2.8` → `2.2.9` → …) as part of every task, even the small ones. Keep all of
|
||||
these in sync:
|
||||
|
||||
- `package.json` → `"version"`
|
||||
|
|
@ -56,7 +56,7 @@ frosted-glass **web** site, and an alternate KDE/Plasma **desktop simulation**
|
|||
utilities in the components (Tailwind is present via PostCSS but the look is
|
||||
hand-written CSS).
|
||||
- Path alias: `@/*` → `src/*`.
|
||||
- Version: `2.2.8` (see `package.json`).
|
||||
- Version: `2.2.9` (see `package.json`).
|
||||
|
||||
### Scripts
|
||||
```bash
|
||||
|
|
@ -102,6 +102,7 @@ src/app/
|
|||
components/
|
||||
ThemeRoot.tsx → picks which shell to mount (the router of the UI)
|
||||
WebLayout.tsx → "web" theme (default): sticky sidebar + glass content
|
||||
FeatureWindow.tsx → decorative CSS "windows" for the two feature panels (game / browser)
|
||||
WindowsDesktop.tsx → "skeuo" theme, desktop: icons, windows, taskbar
|
||||
WinWindow.tsx → a single draggable/resizable window
|
||||
apps.tsx → APPS registry + the content of each app + in-app terminal
|
||||
|
|
@ -157,10 +158,16 @@ Themes: `web` = default frosted-glass website (sidebar + content, light/dark),
|
|||
Projects/hardware use a CSS multi-column
|
||||
**masonry** gallery (`.project-grid`) so short cards don't leave half-empty
|
||||
grid slots; on phones it drops to 2 columns with a shorter card image. The two feature panels (`.web-split`) stretch
|
||||
to equal height and share a fixed `16 / 10` `.web-shot` frame with
|
||||
`object-fit: cover` (+ `object-position: center top`), so source images with
|
||||
different aspect ratios still line up. The old stacked TUI panel layout and the
|
||||
`SlBackground` ASCII train were removed with it.
|
||||
to equal height and share a fixed `16 / 10` `.fw` window (the `FeatureWindow`
|
||||
component) — a decorative, **asset-free CSS mock-up** rather than a screenshot:
|
||||
Revival Inc. is a little necromancy game scene (night sky, moon, tombstones,
|
||||
blinking "PRESS START") and RustyWolf is a little browser window (tab strip,
|
||||
toolbar, address bar, start page). Both are `aria-hidden` and purely
|
||||
presentational; the real copy stays in the `.web-feature-text` paragraph below.
|
||||
The old raster feature shots (`.web-shot` + `next/image`) were removed from the
|
||||
web theme only — `revivalinc.webp` / `RustyWolfPreview.webp` are still used by
|
||||
the desktop theme and the projects gallery, so the files stay. The old stacked
|
||||
TUI panel layout and the `SlBackground` ASCII train were removed earlier.
|
||||
- **Not a terminal.** The web theme deliberately has no TUI chrome: the hero is
|
||||
a name + blurb (no `$ whoami` prompt, no blinking cursor), section eyebrows are
|
||||
plain words (not `README.MD` / `TECH_LIST.JSON` filenames), and the activity
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "dvorakmortonxd",
|
||||
"version": "2.2.8",
|
||||
"version": "2.2.9",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "dvorakmortonxd",
|
||||
"version": "2.2.8",
|
||||
"version": "2.2.9",
|
||||
"dependencies": {
|
||||
"next": "^16.3.8",
|
||||
"react": "^19.3.0",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "dvorakmortonxd",
|
||||
"version": "2.2.8",
|
||||
"version": "2.2.9",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "node scripts/dev-server.mjs",
|
||||
|
|
|
|||
103
src/app/components/FeatureWindow.tsx
Normal file
103
src/app/components/FeatureWindow.tsx
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
"use client";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
/**
|
||||
* Decorative, asset-free mock windows for the two feature panels (Revival Inc.
|
||||
* and RustyWolf). They replace the old raster screenshots so the panels read as
|
||||
* a little OS window instead of a flat image. Purely presentational — the real
|
||||
* copy lives in the `.web-feature-text` paragraph below each window.
|
||||
*/
|
||||
|
||||
function Chrome({ title, children }: { title: string; children: ReactNode }) {
|
||||
return (
|
||||
<div className="fw" aria-hidden="true">
|
||||
<div className="fw-bar">
|
||||
<span className="fw-dots">
|
||||
<span className="fw-dot" />
|
||||
<span className="fw-dot" />
|
||||
<span className="fw-dot" />
|
||||
</span>
|
||||
<span className="fw-title">{title}</span>
|
||||
</div>
|
||||
<div className="fw-body">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function GameBody() {
|
||||
return (
|
||||
<div className="fw-scene">
|
||||
<span className="fw-stars" />
|
||||
<span className="fw-moon" />
|
||||
<span className="fw-hills" />
|
||||
<div className="fw-graves">
|
||||
<span className="fw-grave" />
|
||||
<span className="fw-grave" />
|
||||
<span className="fw-grave" />
|
||||
<span className="fw-grave" />
|
||||
</div>
|
||||
<span className="fw-scan" />
|
||||
<div className="fw-hud">
|
||||
<span className="fw-hearts">♥ ♥ ♥</span>
|
||||
<span className="fw-score">SCORE 0000</span>
|
||||
</div>
|
||||
<span className="fw-start">PRESS START</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BrowserBody() {
|
||||
const tiles = ["G", "Y", "R", "D", "X"];
|
||||
return (
|
||||
<div className="fw-browser">
|
||||
<div className="fw-tabs">
|
||||
<span className="fw-tab">
|
||||
<span className="fw-tab-dot" />
|
||||
New Tab
|
||||
</span>
|
||||
<span className="fw-tab-plus">+</span>
|
||||
</div>
|
||||
<div className="fw-toolbar">
|
||||
<span className="fw-nav">‹</span>
|
||||
<span className="fw-nav">›</span>
|
||||
<span className="fw-nav fw-nav-reload">⟳</span>
|
||||
<span className="fw-url">
|
||||
<span className="fw-lock" />
|
||||
rustywolf://start
|
||||
</span>
|
||||
</div>
|
||||
<div className="fw-page">
|
||||
<div className="fw-wordmark">
|
||||
Rusty<span>Wolf</span>
|
||||
</div>
|
||||
<div className="fw-search">
|
||||
<span className="fw-search-dot" />
|
||||
<span className="fw-search-text">Search or enter address</span>
|
||||
</div>
|
||||
<div className="fw-tiles">
|
||||
{tiles.map((t) => (
|
||||
<span key={t} className="fw-tile">
|
||||
{t}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function FeatureWindow({ variant }: { variant: "game" | "browser" }) {
|
||||
if (variant === "game") {
|
||||
return (
|
||||
<Chrome title="revival-inc — mini jame gam #55">
|
||||
<GameBody />
|
||||
</Chrome>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Chrome title="RustyWolf — new tab">
|
||||
<BrowserBody />
|
||||
</Chrome>
|
||||
);
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import type { ReactNode } from "react";
|
|||
import JokerLogo from "./JokerLogo";
|
||||
import ServiceButton from "./ServiceButton";
|
||||
import ContributionHeatmap from "./ContributionHeatmap";
|
||||
import FeatureWindow from "./FeatureWindow";
|
||||
import Clock from "./Clock";
|
||||
import { useTheme } from "./useTheme";
|
||||
import { useColorScheme } from "./useColorScheme";
|
||||
|
|
@ -139,24 +140,6 @@ function Gym() {
|
|||
);
|
||||
}
|
||||
|
||||
function FeatureShot({
|
||||
src,
|
||||
alt,
|
||||
width,
|
||||
height,
|
||||
}: {
|
||||
src: string;
|
||||
alt: string;
|
||||
width: number;
|
||||
height: number;
|
||||
}) {
|
||||
return (
|
||||
<div className="web-shot">
|
||||
<Image src={src} alt={alt} width={width} height={height} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function WebLayout() {
|
||||
const { toggle } = useTheme();
|
||||
const { scheme, toggle: toggleScheme } = useColorScheme();
|
||||
|
|
@ -253,12 +236,7 @@ export default function WebLayout() {
|
|||
|
||||
<div className="web-split">
|
||||
<Section eyebrow="REVIVAL.INC" title="Revival Inc." className="web-feature">
|
||||
<FeatureShot
|
||||
src="/revivalinc.webp"
|
||||
alt="Revival Inc. thumbnail"
|
||||
width={768}
|
||||
height={576}
|
||||
/>
|
||||
<FeatureWindow variant="game" />
|
||||
<p className="web-feature-text">
|
||||
A game I made for the game jam "Mini Jame Gam #55", the theme was
|
||||
Necromancy and the special item was Milk, the game also includes a tutorial so
|
||||
|
|
@ -269,12 +247,7 @@ export default function WebLayout() {
|
|||
</Section>
|
||||
|
||||
<Section eyebrow="RustyWolf" title="Introducing RustyWolf" className="web-feature">
|
||||
<FeatureShot
|
||||
src="/RustyWolfPreview.webp"
|
||||
alt="RustyWolf Browser Preview"
|
||||
width={1280}
|
||||
height={720}
|
||||
/>
|
||||
<FeatureWindow variant="browser" />
|
||||
<p className="web-feature-text">
|
||||
A lightweight browser made in Rust with WebKitGTK. It's the successor to my
|
||||
previous project Venturesome, aiming to provide a fast and minimalist browsing
|
||||
|
|
@ -345,7 +318,7 @@ export default function WebLayout() {
|
|||
</Section>
|
||||
|
||||
<footer className="web-footer glass">
|
||||
<span className="web-footer-label">v2.2.8</span>
|
||||
<span className="web-footer-label">v2.2.9</span>
|
||||
© 2025-2026 Martin Dvořák. All rights reserved (not really I dont care, just
|
||||
follow the GPL3.0 License)
|
||||
</footer>
|
||||
|
|
|
|||
400
src/app/web.css
400
src/app/web.css
|
|
@ -422,7 +422,7 @@ html[data-theme="web"] body {
|
|||
}
|
||||
.web-split {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
|
||||
grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
|
||||
gap: 26px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
|
@ -430,22 +430,402 @@ html[data-theme="web"] body {
|
|||
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. */
|
||||
/* ============================================================
|
||||
FEATURE WINDOWS
|
||||
Asset-free decorative mock windows that replace the old
|
||||
feature screenshots. Same fixed frame as before so the two
|
||||
feature panels still line up; the "screen" is pure CSS.
|
||||
============================================================ */
|
||||
.fw {
|
||||
--fw-chrome: var(--bg-hard);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
aspect-ratio: 16 / 10;
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--glass-border);
|
||||
margin-bottom: 14px;
|
||||
background: var(--glass-lo);
|
||||
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;
|
||||
.fw-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
height: 32px;
|
||||
padding: 0 12px;
|
||||
background: var(--fw-chrome);
|
||||
border-bottom: 1px solid var(--glass-border);
|
||||
}
|
||||
.fw-dots {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
flex: none;
|
||||
}
|
||||
.fw-dot {
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.fw-dot:nth-child(1) {
|
||||
background: #ff5f57;
|
||||
}
|
||||
.fw-dot:nth-child(2) {
|
||||
background: #febc2e;
|
||||
}
|
||||
.fw-dot:nth-child(3) {
|
||||
background: #28c840;
|
||||
}
|
||||
.fw-title {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 0.68rem;
|
||||
font-weight: 600;
|
||||
color: var(--web-muted);
|
||||
}
|
||||
.fw-body {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ---------- Revival Inc.: a little necromancy game scene ---------- */
|
||||
.fw-scene {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
overflow: hidden;
|
||||
background:
|
||||
radial-gradient(120% 90% at 50% 125%, rgba(124, 58, 237, 0.55), transparent 70%),
|
||||
linear-gradient(180deg, #090e22 0%, #171a3c 46%, #2b1c48 100%);
|
||||
}
|
||||
.fw-stars {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image:
|
||||
radial-gradient(1.6px 1.6px at 12% 22%, rgba(255, 255, 255, 0.9), transparent),
|
||||
radial-gradient(1.4px 1.4px at 28% 12%, rgba(255, 255, 255, 0.7), transparent),
|
||||
radial-gradient(1.8px 1.8px at 44% 28%, rgba(255, 255, 255, 0.85), transparent),
|
||||
radial-gradient(1.3px 1.3px at 62% 14%, rgba(255, 255, 255, 0.6), transparent),
|
||||
radial-gradient(1.6px 1.6px at 78% 30%, rgba(255, 255, 255, 0.8), transparent),
|
||||
radial-gradient(1.4px 1.4px at 88% 10%, rgba(255, 255, 255, 0.7), transparent),
|
||||
radial-gradient(1.2px 1.2px at 8% 44%, rgba(255, 255, 255, 0.5), transparent),
|
||||
radial-gradient(1.5px 1.5px at 36% 40%, rgba(255, 255, 255, 0.55), transparent),
|
||||
radial-gradient(1.3px 1.3px at 70% 44%, rgba(255, 255, 255, 0.5), transparent);
|
||||
animation: fw-twinkle 4s ease-in-out infinite alternate;
|
||||
}
|
||||
@keyframes fw-twinkle {
|
||||
from {
|
||||
opacity: 0.55;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.fw-moon {
|
||||
position: absolute;
|
||||
top: 13%;
|
||||
right: 12%;
|
||||
width: 15%;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle at 35% 30%, #fff8dc, #f2e6b0 60%, #d8c778);
|
||||
box-shadow: 0 0 26px 7px rgba(255, 246, 200, 0.35);
|
||||
}
|
||||
.fw-hills {
|
||||
position: absolute;
|
||||
left: -12%;
|
||||
right: -12%;
|
||||
bottom: 0;
|
||||
height: 44%;
|
||||
background: linear-gradient(180deg, #2c2350, #120e26);
|
||||
border-radius: 50% 50% 0 0 / 42% 42% 0 0;
|
||||
}
|
||||
.fw-graves {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 7%;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
gap: 7%;
|
||||
}
|
||||
.fw-grave {
|
||||
position: relative;
|
||||
width: 12%;
|
||||
max-width: 50px;
|
||||
aspect-ratio: 5 / 7;
|
||||
background: linear-gradient(160deg, #8b93b0, #575d76);
|
||||
border-radius: 50% 50% 4px 4px / 32% 32% 4px 4px;
|
||||
box-shadow:
|
||||
inset -3px -4px 8px rgba(0, 0, 0, 0.35),
|
||||
0 6px 12px -6px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
.fw-grave::before,
|
||||
.fw-grave::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: rgba(18, 20, 38, 0.62);
|
||||
border-radius: 2px;
|
||||
}
|
||||
.fw-grave::before {
|
||||
top: 20%;
|
||||
width: 12%;
|
||||
height: 42%;
|
||||
}
|
||||
.fw-grave::after {
|
||||
top: 33%;
|
||||
width: 44%;
|
||||
height: 11%;
|
||||
}
|
||||
.fw-grave:nth-child(2) {
|
||||
transform: translateY(-7%) scale(1.06);
|
||||
}
|
||||
.fw-grave:nth-child(4) {
|
||||
transform: scale(0.88);
|
||||
}
|
||||
.fw-scan {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
rgba(0, 0, 0, 0.16) 0 1px,
|
||||
transparent 1px 3px
|
||||
);
|
||||
opacity: 0.45;
|
||||
}
|
||||
.fw-hud {
|
||||
position: absolute;
|
||||
top: 8%;
|
||||
left: 5%;
|
||||
right: 5%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-family: var(--font-custom-mono);
|
||||
font-size: 0.58rem;
|
||||
letter-spacing: 0.08em;
|
||||
color: #ffd9e0;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
.fw-hearts {
|
||||
color: #ff5d7a;
|
||||
}
|
||||
.fw-start {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 45%;
|
||||
transform: translate(-50%, -50%);
|
||||
padding: 6px 15px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.55);
|
||||
background: rgba(124, 58, 237, 0.4);
|
||||
color: #fff;
|
||||
font-family: var(--font-custom-mono);
|
||||
font-size: 0.6rem;
|
||||
letter-spacing: 0.16em;
|
||||
white-space: nowrap;
|
||||
animation: fw-blink 1.1s steps(2, start) infinite;
|
||||
}
|
||||
@keyframes fw-blink {
|
||||
50% {
|
||||
opacity: 0.35;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- RustyWolf: a little browser window ---------- */
|
||||
.fw-browser {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: linear-gradient(180deg, var(--glass-hi), var(--glass-lo));
|
||||
}
|
||||
.fw-tabs {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 6px;
|
||||
height: 32px;
|
||||
padding: 7px 10px 0;
|
||||
}
|
||||
.fw-tab {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
max-width: 62%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
padding: 6px 13px;
|
||||
border-radius: 9px 9px 0 0;
|
||||
border: 1px solid var(--btn-border, var(--glass-border));
|
||||
border-bottom: none;
|
||||
background: var(--btn-bg, var(--glass-lo));
|
||||
font-size: 0.66rem;
|
||||
color: var(--fg);
|
||||
}
|
||||
.fw-tab-dot {
|
||||
flex: none;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: var(--accent);
|
||||
}
|
||||
.fw-tab-plus {
|
||||
padding: 0 6px;
|
||||
color: var(--web-muted);
|
||||
font-size: 0.9rem;
|
||||
line-height: 1;
|
||||
}
|
||||
.fw-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
border-top: 1px solid var(--glass-border);
|
||||
border-bottom: 1px solid var(--glass-border);
|
||||
background: var(--fw-chrome);
|
||||
}
|
||||
.fw-nav {
|
||||
flex: none;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg, var(--glass-lo));
|
||||
color: var(--web-muted);
|
||||
font-size: 0.85rem;
|
||||
line-height: 1;
|
||||
}
|
||||
.fw-url {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
height: 26px;
|
||||
padding: 0 12px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--glass-border);
|
||||
background: var(--glass-lo);
|
||||
font-family: var(--font-custom-mono);
|
||||
font-size: 0.66rem;
|
||||
color: var(--web-muted);
|
||||
}
|
||||
.fw-lock {
|
||||
position: relative;
|
||||
flex: none;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border: 1.5px solid currentColor;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.fw-lock::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: -5px;
|
||||
width: 6px;
|
||||
height: 5px;
|
||||
border: 1.5px solid currentColor;
|
||||
border-bottom: none;
|
||||
border-radius: 4px 4px 0 0;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.fw-page {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
padding: 12px;
|
||||
}
|
||||
.fw-wordmark {
|
||||
font-size: clamp(1rem, 2.4vw, 1.35rem);
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.03em;
|
||||
color: var(--fg);
|
||||
}
|
||||
.fw-wordmark span {
|
||||
color: #d97757;
|
||||
}
|
||||
.fw-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 9px;
|
||||
width: min(82%, 320px);
|
||||
padding: 8px 15px;
|
||||
border-radius: 999px;
|
||||
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.72rem;
|
||||
color: var(--web-muted);
|
||||
}
|
||||
.fw-search-dot {
|
||||
position: relative;
|
||||
flex: none;
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
border: 1.6px solid var(--accent);
|
||||
border-radius: 50%;
|
||||
}
|
||||
.fw-search-dot::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
right: -4px;
|
||||
bottom: -3px;
|
||||
width: 6px;
|
||||
height: 1.6px;
|
||||
border-radius: 2px;
|
||||
background: var(--accent);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.fw-tiles {
|
||||
display: flex;
|
||||
gap: 11px;
|
||||
}
|
||||
.fw-tile {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 12px;
|
||||
color: #fff;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 800;
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.3),
|
||||
0 6px 12px -8px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
.fw-tile:nth-child(1) {
|
||||
background: linear-gradient(150deg, #6d5efc, #2f8bff);
|
||||
}
|
||||
.fw-tile:nth-child(2) {
|
||||
background: linear-gradient(150deg, #ec4899, #db2777);
|
||||
}
|
||||
.fw-tile:nth-child(3) {
|
||||
background: linear-gradient(150deg, #f59e0b, #d97757);
|
||||
}
|
||||
.fw-tile:nth-child(4) {
|
||||
background: linear-gradient(150deg, #10b981, #0d9488);
|
||||
}
|
||||
.fw-tile:nth-child(5) {
|
||||
background: linear-gradient(150deg, #8b5cf6, #6366f1);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.fw-stars,
|
||||
.fw-start {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
.web-feature-text {
|
||||
flex: 1;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue